Example #1
0
class CollectionAddon(amo.models.ModelBase):
    addon = models.ForeignKey(Addon)
    collection = models.ForeignKey(Collection)
    # category (deprecated: for "Fashion Your Firefox")
    comments = LinkifiedField(null=True)
    downloads = models.PositiveIntegerField(default=0)
    user = models.ForeignKey(UserProfile, null=True)

    ordering = models.PositiveIntegerField(default=0,
        help_text='Add-ons are displayed in ascending order '
                  'based on this field.')

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'addons_collections'
        unique_together = (('addon', 'collection'),)
Example #2
0
class License(amo.models.ModelBase):
    OTHER = 0

    name = TranslatedField(db_column='name')
    url = models.URLField(null=True)
    builtin = models.PositiveIntegerField(default=OTHER)
    text = LinkifiedField()
    on_form = models.BooleanField(default=False,
        help_text='Is this a license choice in the devhub?')
    some_rights = models.BooleanField(default=False,
        help_text='Show "Some Rights Reserved" instead of the license name?')
    icons = models.CharField(max_length=255, null=True,
        help_text='Space-separated list of icon identifiers.')

    objects = LicenseManager()

    class Meta:
        db_table = 'licenses'

    def __unicode__(self):
        return unicode(self.name)
Example #3
0
class CollectionAddon(amo.models.ModelBase):
    addon = models.ForeignKey(Addon)
    collection = models.ForeignKey(Collection)
    # category (deprecated: for "Fashion Your Firefox")
    comments = LinkifiedField(null=True)
    downloads = models.PositiveIntegerField(default=0)
    user = models.ForeignKey(UserProfile, null=True)

    ordering = models.PositiveIntegerField(
        default=0,
        help_text='Add-ons are displayed in ascending order '
        'based on this field.')

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'addons_collections'
        unique_together = (('addon', 'collection'), )

    @staticmethod
    def post_save_or_delete(sender, instance, **kwargs):
        """Update Collection.addon_count."""
        from . import tasks
        tasks.collection_meta.delay(instance.collection_id, using='default')
Example #4
0
class FancyModel(amo.models.ModelBase):
    """Mix it up with purified and linkified fields."""
    purified = PurifiedField()
    linkified = LinkifiedField()
Example #5
0
class Collection(CollectionBase, amo.models.ModelBase):

    TYPE_CHOICES = amo.COLLECTION_CHOICES.items()

    uuid = models.CharField(max_length=36, blank=True, unique=True)
    name = TranslatedField(require_locale=False)
    # nickname is deprecated.  Use slug.
    nickname = models.CharField(max_length=30,
                                blank=True,
                                unique=True,
                                null=True)
    slug = models.CharField(max_length=30, blank=True, null=True)

    description = LinkifiedField(require_locale=False)
    default_locale = models.CharField(max_length=10,
                                      default='en-US',
                                      db_column='defaultlocale')
    type = models.PositiveIntegerField(db_column='collection_type',
                                       choices=TYPE_CHOICES,
                                       default=0)
    icontype = models.CharField(max_length=25, blank=True)

    listed = models.BooleanField(
        default=True, help_text='Collections are either listed or private.')

    subscribers = models.PositiveIntegerField(default=0)
    downloads = models.PositiveIntegerField(default=0)
    weekly_subscribers = models.PositiveIntegerField(default=0)
    monthly_subscribers = models.PositiveIntegerField(default=0)
    application = models.ForeignKey(Application, null=True)
    addon_count = models.PositiveIntegerField(default=0,
                                              db_column='addonCount')

    upvotes = models.PositiveIntegerField(default=0)
    downvotes = models.PositiveIntegerField(default=0)
    rating = models.FloatField(default=0)
    all_personas = models.BooleanField(
        default=False, help_text='Does this collection only contain personas?')

    addons = models.ManyToManyField(Addon,
                                    through='CollectionAddon',
                                    related_name='collections')
    author = models.ForeignKey(UserProfile,
                               null=True,
                               related_name='collections')
    users = models.ManyToManyField(UserProfile,
                                   through='CollectionUser',
                                   related_name='collections_publishable')

    addon_index = models.CharField(
        max_length=40,
        null=True,
        db_index=True,
        help_text='Custom index for the add-ons in this collection')

    # This gets overwritten in the transformer.
    share_counts = collections.defaultdict(int)

    objects = CollectionManager()

    top_tags = TopTags()

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'collections'
        unique_together = (('author', 'slug'), )

    def __unicode__(self):
        return u'%s (%s)' % (self.name, self.addon_count)

    def flush_urls(self):
        urls = ['*%s' % self.get_url_path(), self.icon_url]
        return urls

    def save(self, **kw):
        if not self.uuid:
            self.uuid = unicode(uuid.uuid4())
        if not self.slug:
            self.slug = self.uuid[:30]
        self.clean_slug()

        # Maintain our index of add-on ids.
        if self.id:
            ids = self.addons.values_list('id', flat=True)
            self.addon_index = self.make_index(ids)

        super(Collection, self).save(**kw)

    def clean_slug(self):
        if self.type in SPECIAL_SLUGS:
            self.slug = SPECIAL_SLUGS[self.type]
            return

        if self.slug in SPECIAL_SLUGS.values():
            self.slug += '~'

        if not self.author:
            return

        qs = self.author.collections.using('default')
        slugs = dict((slug, id) for slug, id in qs.values_list('slug', 'id'))
        if self.slug in slugs and slugs[self.slug] != self.id:
            for idx in range(len(slugs)):
                new = '%s-%s' % (self.slug, idx + 1)
                if new not in slugs:
                    self.slug = new
                    return

    def get_url_path(self):
        return reverse('collections.detail',
                       args=[self.author_username, self.slug])

    def get_abs_url(self):
        return absolutify(self.get_url_path())

    def get_img_dir(self):
        return os.path.join(settings.COLLECTIONS_ICON_PATH,
                            str(self.id / 1000))

    def upvote_url(self):
        return reverse('collections.vote',
                       args=[self.author_username, self.slug, 'up'])

    def downvote_url(self):
        return reverse('collections.vote',
                       args=[self.author_username, self.slug, 'down'])

    def edit_url(self):
        return reverse('collections.edit',
                       args=[self.author_username, self.slug])

    def watch_url(self):
        return reverse('collections.watch',
                       args=[self.author_username, self.slug])

    def delete_url(self):
        return reverse('collections.delete',
                       args=[self.author_username, self.slug])

    def delete_icon_url(self):
        return reverse('collections.delete_icon',
                       args=[self.author_username, self.slug])

    def share_url(self):
        return reverse('collections.share',
                       args=[self.author_username, self.slug])

    def feed_url(self):
        return reverse('collections.detail.rss',
                       args=[self.author_username, self.slug])

    def stats_url(self):
        return reverse('collections.stats',
                       args=[self.author_username, self.slug])

    @property
    def author_username(self):
        return self.author.username if self.author else 'anonymous'

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

    @property
    def url_slug(self):
        """uuid or nickname if chosen"""
        return self.nickname or self.uuid

    @property
    def icon_url(self):
        modified = int(time.mktime(self.modified.timetuple()))
        if self.icontype:
            # [1] is the whole ID, [2] is the directory
            split_id = re.match(r'((\d*?)\d{1,3})$', str(self.id))
            return settings.COLLECTION_ICON_URL % (split_id.group(2)
                                                   or 0, self.id, modified)
        elif self.type == amo.COLLECTION_FAVORITES:
            return settings.MEDIA_URL + 'img/icons/heart.png'
        else:
            return settings.MEDIA_URL + 'img/icons/collection.png'

    def set_addons(self, addon_ids, comments={}):
        """Replace the current add-ons with a new list of add-on ids."""
        order = dict((a, idx) for idx, a in enumerate(addon_ids))

        # Partition addon_ids into add/update/remove buckets.
        existing = set(
            self.addons.using('default').values_list('id', flat=True))
        add, update = [], []
        for addon in addon_ids:
            bucket = update if addon in existing else add
            bucket.append((addon, order[addon]))
        remove = existing.difference(addon_ids)

        cursor = connection.cursor()
        now = datetime.now()

        if remove:
            cursor.execute("DELETE FROM addons_collections "
                           "WHERE collection_id=%s AND addon_id IN (%s)" %
                           (self.id, ','.join(map(str, remove))))
            if self.listed:
                for addon in remove:
                    amo.log(amo.LOG.REMOVE_FROM_COLLECTION, (Addon, addon),
                            self)
        if add:
            insert = '(%s, %s, %s, NOW(), NOW(), 0)'
            values = [insert % (a, self.id, idx) for a, idx in add]
            cursor.execute("""
                INSERT INTO addons_collections
                    (addon_id, collection_id, ordering, created,
                     modified, downloads)
                VALUES %s""" % ','.join(values))
            if self.listed:
                for addon_id, idx in add:
                    amo.log(amo.LOG.ADD_TO_COLLECTION, (Addon, addon_id), self)
        for addon, ordering in update:
            (CollectionAddon.objects.filter(
                collection=self.id, addon=addon).update(ordering=ordering,
                                                        modified=now))

        for addon, comment in comments.iteritems():
            c = (CollectionAddon.objects.using('default').filter(
                collection=self.id, addon=addon))
            if c:
                c[0].comments = comment
                c[0].save(force_update=True)

        self.save()

    def is_subscribed(self, user):
        """Determines if the user is subscribed to this collection."""
        return self.following.filter(user=user).exists()

    def add_addon(self, addon):
        "Adds an addon to the collection."
        CollectionAddon.objects.get_or_create(addon=addon, collection=self)
        if self.listed:
            amo.log(amo.LOG.ADD_TO_COLLECTION, addon, self)
        self.save()  # To invalidate Collection.

    def remove_addon(self, addon):
        CollectionAddon.objects.filter(addon=addon, collection=self).delete()
        if self.listed:
            amo.log(amo.LOG.REMOVE_FROM_COLLECTION, addon, self)
        self.save()  # To invalidate Collection.

    def owned_by(self, user):
        return (user.id == self.author_id)

    def can_view_stats(self, request):
        from access import acl
        if request and request.amo_user:
            return (self.publishable_by(request.amo_user)
                    or acl.action_allowed(request, 'CollectionStats', 'View'))
        return False

    @caching.cached_method
    def publishable_by(self, user):
        return bool(self.owned_by(user) or self.users.filter(pk=user.id))

    @staticmethod
    def transformer(collections):
        if not collections:
            return
        author_ids = set(c.author_id for c in collections)
        authors = dict(
            (u.id, u) for u in UserProfile.objects.filter(id__in=author_ids))
        for c in collections:
            c.author = authors.get(c.author_id)
        c_dict = dict((c.pk, c) for c in collections)
        sharing.attach_share_counts(CollectionShareCountTotal, 'collection',
                                    c_dict)

    @staticmethod
    def post_save(sender, instance, **kwargs):
        from . import tasks
        if kwargs.get('raw'):
            return
        tasks.collection_meta.delay(instance.id, using='default')
        tasks.index_collections.delay([instance.id])

    @staticmethod
    def post_delete(sender, instance, **kwargs):
        from . import tasks
        if kwargs.get('raw'):
            return
        tasks.unindex_collections.delay([instance.id])