예제 #1
0
class Displayable(Slugged, MetaData, TimeStamped):
    """
    Abstract model that provides features of a visible page on the
    website such as publishing fields. Basis of Mezzanine pages,
    blog posts, and Cartridge products.
    """

    status = models.IntegerField(
        _("Status"),
        choices=CONTENT_STATUS_CHOICES,
        default=CONTENT_STATUS_PUBLISHED,
        help_text=_("With Draft chosen, will only be shown for admin users "
                    "on the site."))
    publish_date = models.DateTimeField(
        _("Published from"),
        help_text=_("With Published chosen, won't be shown until this time"),
        blank=True,
        null=True,
        db_index=True)
    expiry_date = models.DateTimeField(
        _("Expires on"),
        help_text=_("With Published chosen, won't be shown after this time"),
        blank=True,
        null=True)
    short_url = models.URLField(blank=True, null=True)
    in_sitemap = models.BooleanField(_("Show in sitemap"), default=True)

    objects = DisplayableManager()
    search_fields = {"keywords": 10, "title": 5}

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        """
        Set default for ``publish_date``. We can't use ``auto_now_add`` on
        the field as it will be blank when a blog post is created from
        the quick blog form in the admin dashboard.
        """
        if self.publish_date is None:
            self.publish_date = now()
        super(Displayable, self).save(*args, **kwargs)

    def get_admin_url(self):
        return admin_url(self, "change", self.id)

    def publish_date_since(self):
        """
        Returns the time since ``publish_date``.
        """
        return timesince(self.publish_date)

    publish_date_since.short_description = _("Published from")

    def get_absolute_url(self):
        """
        Raise an error if called on a subclass without
        ``get_absolute_url`` defined, to ensure all search results
        contains a URL.
        """
        name = self.__class__.__name__
        raise NotImplementedError("The model %s does not have "
                                  "get_absolute_url defined" % name)

    def get_absolute_url_with_host(self):
        """
        Returns host + ``get_absolute_url`` - used by the various
        ``short_url`` mechanics below.

        Technically we should use ``self.site.domain``, here, however
        if we were to invoke the ``short_url`` mechanics on a list of
        data (eg blog post list view), we'd trigger a db query per
        item. Using ``current_request`` should provide the same
        result, since site related data should only be loaded based
        on the current host anyway.
        """
        req = current_request()
        abs_url = self.get_absolute_url()
        build = req.build_absolute_uri(abs_url)
        return build

    def set_short_url(self):
        """
        Generates the ``short_url`` attribute if the model does not
        already have one. Used by the ``set_short_url_for`` template
        tag and ``TweetableAdmin``.

        If no sharing service is defined (bitly is the one implemented,
        but others could be by overriding ``generate_short_url``), the
        ``SHORT_URL_UNSET`` marker gets stored in the DB. In this case,
        ``short_url`` is temporarily (eg not persisted) set to
        host + ``get_absolute_url`` - this is so that we don't
        permanently store ``get_absolute_url``, since it may change
        over time.
        """
        if self.short_url == SHORT_URL_UNSET:
            self.short_url = self.get_absolute_url_with_host()
        elif not self.short_url:
            self.short_url = self.generate_short_url()
            self.save()

    def generate_short_url(self):
        """
        Returns a new short URL generated using bit.ly if credentials for the
        service have been specified.
        """
        from mezzanine.conf import settings
        if settings.BITLY_ACCESS_TOKEN:
            url = "https://api-ssl.bit.ly/v3/shorten?%s" % urlencode(
                {
                    "access_token": settings.BITLY_ACCESS_TOKEN,
                    "uri": self.get_absolute_url_with_host(),
                })
            response = loads(urlopen(url).read().decode("utf-8"))
            if response["status_code"] == 200:
                return response["data"]["url"]
        return SHORT_URL_UNSET

    def _get_next_or_previous_by_publish_date(self, is_next, **kwargs):
        """
        Retrieves next or previous object by publish date. We implement
        our own version instead of Django's so we can hook into the
        published manager and concrete subclasses.
        """
        arg = "publish_date__gt" if is_next else "publish_date__lt"
        order = "publish_date" if is_next else "-publish_date"
        lookup = {arg: self.publish_date}
        concrete_model = base_concrete_model(Displayable, self)
        try:
            queryset = concrete_model.objects.published
        except AttributeError:
            queryset = concrete_model.objects.all
        try:
            return queryset(**kwargs).filter(**lookup).order_by(order)[0]
        except IndexError:
            pass

    def get_next_by_publish_date(self, **kwargs):
        """
        Retrieves next object by publish date.
        """
        return self._get_next_or_previous_by_publish_date(True, **kwargs)

    def get_previous_by_publish_date(self, **kwargs):
        """
        Retrieves previous object by publish date.
        """
        return self._get_next_or_previous_by_publish_date(False, **kwargs)
예제 #2
0
class Displayable(Slugged, MetaData, TimeStamped):
    """
    Abstract model that provides features of a visible page on the
    website such as publishing fields. Basis of Mezzanine pages,
    blog posts, and Cartridge products.
    """

    status = models.IntegerField(
        _("Status"),
        choices=CONTENT_STATUS_CHOICES,
        default=CONTENT_STATUS_PUBLISHED,
        help_text=_("With Draft chosen, will only be shown for admin users "
                    "on the site."))
    publish_date = models.DateTimeField(
        _("Published from"),
        help_text=_("With Published chosen, won't be shown until this time"),
        blank=True,
        null=True)
    expiry_date = models.DateTimeField(
        _("Expires on"),
        help_text=_("With Published chosen, won't be shown after this time"),
        blank=True,
        null=True)
    short_url = models.URLField(blank=True, null=True)
    in_sitemap = models.BooleanField(_("Show in sitemap"), default=True)

    objects = DisplayableManager()
    search_fields = {"keywords": 10, "title": 5}

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        """
        Set default for ``publish_date``. We can't use ``auto_now_add`` on
        the field as it will be blank when a blog post is created from
        the quick blog form in the admin dashboard.
        """
        if self.publish_date is None:
            self.publish_date = now()
        super(Displayable, self).save(*args, **kwargs)

    def get_admin_url(self):
        return admin_url(self, "change", self.id)

    def publish_date_since(self):
        """
        Returns the time since ``publish_date``.
        """
        return timesince(self.publish_date)

    publish_date_since.short_description = _("Published from")

    def get_absolute_url(self):
        """
        Raise an error if called on a subclass without
        ``get_absolute_url`` defined, to ensure all search results
        contains a URL.
        """
        name = self.__class__.__name__
        raise NotImplementedError("The model %s does not have "
                                  "get_absolute_url defined" % name)

    def set_short_url(self):
        """
        Sets the ``short_url`` attribute using the bit.ly credentials
        if they have been specified, and saves it. Used by the
        ``set_short_url_for`` template tag, and ``TweetableAdmin``.
        """
        if not self.short_url:
            from mezzanine.conf import settings
            settings.use_editable()
            parts = (self.site.domain, self.get_absolute_url())
            self.short_url = "http://%s%s" % parts
            if settings.BITLY_ACCESS_TOKEN:
                url = "https://api-ssl.bit.ly/v3/shorten?%s" % urlencode(
                    {
                        "access_token": settings.BITLY_ACCESS_TOKEN,
                        "uri": self.short_url,
                    })
                response = loads(urlopen(url).read().decode("utf-8"))
                if response["status_code"] == 200:
                    self.short_url = response["data"]["url"]
            self.save()
        return ""

    def _get_next_or_previous_by_publish_date(self, is_next, **kwargs):
        """
        Retrieves next or previous object by publish date. We implement
        our own version instead of Django's so we can hook into the
        published manager and concrete subclasses.
        """
        arg = "publish_date__gt" if is_next else "publish_date__lt"
        order = "publish_date" if is_next else "-publish_date"
        lookup = {arg: self.publish_date}
        concrete_model = base_concrete_model(Displayable, self)
        try:
            queryset = concrete_model.objects.published
        except AttributeError:
            queryset = concrete_model.objects.all
        try:
            return queryset(**kwargs).filter(**lookup).order_by(order)[0]
        except IndexError:
            pass

    def get_next_by_publish_date(self, **kwargs):
        """
        Retrieves next object by publish date.
        """
        return self._get_next_or_previous_by_publish_date(True, **kwargs)

    def get_previous_by_publish_date(self, **kwargs):
        """
        Retrieves previous object by publish date.
        """
        return self._get_next_or_previous_by_publish_date(False, **kwargs)
예제 #3
0
class Displayable(Slugged, MetaData):
    """
    Abstract model that provides features of a visible page on the
    website such as publishing fields. Basis of Mezzanine pages,
    blog posts, and Cartridge products.
    """

    status = models.IntegerField(
        _("Status"),
        choices=CONTENT_STATUS_CHOICES,
        default=CONTENT_STATUS_PUBLISHED,
        help_text=_("With Draft chosen, will only be shown for admin users "
                    "on the site."))
    publish_date = models.DateTimeField(
        _("Published from"),
        help_text=_("With Published chosen, won't be shown until this time"),
        blank=True,
        null=True)
    expiry_date = models.DateTimeField(
        _("Expires on"),
        help_text=_("With Published chosen, won't be shown after this time"),
        blank=True,
        null=True)
    short_url = models.URLField(blank=True, null=True)
    in_sitemap = models.BooleanField(_("Show in sitemap"), default=True)
    theme_color = models.CharField(
        "Theme Colour",
        max_length=255,
        default='grey',
        choices=THEME_CHOICES,
        help_text='Select a Theme Colour for this page')

    objects = DisplayableManager()
    search_fields = {"keywords": 10, "title": 5}

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        """
        Set default for ``publish_date``. We can't use ``auto_now_add`` on
        the field as it will be blank when a blog post is created from
        the quick blog form in the admin dashboard.
        """
        if self.publish_date is None:
            self.publish_date = now()
        super(Displayable, self).save(*args, **kwargs)

    def get_admin_url(self):
        return admin_url(self, "change", self.id)

    def publish_date_since(self):
        """
        Returns the time since ``publish_date``.
        """
        return timesince(self.publish_date)

    publish_date_since.short_description = _("Published from")

    def get_absolute_url(self):
        """
        Raise an error if called on a subclass without
        ``get_absolute_url`` defined, to ensure all search results
        contains a URL.
        """
        name = self.__class__.__name__
        raise NotImplementedError("The model %s does not have "
                                  "get_absolute_url defined" % name)

    def _get_next_or_previous_by_publish_date(self, is_next, **kwargs):
        """
        Retrieves next or previous object by publish date. We implement
        our own version instead of Django's so we can hook into the
        published manager and concrete subclasses.
        """
        arg = "publish_date__gt" if is_next else "publish_date__lt"
        order = "publish_date" if is_next else "-publish_date"
        lookup = {arg: self.publish_date}
        concrete_model = base_concrete_model(Displayable, self)
        try:
            queryset = concrete_model.objects.published
        except AttributeError:
            queryset = concrete_model.objects.all
        try:
            return queryset(**kwargs).filter(**lookup).order_by(order)[0]
        except IndexError:
            pass

    def get_next_by_publish_date(self, **kwargs):
        """
        Retrieves next object by publish date.
        """
        return self._get_next_or_previous_by_publish_date(True, **kwargs)

    def get_previous_by_publish_date(self, **kwargs):
        """
        Retrieves previous object by publish date.
        """
        return self._get_next_or_previous_by_publish_date(False, **kwargs)
예제 #4
0
class Displayable(Slugged, MetaData):
    """
    Abstract model that provides features of a visible page on the
    website such as publishing fields. Basis of Mezzanine pages and
    blog posts.
    """

    status = models.IntegerField(_("Status"),
                                 choices=CONTENT_STATUS_CHOICES,
                                 default=CONTENT_STATUS_PUBLISHED)
    publish_date = models.DateTimeField(
        _("Published from"),
        help_text=_("With published checked, won't be shown until this time"),
        blank=True,
        null=True)
    expiry_date = models.DateTimeField(
        _("Expires on"),
        help_text=_("With published checked, won't be shown after this time"),
        blank=True,
        null=True)
    short_url = models.URLField(blank=True, null=True)

    objects = DisplayableManager()
    search_fields = {"keywords": 10, "title": 5}

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        """
        Set default for ``publsh_date`` and ``description`` if none
        given.
        """
        if self.publish_date is None:
            # publish_date will be blank when a blog post is created
            # from the quick blog form in the admin dashboard.
            self.publish_date = datetime.now()
        self.description = strip_tags(self.description_from_content())
        super(Displayable, self).save(*args, **kwargs)

    def description_from_content(self):
        """
        Returns the first block or sentence of the first content-like
        field.
        """
        description = ""
        # Use the first RichTextField, or TextField if none found.
        for field_type in (RichTextField, models.TextField):
            if not description:
                for field in self._meta.fields:
                    if isinstance(field, field_type) and \
                        field.name != "description":
                        description = getattr(self, field.name)
                        if description:
                            break
        # Fall back to the title if description couldn't be determined.
        if not description:
            description = self.title
        # Strip everything after the first block or sentence.
        ends = ("</p>", "<br />", "<br/>", "<br>", "</ul>", "\n", ". ", "! ",
                "? ")
        for end in ends:
            pos = description.lower().find(end)
            if pos > -1:
                description = TagCloser(description[:pos]).html
                break
        else:
            description = truncatewords_html(description, 100)
        return description

    def admin_link(self):
        return "<a href='%s'>%s</a>" % (self.get_absolute_url(),
                                        ugettext("View on site"))

    admin_link.allow_tags = True
    admin_link.short_description = ""
예제 #5
0
class Displayable(Slugged):
    """
    Abstract model that provides features of a visible page on the
    website such as publishing fields and meta data.
    """

    status = models.IntegerField(_("Status"),
                                 choices=CONTENT_STATUS_CHOICES,
                                 default=CONTENT_STATUS_DRAFT)
    publish_date = models.DateTimeField(
        _("Published from"),
        help_text=_("With published checked, won't be shown until this time"),
        blank=True,
        null=True)
    expiry_date = models.DateTimeField(
        _("Expires on"),
        help_text=_("With published checked, won't be shown after this time"),
        blank=True,
        null=True)
    description = models.TextField(_("Description"), blank=True)
    keywords = KeywordsField(verbose_name=_("Keywords"))
    short_url = models.URLField(blank=True, null=True)
    site = models.ForeignKey(Site, editable=False)

    objects = DisplayableManager()
    search_fields = {"keywords": 10, "title": 5}

    class Meta:
        abstract = True

    def save(self, update_site=True, *args, **kwargs):
        """
        Set default for ``publsh_date`` and ``description`` if none
        given. Unless the ``update_site`` argument is ``False``, set
        the site to the current site.
        """
        if self.publish_date is None:
            # publish_date will be blank when a blog post is created
            # from the quick blog form in the admin dashboard.
            self.publish_date = datetime.now()
        if not self.description:
            self.description = strip_tags(self.description_from_content())
        if update_site:
            self.site = Site.objects.get_current()
        super(Displayable, self).save(*args, **kwargs)

    def description_from_content(self):
        """
        Returns the first paragraph of the first content-like field.
        """
        description = ""
        # Use the first RichTextField, or TextField if none found.
        for field_type in (RichTextField, models.TextField):
            if not description:
                for field in self._meta.fields:
                    if isinstance(field, field_type) and \
                        field.name != "description":
                        description = getattr(self, field.name)
                        if description:
                            break
        # Fall back to the title if description couldn't be determined.
        if not description:
            description = self.title
        # Strip everything after the first paragraph or sentence.
        for end in ("</p>", "<br />", "\n", ". "):
            if end in description:
                description = description.split(end)[0] + end
                break
        else:
            description = truncatewords_html(description, 100)
        return description

    def admin_link(self):
        return "<a href='%s'>%s</a>" % (self.get_absolute_url(),
                                        ugettext("View on site"))

    admin_link.allow_tags = True
    admin_link.short_description = ""
예제 #6
0
class Displayable(Slugged, MetaData):
    """
    Abstract model that provides features of a visible page on the
    website such as publishing fields. Basis of Mezzanine pages,
    blog posts, and Cartridge products.
    """

    status = models.IntegerField(
        _("Status"),
        choices=CONTENT_STATUS_CHOICES,
        default=CONTENT_STATUS_PUBLISHED,
        help_text=_("With Draft chosen, will only be shown for admin users "
                    "on the site."))
    publish_date = models.DateTimeField(
        _("Published from"),
        help_text=_("With Published chosen, won't be shown until this time"),
        blank=True,
        null=True)
    expiry_date = models.DateTimeField(
        _("Expires on"),
        help_text=_("With Published chosen, won't be shown after this time"),
        blank=True,
        null=True)
    short_url = models.URLField(blank=True, null=True)

    objects = DisplayableManager()
    search_fields = {"keywords": 10, "title": 5}

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        """
        Set default for ``publish_date``. We can't use ``auto_add`` on
        the field as it will be blank when a blog post is created from
        the quick blog form in the admin dashboard.
        """
        if self.publish_date is None:
            self.publish_date = now()
        super(Displayable, self).save(*args, **kwargs)

    def get_admin_url(self):
        return admin_url(self, "change", self.id)

    def publish_date_since(self):
        """
        Returns the time since ``publish_date``.
        """
        return timesince(self.publish_date)

    publish_date_since.short_description = _("Published from")

    def get_absolute_url(self):
        """
        Raise an error if called on a subclass without
        ``get_absolute_url`` defined, to ensure all search results
        contains a URL.
        """
        name = self.__class__.__name__
        raise NotImplementedError("The model %s does not have "
                                  "get_absolute_url defined" % name)
예제 #7
0
class Product(Displayable, Priced, RichText, AdminThumbMixin):
    """
    Container model for a product that stores information common to
    all of its variations such as the product's title and description.
    """

    content_model = models.CharField(editable=False, max_length=50, null=True)

    available = models.BooleanField(_("Available for purchase"), default=False)
    image = CharField(_("Image"), max_length=100, blank=True, null=True)
    categories = models.ManyToManyField("Category",
                                        blank=True,
                                        verbose_name=_("Product categories"))
    date_added = models.DateTimeField(_("Date added"),
                                      auto_now_add=True,
                                      null=True)
    related_products = models.ManyToManyField(
        "self", verbose_name=_("Related products"), blank=True)
    upsell_products = models.ManyToManyField("self",
                                             verbose_name=_("Upsell products"),
                                             blank=True)
    rating = RatingField(verbose_name=_("Rating"))

    objects = DisplayableManager()

    admin_thumb_field = "image"

    search_fields = {"variations__sku": 100}

    class Meta:
        verbose_name = _("Product")
        verbose_name_plural = _("Products")

    @classmethod
    def get_content_models(cls):
        """
        Return all ``Product`` subclasses.
        """
        is_product_subclass = lambda cls: issubclass(cls, Product)
        cmp = lambda a, b: (int(b is Product) - int(a is Product) or a._meta.
                            verbose_name < b._meta.verbose_name)
        return sorted(filter(is_product_subclass, models.get_models()), cmp)

    def get_content_model(self):
        """
        Provides a generic method of retrieving the instance of the custom
        product's model, if there is one.
        """
        return getattr(self, self.content_model, None)

    def save(self, *args, **kwargs):
        """
        Copies the price fields to the default variation when
        ``SHOP_USE_VARIATIONS`` is False, and the product is
        updated via the admin change list.
        """
        updating = self.id is not None
        super(Product, self).save(*args, **kwargs)
        if updating and not settings.SHOP_USE_VARIATIONS:
            default = self.variations.get(default=True)
            self.copy_price_fields_to(default)
        else:
            self.content_model = self._meta.object_name.lower()

    @models.permalink
    def get_absolute_url(self):
        return ("shop_product", (), {"slug": self.slug})

    def copy_default_variation(self):
        """
        Copies the price and image fields from the default variation
        when the product is updated via the change view.
        """
        default = self.variations.get(default=True)
        default.copy_price_fields_to(self)
        if default.image:
            self.image = default.image.file.name
        self.save()