Beispiel #1
0
class LicenseSerializer(serializers.ModelSerializer):
    is_custom = serializers.SerializerMethodField()
    name = serializers.SerializerMethodField()
    text = TranslationSerializerField()
    url = serializers.SerializerMethodField()

    class Meta:
        model = License
        fields = ('id', 'is_custom', 'name', 'text', 'url')

    def __init__(self, *args, **kwargs):
        super(LicenseSerializer, self).__init__(*args, **kwargs)
        self.db_name = TranslationSerializerField()
        self.db_name.bind('name', self)

    def get_is_custom(self, obj):
        return not bool(obj.builtin)

    def get_url(self, obj):
        return obj.url or self.get_version_license_url(obj)

    def get_version_license_url(self, obj):
        # We need the version associated with the license, because that's where
        # the license_url() method lives. The problem is, normally we would not
        # be able to do that, because there can be multiple versions for a
        # given License. However, since we're serializing through a nested
        # serializer, we cheat and use `instance.version_instance` which is
        # set by SimpleVersionSerializer.to_representation() while serializing.
        # Only get the version license url for non-builtin licenses.
        if not obj.builtin and hasattr(obj, 'version_instance'):
            return absolutify(obj.version_instance.license_url())
        return None

    def get_name(self, obj):
        # See if there is a license constant
        license_constant = obj._constant
        if not license_constant:
            # If not fall back on the name in the database.
            return self.db_name.get_attribute(obj)
        else:
            request = self.context.get('request', None)
            if request and request.method == 'GET' and 'lang' in request.GET:
                # A single lang requested so return a flat string
                return six.text_type(license_constant.name)
            else:
                # Otherwise mock the dict with the default lang.
                lang = getattr(request, 'LANG', None) or settings.LANGUAGE_CODE
                return {lang: six.text_type(license_constant.name)}

    def to_representation(self, instance):
        data = super(LicenseSerializer, self).to_representation(instance)
        request = self.context.get('request', None)
        if request and is_gate_active(
                request, 'del-version-license-is-custom'):
            data.pop('is_custom', None)
        return data
Beispiel #2
0
class PreviewSerializer(serializers.ModelSerializer):
    caption = TranslationSerializerField()
    image_url = serializers.SerializerMethodField()
    thumbnail_url = serializers.SerializerMethodField()
    image_size = serializers.ReadOnlyField(source='image_dimensions')
    thumbnail_size = serializers.ReadOnlyField(source='thumbnail_dimensions')

    class Meta:
        # Note: this serializer can also be used for VersionPreview.
        model = Preview
        fields = (
            'id',
            'caption',
            'image_size',
            'image_url',
            'thumbnail_size',
            'thumbnail_url',
        )

    def get_image_url(self, obj):
        return absolutify(obj.image_url)

    def get_thumbnail_url(self, obj):
        return absolutify(obj.thumbnail_url)
class AddonSerializer(serializers.ModelSerializer):
    authors = BaseUserSerializer(many=True, source='listed_authors')
    categories = serializers.SerializerMethodField()
    current_beta_version = SimpleVersionSerializer()
    current_version = SimpleVersionSerializer()
    description = TranslationSerializerField()
    edit_url = serializers.SerializerMethodField()
    has_eula = serializers.SerializerMethodField()
    has_privacy_policy = serializers.SerializerMethodField()
    homepage = TranslationSerializerField()
    icon_url = serializers.SerializerMethodField()
    is_source_public = serializers.BooleanField(source='view_source')
    name = TranslationSerializerField()
    previews = PreviewSerializer(many=True, source='all_previews')
    ratings = serializers.SerializerMethodField()
    review_url = serializers.SerializerMethodField()
    status = ReverseChoiceField(choices=amo.STATUS_CHOICES_API.items())
    summary = TranslationSerializerField()
    support_email = TranslationSerializerField()
    support_url = TranslationSerializerField()
    tags = serializers.SerializerMethodField()
    theme_data = serializers.SerializerMethodField()
    type = ReverseChoiceField(choices=amo.ADDON_TYPE_CHOICES_API.items())
    url = serializers.SerializerMethodField()

    class Meta:
        model = Addon
        fields = ('id', 'authors', 'average_daily_users', 'categories',
                  'current_beta_version', 'current_version', 'default_locale',
                  'description', 'edit_url', 'guid', 'has_eula',
                  'has_privacy_policy', 'homepage', 'icon_url', 'is_disabled',
                  'is_experimental', 'is_source_public', 'last_updated',
                  'name', 'previews', 'public_stats', 'ratings', 'review_url',
                  'slug', 'status', 'summary', 'support_email', 'support_url',
                  'tags', 'theme_data', 'type', 'url', 'weekly_downloads')

    def to_representation(self, obj):
        data = super(AddonSerializer, self).to_representation(obj)
        if 'theme_data' in data and data['theme_data'] is None:
            data.pop('theme_data')
        if 'homepage' in data:
            data['homepage'] = self.outgoingify(data['homepage'])
        if 'support_url' in data:
            data['support_url'] = self.outgoingify(data['support_url'])
        return data

    def outgoingify(self, data):
        if isinstance(data, basestring):
            return get_outgoing_url(data)
        elif isinstance(data, dict):
            return {
                key: get_outgoing_url(value) if value else None
                for key, value in data.items()
            }
        # Probably None... don't bother.
        return data

    def get_categories(self, obj):
        # Return a dict of lists like obj.app_categories does, but exposing
        # slugs for keys and values instead of objects.
        return {
            app.short: [cat.slug for cat in obj.app_categories[app]]
            for app in obj.app_categories.keys()
        }

    def get_has_eula(self, obj):
        return bool(getattr(obj, 'has_eula', obj.eula))

    def get_has_privacy_policy(self, obj):
        return bool(getattr(obj, 'has_privacy_policy', obj.privacy_policy))

    def get_tags(self, obj):
        if not hasattr(obj, 'tag_list'):
            attach_tags([obj])
        # attach_tags() might not have attached anything to the addon, if it
        # had no tags.
        return getattr(obj, 'tag_list', [])

    def get_url(self, obj):
        return absolutify(obj.get_url_path())

    def get_edit_url(self, obj):
        return absolutify(obj.get_dev_url())

    def get_review_url(self, obj):
        return absolutify(reverse('editors.review', args=[obj.pk]))

    def get_icon_url(self, obj):
        if self.is_broken_persona(obj):
            return absolutify(obj.get_default_icon_url(64))
        return absolutify(obj.get_icon_url(64))

    def get_ratings(self, obj):
        return {
            'average': obj.average_rating,
            'count': obj.total_reviews,
        }

    def get_theme_data(self, obj):
        theme_data = None

        if obj.type == amo.ADDON_PERSONA and not self.is_broken_persona(obj):
            theme_data = obj.persona.theme_data
        return theme_data

    def is_broken_persona(self, obj):
        """Find out if the object is a Persona and either is missing its
        Persona instance or has a broken one.

        Call this everytime something in the serializer is suceptible to call
        something on the Persona instance, explicitly or not, to avoid 500
        errors and/or SQL queries in ESAddonSerializer."""
        try:
            # Sadly, https://code.djangoproject.com/ticket/14368 prevents us
            # from setting obj.persona = None in ESAddonSerializer.fake_object
            # below. This is fixed in Django 1.9, but in the meantime we work
            # around it by creating a Persona instance with a custom '_broken'
            # attribute indicating that it should not be used.
            if obj.type == amo.ADDON_PERSONA and (
                    obj.persona is None or hasattr(obj.persona, '_broken')):
                raise Persona.DoesNotExist
        except Persona.DoesNotExist:
            # We got a DoesNotExist exception, therefore the Persona does not
            # exist or is broken.
            return True
        # Everything is fine, move on.
        return False
Beispiel #4
0
class CollectionSerializer(serializers.ModelSerializer):
    name = TranslationSerializerField()
    description = TranslationSerializerField(required=False)
    url = serializers.SerializerMethodField()
    author = BaseUserSerializer(default=serializers.CurrentUserDefault())
    public = serializers.BooleanField(source='listed', default=True)
    uuid = serializers.UUIDField(format='hex', required=False)

    class Meta:
        model = Collection
        fields = (
            'id',
            'uuid',
            'url',
            'addon_count',
            'author',
            'description',
            'modified',
            'name',
            'slug',
            'public',
            'default_locale',
        )
        writeable_fields = ('description', 'name', 'slug', 'public',
                            'default_locale')
        read_only_fields = tuple(set(fields) - set(writeable_fields))
        validators = [
            UniqueTogetherValidator(
                queryset=Collection.objects.all(),
                message=_('This custom URL is already in use by another one '
                          'of your collections.'),
                fields=('slug', 'author'),
            ),
        ]

    def get_url(self, obj):
        return obj.get_abs_url()

    def validate_name(self, value):
        # if we have a localised dict of values validate them all.
        if isinstance(value, dict):
            return {
                locale: self.validate_name(sub_value)
                for locale, sub_value in value.items()
            }
        if value.strip() == '':
            raise serializers.ValidationError(
                ugettext('Name cannot be empty.'))
        if DeniedName.blocked(value):
            raise serializers.ValidationError(
                ugettext('This name cannot be used.'))
        return value

    def validate_description(self, value):
        if has_links(clean_nl(str(value))):
            # There's some links, we don't want them.
            raise serializers.ValidationError(
                ugettext('No links are allowed.'))
        return value

    def validate_slug(self, value):
        slug_validator(
            value,
            lower=False,
            message=ugettext('The custom URL must consist of letters, '
                             'numbers, underscores or hyphens.'),
        )
        if DeniedName.blocked(value):
            raise serializers.ValidationError(
                ugettext('This custom URL cannot be used.'))

        return value
Beispiel #5
0
class AddonSerializer(serializers.ModelSerializer):
    authors = AddonDeveloperSerializer(many=True, source='listed_authors')
    categories = serializers.SerializerMethodField()
    contributions_url = serializers.URLField(source='contributions')
    current_beta_version = SimpleVersionSerializer()
    current_version = SimpleVersionSerializer()
    description = TranslationSerializerField()
    developer_comments = TranslationSerializerField()
    edit_url = serializers.SerializerMethodField()
    has_eula = serializers.SerializerMethodField()
    has_privacy_policy = serializers.SerializerMethodField()
    homepage = TranslationSerializerField()
    icon_url = serializers.SerializerMethodField()
    icons = serializers.SerializerMethodField()
    is_source_public = serializers.BooleanField(source='view_source')
    is_featured = serializers.SerializerMethodField()
    name = TranslationSerializerField()
    previews = PreviewSerializer(many=True, source='all_previews')
    ratings = serializers.SerializerMethodField()
    review_url = serializers.SerializerMethodField()
    status = ReverseChoiceField(choices=amo.STATUS_CHOICES_API.items())
    summary = TranslationSerializerField()
    support_email = TranslationSerializerField()
    support_url = TranslationSerializerField()
    tags = serializers.SerializerMethodField()
    theme_data = serializers.SerializerMethodField()
    type = ReverseChoiceField(choices=amo.ADDON_TYPE_CHOICES_API.items())
    url = serializers.SerializerMethodField()

    class Meta:
        model = Addon
        fields = (
            'id',
            'authors',
            'average_daily_users',
            'categories',
            'contributions_url',
            'current_beta_version',
            'current_version',
            'default_locale',
            'description',
            'developer_comments',
            'edit_url',
            'guid',
            'has_eula',
            'has_privacy_policy',
            'homepage',
            'icon_url',
            'icons',
            'is_disabled',
            'is_experimental',
            'is_featured',
            'is_source_public',
            'last_updated',
            'name',
            'previews',
            'public_stats',
            'ratings',
            'ratings_url',
            'requires_payment',
            'review_url',
            'slug',
            'status',
            'summary',
            'support_email',
            'support_url',
            'tags',
            'theme_data',
            'type',
            'url',
            'weekly_downloads'
        )

    def to_representation(self, obj):
        data = super(AddonSerializer, self).to_representation(obj)
        if 'theme_data' in data and data['theme_data'] is None:
            data.pop('theme_data')
        if ('request' in self.context and
                'wrap_outgoing_links' in self.context['request'].GET):
            for key in ('homepage', 'support_url', 'contributions_url'):
                if key in data:
                    data[key] = self.outgoingify(data[key])
        if obj.type == amo.ADDON_PERSONA:
            if 'weekly_downloads' in data:
                # weekly_downloads don't make sense for lightweight themes.
                data.pop('weekly_downloads')

            if ('average_daily_users' in data and
                    not self.is_broken_persona(obj)):
                # In addition, their average_daily_users number must come from
                # the popularity field of the attached Persona.
                data['average_daily_users'] = obj.persona.popularity
        return data

    def outgoingify(self, data):
        if isinstance(data, basestring):
            return get_outgoing_url(data)
        elif isinstance(data, dict):
            return {key: get_outgoing_url(value) if value else None
                    for key, value in data.items()}
        # Probably None... don't bother.
        return data

    def get_categories(self, obj):
        # Return a dict of lists like obj.app_categories does, but exposing
        # slugs for keys and values instead of objects.
        return {
            app.short: [cat.slug for cat in obj.app_categories[app]]
            for app in obj.app_categories.keys()
        }

    def get_has_eula(self, obj):
        return bool(getattr(obj, 'has_eula', obj.eula))

    def get_is_featured(self, obj):
        # obj._is_featured is set from ES, so will only be present for list
        # requests.
        if not hasattr(obj, '_is_featured'):
            # Any featuring will do.
            obj._is_featured = obj.is_featured(app=None, lang=None)
        return obj._is_featured

    def get_has_privacy_policy(self, obj):
        return bool(getattr(obj, 'has_privacy_policy', obj.privacy_policy))

    def get_tags(self, obj):
        if not hasattr(obj, 'tag_list'):
            attach_tags([obj])
        # attach_tags() might not have attached anything to the addon, if it
        # had no tags.
        return getattr(obj, 'tag_list', [])

    def get_url(self, obj):
        return absolutify(obj.get_url_path())

    def get_edit_url(self, obj):
        return absolutify(obj.get_dev_url())

    def get_review_url(self, obj):
        return absolutify(reverse('reviewers.review', args=[obj.pk]))

    def get_icon_url(self, obj):
        if self.is_broken_persona(obj):
            return absolutify(obj.get_default_icon_url(64))
        return absolutify(obj.get_icon_url(64))

    def get_icons(self, obj):
        # We're using only 32 and 64 for compatibility reasons with the
        # old search API. https://github.com/mozilla/addons-server/issues/7514
        if self.is_broken_persona(obj):
            get_icon = obj.get_default_icon_url
        else:
            get_icon = obj.get_icon_url

        return {str(size): absolutify(get_icon(size)) for size in (32, 64)}

    def get_ratings(self, obj):
        return {
            'average': obj.average_rating,
            'bayesian_average': obj.bayesian_rating,
            'count': obj.total_ratings,
            'text_count': obj.text_ratings_count,
        }

    def get_theme_data(self, obj):
        theme_data = None

        if obj.type == amo.ADDON_PERSONA and not self.is_broken_persona(obj):
            theme_data = obj.persona.theme_data
        return theme_data

    def is_broken_persona(self, obj):
        """Find out if the object is a Persona and either is missing its
        Persona instance or has a broken one.

        Call this everytime something in the serializer is suceptible to call
        something on the Persona instance, explicitly or not, to avoid 500
        errors and/or SQL queries in ESAddonSerializer."""
        try:
            # Sadly, https://code.djangoproject.com/ticket/14368 prevents us
            # from setting obj.persona = None in ESAddonSerializer.fake_object
            # below. This is fixed in Django 1.9, but in the meantime we work
            # around it by creating a Persona instance with a custom '_broken'
            # attribute indicating that it should not be used.
            if obj.type == amo.ADDON_PERSONA and (
                    obj.persona is None or hasattr(obj.persona, '_broken')):
                raise Persona.DoesNotExist
        except Persona.DoesNotExist:
            # We got a DoesNotExist exception, therefore the Persona does not
            # exist or is broken.
            return True
        # Everything is fine, move on.
        return False
Beispiel #6
0
 def __init__(self, *args, **kwargs):
     super(LicenseSerializer, self).__init__(*args, **kwargs)
     self.db_name = TranslationSerializerField()
     self.db_name.bind('name', self)
 def __init__(self, *args, **kwargs):
     super(LicenseSerializer, self).__init__(*args, **kwargs)
     self.db_name = TranslationSerializerField()
     self.db_name.bind('name', self)
class AddonSerializer(serializers.ModelSerializer):
    authors = AddonDeveloperSerializer(many=True, source='listed_authors')
    categories = serializers.SerializerMethodField()
    contributions_url = serializers.URLField(source='contributions')
    current_version = CurrentVersionSerializer()
    description = TranslationSerializerField()
    developer_comments = TranslationSerializerField()
    edit_url = serializers.SerializerMethodField()
    has_eula = serializers.SerializerMethodField()
    has_privacy_policy = serializers.SerializerMethodField()
    homepage = TranslationSerializerField()
    icon_url = serializers.SerializerMethodField()
    icons = serializers.SerializerMethodField()
    is_source_public = serializers.SerializerMethodField()
    is_featured = serializers.SerializerMethodField()
    name = TranslationSerializerField()
    previews = PreviewSerializer(many=True, source='current_previews')
    ratings = serializers.SerializerMethodField()
    ratings_url = serializers.SerializerMethodField()
    review_url = serializers.SerializerMethodField()
    status = ReverseChoiceField(choices=list(amo.STATUS_CHOICES_API.items()))
    summary = TranslationSerializerField()
    support_email = TranslationSerializerField()
    support_url = TranslationSerializerField()
    tags = serializers.SerializerMethodField()
    type = ReverseChoiceField(choices=list(amo.ADDON_TYPE_CHOICES_API.items()))
    url = serializers.SerializerMethodField()

    class Meta:
        model = Addon
        fields = ('id', 'authors', 'average_daily_users', 'categories',
                  'contributions_url', 'created', 'current_version',
                  'default_locale', 'description', 'developer_comments',
                  'edit_url', 'guid', 'has_eula', 'has_privacy_policy',
                  'homepage', 'icon_url', 'icons', 'is_disabled',
                  'is_experimental', 'is_featured', 'is_recommended',
                  'is_source_public', 'last_updated', 'name', 'previews',
                  'public_stats', 'ratings', 'ratings_url', 'requires_payment',
                  'review_url', 'slug', 'status', 'summary', 'support_email',
                  'support_url', 'tags', 'type', 'url', 'weekly_downloads')

    def to_representation(self, obj):
        data = super(AddonSerializer, self).to_representation(obj)
        request = self.context.get('request', None)

        if ('request' in self.context
                and 'wrap_outgoing_links' in self.context['request'].GET):
            for key in ('homepage', 'support_url', 'contributions_url'):
                if key in data:
                    data[key] = self.outgoingify(data[key])
        if request and is_gate_active(request, 'del-addons-created-field'):
            data.pop('created', None)
        if request and not is_gate_active(request, 'is-source-public-shim'):
            data.pop('is_source_public', None)
        return data

    def outgoingify(self, data):
        if data:
            if isinstance(data, str):
                return get_outgoing_url(data)
            elif isinstance(data, dict):
                return {
                    key: get_outgoing_url(value) if value else None
                    for key, value in data.items()
                }
        # None or empty string... don't bother.
        return data

    def get_categories(self, obj):
        return {
            app_short_name: [cat.slug for cat in categories]
            for app_short_name, categories in obj.app_categories.items()
        }

    def get_has_eula(self, obj):
        return bool(getattr(obj, 'has_eula', obj.eula))

    def get_is_featured(self, obj):
        # obj._is_featured is set from ES, so will only be present for list
        # requests.
        if not hasattr(obj, '_is_featured'):
            # Any featuring will do.
            obj._is_featured = obj.is_featured(app=None, lang=None)
        return obj._is_featured

    def get_has_privacy_policy(self, obj):
        return bool(getattr(obj, 'has_privacy_policy', obj.privacy_policy))

    def get_tags(self, obj):
        if not hasattr(obj, 'tag_list'):
            attach_tags([obj])
        # attach_tags() might not have attached anything to the addon, if it
        # had no tags.
        return getattr(obj, 'tag_list', [])

    def get_url(self, obj):
        # Use absolutify(get_detail_url()), get_absolute_url() calls
        # get_url_path() which does an extra check on current_version that is
        # annoying in subclasses which don't want to load that version.
        return absolutify(obj.get_detail_url())

    def get_edit_url(self, obj):
        return absolutify(obj.get_dev_url())

    def get_ratings_url(self, obj):
        return absolutify(obj.ratings_url)

    def get_review_url(self, obj):
        return absolutify(reverse('reviewers.review', args=[obj.pk]))

    def get_icon_url(self, obj):
        return absolutify(obj.get_icon_url(64))

    def get_icons(self, obj):
        get_icon = obj.get_icon_url

        return {
            str(size): absolutify(get_icon(size))
            for size in amo.ADDON_ICON_SIZES
        }

    def get_ratings(self, obj):
        return {
            'average': obj.average_rating,
            'bayesian_average': obj.bayesian_rating,
            'count': obj.total_ratings,
            'text_count': obj.text_ratings_count,
        }

    def get_is_source_public(self, obj):
        return False
class AddonSerializer(serializers.ModelSerializer):
    authors = AddonDeveloperSerializer(many=True, source='listed_authors')
    categories = serializers.SerializerMethodField()
    contributions_url = ContributionSerializerField(source='contributions')
    current_version = CurrentVersionSerializer()
    description = TranslationSerializerField()
    developer_comments = TranslationSerializerField()
    edit_url = serializers.SerializerMethodField()
    has_eula = serializers.SerializerMethodField()
    has_privacy_policy = serializers.SerializerMethodField()
    homepage = OutgoingTranslationField()
    icon_url = serializers.SerializerMethodField()
    icons = serializers.SerializerMethodField()
    is_source_public = serializers.SerializerMethodField()
    is_featured = serializers.SerializerMethodField()
    name = TranslationSerializerField()
    previews = PreviewSerializer(many=True, source='current_previews')
    promoted = PromotedAddonSerializer()
    ratings = serializers.SerializerMethodField()
    ratings_url = serializers.SerializerMethodField()
    review_url = serializers.SerializerMethodField()
    status = ReverseChoiceField(choices=list(amo.STATUS_CHOICES_API.items()))
    summary = TranslationSerializerField()
    support_email = TranslationSerializerField()
    support_url = OutgoingTranslationField()
    tags = serializers.SerializerMethodField()
    type = ReverseChoiceField(choices=list(amo.ADDON_TYPE_CHOICES_API.items()))
    url = serializers.SerializerMethodField()
    versions_url = serializers.SerializerMethodField()

    class Meta:
        model = Addon
        fields = (
            'id',
            'authors',
            'average_daily_users',
            'categories',
            'contributions_url',
            'created',
            'current_version',
            'default_locale',
            'description',
            'developer_comments',
            'edit_url',
            'guid',
            'has_eula',
            'has_privacy_policy',
            'homepage',
            'icon_url',
            'icons',
            'is_disabled',
            'is_experimental',
            'is_featured',
            'is_source_public',
            'last_updated',
            'name',
            'previews',
            'promoted',
            'ratings',
            'ratings_url',
            'requires_payment',
            'review_url',
            'slug',
            'status',
            'summary',
            'support_email',
            'support_url',
            'tags',
            'type',
            'url',
            'versions_url',
            'weekly_downloads',
        )

    def to_representation(self, obj):
        data = super(AddonSerializer, self).to_representation(obj)
        request = self.context.get('request', None)

        if request and is_gate_active(request, 'del-addons-created-field'):
            data.pop('created', None)
        if request and not is_gate_active(request, 'is-source-public-shim'):
            data.pop('is_source_public', None)
        if request and not is_gate_active(request, 'is-featured-addon-shim'):
            data.pop('is_featured', None)
        return data

    def get_categories(self, obj):
        return {
            app_short_name: [cat.slug for cat in categories]
            for app_short_name, categories in obj.app_categories.items()
        }

    def get_has_eula(self, obj):
        return bool(getattr(obj, 'has_eula', obj.eula))

    def get_is_featured(self, obj):
        # featured is gone, but we need to keep the API backwards compatible so
        # fake it with promoted status instead.
        return bool(obj.promoted and obj.promoted.group == RECOMMENDED)

    def get_has_privacy_policy(self, obj):
        return bool(getattr(obj, 'has_privacy_policy', obj.privacy_policy))

    def get_tags(self, obj):
        if not hasattr(obj, 'tag_list'):
            attach_tags([obj])
        # attach_tags() might not have attached anything to the addon, if it
        # had no tags.
        return getattr(obj, 'tag_list', [])

    def get_url(self, obj):
        # Use absolutify(get_detail_url()), get_absolute_url() calls
        # get_url_path() which does an extra check on current_version that is
        # annoying in subclasses which don't want to load that version.
        return absolutify(obj.get_detail_url())

    def get_edit_url(self, obj):
        return absolutify(obj.get_dev_url())

    def get_ratings_url(self, obj):
        return absolutify(obj.ratings_url)

    def get_versions_url(self, obj):
        return absolutify(obj.versions_url)

    def get_review_url(self, obj):
        return absolutify(reverse('reviewers.review', args=[obj.pk]))

    def get_icon_url(self, obj):
        return absolutify(obj.get_icon_url(64))

    def get_icons(self, obj):
        get_icon = obj.get_icon_url

        return {
            str(size): absolutify(get_icon(size))
            for size in amo.ADDON_ICON_SIZES
        }

    def get_ratings(self, obj):
        ratings = {
            'average': obj.average_rating,
            'bayesian_average': obj.bayesian_rating,
            'count': obj.total_ratings,
            'text_count': obj.text_ratings_count,
        }
        if (request := self.context.get(
                'request', None)) and (grouped := get_grouped_ratings(
                    request, obj)):
Beispiel #10
0
class AddonSerializer(serializers.ModelSerializer):
    authors = AddonDeveloperSerializer(many=True,
                                       source='listed_authors',
                                       read_only=True)
    categories = CategoriesSerializerField(source='all_categories',
                                           required=False)
    contributions_url = ContributionSerializerField(source='contributions',
                                                    read_only=True)
    current_version = CurrentVersionSerializer(read_only=True)
    description = TranslationSerializerField(required=False)
    developer_comments = TranslationSerializerField(required=False)
    edit_url = serializers.SerializerMethodField()
    has_eula = serializers.SerializerMethodField()
    has_privacy_policy = serializers.SerializerMethodField()
    homepage = OutgoingURLTranslationField(required=False)
    icon_url = serializers.SerializerMethodField()
    icons = serializers.SerializerMethodField()
    is_disabled = SplitField(
        serializers.BooleanField(source='disabled_by_user', required=False),
        serializers.BooleanField(),
    )
    is_source_public = serializers.SerializerMethodField()
    is_featured = serializers.SerializerMethodField()
    name = TranslationSerializerField(required=False, max_length=50)
    previews = PreviewSerializer(many=True,
                                 source='current_previews',
                                 read_only=True)
    promoted = PromotedAddonSerializer(read_only=True)
    ratings = serializers.SerializerMethodField()
    ratings_url = serializers.SerializerMethodField()
    review_url = serializers.SerializerMethodField()
    status = ReverseChoiceField(choices=list(amo.STATUS_CHOICES_API.items()),
                                read_only=True)
    summary = TranslationSerializerField(required=False, max_length=250)
    support_email = EmailTranslationField(required=False)
    support_url = OutgoingURLTranslationField(required=False)
    tags = serializers.ListField(
        child=LazyChoiceField(
            choices=Tag.objects.values_list('tag_text', flat=True)),
        max_length=amo.MAX_TAGS,
        source='tag_list',
        required=False,
    )
    type = ReverseChoiceField(choices=list(amo.ADDON_TYPE_CHOICES_API.items()),
                              read_only=True)
    url = serializers.SerializerMethodField()
    version = DeveloperVersionSerializer(write_only=True)
    versions_url = serializers.SerializerMethodField()

    class Meta:
        model = Addon
        fields = (
            'id',
            'authors',
            'average_daily_users',
            'categories',
            'contributions_url',
            'created',
            'current_version',
            'default_locale',
            'description',
            'developer_comments',
            'edit_url',
            'guid',
            'has_eula',
            'has_privacy_policy',
            'homepage',
            'icon_url',
            'icons',
            'is_disabled',
            'is_experimental',
            'is_featured',
            'is_source_public',
            'last_updated',
            'name',
            'previews',
            'promoted',
            'ratings',
            'ratings_url',
            'requires_payment',
            'review_url',
            'slug',
            'status',
            'summary',
            'support_email',
            'support_url',
            'tags',
            'type',
            'url',
            'version',
            'versions_url',
            'weekly_downloads',
        )
        writeable_fields = (
            'categories',
            'description',
            'developer_comments',
            'homepage',
            'is_disabled',
            'is_experimental',
            'name',
            'requires_payment',
            'slug',
            'summary',
            'support_email',
            'support_url',
            'tags',
            'version',
        )
        read_only_fields = tuple(set(fields) - set(writeable_fields))

    def __init__(self, instance=None, data=serializers.empty, **kwargs):
        if instance and isinstance(data, dict):
            data.pop('version',
                     None)  # we only support version field for create
        super().__init__(instance=instance, data=data, **kwargs)

    def to_representation(self, obj):
        data = super().to_representation(obj)
        request = self.context.get('request', None)

        if request and is_gate_active(request, 'del-addons-created-field'):
            data.pop('created', None)
        if request and not is_gate_active(request, 'is-source-public-shim'):
            data.pop('is_source_public', None)
        if request and not is_gate_active(request, 'is-featured-addon-shim'):
            data.pop('is_featured', None)
        return data

    def get_has_eula(self, obj):
        return bool(getattr(obj, 'has_eula', obj.eula))

    def get_is_featured(self, obj):
        # featured is gone, but we need to keep the API backwards compatible so
        # fake it with promoted status instead.
        return bool(obj.promoted and obj.promoted.group == RECOMMENDED)

    def get_has_privacy_policy(self, obj):
        return bool(getattr(obj, 'has_privacy_policy', obj.privacy_policy))

    def get_url(self, obj):
        # Use absolutify(get_detail_url()), get_absolute_url() calls
        # get_url_path() which does an extra check on current_version that is
        # annoying in subclasses which don't want to load that version.
        return absolutify(obj.get_detail_url())

    def get_edit_url(self, obj):
        return absolutify(obj.get_dev_url())

    def get_ratings_url(self, obj):
        return absolutify(obj.ratings_url)

    def get_versions_url(self, obj):
        return absolutify(obj.versions_url)

    def get_review_url(self, obj):
        return absolutify(reverse('reviewers.review', args=[obj.pk]))

    def get_icon_url(self, obj):
        return absolutify(obj.get_icon_url(64))

    def get_icons(self, obj):
        get_icon = obj.get_icon_url

        return {
            str(size): absolutify(get_icon(size))
            for size in amo.ADDON_ICON_SIZES
        }

    def get_ratings(self, obj):
        ratings = {
            'average': obj.average_rating,
            'bayesian_average': obj.bayesian_rating,
            'count': obj.total_ratings,
            'text_count': obj.text_ratings_count,
        }
        if (request := self.context.get(
                'request', None)) and (grouped := get_grouped_ratings(
                    request, obj)):
Beispiel #11
0
class AddonSerializer(serializers.ModelSerializer):
    authors = AddonDeveloperSerializer(many=True, source='listed_authors')
    categories = serializers.SerializerMethodField()
    contributions_url = serializers.URLField(source='contributions')
    current_version = CurrentVersionSerializer()
    description = TranslationSerializerField()
    developer_comments = TranslationSerializerField()
    edit_url = serializers.SerializerMethodField()
    has_eula = serializers.SerializerMethodField()
    has_privacy_policy = serializers.SerializerMethodField()
    homepage = TranslationSerializerField()
    icon_url = serializers.SerializerMethodField()
    icons = serializers.SerializerMethodField()
    is_source_public = serializers.BooleanField(source='view_source')
    is_featured = serializers.SerializerMethodField()
    name = TranslationSerializerField()
    previews = PreviewSerializer(many=True, source='current_previews')
    ratings = serializers.SerializerMethodField()
    ratings_url = serializers.SerializerMethodField()
    review_url = serializers.SerializerMethodField()
    status = ReverseChoiceField(choices=list(amo.STATUS_CHOICES_API.items()))
    summary = TranslationSerializerField()
    support_email = TranslationSerializerField()
    support_url = TranslationSerializerField()
    tags = serializers.SerializerMethodField()
    theme_data = serializers.SerializerMethodField()
    type = ReverseChoiceField(choices=list(amo.ADDON_TYPE_CHOICES_API.items()))
    url = serializers.SerializerMethodField()

    class Meta:
        model = Addon
        fields = (
            'id',
            'authors',
            'average_daily_users',
            'categories',
            'contributions_url',
            'created',
            'current_version',
            'default_locale',
            'description',
            'developer_comments',
            'edit_url',
            'guid',
            'has_eula',
            'has_privacy_policy',
            'homepage',
            'icon_url',
            'icons',
            'is_disabled',
            'is_experimental',
            'is_featured',
            'is_recommended',
            'is_source_public',
            'last_updated',
            'name',
            'previews',
            'public_stats',
            'ratings',
            'ratings_url',
            'requires_payment',
            'review_url',
            'slug',
            'status',
            'summary',
            'support_email',
            'support_url',
            'tags',
            'theme_data',
            'type',
            'url',
            'weekly_downloads'
        )

    def to_representation(self, obj):
        data = super(AddonSerializer, self).to_representation(obj)
        request = self.context.get('request', None)

        if 'theme_data' in data and data['theme_data'] is None:
            data.pop('theme_data')
        if ('request' in self.context and
                'wrap_outgoing_links' in self.context['request'].GET):
            for key in ('homepage', 'support_url', 'contributions_url'):
                if key in data:
                    data[key] = self.outgoingify(data[key])
        if obj.type == amo.ADDON_PERSONA:
            if 'weekly_downloads' in data:
                # weekly_downloads don't make sense for lightweight themes.
                data.pop('weekly_downloads')

            if ('average_daily_users' in data and
                    not self.is_broken_persona(obj)):
                # In addition, their average_daily_users number must come from
                # the popularity field of the attached Persona.
                data['average_daily_users'] = obj.persona.popularity
        if request and is_gate_active(request, 'del-addons-created-field'):
            data.pop('created', None)
        return data

    def outgoingify(self, data):
        if data:
            if isinstance(data, six.string_types):
                return get_outgoing_url(data)
            elif isinstance(data, dict):
                return {key: get_outgoing_url(value) if value else None
                        for key, value in data.items()}
        # None or empty string... don't bother.
        return data

    def get_categories(self, obj):
        return {
            app_short_name: [cat.slug for cat in categories]
            for app_short_name, categories in obj.app_categories.items()
        }

    def get_has_eula(self, obj):
        return bool(getattr(obj, 'has_eula', obj.eula))

    def get_is_featured(self, obj):
        # obj._is_featured is set from ES, so will only be present for list
        # requests.
        if not hasattr(obj, '_is_featured'):
            # Any featuring will do.
            obj._is_featured = obj.is_featured(app=None, lang=None)
        return obj._is_featured

    def get_has_privacy_policy(self, obj):
        return bool(getattr(obj, 'has_privacy_policy', obj.privacy_policy))

    def get_tags(self, obj):
        if not hasattr(obj, 'tag_list'):
            attach_tags([obj])
        # attach_tags() might not have attached anything to the addon, if it
        # had no tags.
        return getattr(obj, 'tag_list', [])

    def get_url(self, obj):
        # Use get_detail_url(), get_url_path() does an extra check on
        # current_version that is annoying in subclasses which don't want to
        # load that version.
        return absolutify(obj.get_detail_url())

    def get_edit_url(self, obj):
        return absolutify(obj.get_dev_url())

    def get_ratings_url(self, obj):
        return absolutify(obj.ratings_url)

    def get_review_url(self, obj):
        return absolutify(reverse('reviewers.review', args=[obj.pk]))

    def get_icon_url(self, obj):
        if self.is_broken_persona(obj):
            return absolutify(obj.get_default_icon_url(64))
        return absolutify(obj.get_icon_url(64))

    def get_icons(self, obj):
        if self.is_broken_persona(obj):
            get_icon = obj.get_default_icon_url
        else:
            get_icon = obj.get_icon_url

        return {str(size): absolutify(get_icon(size))
                for size in amo.ADDON_ICON_SIZES}

    def get_ratings(self, obj):
        return {
            'average': obj.average_rating,
            'bayesian_average': obj.bayesian_rating,
            'count': obj.total_ratings,
            'text_count': obj.text_ratings_count,
        }

    def get_theme_data(self, obj):
        theme_data = None

        if obj.type == amo.ADDON_PERSONA and not self.is_broken_persona(obj):
            theme_data = obj.persona.theme_data
        return theme_data

    def is_broken_persona(self, obj):
        """Find out if the object is a Persona and either is missing its
        Persona instance or has a broken one.

        Call this everytime something in the serializer is suceptible to call
        something on the Persona instance, explicitly or not, to avoid 500
        errors and/or SQL queries in ESAddonSerializer."""
        try:
            # Setting obj.persona = None in ESAddonSerializer.fake_object()
            # below sadly isn't enough, so we work around it in that method by
            # creating a Persona instance with a custom '_broken'
            # attribute indicating that it should not be used.
            if obj.type == amo.ADDON_PERSONA and (
                    obj.persona is None or hasattr(obj.persona, '_broken')):
                raise Persona.DoesNotExist
        except Persona.DoesNotExist:
            # We got a DoesNotExist exception, therefore the Persona does not
            # exist or is broken.
            return True
        # Everything is fine, move on.
        return False
Beispiel #12
0
class AddonSerializer(serializers.ModelSerializer):
    current_version = VersionSerializer()
    description = TranslationSerializerField()
    edit_url = serializers.SerializerMethodField()
    homepage = TranslationSerializerField()
    icon_url = serializers.SerializerMethodField()
    name = TranslationSerializerField()
    review_url = serializers.SerializerMethodField()
    status = ReverseChoiceField(choices=amo.STATUS_CHOICES_API.items())
    summary = TranslationSerializerField()
    support_email = TranslationSerializerField()
    support_url = TranslationSerializerField()
    tags = serializers.SerializerMethodField()
    theme_data = serializers.SerializerMethodField()
    type = ReverseChoiceField(choices=amo.ADDON_TYPE_CHOICES_API.items())
    url = serializers.SerializerMethodField()

    # FIXME:
    # - categories (need to sort out the id/slug mess in existing search code)
    # - previews
    # - average rating, number of downloads, hotness
    # - dictionary-specific things
    # - persona-specific things
    # - contributions-related things
    # - annoying/thankyou and related fields
    # - authors
    # - dependencies, site_specific, external_software
    # - thereason/thefuture (different endpoint ?)
    # - in collections, other add-ons by author, eula, privacy policy
    # - eula / privacy policy (different endpoint)
    # - all the reviewer/admin-specific fields (different serializer/endpoint)

    class Meta:
        model = Addon
        fields = ('id', 'current_version', 'default_locale', 'description',
                  'edit_url', 'guid', 'homepage', 'icon_url', 'is_listed',
                  'name', 'last_updated', 'public_stats', 'review_url', 'slug',
                  'status', 'summary', 'support_email', 'support_url', 'tags',
                  'theme_data', 'type', 'url')

    def to_representation(self, obj):
        data = super(AddonSerializer, self).to_representation(obj)
        if data['theme_data'] is None:
            data.pop('theme_data')
        return data

    def get_tags(self, obj):
        if not hasattr(obj, 'tag_list'):
            attach_tags([obj])
        # attach_tags() might not have attached anything to the addon, if it
        # had no tags.
        return getattr(obj, 'tag_list', [])

    def get_url(self, obj):
        return absolutify(obj.get_url_path())

    def get_edit_url(self, obj):
        return absolutify(obj.get_dev_url())

    def get_review_url(self, obj):
        return absolutify(reverse('editors.review', args=[obj.pk]))

    def get_icon_url(self, obj):
        if self.is_broken_persona(obj):
            return absolutify(obj.get_default_icon_url(64))
        return absolutify(obj.get_icon_url(64))

    def get_theme_data(self, obj):
        theme_data = None

        if obj.type == amo.ADDON_PERSONA and not self.is_broken_persona(obj):
            theme_data = obj.persona.theme_data
        return theme_data

    def is_broken_persona(self, obj):
        """Find out if the object is a Persona and either is missing its
        Persona instance or has a broken one.

        Call this everytime something in the serializer is suceptible to call
        something on the Persona instance, explicitely or not, to avoid 500
        errors and/or SQL queries in ESAddonSerializer."""
        try:
            # Sadly, https://code.djangoproject.com/ticket/14368 prevents us
            # from setting obj.persona = None in ESAddonSerializer.fake_object
            # below. This is fixed in Django 1.9, but in the meantime we work
            # around it by creating a Persona instance with a custom '_broken'
            # attribute indicating that it should not be used.
            if obj.type == amo.ADDON_PERSONA and (
                    obj.persona is None or hasattr(obj.persona, '_broken')):
                raise Persona.DoesNotExist
        except Persona.DoesNotExist:
            # We got a DoesNotExist exception, therefore the Persona does not
            # exist or is broken.
            return True
        # Everything is fine, move on.
        return False
Beispiel #13
0
class AddonSerializer(serializers.ModelSerializer):
    authors = AddonDeveloperSerializer(many=True, source='listed_authors')
    categories = serializers.SerializerMethodField()
    contributions_url = serializers.SerializerMethodField()
    current_version = CurrentVersionSerializer()
    description = TranslationSerializerField()
    developer_comments = TranslationSerializerField()
    edit_url = serializers.SerializerMethodField()
    has_eula = serializers.SerializerMethodField()
    has_privacy_policy = serializers.SerializerMethodField()
    homepage = TranslationSerializerField()
    icon_url = serializers.SerializerMethodField()
    icons = serializers.SerializerMethodField()
    is_source_public = serializers.SerializerMethodField()
    is_featured = serializers.SerializerMethodField()
    name = TranslationSerializerField()
    previews = PreviewSerializer(many=True, source='current_previews')
    promoted = PromotedAddonSerializer()
    ratings = serializers.SerializerMethodField()
    ratings_url = serializers.SerializerMethodField()
    review_url = serializers.SerializerMethodField()
    status = ReverseChoiceField(choices=list(amo.STATUS_CHOICES_API.items()))
    summary = TranslationSerializerField()
    support_email = TranslationSerializerField()
    support_url = TranslationSerializerField()
    tags = serializers.SerializerMethodField()
    type = ReverseChoiceField(choices=list(amo.ADDON_TYPE_CHOICES_API.items()))
    url = serializers.SerializerMethodField()

    class Meta:
        model = Addon
        fields = ('id', 'authors', 'average_daily_users', 'categories',
                  'contributions_url', 'created', 'current_version',
                  'default_locale', 'description', 'developer_comments',
                  'edit_url', 'guid', 'has_eula', 'has_privacy_policy',
                  'homepage', 'icon_url', 'icons', 'is_disabled',
                  'is_experimental', 'is_featured', 'is_source_public',
                  'last_updated', 'name', 'previews', 'promoted', 'ratings',
                  'ratings_url', 'requires_payment', 'review_url', 'slug',
                  'status', 'summary', 'support_email', 'support_url', 'tags',
                  'type', 'url', 'weekly_downloads')

    def to_representation(self, obj):
        data = super(AddonSerializer, self).to_representation(obj)
        request = self.context.get('request', None)

        if ('request' in self.context
                and 'wrap_outgoing_links' in self.context['request'].GET):
            for key in ('homepage', 'support_url', 'contributions_url'):
                if key in data:
                    data[key] = self.outgoingify(data[key])
        if request and is_gate_active(request, 'del-addons-created-field'):
            data.pop('created', None)
        if request and not is_gate_active(request, 'is-source-public-shim'):
            data.pop('is_source_public', None)
        if request and not is_gate_active(request, 'is-featured-addon-shim'):
            data.pop('is_featured', None)
        return data

    def outgoingify(self, data):
        if data:
            if isinstance(data, str):
                return get_outgoing_url(data)
            elif isinstance(data, dict):
                return {
                    key: get_outgoing_url(value) if value else None
                    for key, value in data.items()
                }
        # None or empty string... don't bother.
        return data

    def get_categories(self, obj):
        return {
            app_short_name: [cat.slug for cat in categories]
            for app_short_name, categories in obj.app_categories.items()
        }

    def get_has_eula(self, obj):
        return bool(getattr(obj, 'has_eula', obj.eula))

    def get_is_featured(self, obj):
        # featured is gone, but we need to keep the API backwards compatible so
        # fake it with promoted status instead.
        return bool(obj.promoted and obj.promoted.group == RECOMMENDED)

    def get_has_privacy_policy(self, obj):
        return bool(getattr(obj, 'has_privacy_policy', obj.privacy_policy))

    def get_tags(self, obj):
        if not hasattr(obj, 'tag_list'):
            attach_tags([obj])
        # attach_tags() might not have attached anything to the addon, if it
        # had no tags.
        return getattr(obj, 'tag_list', [])

    def get_url(self, obj):
        # Use absolutify(get_detail_url()), get_absolute_url() calls
        # get_url_path() which does an extra check on current_version that is
        # annoying in subclasses which don't want to load that version.
        return absolutify(obj.get_detail_url())

    def get_contributions_url(self, obj):
        if not obj.contributions:
            # don't add anything when it's not set.
            return obj.contributions
        parts = urlsplit(obj.contributions)
        query = QueryDict(parts.query, mutable=True)
        query.update(amo.CONTRIBUTE_UTM_PARAMS)
        return urlunsplit((parts.scheme, parts.netloc, parts.path,
                           query.urlencode(), parts.fragment))

    def get_edit_url(self, obj):
        return absolutify(obj.get_dev_url())

    def get_ratings_url(self, obj):
        return absolutify(obj.ratings_url)

    def get_review_url(self, obj):
        return absolutify(reverse('reviewers.review', args=[obj.pk]))

    def get_icon_url(self, obj):
        return absolutify(obj.get_icon_url(64))

    def get_icons(self, obj):
        get_icon = obj.get_icon_url

        return {
            str(size): absolutify(get_icon(size))
            for size in amo.ADDON_ICON_SIZES
        }

    def get_ratings(self, obj):
        return {
            'average': obj.average_rating,
            'bayesian_average': obj.bayesian_rating,
            'count': obj.total_ratings,
            'text_count': obj.text_ratings_count,
        }

    def get_is_source_public(self, obj):
        return False