Esempio n. 1
0
class UserAbuseReportSerializer(serializers.ModelSerializer):
    reporter = BaseUserSerializer(read_only=True)
    user = BaseUserSerializer()

    class Meta:
        model = AbuseReport
        fields = ('reporter', 'user', 'message')
Esempio n. 2
0
class ActivityLogSerializer(serializers.ModelSerializer):
    action = serializers.SerializerMethodField()
    action_label = serializers.SerializerMethodField()
    comments = serializers.SerializerMethodField()
    date = serializers.DateTimeField(source='created')
    user = BaseUserSerializer()
    highlight = serializers.SerializerMethodField()

    class Meta:
        model = ActivityLog
        fields = ('id', 'action', 'action_label', 'comments', 'user', 'date',
                  'highlight')

    def __init__(self, *args, **kwargs):
        super(ActivityLogSerializer, self).__init__(*args, **kwargs)
        self.to_highlight = kwargs.get('context', []).get('to_highlight', [])

    def get_comments(self, obj):
        return getattr(obj.log(), 'sanitize', obj.details['comments'])

    def get_action_label(self, obj):
        log = obj.log()
        return _(u'Review note') if not hasattr(log, 'short') else log.short

    def get_action(self, obj):
        return self.get_action_label(obj).replace(' ', '-').lower()

    def get_highlight(self, obj):
        return obj in self.to_highlight
Esempio n. 3
0
class ActivityLogSerializer(serializers.ModelSerializer):
    action = serializers.SerializerMethodField()
    action_label = serializers.SerializerMethodField()
    comments = serializers.SerializerMethodField()
    date = serializers.DateTimeField(source='created')
    user = BaseUserSerializer()
    highlight = serializers.SerializerMethodField()

    class Meta:
        model = ActivityLog
        fields = ('id', 'action', 'action_label', 'comments', 'user', 'date',
                  'highlight')

    def __init__(self, *args, **kwargs):
        super(ActivityLogSerializer, self).__init__(*args, **kwargs)
        self.to_highlight = kwargs.get('context', []).get('to_highlight', [])

    def get_comments(self, obj):
        # We have to do .unescape on the output of obj.to_string because the
        # 'string' it returns is supposed to be used in markup and doesn't get
        # serialized correctly unless we unescape it.
        comments = (obj.details['comments']
                    if obj.details else obj.to_string().unescape())
        return getattr(obj.log(), 'sanitize', comments)

    def get_action_label(self, obj):
        log = obj.log()
        return _(u'Review note') if not hasattr(log, 'short') else log.short

    def get_action(self, obj):
        return self.get_action_label(obj).replace(' ', '-').lower()

    def get_highlight(self, obj):
        return obj in self.to_highlight
Esempio n. 4
0
class BaseReviewSerializer(serializers.ModelSerializer):
    # title and body are TranslatedFields, but there is never more than one
    # translation for each review - it's essentially useless. Because of that
    # we use a simple CharField in the API, hiding the fact that it's a
    # TranslatedField underneath.
    addon = serializers.SerializerMethodField()
    body = serializers.CharField(allow_null=True, required=False)
    is_latest = serializers.BooleanField(read_only=True)
    previous_count = serializers.IntegerField(read_only=True)
    title = serializers.CharField(allow_null=True, required=False)
    user = BaseUserSerializer(read_only=True)

    class Meta:
        model = Review
        fields = ('id', 'addon', 'body', 'created', 'is_latest',
                  'previous_count', 'title', 'user')

    def get_addon(self, obj):
        # We only return the addon id and slug for convenience, so just return
        # them directly to avoid instantiating a full serializer. Also avoid
        # database queries if possible by re-using the addon object from the
        # view if there is one.
        addon = self.context['view'].get_addon_object() or obj.addon

        return {'id': addon.id, 'slug': addon.slug}

    def validate(self, data):
        data = super(BaseReviewSerializer, self).validate(data)
        request = self.context['request']

        data['user_responsible'] = request.user

        if not self.partial:
            # Get the add-on pk from the URL, no need to pass it as POST data
            # since the URL is always going to have it.
            data['addon'] = self.context['view'].get_addon_object()

            # Get the user from the request, don't allow clients to pick one
            # themselves.
            data['user'] = request.user

            # Also include the user ip adress.
            data['ip_address'] = request.META.get('REMOTE_ADDR', '')

        # Clean up body and automatically flag the review if an URL was in it.
        body = data.get('body', '')
        if body:
            if '<br>' in body:
                data['body'] = re.sub('<br>', '\n', body)
            # Unquote the body when searching for links, in case someone tries
            # 'example%2ecom'.
            if ReviewForm.link_pattern.search(unquote(body)) is not None:
                data['flag'] = True
                data['editorreview'] = True

        return data
 def test_user_report(self):
     user = user_factory()
     report = AbuseReport(user=user, message='bad stuff')
     serial = self.serialize(report)
     user_serial = BaseUserSerializer(user).data
     assert serial == {
         'reporter': None,
         'user': user_serial,
         'message': 'bad stuff'
     }
Esempio n. 6
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)

    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=_(u'This slug is already in use by another one '
                          u'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.iteritems()
            }
        if DeniedName.blocked(value):
            raise serializers.ValidationError(
                ugettext(u'This name cannot be used.'))
        return value

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

    def validate_slug(self, value):
        slug_validator(value,
                       lower=False,
                       message=ugettext(
                           u'Enter a valid slug consisting of letters, '
                           u'numbers, underscores or hyphens.'))
        if DeniedName.blocked(value):
            raise serializers.ValidationError(
                ugettext(u'This slug cannot be used.'))

        return value
Esempio n. 7
0
class SimpleCollectionSerializer(serializers.ModelSerializer):
    name = TranslationSerializerField()
    description = TranslationSerializerField()
    url = serializers.SerializerMethodField()
    author = BaseUserSerializer()

    class Meta:
        model = Collection
        fields = ('id', 'url', 'addon_count', 'author', 'description',
                  'modified', 'name')

    def get_url(self, obj):
        return obj.get_abs_url()
Esempio n. 8
0
class AddonAbuseReportSerializer(serializers.ModelSerializer):
    addon = serializers.SerializerMethodField()
    reporter = BaseUserSerializer(read_only=True)

    class Meta:
        model = AbuseReport
        fields = ('reporter', 'addon', 'message')

    def get_addon(self, obj):
        addon = obj.addon
        if not addon and not obj.guid:
            return None
        return {
            'guid': addon.guid if addon else obj.guid,
            'id': addon.id if addon else None,
            'slug': addon.slug if addon else None,
        }
Esempio n. 9
0
class ActivityLogSerializer(serializers.ModelSerializer):
    action = serializers.SerializerMethodField()
    action_label = serializers.SerializerMethodField()
    comments = serializers.SerializerMethodField()
    date = serializers.DateTimeField(source='created')
    user = BaseUserSerializer()

    class Meta:
        model = ActivityLog
        fields = ('id', 'action', 'action_label', 'comments', 'user', 'date')

    def get_comments(self, obj):
        return obj.details['comments']

    def get_action_label(self, obj):
        log = obj.log()
        return _(u'Review note') if not hasattr(log, 'short') else log.short

    def get_action(self, obj):
        return self.get_action_label(obj).replace(' ', '-').lower()
Esempio n. 10
0
class BaseReviewSerializer(serializers.ModelSerializer):
    # title and body are TranslatedFields, but there is never more than one
    # translation for each review - it's essentially useless. Because of that
    # we use a simple CharField in the API, hiding the fact that it's a
    # TranslatedField underneath.
    body = serializers.CharField(allow_null=True, required=False)
    title = serializers.CharField(allow_null=True, required=False)
    user = BaseUserSerializer(read_only=True)

    class Meta:
        model = Review
        fields = ('id', 'body', 'created', 'title', 'user')

    def validate(self, data):
        data = super(BaseReviewSerializer, self).validate(data)
        # Get the add-on pk from the URL, no need to pass it as POST data since
        # the URL is always going to have it.
        data['addon'] = self.context['view'].get_addon_object()

        # Get the user from the request, don't allow clients to pick one
        # themselves.
        data['user'] = self.context['request'].user

        return data
Esempio n. 11
0
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
Esempio n. 12
0
 def serialize(self):
     # Manually reload the user first to clear any cached properties.
     self.user = UserProfile.objects.get(pk=self.user.pk)
     serializer = BaseUserSerializer(context={'request': self.request})
     return serializer.to_representation(self.user)
Esempio n. 13
0
class ESAddonSerializer(BaseESSerializer, AddonSerializer):
    # Override various fields for related objects which we don't want to expose
    # data the same way than the regular serializer does (usually because we
    # some of the data is not indexed in ES).
    authors = BaseUserSerializer(many=True, source='listed_authors')
    current_beta_version = SimpleESVersionSerializer()
    current_version = SimpleESVersionSerializer()
    previews = ESPreviewSerializer(many=True, source='all_previews')

    datetime_fields = ('created', 'last_updated', 'modified')
    translated_fields = ('name', 'description', 'developer_comments',
                         'homepage', 'summary', 'support_email', 'support_url')

    def fake_file_object(self, obj, data):
        file_ = File(id=data['id'],
                     created=self.handle_date(data['created']),
                     hash=data['hash'],
                     filename=data['filename'],
                     is_webextension=data.get('is_webextension'),
                     is_restart_required=data.get('is_restart_required',
                                                  False),
                     platform=data['platform'],
                     size=data['size'],
                     status=data['status'],
                     strict_compatibility=data.get('strict_compatibility',
                                                   False),
                     version=obj)
        file_.webext_permissions_list = data.get('webext_permissions_list', [])
        return file_

    def fake_version_object(self, obj, data, channel):
        if data:
            version = Version(addon=obj,
                              id=data['id'],
                              reviewed=self.handle_date(data['reviewed']),
                              version=data['version'],
                              channel=channel)
            version.all_files = [
                self.fake_file_object(version, file_data)
                for file_data in data.get('files', [])
            ]

            # In ES we store integers for the appversion info, we need to
            # convert it back to strings.
            compatible_apps = {}
            for app_id, compat_dict in data.get('compatible_apps', {}).items():
                app_name = APPS_ALL[int(app_id)]
                compatible_apps[app_name] = ApplicationsVersions(
                    min=AppVersion(version=compat_dict.get('min_human', '')),
                    max=AppVersion(version=compat_dict.get('max_human', '')))
            version.compatible_apps = compatible_apps
        else:
            version = None
        return version

    def fake_object(self, data):
        """Create a fake instance of Addon and related models from ES data."""
        obj = Addon(id=data['id'], slug=data['slug'])

        # Attach base attributes that have the same name/format in ES and in
        # the model.
        self._attach_fields(
            obj, data,
            ('average_daily_users', 'bayesian_rating', 'created',
             'default_locale', 'guid', 'has_eula', 'has_privacy_policy',
             'hotness', 'icon_type', 'is_experimental', 'last_updated',
             'modified', 'public_stats', 'requires_payment', 'slug', 'status',
             'type', 'view_source', 'weekly_downloads'))

        # Attach attributes that do not have the same name/format in ES.
        obj.tag_list = data.get('tags', [])
        obj.all_categories = [
            CATEGORIES_BY_ID[cat_id] for cat_id in data.get('category', [])
        ]

        # Not entirely accurate, but enough in the context of the search API.
        obj.disabled_by_user = data.get('is_disabled', False)

        # Attach translations (they require special treatment).
        self._attach_translations(obj, data, self.translated_fields)

        # Attach related models (also faking them). `current_version` is a
        # property we can't write to, so we use the underlying field which
        # begins with an underscore. `current_beta_version` and
        # `latest_unlisted_version` are writeable cached_property so we can
        # directly write to them.
        obj.current_beta_version = self.fake_version_object(
            obj, data.get('current_beta_version'), amo.RELEASE_CHANNEL_LISTED)
        obj._current_version = self.fake_version_object(
            obj, data.get('current_version'), amo.RELEASE_CHANNEL_LISTED)
        obj.latest_unlisted_version = self.fake_version_object(
            obj, data.get('latest_unlisted_version'),
            amo.RELEASE_CHANNEL_UNLISTED)

        data_authors = data.get('listed_authors', [])
        obj.listed_authors = [
            UserProfile(id=data_author['id'],
                        display_name=data_author['name'],
                        username=data_author['username'],
                        is_public=data_author.get('is_public', False))
            for data_author in data_authors
        ]

        # We set obj.all_previews to the raw preview data because
        # ESPreviewSerializer will handle creating the fake Preview object
        # for us when its to_representation() method is called.
        obj.all_previews = data.get('previews', [])

        ratings = data.get('ratings', {})
        obj.average_rating = ratings.get('average')
        obj.total_reviews = ratings.get('count')
        obj.text_reviews_count = ratings.get('text_count')

        obj._is_featured = data.get('is_featured', False)

        if data['type'] == amo.ADDON_PERSONA:
            persona_data = data.get('persona')
            if persona_data:
                obj.persona = Persona(
                    addon=obj,
                    accentcolor=persona_data['accentcolor'],
                    display_username=persona_data['author'],
                    header=persona_data['header'],
                    footer=persona_data['footer'],
                    # "New" Persona do not have a persona_id, it's a relic from
                    # old ones.
                    persona_id=0 if persona_data['is_new'] else 42,
                    textcolor=persona_data['textcolor'])
            else:
                # Sadly, https://code.djangoproject.com/ticket/14368 prevents
                # us from setting obj.persona = None. This is fixed in
                # Django 1.9, but in the meantime, work around it by creating
                # a Persona instance with a custom attribute indicating that
                # it should not be used.
                obj.persona = Persona()
                obj.persona._broken = True

        return obj
Esempio n. 14
0
class ESAddonSerializerWithUnlistedData(ESBaseAddonSerializer,
                                        AddonSerializerWithUnlistedData):
    # Override authors because we don't want picture_url in serializer.
    authors = BaseUserSerializer(many=True, source='listed_authors')
    previews = ESPreviewSerializer(many=True, source='all_previews')
Esempio n. 15
0
class BaseReviewSerializer(serializers.ModelSerializer):
    # title and body are TranslatedFields, but there is never more than one
    # translation for each review - it's essentially useless. Because of that
    # we use a simple CharField in the API, hiding the fact that it's a
    # TranslatedField underneath.
    addon = serializers.SerializerMethodField()
    body = serializers.CharField(allow_null=True, required=False)
    is_latest = serializers.BooleanField(read_only=True)
    previous_count = serializers.IntegerField(read_only=True)
    title = serializers.CharField(allow_null=True, required=False)
    user = BaseUserSerializer(read_only=True)

    class Meta:
        model = Review
        fields = ('id', 'addon', 'body', 'created', 'is_latest',
                  'previous_count', 'title', 'user')

    def get_addon(self, obj):
        # We only return the addon id and slug for convenience, so just return
        # them directly to avoid instantiating a full serializer. Also avoid
        # database queries if possible by re-using the addon object from the
        # view if there is one.
        addon = self.context['view'].get_addon_object() or obj.addon

        return {'id': addon.id, 'slug': addon.slug}

    def validate(self, data):
        data = super(BaseReviewSerializer, self).validate(data)
        request = self.context['request']

        data['user_responsible'] = request.user

        # There are a few fields that need to be set at creation time and never
        # modified afterwards:
        if not self.partial:
            # Because we want to avoid extra queries, addon is a
            # SerializerMethodField, which means it needs to be validated
            # manually. Fortunately the view does most of the work for us.
            data['addon'] = self.context['view'].get_addon_object()
            if data['addon'] is None:
                raise serializers.ValidationError(
                    {'addon': ugettext('This field is required.')})

            # Get the user from the request, don't allow clients to pick one
            # themselves.
            data['user'] = request.user

            # Also include the user ip adress.
            data['ip_address'] = request.META.get('REMOTE_ADDR', '')
        else:
            # When editing, you can't change the add-on.
            if self.context['request'].data.get('addon'):
                raise serializers.ValidationError({
                    'addon':
                    ugettext(u'You can\'t change the add-on of a review once'
                             u' it has been created.')
                })

        # Clean up body and automatically flag the review if an URL was in it.
        body = data.get('body', '')
        if body:
            if '<br>' in body:
                data['body'] = re.sub('<br>', '\n', body)
            # Unquote the body when searching for links, in case someone tries
            # 'example%2ecom'.
            if ReviewForm.link_pattern.search(unquote(body)) is not None:
                data['flag'] = True
                data['editorreview'] = True

        return data