Exemple #1
0
 def test_extract(self):
     obj, doc = self._get_doc()
     eq_(doc['id'], obj.id)
     eq_(doc['guid'], obj.guid)
     eq_(doc['app_slug'], obj.app_slug)
     eq_(doc['category'], [])
     eq_(doc['default_locale'], obj.default_locale)
     eq_(doc['description'], list(
         set(s for _, s in obj.translations[obj.description_id])))
     eq_(doc['description_translations'],
         [{'lang': to_language(l), 'string': s}
          for l, s in obj.translations[obj.description_id]])
     eq_(doc['device'], [])
     eq_(doc['name'], list(
         set(s for _, s in obj.translations[obj.name_id])))
     eq_(doc['name_translations'],
         [{'lang': to_language(l), 'string': s}
          for l, s in obj.translations[obj.name_id]])
     eq_(doc['promo_img_hash'], obj.promo_img_hash)
     eq_(doc['status'], obj.status)
     eq_(doc['trending'], 0)
     eq_(doc['is_escalated'], False)
     eq_(doc['latest_version']['status'], mkt.STATUS_PUBLIC)
     eq_(doc['latest_version']['has_editor_comment'], False)
     eq_(doc['latest_version']['has_info_request'], False)
Exemple #2
0
 def test_extract(self):
     obj, doc = self._get_doc()
     eq_(doc['id'], obj.id)
     eq_(doc['guid'], obj.guid)
     eq_(doc['app_slug'], obj.app_slug)
     eq_(doc['category'], [])
     eq_(doc['default_locale'], obj.default_locale)
     eq_(doc['description'],
         list(set(s for _, s in obj.translations[obj.description_id])))
     eq_(doc['description_translations'], [{
         'lang': to_language(l),
         'string': s
     } for l, s in obj.translations[obj.description_id]])
     eq_(doc['device'], [])
     eq_(doc['name'],
         list(set(s for _, s in obj.translations[obj.name_id])))
     eq_(doc['name_translations'], [{
         'lang': to_language(l),
         'string': s
     } for l, s in obj.translations[obj.name_id]])
     eq_(doc['promo_img_hash'], obj.promo_img_hash)
     eq_(doc['status'], obj.status)
     eq_(doc['trending'], 0)
     eq_(doc['is_escalated'], False)
     eq_(doc['latest_version']['status'], mkt.STATUS_PUBLIC)
     eq_(doc['latest_version']['has_editor_comment'], False)
     eq_(doc['latest_version']['has_info_request'], False)
def get_mail_context(note):
    """
    Get context data for comm emails, specifically for review action emails.
    """
    app = note.thread.addon

    if app.name and app.name.locale != app.default_locale:
        # We need to display the name in some language that is relevant to the
        # recipient(s) instead of using the reviewer's. addon.default_locale
        # should work.
        lang = to_language(app.default_locale)
        with translation.override(lang):
            app = Webapp.with_deleted.get(id=app.id)
    elif not app.name:
        # For deleted apps.
        app.name = app.app_slug

    return {
        'mkt':
        mkt,
        'app':
        app,
        'comm':
        comm,
        'note':
        note,
        'review_url':
        absolutify(reverse('reviewers.apps.review', args=[app.app_slug])),
        'settings':
        settings
    }
Exemple #4
0
    def parse(self):
        """Parse archive and return extension data as expected by the models.

        May raise forms.ValidationError."""
        raw_data = self.manifest_contents
        data = {}

        required_fields = ('name', 'version')
        for field in required_fields:
            if not raw_data.get(field):
                raise forms.ValidationError(
                    _(u'The "%s" field is missing or empty in the'
                      u' add-on manifest.' % field))
            data[field] = raw_data[field]

        extra_fields = ('description',)
        for field in extra_fields:
            if field in raw_data:
                data[field] = raw_data[field]

        default_locale = raw_data.get('default_locale')
        if default_locale:
            # We actually need language (e.g. "en-US") for translations, not
            # locale (e.g. "en_US"). The extension contains locales though, not
            # languages, so transform the field in the manifest before adding
            # it to the data we'll pass to the model.
            data['default_language'] = to_language(default_locale)

        data['manifest'] = self.manifest_contents
        return data
Exemple #5
0
    def extract_field_translations(cls, obj, field, db_field=None,
                                   include_field_for_search=False):
        """
        Returns a dict with:
        - A special list (with _translations key suffix) mapping languages to
          translations, to be deserialized by ESTranslationSerializerField.
        - A list with all translations, intended to be analyzed and used for
          searching (only included if include_field_for_search is True,
          defaults to False).
        """
        if db_field is None:
            db_field = '%s_id' % field

        extend_with_me = {
            '%s_translations' % field: [
                {'lang': to_language(lang), 'string': string}
                for lang, string in obj.translations[getattr(obj, db_field)]
                if string
            ]
        }
        if include_field_for_search:
            extend_with_me[field] = list(
                set(s for _, s in obj.translations[getattr(obj, db_field)])
            )
        return extend_with_me
Exemple #6
0
    def extract_field_translations(cls,
                                   obj,
                                   field,
                                   db_field=None,
                                   include_field_for_search=False):
        """
        Returns a dict with:
        - A special list (with _translations key suffix) mapping languages to
          translations, to be deserialized by ESTranslationSerializerField.
        - A list with all translations, intended to be analyzed and used for
          searching (only included if include_field_for_search is True,
          defaults to False).
        """
        if db_field is None:
            db_field = '%s_id' % field

        extend_with_me = {
            '%s_translations' % field: [{
                'lang': to_language(lang),
                'string': string
            } for lang, string in obj.translations[getattr(obj, db_field)]
                                        if string]
        }
        if include_field_for_search:
            extend_with_me[field] = list(
                set(s for _, s in obj.translations[getattr(obj, db_field)]))
        return extend_with_me
Exemple #7
0
 def get_context_data(self):
     # We need to display the name in some language that is relevant to the
     # recipient(s) instead of using the reviewer's. addon.default_locale
     # should work.
     if self.addon.name.locale != self.addon.default_locale:
         lang = to_language(self.addon.default_locale)
         with translation.override(lang):
             app = Webapp.objects.get(id=self.addon.id)
     else:
         app = self.addon
     return {
         'name':
         app.name,
         'reviewer':
         self.request.user.name,
         'detail_url':
         absolutify(app.get_url_path()),
         'review_url':
         absolutify(reverse('reviewers.apps.review', args=[app.app_slug])),
         'status_url':
         absolutify(app.get_dev_url('versions')),
         'comments':
         self.data['comments'],
         'MKT_SUPPORT_EMAIL':
         settings.MKT_SUPPORT_EMAIL,
         'SITE_URL':
         settings.SITE_URL
     }
Exemple #8
0
def get_mail_context(note):
    """
    Get context data for comm emails, specifically for review action emails.
    """
    app = note.thread.webapp

    if app.name and app.name.locale != app.default_locale:
        # We need to display the name in some language that is relevant to the
        # recipient(s) instead of using the reviewer's. webapp.default_locale
        # should work.
        lang = to_language(app.default_locale)
        with translation.override(lang):
            app = Webapp.with_deleted.get(id=app.id)
    elif not app.name:
        # For deleted apps.
        app.name = app.app_slug

    return {
        'mkt': mkt,
        'app': app,
        'comm': comm,
        'note': note,
        'review_url': absolutify(reverse('reviewers.apps.review',
                                 args=[app.app_slug])),
        'settings': settings
    }
Exemple #9
0
def get_mail_context(note, user_id):
    """
    Get context data for comm emails, specifically for review action emails.
    """
    obj = note.thread.obj

    # grep: comm-content-type.
    if obj.name and obj.__class__ == Webapp:
        # We need to display the name in some language that is relevant to the
        # recipient(s) instead of using the reviewer's. addon.default_locale
        # should work.
        lang = to_language(obj.default_locale)
        with translation.override(lang):
            obj = Webapp.with_deleted.get(id=obj.id)
    elif not obj.name:
        # For deleted objects.
        obj.name = obj.app_slug if hasattr(obj, 'app_slug') else obj.slug

    if user_id:
        UserProfile.objects.get(id=user_id)

    # grep: comm-content-type.
    manage_url = ''
    obj_type = ''
    thread_url = ''
    if obj.__class__ == Webapp:
        manage_url = absolutify(obj.get_dev_url('versions'))
        obj_type = 'app'
        thread_url = absolutify(
            reverse('commonplace.commbadge.show_thread',
                    args=[note.thread.id]))
    elif obj.__class__ == Extension:
        manage_url = absolutify(
            reverse('commonplace.content.addon_manage', args=[obj.slug]))
        # Not "Firefox OS add-on" for a/an consistency with "app".
        obj_type = 'add-on'
        if user_id:
            user = UserProfile.objects.get(id=user_id)
            if acl.action_allowed_user(user, 'ContentTools', 'AddonReview'):
                thread_url = absolutify(
                    reverse('commonplace.content.addon_review',
                            args=[obj.slug]))
            else:
                thread_url = manage_url

    return {
        'mkt': mkt,
        'comm': comm,
        'is_app': obj.__class__ == Webapp,
        'is_extension': obj.__class__ == Extension,
        'manage_url': manage_url,
        'note': note,
        'obj': obj,
        'obj_type': obj_type,
        'settings': settings,
        'thread_url': thread_url
    }
Exemple #10
0
def get_mail_context(note, user_id):
    """
    Get context data for comm emails, specifically for review action emails.
    """
    obj = note.thread.obj

    # grep: comm-content-type.
    if obj.name and obj.__class__ == Webapp:
        # We need to display the name in some language that is relevant to the
        # recipient(s) instead of using the reviewer's. addon.default_locale
        # should work.
        lang = to_language(obj.default_locale)
        with translation.override(lang):
            obj = Webapp.with_deleted.get(id=obj.id)
    elif not obj.name:
        # For deleted objects.
        obj.name = obj.app_slug if hasattr(obj, 'app_slug') else obj.slug

    if user_id:
        UserProfile.objects.get(id=user_id)

    # grep: comm-content-type.
    manage_url = ''
    obj_type = ''
    thread_url = ''
    if obj.__class__ == Webapp:
        manage_url = absolutify(obj.get_dev_url('versions'))
        obj_type = 'app'
        thread_url = absolutify(reverse('commonplace.commbadge.show_thread',
                                        args=[note.thread.id]))
    elif obj.__class__ == Extension:
        manage_url = absolutify(reverse('commonplace.content.addon_manage',
                                        args=[obj.slug]))
        # Not "Firefox OS add-on" for a/an consistency with "app".
        obj_type = 'add-on'
        if user_id:
            user = UserProfile.objects.get(id=user_id)
            if acl.action_allowed_user(user, 'ContentTools', 'AddonReview'):
                thread_url = absolutify(
                    reverse('commonplace.content.addon_review',
                            args=[obj.slug]))
            else:
                thread_url = manage_url

    return {
        'mkt': mkt,
        'comm': comm,
        'is_app': obj.__class__ == Webapp,
        'is_extension': obj.__class__ == Extension,
        'manage_url': manage_url,
        'note': note,
        'obj': obj,
        'obj_type': obj_type,
        'settings': settings,
        'thread_url': thread_url
    }
Exemple #11
0
    def localized_properties(self):
        props = {}

        for attr in ('name', 'description'):
            tr_object = getattr(self.webapp, attr)
            for tr in Translation.objects.filter(id=tr_object.id):
                language = to_language(tr.locale)
                props.setdefault(language, {})
                props[language][attr] = tr.localized_string

        return props
Exemple #12
0
    def localized_properties(self):
        props = {}

        for attr in ('name', 'description'):
            tr_object = getattr(self.webapp, attr)
            for tr in Translation.objects.filter(id=tr_object.id):
                language = to_language(tr.locale)
                props.setdefault(language, {})
                props[language][attr] = tr.localized_string

        return props
Exemple #13
0
 def test_extract(self):
     obj, doc = self._get_doc()
     eq_(doc["id"], obj.id)
     eq_(doc["guid"], obj.guid)
     eq_(doc["app_slug"], obj.app_slug)
     eq_(doc["category"], [])
     eq_(doc["default_locale"], obj.default_locale)
     eq_(doc["description"], list(set(s for _, s in obj.translations[obj.description_id])))
     eq_(
         doc["description_translations"],
         [{"lang": to_language(l), "string": s} for l, s in obj.translations[obj.description_id]],
     )
     eq_(doc["device"], [])
     eq_(doc["name"], list(set(s for _, s in obj.translations[obj.name_id])))
     eq_(doc["name_translations"], [{"lang": to_language(l), "string": s} for l, s in obj.translations[obj.name_id]])
     eq_(doc["status"], obj.status)
     eq_(doc["trending"], 0)
     eq_(doc["is_escalated"], False)
     eq_(doc["latest_version"]["status"], mkt.STATUS_PUBLIC)
     eq_(doc["latest_version"]["has_editor_comment"], False)
     eq_(doc["latest_version"]["has_info_request"], False)
Exemple #14
0
 def extract_manifest_fields(cls, manifest_data, fields=None):
     """Extract the specified `fields` from `manifest_data`, applying
     transformations if necessary. If `fields` is absent, then use
     `cls.manifest_is_source_of_truth_fields`."""
     if fields is None:
         fields = cls.manifest_is_source_of_truth_fields
     data = {k: manifest_data[k] for k in fields if k in manifest_data}
     if 'default_language' in fields:
         # Manifest contains locales (e.g. "en_US"), not languages
         # (e.g. "en-US"). The field is also called differently as a result
         # (default_locale vs default_language), so we need to transform
         # both the key and the value before adding it to data.
         default_locale = manifest_data.get('default_locale')
         if default_locale:
             data['default_language'] = to_language(default_locale)
     return data
 def set_default_locale(self, instance, row):
     lang = to_language(self.clean_string(row['Language of Meta Data']))
     if not lang or lang == 'english':
         # Exception because 'en-US' is set as 'English (US)'.
         lang = 'en-US'
     elif lang == 'chinese':
         # Consider 'chinese' without more information as simplified
         # chinese, zh-CN.
         lang = 'zh-CN'
     elif lang == 'portuguese':
         # We don't support pt-PT in Marketplace, use pt-BR.
         lang = 'pt-BR'
     if lang not in self.languages:
         lang = self.reversed_languages.get(lang)
         if lang is None:
             raise ParsingError(
                 u'Website %s has unknown language set for its metadata: %s'
                 % (row['Unique Moz ID'], row['Language of Meta Data']))
     instance.default_locale = lang
 def set_default_locale(self, instance, row):
     lang = to_language(self.clean_string(row['Language of Meta Data']))
     if not lang or lang == 'english':
         # Exception because 'en-US' is set as 'English (US)'.
         lang = 'en-US'
     elif lang == 'chinese':
         # Consider 'chinese' without more information as simplified
         # chinese, zh-CN.
         lang = 'zh-CN'
     elif lang == 'portuguese':
         # We don't support pt-PT in Marketplace, use pt-BR.
         lang = 'pt-BR'
     if lang not in self.languages:
         lang = self.reversed_languages.get(lang)
         if lang is None:
             raise ParsingError(
                 u'Website %s has unknown language set for its metadata: %s'
                 % (row['Unique Moz ID'], row['Language of Meta Data']))
     instance.default_locale = lang
Exemple #17
0
def get_mail_context(note):
    """
    Get context data for comm emails, specifically for review action emails.
    """
    obj = note.thread.obj

    # grep: comm-content-type.
    if obj.name and obj.__class__ == Webapp:
        # We need to display the name in some language that is relevant to the
        # recipient(s) instead of using the reviewer's. addon.default_locale
        # should work.
        lang = to_language(obj.default_locale)
        with translation.override(lang):
            obj = Webapp.with_deleted.get(id=obj.id)
    elif not obj.name:
        # For deleted objects.
        obj.name = obj.app_slug if hasattr(obj, 'app_slug') else obj.slug

    # grep: comm-content-type.
    manage_url = ''
    review_url = ''
    if obj.__class__ == Webapp:
        manage_url = absolutify(obj.get_dev_url('versions'))
        review_url = absolutify(reverse('reviewers.apps.review',
                                        args=[obj.app_slug]))
    elif obj.__class__ == Extension:
        manage_url = absolutify(reverse('commonplace.content.addon_manage',
                                        args=[obj.slug]))
        review_url = absolutify(reverse('commonplace.content.addon_review',
                                        args=[obj.slug]))

    return {
        'mkt': mkt,
        'comm': comm,
        'is_app': obj.__class__ == Webapp,
        'is_extension': obj.__class__ == Extension,
        'manage_url': manage_url,
        'note': note,
        'obj': obj,
        'review_url': review_url,
        'settings': settings
    }
Exemple #18
0
 def get_context_data(self):
     # We need to display the name in some language that is relevant to the
     # recipient(s) instead of using the reviewer's. addon.default_locale
     # should work.
     if self.addon.name.locale != self.addon.default_locale:
         lang = to_language(self.addon.default_locale)
         with translation.override(lang):
             app = Webapp.objects.get(id=self.addon.id)
     else:
         app = self.addon
     return {'name': app.name,
             'reviewer': self.request.user.name,
             'detail_url': absolutify(
                 app.get_url_path()),
             'review_url': absolutify(reverse('reviewers.apps.review',
                                              args=[app.app_slug])),
             'status_url': absolutify(app.get_dev_url('versions')),
             'comments': self.data['comments'],
             'MKT_SUPPORT_EMAIL': settings.MKT_SUPPORT_EMAIL,
             'SITE_URL': settings.SITE_URL}
Exemple #19
0
    def extract_manifest_fields(cls, manifest_data, fields=None):
        """Extract the specified `fields` from `manifest_data`, applying
        transformations if necessary. If `fields` is absent, then use
        `cls.manifest_is_source_of_truth_fields`."""
        if fields is None:
            fields = cls.manifest_is_source_of_truth_fields
        data = {k: manifest_data[k] for k in fields if k in manifest_data}

        # Determine default language to use for translations.
        # Web Extensions Manifest contains locales (e.g. "en_US"), not
        # languages (e.g. "en-US"). The field is also called differently as a
        # result (default_locale vs default_language), so we need to transform
        # both the key and the value before adding it to data. A default value
        # needs to be set to correctly generate the translated fields below.
        default_language = to_language(
            manifest_data.get('default_locale',
                              cls._meta.get_field('default_language').default))
        if 'default_language' in fields:
            data['default_language'] = default_language

        # Be nice and strip leading / trailing whitespace chars from
        # strings.
        for key, value in data.items():
            if isinstance(value, basestring):
                data[key] = value.strip()

        # Translated fields should not be extracted as simple strings,
        # otherwise we end up setting a locale on the translation that is
        # dependent on the locale of the thread. Use dicts instead, always
        # setting default_language as the language for now (since we don't
        # support i18n in web extensions yet).
        for field in cls._meta.translated_fields:
            field_name = field.name
            if field_name in data:
                data[field_name] = {
                    default_language: manifest_data[field_name]
                }

        return data
Exemple #20
0
    def extract_manifest_fields(cls, manifest_data, fields=None):
        """Extract the specified `fields` from `manifest_data`, applying
        transformations if necessary. If `fields` is absent, then use
        `cls.manifest_is_source_of_truth_fields`."""
        if fields is None:
            fields = cls.manifest_is_source_of_truth_fields
        data = {k: manifest_data[k] for k in fields if k in manifest_data}

        # Determine default language to use for translations.
        # Web Extensions Manifest contains locales (e.g. "en_US"), not
        # languages (e.g. "en-US"). The field is also called differently as a
        # result (default_locale vs default_language), so we need to transform
        # both the key and the value before adding it to data. A default value
        # needs to be set to correctly generate the translated fields below.
        default_language = to_language(manifest_data.get(
            'default_locale', cls._meta.get_field('default_language').default))
        if 'default_language' in fields:
            data['default_language'] = default_language

        # Be nice and strip leading / trailing whitespace chars from
        # strings.
        for key, value in data.items():
            if isinstance(value, basestring):
                data[key] = value.strip()

        # Translated fields should not be extracted as simple strings,
        # otherwise we end up setting a locale on the translation that is
        # dependent on the locale of the thread. Use dicts instead, always
        # setting default_language as the language for now (since we don't
        # support i18n in web extensions yet).
        for field in cls._meta.translated_fields:
            field_name = field.name
            if field_name in data:
                data[field_name] = {
                    default_language: manifest_data[field_name]
                }

        return data
Exemple #21
0
    def from_upload(cls, upload, user=None, instance=None):
        """Handle creating/editing the Extension instance and saving it to db,
        as well as file operations, from a FileUpload instance. Can throw
        a ValidationError or SigningError, so should always be called within a
        try/except."""
        if instance is not None:
            # Not implemented yet. Need to deal with versions correctly, we
            # don't know yet if we want to keep older versions around or not,
            # how status changes etc.
            raise NotImplementedError

        parser = ExtensionParser(upload, instance=instance)
        data = parser.parse()
        fields = ('version', 'name', 'default_language')
        default_locale = data.get('default_locale')

        if default_locale:
            # We actually need language (en-US) for translations, not locale
            # (en_US). The extension contains locales though, so transform the
            # field in the manifest before storing in db.
            data['default_language'] = to_language(default_locale)

        # Filter out parsing results to only keep fields we store in db.
        data = dict((k, v) for k, v in data.items() if k in fields)

        # Build a new instance.
        instance = cls(**data)
        instance.manifest = parser.manifest_contents
        instance.save()

        # Now that the instance has been saved, we can add the author,
        # generate a file path, move the file and set it to PENDING.
        instance.authors.add(user)
        instance.handle_file_operations(upload)
        instance.update(status=STATUS_PENDING)
        return instance
Exemple #22
0
    def from_upload(cls, upload, instance=None):
        """Handle creating/editing the Extension instance and saving it to db,
        as well as file operations, from a FileUpload instance. Can throw
        a ValidationError or SigningError, so should always be called within a
        try/except."""
        if instance is not None:
            # Not implemented yet. Need to deal with versions correctly, we
            # don't know yet if we want to keep older versions around or not,
            # how status changes etc.
            raise NotImplementedError

        parser = ExtensionParser(upload, instance=instance)
        data = parser.parse()
        fields = ('version', 'name', 'default_language')
        default_locale = data.get('default_locale')

        if default_locale:
            # We actually need language (en-US) for translations, not locale
            # (en_US). The extension contains locales though, so transform the
            # field in the manifest before storing in db.
            data['default_language'] = to_language(default_locale)

        # Filter out parsing results to only keep fields we store in db.
        data = dict((k, v) for k, v in data.items() if k in fields)

        # Build a new instance.
        instance = cls(**data)
        instance.manifest = parser.manifest_contents
        instance.clean_slug()
        instance.save()

        # Now that the instance has been saved, we can generate a file path,
        # move the file and set it to PENDING.
        instance.handle_file_operations(upload)
        instance.update(status=STATUS_PENDING)
        return instance
Exemple #23
0
 def fetch_all_translations(self, obj, source, field):
     translations = field.__class__.objects.filter(
         id=field.id, localized_string__isnull=False)
     return dict((to_language(trans.locale), unicode(trans))
                 for trans in translations) if translations else None
Exemple #24
0
class Collection(ModelBase):
    # `collection_type` for rocketfuel, not transonic.
    collection_type = models.IntegerField(choices=COLLECTION_TYPES)
    description = PurifiedField()
    name = PurifiedField()
    is_public = models.BooleanField(default=False)
    category = models.CharField(default=None,
                                null=True,
                                blank=True,
                                max_length=30,
                                choices=CATEGORY_CHOICES)
    region = models.PositiveIntegerField(
        default=None,
        null=True,
        blank=True,
        choices=mkt.regions.REGIONS_CHOICES_ID,
        db_index=True)
    carrier = models.IntegerField(default=None,
                                  null=True,
                                  blank=True,
                                  choices=mkt.carriers.CARRIER_CHOICES,
                                  db_index=True)
    author = models.CharField(max_length=255, default='', blank=True)
    slug = models.CharField(blank=True,
                            max_length=30,
                            help_text='Used in collection URLs.')
    default_language = models.CharField(
        max_length=10,
        choices=((to_language(lang), desc)
                 for lang, desc in settings.LANGUAGES.items()),
        default=to_language(settings.LANGUAGE_CODE))
    curators = models.ManyToManyField('users.UserProfile')
    background_color = ColorField(null=True)
    text_color = ColorField(null=True)
    image_hash = models.CharField(default=None, max_length=8, null=True)
    can_be_hero = models.BooleanField(
        default=False,
        help_text=
        ('Indicates whether an operator shelf collection can be displayed with'
         'a hero graphic'))
    _apps = models.ManyToManyField(Webapp,
                                   through='CollectionMembership',
                                   related_name='app_collections')

    objects = ManagerBase()
    public = PublicCollectionsManager()

    class Meta:
        db_table = 'app_collections'
        # This ordering will change soon since we'll need to be able to order
        # collections themselves, but this helps tests for now.
        ordering = ('-id', )

    def __unicode__(self):
        return self.name.localized_string_clean

    def save(self, **kw):
        self.clean_slug()
        return super(Collection, self).save(**kw)

    @use_master
    def clean_slug(self):
        clean_slug(self, 'slug')

    @classmethod
    def get_fallback(cls):
        return cls._meta.get_field('default_language')

    def image_path(self, suffix=''):
        # The argument `suffix` isn't used here but is in the feed.
        return os.path.join(settings.COLLECTIONS_ICON_PATH,
                            str(self.pk / 1000),
                            'app_collection_%s.png' % (self.pk, ))

    def apps(self):
        """
        Public apps on the collection, ordered by their position in the
        CollectionMembership model.

        Use this method everytime you want to display apps for a collection to
        an user.
        """
        return self._apps.filter(
            disabled_by_user=False,
            status=amo.STATUS_PUBLIC).order_by('collectionmembership')

    def add_app(self, app, order=None):
        """
        Add an app to this collection. If specified, the app will be created
        with the specified `order`. If not, it will be added to the end of the
        collection.
        """
        qs = CollectionMembership.objects.filter(collection=self)
        if order is None:
            aggregate = qs.aggregate(models.Max('order'))['order__max']
            order = aggregate + 1 if aggregate is not None else 0
        rval = CollectionMembership.objects.create(collection=self,
                                                   app=app,
                                                   order=order)
        # Help django-cache-machine: it doesn't like many 2 many relations,
        # the cache is never invalidated properly when adding a new object.
        CollectionMembership.objects.invalidate(*qs)
        index_webapps.delay([app.pk])
        return rval

    def remove_app(self, app):
        """
        Remove the passed app from this collection, returning a boolean
        indicating whether a successful deletion took place.
        """
        try:
            membership = self.collectionmembership_set.get(app=app)
        except CollectionMembership.DoesNotExist:
            return False
        else:
            membership.delete()
            index_webapps.delay([app.pk])
            return True

    def reorder(self, new_order):
        """
        Passed a list of app IDs, e.g.

        [18, 24, 9]

        will change the order of each item in the collection to match the
        passed order. A ValueError will be raised if each app in the
        collection is not included in the ditionary.
        """
        existing_pks = self.apps().no_cache().values_list('pk', flat=True)
        if set(existing_pks) != set(new_order):
            raise ValueError('Not all apps included')
        for order, pk in enumerate(new_order):
            CollectionMembership.objects.get(collection=self,
                                             app_id=pk).update(order=order)
        index_webapps.delay(new_order)

    def has_curator(self, userprofile):
        """
        Returns boolean indicating whether the passed user profile is a curator
        on this collection.

        ID comparison used instead of directly checking objects to ensure that
        UserProfile objects could be passed.
        """
        return userprofile.id in self.curators.values_list('id', flat=True)

    def add_curator(self, userprofile):
        ret = self.curators.add(userprofile)
        Collection.objects.invalidate(*self.curators.all())
        return ret

    def remove_curator(self, userprofile):
        ret = self.curators.remove(userprofile)
        Collection.objects.invalidate(*self.curators.all())
        return ret
Exemple #25
0
    def extract_document(cls, pk=None, obj=None):
        """Extracts the ElasticSearch index document for this instance."""
        from mkt.webapps.models import (AppFeatures, attach_devices,
                                        attach_prices, attach_tags,
                                        attach_translations, Geodata,
                                        Installed, RatingDescriptors,
                                        RatingInteractives)

        if obj is None:
            obj = cls.get_model().objects.no_cache().get(pk=pk)

        # Attach everything we need to index apps.
        for transform in (attach_devices, attach_prices, attach_tags,
                          attach_translations):
            transform([obj])

        latest_version = obj.latest_version
        version = obj.current_version
        geodata = obj.geodata
        features = (version.features.to_dict()
                    if version else AppFeatures().to_dict())

        try:
            status = latest_version.statuses[0][1] if latest_version else None
        except IndexError:
            status = None

        installed_ids = list(Installed.objects.filter(addon=obj)
                             .values_list('id', flat=True))

        attrs = ('app_slug', 'bayesian_rating', 'created', 'id', 'is_disabled',
                 'last_updated', 'modified', 'premium_type', 'status',
                 'uses_flash', 'weekly_downloads')
        d = dict(zip(attrs, attrgetter(*attrs)(obj)))

        d['boost'] = len(installed_ids) or 1
        d['app_type'] = obj.app_type_id
        d['author'] = obj.developer_name
        d['banner_regions'] = geodata.banner_regions_slugs()
        d['category'] = obj.categories if obj.categories else []
        if obj.is_published:
            d['collection'] = [{'id': cms.collection_id, 'order': cms.order}
                               for cms in obj.collectionmembership_set.all()]
        else:
            d['collection'] = []
        d['content_ratings'] = (obj.get_content_ratings_by_body(es=True) or
                                None)
        try:
            d['content_descriptors'] = obj.rating_descriptors.to_keys()
        except RatingDescriptors.DoesNotExist:
            d['content_descriptors'] = []
        d['current_version'] = version.version if version else None
        d['default_locale'] = obj.default_locale
        d['description'] = list(
            set(string for _, string in obj.translations[obj.description_id]))
        d['device'] = getattr(obj, 'device_ids', [])
        d['features'] = features
        d['has_public_stats'] = obj.public_stats
        d['icon_hash'] = obj.icon_hash
        try:
            d['interactive_elements'] = obj.rating_interactives.to_keys()
        except RatingInteractives.DoesNotExist:
            d['interactive_elements'] = []
        d['is_escalated'] = obj.escalationqueue_set.exists()
        d['is_offline'] = getattr(obj, 'is_offline', False)
        d['is_priority'] = obj.priority_review
        d['is_rereviewed'] = obj.rereviewqueue_set.exists()
        if latest_version:
            d['latest_version'] = {
                'status': status,
                'is_privileged': latest_version.is_privileged,
                'has_editor_comment': latest_version.has_editor_comment,
                'has_info_request': latest_version.has_info_request,
                'nomination_date': latest_version.nomination,
                'created_date': latest_version.created,
            }
        else:
            d['latest_version'] = {
                'status': None,
                'is_privileged': None,
                'has_editor_comment': None,
                'has_info_request': None,
                'nomination_date': None,
                'created_date': None,
            }
        d['manifest_url'] = obj.get_manifest_url()
        d['package_path'] = obj.get_package_path()
        d['name'] = list(
            set(string for _, string in obj.translations[obj.name_id]))
        d['name_sort'] = unicode(obj.name).lower()
        d['owners'] = [au.user.id for au in
                       obj.addonuser_set.filter(role=amo.AUTHOR_ROLE_OWNER)]
        d['popularity'] = len(installed_ids)
        d['previews'] = [{'filetype': p.filetype, 'modified': p.modified,
                          'id': p.id, 'sizes': p.sizes}
                         for p in obj.previews.all()]
        try:
            p = obj.addonpremium.price
            d['price_tier'] = p.name
        except AddonPremium.DoesNotExist:
            d['price_tier'] = None

        d['ratings'] = {
            'average': obj.average_rating,
            'count': obj.total_reviews,
        }
        d['region_exclusions'] = obj.get_excluded_region_ids()
        d['reviewed'] = obj.versions.filter(
            deleted=False).aggregate(Min('reviewed')).get('reviewed__min')
        if version:
            d['supported_locales'] = filter(
                None, version.supported_locales.split(','))
        else:
            d['supported_locales'] = []

        d['tags'] = getattr(obj, 'tag_list', [])
        if obj.upsell and obj.upsell.premium.is_published():
            upsell_obj = obj.upsell.premium
            d['upsell'] = {
                'id': upsell_obj.id,
                'app_slug': upsell_obj.app_slug,
                'icon_url': upsell_obj.get_icon_url(128),
                # TODO: Store all localizations of upsell.name.
                'name': unicode(upsell_obj.name),
                'region_exclusions': upsell_obj.get_excluded_region_ids()
            }

        d['versions'] = [dict(version=v.version,
                              resource_uri=reverse_version(v))
                         for v in obj.versions.all()]

        # Handle our localized fields.
        for field in ('description', 'homepage', 'name', 'support_email',
                      'support_url'):
            d['%s_translations' % field] = [
                {'lang': to_language(lang), 'string': string}
                for lang, string
                in obj.translations[getattr(obj, '%s_id' % field)]
                if string]
        if version:
            attach_trans_dict(Version, [version])
            d['release_notes_translations'] = [
                {'lang': to_language(lang), 'string': string}
                for lang, string
                in version.translations[version.releasenotes_id]]
        else:
            d['release_notes_translations'] = None
        attach_trans_dict(Geodata, [geodata])
        d['banner_message_translations'] = [
            {'lang': to_language(lang), 'string': string}
            for lang, string
            in geodata.translations[geodata.banner_message_id]]

        for region in mkt.regions.ALL_REGION_IDS:
            d['popularity_%s' % region] = d['popularity']

        # Bump the boost if the add-on is public.
        if obj.status == amo.STATUS_PUBLIC:
            d['boost'] = max(d['boost'], 1) * 4

        # If the app is compatible with Firefox OS, push suggestion data in the
        # index - This will be used by RocketbarView API, which is specific to
        # Firefox OS.
        if DEVICE_GAIA.id in d['device'] and obj.is_published():
            d['name_suggest'] = {
                'input': d['name'],
                'output': unicode(obj.id),  # We only care about the payload.
                'weight': d['boost'],
                'payload': {
                    'default_locale': d['default_locale'],
                    'icon_hash': d['icon_hash'],
                    'id': d['id'],
                    'manifest_url': d['manifest_url'],
                    'modified': d['modified'],
                    'name_translations': d['name_translations'],
                    'slug': d['app_slug'],
                }
            }

        # Indices for each language. languages is a list of locales we want to
        # index with analyzer if the string's locale matches.
        for analyzer, languages in amo.SEARCH_ANALYZER_MAP.iteritems():
            if (not settings.ES_USE_PLUGINS and
                analyzer in amo.SEARCH_ANALYZER_PLUGINS):
                continue

            d['name_' + analyzer] = list(
                set(string for locale, string in obj.translations[obj.name_id]
                    if locale.lower() in languages))
            d['description_' + analyzer] = list(
                set(string for locale, string
                    in obj.translations[obj.description_id]
                    if locale.lower() in languages))

        return d
Exemple #26
0
 def check(a, b):
     eq_(to_language(a), b)
Exemple #27
0
def _make_language_choices(languages):
    return [(to_language(lang_code), lang_name)
            for lang_code, lang_name in languages.items()]
Exemple #28
0
 def trans_locale(self, locale):
     return to_language(settings.SHORTER_LANGUAGES.get(locale, locale))
Exemple #29
0
def _make_language_choices(languages):
    return [(to_language(lang_code), lang_name)
            for lang_code, lang_name in languages.items()]
Exemple #30
0
 def fetch_all_translations(self, obj, source, field):
     translations = field.__class__.objects.filter(id=field.id, localized_string__isnull=False)
     return dict((to_language(trans.locale), unicode(trans)) for trans in translations) if translations else None
Exemple #31
0
 def trans_locale(self, locale):
     return to_language(settings.SHORTER_LANGUAGES.get(locale, locale))