Beispiel #1
0
class Article(models.Model):
    title = models.CharField('title', max_length=234)
    slug = models.SlugField()
    section = AppHookConfigField(ExampleConfig, verbose_name='section')
    published = models.BooleanField(default=True, blank=True)

    objects = AppHookConfigManager()
class UserProfile(models.Model):
    ROLES = ((0, 'Others'), (1, 'Suborg Admin'), (2, 'Mentor'), (3, 'Student'))

    user = models.ForeignKey(User, on_delete=models.CASCADE)
    role = models.IntegerField(name='role', choices=ROLES, default=0)
    gsoc_year = models.ForeignKey(GsocYear,
                                  on_delete=models.CASCADE,
                                  null=True,
                                  blank=False)
    suborg_full_name = models.ForeignKey(SubOrg,
                                         on_delete=models.CASCADE,
                                         null=True,
                                         blank=False)
    accepted_proposal_pdf = models.FileField(blank=True,
                                             null=True,
                                             upload_to=PROPOSALS_PATH)
    proposal_confirmed = models.BooleanField(default=False)
    app_config = AppHookConfigField(
        NewsBlogConfig,
        verbose_name=_('Section'),
        blank=True,
        null=True,
    )
    hidden = models.BooleanField(name='hidden', default=False)

    objects = UserProfileManager()
    all_objects = models.Manager()

    def confirm_proposal(self):
        self.proposal_confirmed = True
        self.save()
Beispiel #3
0
class TranslatableArticle(TranslatableModel):
    translations = TranslatedFields(title=models.CharField('title',
                                                           max_length=234),
                                    slug=models.SlugField())
    section = AppHookConfigField(ExampleConfig, verbose_name='section')

    objects = AppHookConfigTranslatableManager()
Beispiel #4
0
class UserProfile(models.Model):
    ROLES = ((0, "Others"), (1, "Suborg Admin"), (2, "Mentor"), (3, "Student"))

    user = models.ForeignKey(User, on_delete=models.CASCADE)
    role = models.IntegerField(name="role", choices=ROLES, default=0)
    gsoc_year = models.ForeignKey(GsocYear,
                                  on_delete=models.CASCADE,
                                  null=True,
                                  blank=False)
    suborg_full_name = models.ForeignKey(SubOrg,
                                         on_delete=models.CASCADE,
                                         null=True,
                                         blank=False)
    accepted_proposal_pdf = models.FileField(blank=True,
                                             null=True,
                                             upload_to=PROPOSALS_PATH)
    proposal_confirmed = models.BooleanField(default=False)
    app_config = AppHookConfigField(NewsBlogConfig,
                                    verbose_name=_("Section"),
                                    blank=True,
                                    null=True)
    hidden = models.BooleanField(name="hidden", default=False)
    reminder_disabled = models.BooleanField(default=False)
    current_blog_count = models.IntegerField(default=0)
    github_handle = models.TextField(null=True, blank=True, max_length=100)

    objects = UserProfileManager()
    all_objects = models.Manager()

    def confirm_proposal(self):
        self.proposal_confirmed = True
        self.save()
Beispiel #5
0
class BlogCategory(TranslatableModel):
    """
    Blog category
    """
    parent = models.ForeignKey('self',
                               verbose_name=_('parent'),
                               null=True,
                               blank=True)
    date_created = models.DateTimeField(_('created at'), auto_now_add=True)
    date_modified = models.DateTimeField(_('modified at'), auto_now=True)
    app_config = AppHookConfigField(BlogConfig,
                                    null=True,
                                    verbose_name=_('app. config'))

    translations = TranslatedFields(
        name=models.CharField(_('name'), max_length=255),
        slug=models.SlugField(_('slug'),
                              max_length=255,
                              blank=True,
                              db_index=True),
        meta={'unique_together': (('language_code', 'slug'), )})

    objects = AppHookConfigTranslatableManager()

    class Meta:
        verbose_name = _('blog category')
        verbose_name_plural = _('blog categories')

    @property
    def count(self):
        return self.blog_posts.namespace(
            self.app_config.namespace).published().count()

    def get_absolute_url(self, lang=None):
        if not lang:
            lang = get_language()
        if self.has_translation(lang, ):
            slug = self.safe_translation_getter('slug', language_code=lang)
            return reverse('%s:posts-category' % self.app_config.namespace,
                           kwargs={'category': slug},
                           current_app=self.app_config.namespace)
        # in case category doesn't exist in this language, gracefully fallback
        # to posts-latest
        return reverse('%s:posts-latest' % self.app_config.namespace,
                       current_app=self.app_config.namespace)

    def __str__(self):
        return self.safe_translation_getter('name')

    def save(self, *args, **kwargs):
        super(BlogCategory, self).save(*args, **kwargs)
        for lang in self.get_available_languages():
            self.set_current_language(lang)
            if not self.slug and self.name:
                self.slug = slugify(force_text(self.name))
        self.save_translations()
Beispiel #6
0
class Entry(models.Model):
    app_config = AppHookConfigField(FaqConfig)
    question = models.TextField(blank=True, default='')
    answer = models.TextField()

    objects = AppHookConfigManager()

    def __unicode__(self):
        return self.question

    class Meta:
        verbose_name_plural = 'entries'
Beispiel #7
0
class UserProfile(models.Model):
    ROLES = (
        (0, 'Others'),
        (1, 'Suborg Admin'),
        (2, 'Mentor'),
        (3, 'Student')
    )

    user = models.ForeignKey(User, on_delete=models.CASCADE)
    role = models.IntegerField(name='role', choices=ROLES, default=0)
    gsoc_year = models.ForeignKey(GsocYear, on_delete=models.CASCADE, null=True, blank=False)
    suborg_full_name = models.ForeignKey(SubOrg, on_delete=models.CASCADE, null=True, blank=False)
    accepted_proposal_pdf = models.FileField(blank=True, null=True)
    app_config = AppHookConfigField(NewsBlogConfig, verbose_name=_('Section'), blank=True, null=True)
Beispiel #8
0
class AdventCalenderDay(models.Model):
    app_config = AppHookConfigField(AdventCalendarConfig,
                                    verbose_name=_('calendar'),
                                    default=None)
    day = models.DateField(verbose_name=_('date'))
    placeholder = PlaceholderField(placeholder_name)
    order = models.IntegerField(verbose_name=_('display order'), default=0)

    def __str__(self):
        return _('Advent calendar') + ' ' + self.day.strftime('%Y-%m-%d')

    class Meta:
        verbose_name = _('Advent calendar day')
        verbose_name_plural = _('Advent calendar days')
Beispiel #9
0
class Booking(models.Model):
    app_config = AppHookConfigField(BookingConfig,
                                    verbose_name=_('Booking object'),
                                    default=None)
    name = models.CharField(verbose_name=_('name'), max_length=30, default='')
    id = models.AutoField(primary_key=True)
    madeBy = models.ForeignKey(User, verbose_name=_('made by'))
    start = models.DateTimeField(verbose_name=_('start'), unique=True)
    end = models.DateTimeField(verbose_name=_('end'), unique=True)

    objects = BookingManager()

    def __unicode__(self):
        return self.start.isoformat()

    class Meta:
        verbose_name = _('Booking')
        verbose_name_plural = _('Bookings')
Beispiel #10
0
class BasePostPlugin(CMSPlugin):
    app_config = AppHookConfigField(BlogConfig,
                                    null=True,
                                    verbose_name=_('app. config'),
                                    blank=True)

    class Meta:
        abstract = True

    def post_queryset(self, request=None):
        language = get_language()
        posts = Post._default_manager
        if self.app_config:
            posts = posts.namespace(self.app_config.namespace)
        posts = posts.active_translations(language_code=language)
        if not request or not getattr(request, 'toolbar',
                                      False) or not request.toolbar.edit_mode:
            posts = posts.published()
        return posts.all()
Beispiel #11
0
class BasePostPlugin(CMSPlugin):
    app_config = AppHookConfigField(BlogConfig,
                                    null=True,
                                    verbose_name=_("app. config"),
                                    blank=True)
    current_site = models.BooleanField(
        _("current site"),
        default=True,
        help_text=_("Select items from the current site only"))
    template_folder = models.CharField(
        max_length=200,
        verbose_name=_("Plugin template"),
        help_text=_("Select plugin template to load for this instance"),
        default=BLOG_PLUGIN_TEMPLATE_FOLDERS[0][0],
        choices=BLOG_PLUGIN_TEMPLATE_FOLDERS,
    )

    class Meta:
        abstract = True

    def optimize(self, qs):
        """
        Apply select_related / prefetch_related to optimize the view queries
        :param qs: queryset to optimize
        :return: optimized queryset
        """
        return qs.select_related("app_config").prefetch_related(
            "translations", "categories", "categories__translations",
            "categories__app_config")

    def post_queryset(self, request=None, published_only=True):
        language = get_language()
        posts = Post.objects
        if self.app_config:
            posts = posts.namespace(self.app_config.namespace)
        if self.current_site:
            posts = posts.on_site(get_current_site(request))
        posts = posts.active_translations(language_code=language)
        if (published_only or not request
                or not getattr(request, "toolbar", False)
                or not request.toolbar.edit_mode_active):
            posts = posts.published(current_site=self.current_site)
        return self.optimize(posts.all())
class Post(KnockerModel, BlogMetaMixin, TranslatableModel):
    """
    Blog post
    """
    author = models.ForeignKey(dj_settings.AUTH_USER_MODEL,
                               verbose_name=_('author'),
                               null=True,
                               blank=True,
                               related_name='djangocms_blog_post_author',
                               on_delete=models.PROTECT)

    date_created = models.DateTimeField(_('created'), auto_now_add=True)
    date_modified = models.DateTimeField(_('last modified'), auto_now=True)
    date_published = models.DateTimeField(_('published since'),
                                          null=True,
                                          blank=True)
    date_published_end = models.DateTimeField(_('published until'),
                                              null=True,
                                              blank=True)
    date_featured = models.DateTimeField(_('featured date'),
                                         null=True,
                                         blank=True)
    publish = models.BooleanField(_('publish'), default=False)
    categories = models.ManyToManyField('djangocms_blog.BlogCategory',
                                        verbose_name=_('category'),
                                        related_name='blog_posts',
                                        blank=True)
    main_image = FilerImageField(verbose_name=_('main image'),
                                 blank=True,
                                 null=True,
                                 on_delete=models.SET_NULL,
                                 related_name='djangocms_blog_post_image')
    main_image_thumbnail = models.ForeignKey(
        thumbnail_model,
        verbose_name=_('main image thumbnail'),
        related_name='djangocms_blog_post_thumbnail',
        on_delete=models.SET_NULL,
        blank=True,
        null=True)
    main_image_full = models.ForeignKey(
        thumbnail_model,
        verbose_name=_('main image full'),
        related_name='djangocms_blog_post_full',
        on_delete=models.SET_NULL,
        blank=True,
        null=True)
    enable_comments = models.BooleanField(
        verbose_name=_('enable comments on post'),
        default=get_setting('ENABLE_COMMENTS'))
    sites = models.ManyToManyField(
        'sites.Site',
        verbose_name=_('Site(s)'),
        blank=True,
        help_text=_('Select sites in which to show the post. '
                    'If none is set it will be '
                    'visible in all the configured sites.'))
    app_config = AppHookConfigField(BlogConfig,
                                    null=True,
                                    verbose_name=_('app.config'))

    translations = TranslatedFields(
        title=models.CharField(_('title'), max_length=752),
        slug=models.SlugField(_('slug'),
                              max_length=752,
                              blank=True,
                              db_index=True,
                              allow_unicode=True),
        subtitle=models.CharField(verbose_name=_('subtitle'),
                                  max_length=767,
                                  blank=True,
                                  default=''),
        abstract=HTMLField(_('abstract'),
                           blank=True,
                           default='',
                           configuration='BLOG_ABSTRACT_CKEDITOR'),
        meta_description=models.TextField(
            verbose_name=_('post meta description'), blank=True, default=''),
        meta_keywords=models.TextField(verbose_name=_('post meta keywords'),
                                       blank=True,
                                       default=''),
        meta_title=models.CharField(
            verbose_name=_('post meta title'),
            help_text=_('used in title tag and social sharing'),
            max_length=2000,
            blank=True,
            default=''),
        post_text=HTMLField(_('text'),
                            default='',
                            blank=True,
                            configuration='BLOG_POST_TEXT_CKEDITOR'),
        meta={'unique_together': (('language_code', 'slug'), )})
    media = PlaceholderField('media', related_name='media')
    content = PlaceholderField('post_content', related_name='post_content')
    liveblog = PlaceholderField('live_blog', related_name='live_blog')
    enable_liveblog = models.BooleanField(
        verbose_name=_('enable liveblog on post'), default=False)

    objects = GenericDateTaggedManager()
    tags = TaggableManager(blank=True, related_name='djangocms_blog_tags')

    related = SortedManyToManyField('self',
                                    verbose_name=_('Related Posts'),
                                    blank=True,
                                    symmetrical=False)

    amount = models.CharField(max_length=200,
                              default='R50',
                              choices=donations_amount)
    goal = models.CharField(max_length=200,
                            default='R30 000',
                            choices=donations_goal)

    _metadata = {
        'title': 'get_title',
        'description': 'get_description',
        'keywords': 'get_keywords',
        'og_description': 'get_description',
        'twitter_description': 'get_description',
        'gplus_description': 'get_description',
        'locale': 'get_locale',
        'image': 'get_image_full_url',
        'image_width': 'get_image_width',
        'image_height': 'get_image_height',
        'object_type': 'get_meta_attribute',
        'og_type': 'get_meta_attribute',
        'og_app_id': 'get_meta_attribute',
        'og_profile_id': 'get_meta_attribute',
        'og_publisher': 'get_meta_attribute',
        'og_author_url': 'get_meta_attribute',
        'og_author': 'get_meta_attribute',
        'twitter_type': 'get_meta_attribute',
        'twitter_site': 'get_meta_attribute',
        'twitter_author': 'get_meta_attribute',
        'gplus_type': 'get_meta_attribute',
        'gplus_author': 'get_meta_attribute',
        'published_time': 'date_published',
        'modified_time': 'date_modified',
        'expiration_time': 'date_published_end',
        'tag': 'get_tags',
        'url': 'get_absolute_url',
    }

    class Meta:
        verbose_name = _('blog article')
        verbose_name_plural = _('blog articles')
        ordering = ('-date_published', '-date_created')
        get_latest_by = 'date_published'

    def __str__(self):
        default = ugettext('Post (no translation)')
        return self.safe_translation_getter('title',
                                            any_language=True,
                                            default=default)

    @property
    def guid(self, language=None):
        if not language:
            language = self.get_current_language()
        base_string = '-{0}-{2}-{1}-'.format(
            language, self.app_config.namespace,
            self.safe_translation_getter('slug',
                                         language_code=language,
                                         any_language=True))
        return hashlib.sha256(force_bytes(base_string)).hexdigest()

    @property
    def date(self):
        if self.date_featured:
            return self.date_featured
        return self.date_published

    def save(self, *args, **kwargs):
        """
        Handle some auto configuration during save
        """
        if self.publish and self.date_published is None:
            self.date_published = timezone.now()
        if not self.slug and self.title:
            self.slug = slugify(self.title)
        super(Post, self).save(*args, **kwargs)

    def save_translation(self, translation, *args, **kwargs):
        """
        Handle some auto configuration during save
        """
        if not translation.slug and translation.title:
            translation.slug = slugify(translation.title)
        super(Post, self).save_translation(translation, *args, **kwargs)

    def get_absolute_url(self, lang=None):
        if not lang or lang not in self.get_available_languages():
            lang = get_language()
        if not lang or lang not in self.get_available_languages():
            lang = self.get_current_language()
        with switch_language(self, lang):
            category = self.categories.first()
            kwargs = {}
            if self.date_published:
                current_date = self.date_published
            else:
                current_date = self.date_created
            urlconf = get_setting('PERMALINK_URLS')[
                self.app_config.url_patterns]
            if '<year>' in urlconf:
                kwargs['year'] = current_date.year
            if '<month>' in urlconf:
                kwargs['month'] = '%02d' % current_date.month
            if '<day>' in urlconf:
                kwargs['day'] = '%02d' % current_date.day
            if '<slug>' in urlconf:
                kwargs['slug'] = self.safe_translation_getter(
                    'slug', language_code=lang, any_language=True)  # NOQA
            if '<category>' in urlconf:
                kwargs['category'] = category.safe_translation_getter(
                    'slug', language_code=lang, any_language=True)  # NOQA
            return reverse('%s:post-detail' % self.app_config.namespace,
                           kwargs=kwargs)

    def get_title(self):
        title = self.safe_translation_getter('meta_title', any_language=True)
        if not title:
            title = self.safe_translation_getter('title', any_language=True)
        return title.strip()

    def get_keywords(self):
        """
        Returns the list of keywords (as python list)
        :return: list
        """
        return self.safe_translation_getter('meta_keywords',
                                            default='').strip().split(',')

    def get_description(self):
        description = self.safe_translation_getter('meta_description',
                                                   any_language=True)
        if not description:
            description = self.safe_translation_getter('abstract',
                                                       any_language=True)
        return escape(strip_tags(description)).strip()

    def get_image_full_url(self):
        if self.main_image:
            return self.build_absolute_uri(self.main_image.url)
        return ''

    def get_image_width(self):
        if self.main_image:
            return self.main_image.width

    def get_image_height(self):
        if self.main_image:
            return self.main_image.height

    def get_tags(self):
        """
        Returns the list of object tags as comma separated list
        """
        taglist = [tag.name for tag in self.tags.all()]
        return ','.join(taglist)

    def get_author(self):
        """
        Return the author (user) objects
        """
        return self.author

    def _set_default_author(self, current_user):
        if not self.author_id and self.app_config.set_author:
            if get_setting('AUTHOR_DEFAULT') is True:
                user = current_user
            else:
                user = get_user_model().objects.get(
                    username=get_setting('AUTHOR_DEFAULT'))
            self.author = user

    def thumbnail_options(self):
        if self.main_image_thumbnail_id:
            return self.main_image_thumbnail.as_dict
        else:
            return get_setting('IMAGE_THUMBNAIL_SIZE')

    def full_image_options(self):
        if self.main_image_full_id:
            return self.main_image_full.as_dict
        else:
            return get_setting('IMAGE_FULL_SIZE')

    @property
    def is_published(self):
        """
        Checks wether the blog post is *really* published by checking publishing dates too
        """
        return (self.publish and
                (self.date_published and self.date_published <= timezone.now())
                and (self.date_published_end is None
                     or self.date_published_end > timezone.now()))

    def should_knock(self, signal_type, created=False):
        """
        Returns whether to emit knocks according to the post state
        """
        new = (self.app_config.send_knock_create and self.is_published
               and self.date_published == self.date_modified)
        updated = self.app_config.send_knock_update and self.is_published
        return (new or updated) and signal_type in ('post_save', 'post_delete')

    def get_cache_key(self, language, prefix):
        return 'djangocms-blog:{2}:{0}:{1}'.format(language, self.guid, prefix)

    @property
    def liveblog_group(self):
        return 'liveblog-{apphook}-{lang}-{post}'.format(
            lang=self.get_current_language(),
            apphook=self.app_config.namespace,
            post=self.safe_translation_getter('slug', any_language=True))

    def get_gallery_image(self):
        """
        Returns the best gallery image
        """
        from api.models import Gallery
        if self.id:
            gallery = Gallery.objects.filter(blog_post=self.id)
            if gallery:
                self.main_image = gallery[0].image
                self.save()
                return gallery[0].image
        return False
class Article(TranslatedAutoSlugifyMixin,
              TranslationHelperMixin,
              TranslatableModel):

    # TranslatedAutoSlugifyMixin options
    slug_source_field_name = 'title'
    slug_default = _('untitled-article')
    # when True, updates the article's search_data field
    # whenever the article is saved or a plugin is saved
    # on the article's content placeholder.
    update_search_on_save = getattr(
        settings,
        'ALDRYN_NEWSBLOG_UPDATE_SEARCH_DATA_ON_SAVE',
        False
    )

    translations = TranslatedFields(
        title=models.CharField(_('title'), max_length=234),
        slug=models.SlugField(
            verbose_name=_('slug'),
            max_length=255,
            db_index=True,
            blank=True,
            help_text=_(
                'Used in the URL. If changed, the URL will change. '
                'Clear it to have it re-created automatically.'),
        ),
        lead_in=HTMLField(
            verbose_name=_('lead'), default='',
            help_text=_(
                'The lead gives the reader the main idea of the story, this '
                'is useful in overviews, lists or as an introduction to your '
                'article.'
            ),
            blank=True,
        ),
        meta_title=models.CharField(
            max_length=255, verbose_name=_('meta title'),
            blank=True, default=''),
        meta_description=models.TextField(
            verbose_name=_('meta description'), blank=True, default=''),
        meta_keywords=models.TextField(
            verbose_name=_('meta keywords'), blank=True, default=''),
        meta={'unique_together': (('language_code', 'slug', ), )},

        search_data=models.TextField(blank=True, editable=False)
    )

    content = PlaceholderField('newsblog_article_content',
                               related_name='newsblog_article_content')
    author = models.ForeignKey(
        Person,
        null=True,
        blank=True,
        verbose_name='Author (Django CMS User)',
        on_delete=models.SET_NULL,
        help_text=_("Only used if AUTHOR is not set.")
    )
    author_override = models.ForeignKey(
        'Author',
        null=True,
        blank=True,
        verbose_name=_('author'),
        on_delete=models.SET_NULL,
    )
    app_config = AppHookConfigField(
        NewsBlogConfig,
        verbose_name=_('Section'),
        help_text='',
    )

    categories = ManyToManyField('aldryn_newsblog.Category',
                                 verbose_name=_('categories'),
                                 blank=True)

    publishing_date = models.DateTimeField(_('publishing date'),
                                           default=now)
    is_published = models.BooleanField(_('is published'), default=False,
                                       db_index=True)
    is_featured = models.BooleanField(_('is featured'), default=False,
                                      db_index=True)
    featured_image = FilerImageField(
        verbose_name=_('featured image'),
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )
    tags = TaggableManager(blank=True)

    # Setting "symmetrical" to False since it's a bit unexpected that if you
    # set "B relates to A" you immediately have also "A relates to B". It have
    # to be forced to False because by default it's True if rel.to is "self":
    #
    # https://github.com/django/django/blob/1.8.4/django/db/models/fields/related.py#L2144
    #
    # which in the end causes to add reversed releted-to entry as well:
    #
    # https://github.com/django/django/blob/1.8.4/django/db/models/fields/related.py#L977
    related = SortedManyToManyField(
        'self', verbose_name=_('related articles'), blank=True, symmetrical=False
    )

    article_tags = ManyToManyField(
        'ArticleTag', verbose_name=_('tags'), blank=True
    )

    objects = RelatedManager()

    class Meta:
        ordering = ['-publishing_date']

    @property
    def owner(self):
        return self.author.user

    @owner.setter
    def owner(self, user):
        person = get_person_by_user_model_instance(user)
        self.author = person
        self.save()

    @property
    def published(self):
        """
        Returns True only if the article (is_published == True) AND has a
        published_date that has passed.
        """
        return self.is_published and self.publishing_date <= now()

    @property
    def future(self):
        """
        Returns True if the article is published but is scheduled for a
        future date/time.
        """
        return self.is_published and self.publishing_date > now()

    def get_absolute_url(self, language=None):
        """Returns the url for this Article in the selected permalink format."""
        if not language:
            language = get_current_language()
        kwargs = {}
        permalink_type = self.app_config.permalink_type
        if 'y' in permalink_type:
            kwargs.update(year=self.publishing_date.year)
        if 'm' in permalink_type:
            kwargs.update(month="%02d" % self.publishing_date.month)
        if 'd' in permalink_type:
            kwargs.update(day="%02d" % self.publishing_date.day)
        if 'i' in permalink_type:
            kwargs.update(pk=self.pk)
        if 's' in permalink_type:
            slug, lang = self.known_translation_getter(
                'slug', default=None, language_code=language)
            if slug and lang:
                site_id = getattr(settings, 'SITE_ID', None)
                if get_redirect_on_fallback(language, site_id):
                    language = lang
                kwargs.update(slug=slug)

        if self.app_config and self.app_config.namespace:
            namespace = '{0}:'.format(self.app_config.namespace)
        else:
            namespace = ''

        with override(language):
            return reverse('{0}article-detail'.format(namespace), kwargs=kwargs)

    def get_search_data(self, language=None, request=None):
        """
        Provides an index for use with Haystack, or, for populating
        Article.translations.search_data.
        """
        if not self.pk:
            return ''
        if language is None:
            language = get_current_language()
        if request is None:
            request = get_request(language=language)
        description = self.safe_translation_getter('lead_in', '')
        text_bits = [strip_tags(description)]
        for category in self.categories.all():
            text_bits.append(
                force_text(category.safe_translation_getter('name')))
        for tag in self.article_tags.all():
            text_bits.append(force_text(tag.name))
        if self.content:
            plugins = self.content.cmsplugin_set.filter(language=language)
            for base_plugin in plugins:
                plugin_text_content = ' '.join(
                    get_plugin_index_data(base_plugin, request))
                text_bits.append(plugin_text_content)
        return ' '.join(text_bits)

    def save(self, *args, **kwargs):
        # Update the search index
        if self.update_search_on_save:
            self.search_data = self.get_search_data()

        # slug would be generated by TranslatedAutoSlugifyMixin
        super(Article, self).save(*args, **kwargs)

    def __str__(self):
        try:
            return self.safe_translation_getter('title', any_language=True)
        except ValueError:
            default_language = settings.LANGUAGE_CODE
            return self.safe_translation_getter('title', language_code=default_language, any_language=True)

    def get_author(self):
        return self.author_override or self.author

    def clean(self):
        if not self.author_override and not self.author:
            raise ValidationError(
                _("You must specify either AUTHOR or AUTHOR (DJANGO CMS USER)."),
                code='missing'
            )

    def add_tag(self, tag_slug, tag_name=None):
        cleaned_slug = slugify(tag_slug)
        if not cleaned_slug in self.article_tags.all().values_list('translations__slug', flat=True):
            article_tag = ArticleTag.objects.get_or_create(
                translations__slug=cleaned_slug,
                newsblog_config=self.app_config,
                defaults={
                    'slug': cleaned_slug,
                    'name': tag_name or tag_slug,
                    'newsblog_config': self.app_config,
                }
            )[0]
            self.article_tags.add(article_tag)

    def add_tags(self, tags):
        for tag in tags:
            self.add_tag(tag)
Beispiel #14
0
class Post(ModelMeta, TranslatableModel):
    """
    Blog post
    """
    author = models.ForeignKey(dj_settings.AUTH_USER_MODEL,
                               verbose_name=_('author'),
                               null=True,
                               blank=True,
                               related_name='djangocms_blog_post_author')

    date_created = models.DateTimeField(_('created'), auto_now_add=True)
    date_modified = models.DateTimeField(_('last modified'), auto_now=True)
    date_published = models.DateTimeField(_('published since'),
                                          default=timezone.now)
    date_published_end = models.DateTimeField(_('published until'),
                                              null=True,
                                              blank=True)
    publish = models.BooleanField(_('publish'), default=False)
    categories = models.ManyToManyField(
        'djangocms_blog.BlogCategory',
        verbose_name=_('category'),
        related_name='blog_posts',
    )
    main_image = FilerImageField(verbose_name=_('main image'),
                                 blank=True,
                                 null=True,
                                 on_delete=models.SET_NULL,
                                 related_name='djangocms_blog_post_image')
    main_image_thumbnail = models.ForeignKey(
        'cmsplugin_filer_image.ThumbnailOption',
        verbose_name=_('main image thumbnail'),
        related_name='djangocms_blog_post_thumbnail',
        on_delete=models.SET_NULL,
        blank=True,
        null=True)
    main_image_full = models.ForeignKey(
        'cmsplugin_filer_image.ThumbnailOption',
        verbose_name=_('main image full'),
        related_name='djangocms_blog_post_full',
        on_delete=models.SET_NULL,
        blank=True,
        null=True)
    enable_comments = models.BooleanField(
        verbose_name=_('enable comments on post'),
        default=get_setting('ENABLE_COMMENTS'))
    sites = models.ManyToManyField(
        'sites.Site',
        verbose_name=_('Site(s)'),
        blank=True,
        help_text=_('Select sites in which to show the post. '
                    'If none is set it will be '
                    'visible in all the configured sites.'))
    app_config = AppHookConfigField(BlogConfig,
                                    null=True,
                                    verbose_name=_('app. config'))

    translations = TranslatedFields(
        title=models.CharField(_('title'), max_length=255),
        slug=models.SlugField(_('slug'), blank=True, db_index=True),
        abstract=HTMLField(_('abstract'), blank=True, default=''),
        meta_description=models.TextField(
            verbose_name=_('post meta description'), blank=True, default=''),
        meta_keywords=models.TextField(verbose_name=_('post meta keywords'),
                                       blank=True,
                                       default=''),
        meta_title=models.CharField(
            verbose_name=_('post meta title'),
            help_text=_('used in title tag and social sharing'),
            max_length=255,
            blank=True,
            default=''),
        post_text=HTMLField(_('text'), default='', blank=True),
        meta={'unique_together': (('language_code', 'slug'), )})
    content = PlaceholderField('post_content', related_name='post_content')

    objects = GenericDateTaggedManager()
    tags = TaggableManager(blank=True, related_name='djangocms_blog_tags')

    _metadata = {
        'title': 'get_title',
        'description': 'get_description',
        'keywords': 'get_keywords',
        'og_description': 'get_description',
        'twitter_description': 'get_description',
        'gplus_description': 'get_description',
        'locale': 'get_locale',
        'image': 'get_image_full_url',
        'object_type': 'get_meta_attribute',
        'og_type': 'get_meta_attribute',
        'og_app_id': 'get_meta_attribute',
        'og_profile_id': 'get_meta_attribute',
        'og_publisher': 'get_meta_attribute',
        'og_author_url': 'get_meta_attribute',
        'og_author': 'get_meta_attribute',
        'twitter_type': 'get_meta_attribute',
        'twitter_site': 'get_meta_attribute',
        'twitter_author': 'get_meta_attribute',
        'gplus_type': 'get_meta_attribute',
        'gplus_author': 'get_meta_attribute',
        'published_time': 'date_published',
        'modified_time': 'date_modified',
        'expiration_time': 'date_published_end',
        'tag': 'get_tags',
        'url': 'get_absolute_url',
    }

    class Meta:
        verbose_name = _('blog article')
        verbose_name_plural = _('blog articles')
        ordering = ('-date_published', '-date_created')
        get_latest_by = 'date_published'

    def __str__(self):
        return self.safe_translation_getter('title')

    def get_absolute_url(self, lang=None):
        if not lang:
            lang = get_language()
        category = self.categories.first()
        kwargs = {}
        urlconf = get_setting('PERMALINK_URLS')[self.app_config.url_patterns]
        if '<year>' in urlconf:
            kwargs['year'] = self.date_published.year
        if '<month>' in urlconf:
            kwargs['month'] = '%02d' % self.date_published.month
        if '<day>' in urlconf:
            kwargs['day'] = '%02d' % self.date_published.day
        if '<slug>' in urlconf:
            kwargs['slug'] = self.safe_translation_getter(
                'slug', language_code=lang, any_language=True)  # NOQA
        if '<category>' in urlconf:
            kwargs['category'] = category.safe_translation_getter(
                'slug', language_code=lang, any_language=True)  # NOQA
        return reverse('%s:post-detail' % self.app_config.namespace,
                       kwargs=kwargs)

    def get_meta_attribute(self, param):
        """
        Retrieves django-meta attributes from apphook config instance
        :param param: django-meta attribute passed as key
        """
        attr = None
        value = getattr(self.app_config, param)
        if value:
            attr = getattr(self, value, None)
        if attr is not None:
            if callable(attr):
                try:
                    data = attr(param)
                except TypeError:
                    data = attr()
            else:
                data = attr
        else:
            data = value
        return data

    def save_translation(self, translation, *args, **kwargs):
        if not translation.slug and translation.title:
            translation.slug = slugify(translation.title)
        super(Post, self).save_translation(translation, *args, **kwargs)

    def get_title(self):
        title = self.safe_translation_getter('meta_title', any_language=True)
        if not title:
            title = self.safe_translation_getter('title', any_language=True)
        return title.strip()

    def get_keywords(self):
        return self.safe_translation_getter('meta_keywords').strip().split(',')

    def get_locale(self):
        return self.get_current_language()

    def get_description(self):
        description = self.safe_translation_getter('meta_description',
                                                   any_language=True)
        if not description:
            description = self.safe_translation_getter('abstract',
                                                       any_language=True)
        return escape(strip_tags(description)).strip()

    def get_image_full_url(self):
        if self.main_image:
            return self.make_full_url(self.main_image.url)
        return ''

    def get_tags(self):
        taglist = [tag.name for tag in self.tags.all()]
        return ','.join(taglist)

    def get_author(self):
        return self.author

    def thumbnail_options(self):
        if self.main_image_thumbnail_id:
            return self.main_image_thumbnail.as_dict
        else:
            return get_setting('IMAGE_THUMBNAIL_SIZE')

    def full_image_options(self):
        if self.main_image_full_id:
            return self.main_image_full.as_dict
        else:
            return get_setting('IMAGE_FULL_SIZE')

    def get_full_url(self):
        return self.make_full_url(self.get_absolute_url())
Beispiel #15
0
class Article(CustomArticleMixin, TranslatedAutoSlugifyMixin,
              TranslationHelperMixin, TranslatableModel):

    # TranslatedAutoSlugifyMixin options
    slug_source_field_name = 'title'
    slug_default = _('untitled-article')
    # when True, updates the article's search_data field
    # whenever the article is saved or a plugin is saved
    # on the article's content placeholder.
    update_search_on_save = getattr(
        settings,
        'ALDRYN_NEWSBLOG_UPDATE_SEARCH_DATA_ON_SAVE', False) or getattr(
            settings, 'ALDRYN_NEWSBLOG_AUTO_CALCULATE_READ_TIME', False)

    translations = TranslatedFields(
        title=models.CharField(_('title'), max_length=234),
        slug=models.SlugField(
            verbose_name=_('slug'),
            max_length=255,
            db_index=True,
            blank=True,
            help_text=_('Used in the URL. If changed, the URL will change. '
                        'Clear it to have it re-created automatically.'),
        ),
        lead_in=HTMLField(
            verbose_name=_('Summary'),
            default='',
            help_text=_(
                'The Summary gives the reader the main idea of the story, this '
                'is useful in overviews, lists or as an introduction to your '
                'article.'),
            blank=True,
        ),
        read_time=models.CharField(max_length=255,
                                   verbose_name=_('Read time'),
                                   blank=True,
                                   default=''),
        meta_title=models.CharField(max_length=255,
                                    verbose_name=_('meta title'),
                                    blank=True,
                                    default=''),
        meta_description=models.TextField(verbose_name=_('meta description'),
                                          blank=True,
                                          default=''),
        meta_keywords=models.TextField(verbose_name=_('meta keywords'),
                                       blank=True,
                                       default=''),
        meta={'unique_together': ((
            'language_code',
            'slug',
        ), )},
        search_data=models.TextField(blank=True, editable=False),
        author_trans=models.ForeignKey(Person,
                                       on_delete=models.SET_NULL,
                                       related_name='articles_trans',
                                       null=True,
                                       blank=True,
                                       verbose_name=_('author')),
        author_2_trans=models.ForeignKey(Person,
                                         on_delete=models.SET_NULL,
                                         related_name='articles_trans_2',
                                         null=True,
                                         blank=True,
                                         verbose_name=_('second author')),
        author_3_trans=models.ForeignKey(Person,
                                         on_delete=models.SET_NULL,
                                         related_name='articles_trans_3',
                                         null=True,
                                         blank=True,
                                         verbose_name=_('third author')),
        is_published_trans=models.BooleanField(_('is published'),
                                               default=False,
                                               db_index=True),
        is_featured_trans=models.BooleanField(_('is featured'),
                                              default=False,
                                              db_index=True),
    )

    content = PlaceholderField('newsblog_article_content',
                               related_name='newsblog_article_content')
    related_articles = PlaceholderField(
        'newsblog_related_articles', related_name='newsblog_related_articles')
    article_carousel = PlaceholderField(
        'newsblog_article_carousel', related_name='newsblog_article_carousel')
    article_sidebar = PlaceholderField('newsblog_article_sidebar',
                                       related_name='newsblog_article_sidebar')
    hide_authors = models.BooleanField(
        _('Hide Authors'),
        default=False,
    )
    author = models.ForeignKey(Person,
                               on_delete=models.SET_NULL,
                               null=True,
                               blank=True,
                               verbose_name=_('author'))
    author_2 = models.ForeignKey(Person,
                                 on_delete=models.SET_NULL,
                                 related_name='author_2',
                                 null=True,
                                 blank=True,
                                 verbose_name=_('second author'))
    author_3 = models.ForeignKey(Person,
                                 on_delete=models.SET_NULL,
                                 related_name='author_3',
                                 null=True,
                                 blank=True,
                                 verbose_name=_('third author'))
    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              on_delete=models.SET_NULL,
                              verbose_name=_('owner'),
                              null=True,
                              blank=True)
    app_config = AppHookConfigField(
        NewsBlogConfig,
        on_delete=models.CASCADE,
        verbose_name=_('Section'),
        help_text='',
    )
    locations = SortedManyToManyField('js_locations.location',
                                      verbose_name=_('locations'),
                                      blank=True)
    categories = CategoryManyToManyField('aldryn_categories.Category',
                                         verbose_name=_('categories'),
                                         blank=True)
    services = SortedManyToManyField('js_services.Service',
                                     verbose_name=_('services'),
                                     blank=True)
    feeds = models.ManyToManyField(NewsBlogFeed,
                                   verbose_name=_('feeds'),
                                   blank=True)
    publishing_date = models.DateTimeField(_('publishing date'), default=now)
    is_published = models.BooleanField(_('is published'),
                                       default=False,
                                       db_index=True)
    is_featured = models.BooleanField(_('is featured'),
                                      default=False,
                                      db_index=True)
    featured_image = FilerImageField(
        verbose_name=_('featured image'),
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )
    share_image = FilerImageField(
        verbose_name=_('social share image'),
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        help_text=
        'This image will only be shown on social channels. Minimum size: 1200x630px',
        related_name='+')
    logo_image = FilerImageField(verbose_name=_('logo image'),
                                 null=True,
                                 blank=True,
                                 on_delete=models.SET_NULL,
                                 related_name='+')
    svg_image = FilerFileField(
        verbose_name=_('logo SVG'),
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )
    medium = models.ForeignKey(ArticleMedium,
                               on_delete=models.SET_NULL,
                               verbose_name=_('medium'),
                               null=True,
                               blank=True)

    show_on_sitemap = models.BooleanField(_('Show on sitemap'),
                                          null=False,
                                          default=True)
    show_on_xml_sitemap = models.BooleanField(_('Show on xml sitemap'),
                                              null=False,
                                              default=True)
    noindex = models.BooleanField(_('noindex'), null=False, default=False)
    nofollow = models.BooleanField(_('nofollow'), null=False, default=False)
    canonical_url = models.CharField(blank=True,
                                     null=True,
                                     max_length=255,
                                     verbose_name=_('Canonical URL'))

    layout = models.CharField(blank=True,
                              default='',
                              max_length=60,
                              verbose_name=_('layout'))
    custom_fields = JSONField(blank=True, null=True)
    # Setting "symmetrical" to False since it's a bit unexpected that if you
    # set "B relates to A" you immediately have also "A relates to B". It have
    # to be forced to False because by default it's True if rel.to is "self":
    #
    # https://github.com/django/django/blob/1.8.4/django/db/models/fields/related.py#L2144
    #
    # which in the end causes to add reversed releted-to entry as well:
    #
    # https://github.com/django/django/blob/1.8.4/django/db/models/fields/related.py#L977
    related = SortedManyToManyField('self',
                                    verbose_name=_('specific articles'),
                                    blank=True,
                                    symmetrical=False)

    objects = RelatedManager()
    all_objects = AllManager()
    search_objects = SearchManager()

    class Meta:
        ordering = ['-publishing_date']

    def get_class(self):
        '''Return class name'''
        return self.__class__.__name__

    @property
    def type(self):
        '''Article Type / Section.'''
        return self.app_config

    @cached_property
    def cached_type(self):
        '''Article Type / Section.'''
        return self.app_config

    @property
    def type_slug(self):
        '''Article Type / Section Machine Name'''
        return self.app_config.namespace

    @property
    def published(self):
        """
        Returns True only if the article (is_published == True) AND has a
        published_date that has passed.
        """
        language = get_current_language()
        return self.published_for_language(language)

    def published_for_language(self, language):
        if TRANSLATE_IS_PUBLISHED:
            return ((self.safe_translation_getter('is_published_trans',
                                                  language_code=language,
                                                  any_language=False) or False)
                    and self.publishing_date <= now())
        return (self.is_published and self.publishing_date <= now())

    @property
    def authors(self):
        authors = []
        if TRANSLATE_AUTHORS:
            if self.author_trans and self.author_trans.published:
                authors.append(self.author_trans)
            if self.author_2_trans and self.author_2_trans.published:
                authors.append(self.author_2_trans)
            if self.author_3_trans and self.author_3_trans.published:
                authors.append(self.author_3_trans)
        else:
            if self.author and self.author.published:
                authors.append(self.author)
            if self.author_2 and self.author_2.published:
                authors.append(self.author_2)
            if self.author_3 and self.author_3.published:
                authors.append(self.author_3)
        return authors

    @property
    def future(self):
        """
        Returns True if the article is published but is scheduled for a
        future date/time.
        """
        return (self.is_published and self.publishing_date > now())

    def get_absolute_url(self, language=None):
        """Returns the url for this Article in the selected permalink format."""
        if not language:
            language = get_current_language()
        kwargs = {}
        permalink_type = self.cached_type.permalink_type
        if 'y' in permalink_type:
            kwargs.update(year=self.publishing_date.year)
        if 'm' in permalink_type:
            kwargs.update(month="%02d" % self.publishing_date.month)
        if 'd' in permalink_type:
            kwargs.update(day="%02d" % self.publishing_date.day)
        if 'i' in permalink_type:
            kwargs.update(pk=self.pk)
        if 's' in permalink_type:
            slug, lang = self.known_translation_getter('slug',
                                                       default=None,
                                                       language_code=language)
            if slug and lang:
                site_id = getattr(settings, 'SITE_ID', None)
                if get_redirect_on_fallback(language, site_id):
                    language = lang
                kwargs.update(slug=slug)

        if self.cached_type.namespace:
            namespace = self.cached_type.namespace
            try:
                reverse('{0}:article-list'.format(namespace))
            except:
                namespace = NewsBlogConfig.default_namespace
        else:
            namespace = NewsBlogConfig.default_namespace

        with override(language):
            return reverse('{0}:article-detail'.format(namespace),
                           kwargs=kwargs)

    def get_public_url(self, language=None):
        if not language:
            language = get_current_language()
        if not TRANSLATE_IS_PUBLISHED and self.published:
            return self.get_absolute_url(language)
        if (TRANSLATE_IS_PUBLISHED and \
                (self.safe_translation_getter('is_published_trans', language_code=language, any_language=False) or False) and \
                self.publishing_date <= now()):
            return self.get_absolute_url(language)
        return ''

    def get_search_data(self, language=None, request=None):
        """
        Provides an index for use with Haystack, or, for populating
        Article.translations.search_data.
        """
        if not self.pk:
            return ''
        if language is None:
            language = get_current_language()
        if request is None:
            request = get_request(language=language)
        title = self.safe_translation_getter('title', '')
        description = self.safe_translation_getter('lead_in', '')
        text_bits = [title, strip_tags(description)]
        for category in self.categories.all():
            text_bits.append(
                force_unicode(category.safe_translation_getter('name')))
        for service in self.services.all():
            text_bits.append(
                force_unicode(service.safe_translation_getter('title')))
        text_bits.append('=c=o=n=t=e=n=t=')
        if self.content:
            plugins = self.content.cmsplugin_set.filter(language=language)
            for base_plugin in plugins:
                plugin_text_content = ' '.join(
                    get_plugin_index_data(base_plugin, request))
                text_bits.append(plugin_text_content)
        return ' '.join(text_bits)

    def save(self, *args, **kwargs):
        # Update the search index
        if self.update_search_on_save:
            self.search_data = self.get_search_data()
            auto_read_time = getattr(
                settings, 'ALDRYN_NEWSBLOG_AUTO_CALCULATE_READ_TIME', False)
            if callable(auto_read_time):
                auto_read_time = auto_read_time(self)
            if auto_read_time and self.app_config.auto_read_time:
                read_time = self.get_read_time()
                if read_time:
                    self.read_time = read_time
        # Ensure there is an owner.
        if self.app_config.create_authors and self.owner and self.author is None:
            if hasattr(Person, 'first_name') and hasattr(Person, 'last_name'):
                defaults = {
                    'first_name': self.owner.first_name,
                    'last_name': self.owner.last_name,
                }
            else:
                defaults = {
                    'name':
                    ' '.join((
                        self.owner.first_name,
                        self.owner.last_name,
                    )),
                }
            self.author = Person.objects.get_or_create(user=self.owner,
                                                       defaults=defaults)[0]
        # slug would be generated by TranslatedAutoSlugifyMixin
        #if not self.medium:
        #self.medium = ArticleMedium.objects.first()
        super(Article, self).save(*args, **kwargs)

    def get_read_time(self):
        if '=c=o=n=t=e=n=t=' in self.search_data:
            read_time_function = getattr(
                settings, 'ALDRYN_NEWSBLOG_READ_TIME_FUNCTION',
                lambda x: x // 200 + (0 if x % 200 == 0 else 1))
            return read_time_function(
                len(self.search_data.split('=c=o=n=t=e=n=t=')[1].split()))

    def get_placeholders(self):
        return [
            self.content, self.related_articles, self.article_carousel,
            self.article_sidebar
        ]

    def __str__(self):
        return self.safe_translation_getter('title', any_language=True)

    def get_related_articles_by_services(self, article_category=None):
        articles = self.__class__.objects.published().filter(
            services__in=self.services.all()).distinct().exclude(id=self.id)
        if article_category:
            return articles.namespace(article_category)
        return articles

    def get_related_articles_by_categories(self, article_category=None):
        articles = self.__class__.objects.published().filter(
            categories__in=self.categories.all()).distinct().exclude(
                id=self.id)
        if article_category:
            return articles.namespace(article_category)
        return articles

    def related_articles_by_services(self):
        return self.get_related_articles_by_services()

    def related_articles_by_categories(self):
        return self.get_related_articles_by_categories()

    def related_articles_same_type_by_services(self):
        return self.get_related_articles_by_services(self.app_config.namespace)

    def related_articles_same_type_by_categories(self):
        return self.get_related_articles_by_categories(
            self.app_config.namespace)
Beispiel #16
0
class News(Article):
    config = AppHookConfigField(AnotherExampleConfig, verbose_name='config')

    objects = AppHookConfigManager()
Beispiel #17
0
class Post(KnockerModel, BlogMetaMixin, TranslatableModel):
    """
    Blog post
    """

    author = models.ForeignKey(
        dj_settings.AUTH_USER_MODEL,
        verbose_name=_("author"),
        null=True,
        blank=True,
        related_name="djangocms_blog_post_author",
        on_delete=models.PROTECT,
    )

    date_created = models.DateTimeField(_("created"), auto_now_add=True)
    date_modified = models.DateTimeField(_("last modified"), auto_now=True)
    date_published = models.DateTimeField(_("published since"),
                                          null=True,
                                          blank=True)
    date_published_end = models.DateTimeField(_("published until"),
                                              null=True,
                                              blank=True)
    date_featured = models.DateTimeField(_("featured date"),
                                         null=True,
                                         blank=True)
    categories = models.ManyToManyField("djangocms_blog.BlogCategory",
                                        verbose_name=_("category"),
                                        related_name="blog_posts",
                                        blank=True)

    enable_comments = models.BooleanField(
        verbose_name=_("enable comments on post"),
        default=get_setting("ENABLE_COMMENTS"))
    sites = models.ManyToManyField(
        "sites.Site",
        verbose_name=_("Site(s)"),
        blank=True,
        help_text=_("Select sites in which to show the post. "
                    "If none is set it will be "
                    "visible in all the configured sites."),
    )
    app_config = AppHookConfigField(BlogConfig,
                                    null=True,
                                    verbose_name=_("app. config"))

    translations = TranslatedFields(
        title=models.CharField(_("title"), max_length=752),
        slug=models.SlugField(_("slug"),
                              max_length=752,
                              blank=True,
                              db_index=True,
                              allow_unicode=True),
        subtitle=models.CharField(verbose_name=_("subtitle"),
                                  max_length=767,
                                  blank=True,
                                  default=""),
        abstract=HTMLField(_("abstract"),
                           blank=True,
                           default="",
                           configuration="BLOG_ABSTRACT_CKEDITOR"),
        main_image=FilerImageField(
            verbose_name=_("main image"),
            blank=True,
            null=True,
            on_delete=models.SET_NULL,
            related_name="djangocms_blog_post_image",
        ),
        main_image_thumbnail=models.ForeignKey(
            thumbnail_model,
            verbose_name=_("main image thumbnail"),
            related_name="djangocms_blog_post_thumbnail",
            on_delete=models.SET_NULL,
            blank=True,
            null=True,
        ),
        main_image_full=models.ForeignKey(
            thumbnail_model,
            verbose_name=_("main image full"),
            related_name="djangocms_blog_post_full",
            on_delete=models.SET_NULL,
            blank=True,
            null=True,
        ),
        meta_description=models.TextField(
            verbose_name=_("post meta description"), blank=True, default=""),
        meta_keywords=models.TextField(verbose_name=_("post meta keywords"),
                                       blank=True,
                                       default=""),
        meta_title=models.CharField(
            verbose_name=_("post meta title"),
            help_text=_("used in title tag and social sharing"),
            max_length=2000,
            blank=True,
            default="",
        ),
        post_text=HTMLField(_("text"),
                            default="",
                            blank=True,
                            configuration="BLOG_POST_TEXT_CKEDITOR"),
        meta={"unique_together": (("language_code", "slug"), )},
        is_publish=models.BooleanField(_('publish'), default=False),
    )
    media = PlaceholderField("media", related_name="media")
    content = PlaceholderField("post_content", related_name="post_content")
    liveblog = PlaceholderField("live_blog", related_name="live_blog")
    enable_liveblog = models.BooleanField(
        verbose_name=_("enable liveblog on post"), default=False)

    objects = GenericDateTaggedManager()
    tags = TaggableManager(blank=True, related_name="djangocms_blog_tags")

    related = SortedManyToManyField("self",
                                    verbose_name=_("Related Posts"),
                                    blank=True,
                                    symmetrical=False)

    _metadata = {
        "title": "get_title",
        "description": "get_description",
        "keywords": "get_keywords",
        "og_description": "get_description",
        "twitter_description": "get_description",
        "gplus_description": "get_description",
        "locale": "get_locale",
        "image": "get_image_full_url",
        "image_width": "get_image_width",
        "image_height": "get_image_height",
        "object_type": "get_meta_attribute",
        "og_type": "get_meta_attribute",
        "og_app_id": "get_meta_attribute",
        "og_profile_id": "get_meta_attribute",
        "og_publisher": "get_meta_attribute",
        "og_author_url": "get_meta_attribute",
        "og_author": "get_meta_attribute",
        "twitter_type": "get_meta_attribute",
        "twitter_site": "get_meta_attribute",
        "twitter_author": "get_meta_attribute",
        "gplus_type": "get_meta_attribute",
        "gplus_author": "get_meta_attribute",
        "published_time": "date_published",
        "modified_time": "date_modified",
        "expiration_time": "date_published_end",
        "tag": "get_tags",
        "url": "get_absolute_url",
    }

    class Meta:
        verbose_name = _("blog article")
        verbose_name_plural = _("blog articles")
        ordering = ("-date_published", "-date_created")
        get_latest_by = "date_published"

    def __str__(self):
        default = gettext("Post (no translation)")
        return self.safe_translation_getter("title",
                                            any_language=True,
                                            default=default)

    @property
    def guid(self, language=None):
        if not language:
            language = self.get_current_language()
        base_string = "-{0}-{2}-{1}-".format(
            language,
            self.app_config.namespace,
            self.safe_translation_getter("slug",
                                         language_code=language,
                                         any_language=True),
        )
        return hashlib.sha256(force_bytes(base_string)).hexdigest()

    @property
    def date(self):
        if self.date_featured:
            return self.date_featured
        return self.date_published

    def save(self, *args, **kwargs):
        """
        Handle some auto configuration during save
        """
        if self.safe_translation_getter(
                "is_publish") and self.date_published is None:
            self.date_published = timezone.now()
        if not self.slug and self.title:
            self.slug = slugify(self.title)
        super().save(*args, **kwargs)

    def save_translation(self, translation, *args, **kwargs):
        """
        Handle some auto configuration during save
        """
        if not translation.slug and translation.title:
            translation.slug = slugify(translation.title)
        super().save_translation(translation, *args, **kwargs)

    def get_absolute_url(self, lang=None):
        lang = _get_language(self, lang)
        with switch_language(self, lang):
            category = self.categories.first()
            kwargs = {}
            if self.date_published:
                current_date = self.date_published
            else:
                current_date = self.date_created
            urlconf = get_setting("PERMALINK_URLS")[
                self.app_config.url_patterns]
            if "<year>" in urlconf:
                kwargs["year"] = current_date.year
            if "<month>" in urlconf:
                kwargs["month"] = "%02d" % current_date.month
            if "<day>" in urlconf:
                kwargs["day"] = "%02d" % current_date.day
            if "<slug>" in urlconf:
                kwargs["slug"] = self.safe_translation_getter(
                    "slug", language_code=lang, any_language=True)  # NOQA
            if "<category>" in urlconf:
                kwargs["category"] = category.safe_translation_getter(
                    "slug", language_code=lang, any_language=True)  # NOQA
            return reverse("%s:post-detail" % self.app_config.namespace,
                           kwargs=kwargs)

    def get_title(self):
        title = self.safe_translation_getter("meta_title", any_language=True)
        if not title:
            title = self.safe_translation_getter("title", any_language=True)
        return title.strip()

    def get_keywords(self):
        """
        Returns the list of keywords (as python list)
        :return: list
        """
        return self.safe_translation_getter("meta_keywords",
                                            default="").strip().split(",")

    def get_description(self):
        description = self.safe_translation_getter("meta_description",
                                                   any_language=True)
        if not description:
            description = self.safe_translation_getter("abstract",
                                                       any_language=True)
        return escape(strip_tags(description)).strip()

    def get_image_full_url(self):
        if self.main_image:
            return self.build_absolute_uri(self.main_image.url)
        return ""

    def get_image_width(self):
        if self.main_image:
            return self.main_image.width

    def get_image_height(self):
        if self.main_image:
            return self.main_image.height

    def get_tags(self):
        """
        Returns the list of object tags as comma separated list
        """
        taglist = [tag.name for tag in self.tags.all()]
        return ",".join(taglist)

    def get_author(self):
        """
        Return the author (user) objects
        """
        return self.author

    def _set_default_author(self, current_user):
        if not self.author_id and self.app_config.set_author:
            if get_setting("AUTHOR_DEFAULT") is True:
                user = current_user
            else:
                user = get_user_model().objects.get(
                    username=get_setting("AUTHOR_DEFAULT"))
            self.author = user

    def thumbnail_options(self):
        if self.main_image_thumbnail_id:
            return self.main_image_thumbnail.as_dict
        else:
            return get_setting("IMAGE_THUMBNAIL_SIZE")

    def full_image_options(self):
        if self.main_image_full_id:
            return self.main_image_full.as_dict
        else:
            return get_setting("IMAGE_FULL_SIZE")

    @property
    def is_published(self):
        """
        Checks wether the blog post is *really* published by checking publishing dates too
        """
        return (self.safe_translation_getter("is_publish") and
                (self.date_published and self.date_published <= timezone.now())
                and (self.date_published_end is None
                     or self.date_published_end > timezone.now()))

    def should_knock(self, signal_type, created=False):
        """
        Returns whether to emit knocks according to the post state
        """
        new = self.app_config.send_knock_create and self.is_published and self.date_published == self.date_modified
        updated = self.app_config.send_knock_update and self.is_published
        return (new or updated) and signal_type in ("post_save", "post_delete")

    def get_cache_key(self, language, prefix):
        return "djangocms-blog:{2}:{0}:{1}".format(language, self.guid, prefix)

    @property
    def liveblog_group(self):
        return "liveblog-{apphook}-{lang}-{post}".format(
            lang=self.get_current_language(),
            apphook=self.app_config.namespace,
            post=self.safe_translation_getter("slug", any_language=True),
        )
Beispiel #18
0
class Event(CustomEventMixin, TranslatedAutoSlugifyMixin,
            TranslationHelperMixin, TranslatableModel):

    # TranslatedAutoSlugifyMixin options
    slug_source_field_name = 'title'
    slug_default = _('untitled-event')
    # when True, updates the event's search_data field
    # whenever the event is saved or a plugin is saved
    # on the event's content placeholder.
    update_search_on_save = getattr(settings,
                                    'EVENTS_UPDATE_SEARCH_DATA_ON_SAVE', False)

    translations = TranslatedFields(
        title=models.CharField(_('title'), max_length=234),
        slug=models.SlugField(
            verbose_name=_('slug'),
            max_length=255,
            db_index=True,
            blank=True,
            help_text=_('Used in the URL. If changed, the URL will change. '
                        'Clear it to have it re-created automatically.'),
        ),
        lead_in=HTMLField(
            verbose_name=_('Summary'),
            default='',
            help_text=_(
                'The Summary gives the reader the main idea of the story, this '
                'is useful in overviews, lists or as an introduction to your '
                'event.'),
            blank=True,
        ),
        location=HTMLField(
            verbose_name=_('Location'),
            default='',
            blank=True,
        ),
        display_location=models.CharField(
            _('Display Location'),
            max_length=255,
            null=True,
            blank=True,
        ),
        meta_title=models.CharField(max_length=255,
                                    verbose_name=_('meta title'),
                                    blank=True,
                                    default=''),
        meta_description=models.TextField(verbose_name=_('meta description'),
                                          blank=True,
                                          default=''),
        meta_keywords=models.TextField(verbose_name=_('meta keywords'),
                                       blank=True,
                                       default=''),
        meta={'unique_together': ((
            'language_code',
            'slug',
        ), )},
        search_data=models.TextField(blank=True, editable=False),
        is_published_trans=models.BooleanField(_('is published'),
                                               default=False,
                                               db_index=True),
        is_featured_trans=models.BooleanField(_('is featured'),
                                              default=False,
                                              db_index=True),
    )

    price = models.CharField(max_length=255,
                             verbose_name=_('Event Price'),
                             blank=True,
                             default='')
    cpd_points = models.CharField(max_length=255,
                                  verbose_name=_('CPD Points'),
                                  blank=True,
                                  default='')
    event_start = models.DateTimeField(_('Event start'), default=now)
    event_end = models.DateTimeField(_('Event end'), null=True, blank=True)
    latitude = models.DecimalField(max_digits=8,
                                   decimal_places=5,
                                   verbose_name=_('Event latitude'),
                                   blank=True,
                                   null=True)
    longitude = models.DecimalField(max_digits=8,
                                    decimal_places=5,
                                    verbose_name=_('Event longitude'),
                                    blank=True,
                                    null=True)
    host = models.ForeignKey(Person,
                             on_delete=models.SET_NULL,
                             null=True,
                             blank=True,
                             verbose_name=_('host'))
    host_2 = models.ForeignKey(Person,
                               on_delete=models.SET_NULL,
                               related_name='host_2',
                               null=True,
                               blank=True,
                               verbose_name=_('second host'))
    host_3 = models.ForeignKey(Person,
                               on_delete=models.SET_NULL,
                               related_name='host_3',
                               null=True,
                               blank=True,
                               verbose_name=_('third host'))
    registration_until = models.DateTimeField(_('Allow registration until'),
                                              blank=True,
                                              null=True)
    registration_content = PlaceholderField(
        'Hide After Happened',
        related_name='events_event_registration_content')
    sidebar = PlaceholderField('Event Sidebar',
                               related_name='events_event_sidebar')
    registration_link = models.CharField(
        max_length=255,
        verbose_name=_('Registration link'),
        blank=True,
        default='',
        help_text=_('link to an external registration system'),
    )
    external_link = models.CharField(
        max_length=255,
        verbose_name=_('External link'),
        blank=True,
        default='',
        help_text=_('link to an external registration system'),
    )
    link_text = models.CharField(
        max_length=255,
        verbose_name=_('Link Text'),
        blank=True,
        default='',
        help_text=_(
            'Text to appear on either the Registration Link or External Link'),
    )
    redirect_url = models.CharField(
        max_length=255,
        verbose_name=_('Redirect URL'),
        blank=True,
        default='',
        help_text=
        _('when this value is filled in the Event page does not load, it redirects to the entered url'
          ),
    )

    content = PlaceholderField('Event Content',
                               related_name='events_event_content')
    app_config = AppHookConfigField(
        EventsConfig,
        verbose_name=_('Section'),
        help_text='',
    )
    template = models.CharField(
        max_length=255,
        verbose_name=_('Event template'),
        blank=True,
        default='',
    )
    channel = models.ForeignKey(Channel,
                                on_delete=models.SET_NULL,
                                null=True,
                                blank=True,
                                verbose_name=_('channel'))
    categories = CategoryManyToManyField(Category,
                                         verbose_name=_('categories'),
                                         blank=True)
    publishing_date = models.DateTimeField(_('publishing date'), default=now)
    is_published = models.BooleanField(_('is published'),
                                       default=False,
                                       db_index=True)
    is_featured = models.BooleanField(_('is featured'),
                                      default=False,
                                      db_index=True)
    hero_event = models.BooleanField(_('Hero Event'),
                                     default=False,
                                     db_index=True)
    featured_image = FilerImageField(
        verbose_name=_('featured image'),
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )
    share_image = FilerImageField(
        verbose_name=_('social share image'),
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        help_text=
        'This image will only be shown on social channels. Minimum size: 1200x630px',
        related_name='+')

    show_on_sitemap = models.BooleanField(_('Show on sitemap'),
                                          null=False,
                                          default=True)
    show_on_xml_sitemap = models.BooleanField(_('Show on xml sitemap'),
                                              null=False,
                                              default=True)
    noindex = models.BooleanField(_('noindex'), null=False, default=False)
    nofollow = models.BooleanField(_('nofollow'), null=False, default=False)
    canonical_url = models.CharField(blank=True,
                                     null=True,
                                     max_length=255,
                                     verbose_name=_('Canonical URL'))

    custom_fields = JSONField(blank=True, null=True)
    # Setting "symmetrical" to False since it's a bit unexpected that if you
    # set "B relates to A" you immediately have also "A relates to B". It have
    # to be forced to False because by default it's True if rel.to is "self":
    #
    # https://github.com/django/django/blob/1.8.4/django/db/models/fields/related.py#L2144
    #
    # which in the end causes to add reversed releted-to entry as well:
    #
    # https://github.com/django/django/blob/1.8.4/django/db/models/fields/related.py#L977
    services = SortedManyToManyField('js_services.Service',
                                     verbose_name=_('services'),
                                     blank=True)
    locations = SortedManyToManyField('js_locations.location',
                                      verbose_name=_('locations'),
                                      blank=True)

    objects = RelatedManager()
    all_objects = AllManager()
    search_objects = SearchManager()

    class Meta:
        ordering = ['-event_start']

    def get_class(self):
        '''Return class name'''
        return self.__class__.__name__

    @property
    def type(self):
        '''Event Type / Section.'''
        return self.app_config

    @property
    def type_slug(self):
        '''Event Type / Section Machine Name'''
        return self.app_config.namespace

    @property
    def published(self):
        """
        Returns True only if the event (is_published == True) AND has a
        published_date that has passed.
        """
        language = get_current_language()
        return self.published_for_language(language)

    def published_for_language(self, language):
        if TRANSLATE_IS_PUBLISHED:
            return ((self.safe_translation_getter('is_published_trans',
                                                  language_code=language,
                                                  any_language=False) or False)
                    and self.publishing_date <= now())
        return (self.is_published and self.publishing_date <= now())

    @property
    def future(self):
        """
        Returns True if the event is published but is scheduled for a
        future date/time.
        """
        if TRANSLATE_IS_PUBLISHED:
            return ((self.safe_translation_getter('is_published_trans',
                                                  language_code=language,
                                                  any_language=False) or False)
                    and self.publishing_date > now())
        return (self.is_published and self.publishing_date > now())

    @property
    def upcoming(self):
        return self.event_start > now()

    @property
    def past(self):
        return self.event_start > now()

    @property
    def show_registration_content(self):
        return (self.registration_until or self.event_start) > now()

    @property
    def start_date(self):
        return self.event_start.date()

    @property
    def start_time(self):
        return self.event_start.time()

    @property
    def end_date(self):
        if self.event_end:
            return self.event_end.date()

    @property
    def end_time(self):
        if self.event_end:
            return self.event_end.time()

    @property
    def hosts(self):
        hosts = []
        if self.host and self.host.published:
            hosts.append(self.host)
        if self.host_2 and self.host_2.published:
            hosts.append(self.host_2)
        if self.host_3 and self.host_3.published:
            hosts.append(self.host_3)
        return hosts

    def get_absolute_url(self, language=None):
        """Returns the url for this Event in the selected permalink format."""
        if not language:
            language = get_current_language()
        kwargs = {}
        permalink_type = self.app_config.permalink_type
        if 'y' in permalink_type:
            kwargs.update(year=self.publishing_date.year)
        if 'm' in permalink_type:
            kwargs.update(month="%02d" % self.publishing_date.month)
        if 'd' in permalink_type:
            kwargs.update(day="%02d" % self.publishing_date.day)
        if 'i' in permalink_type:
            kwargs.update(pk=self.pk)
        if 's' in permalink_type:
            slug, lang = self.known_translation_getter('slug',
                                                       default=None,
                                                       language_code=language)
            if slug and lang:
                site_id = getattr(settings, 'SITE_ID', None)
                if get_redirect_on_fallback(language, site_id):
                    language = lang
                kwargs.update(slug=slug)

        if self.app_config and self.app_config.namespace:
            namespace = '{0}:'.format(self.app_config.namespace)
        else:
            namespace = EventsConfig.default_namespace

        with override(language):
            return reverse('{0}event-detail'.format(namespace), kwargs=kwargs)

    def get_public_url(self, language=None):
        if not language:
            language = get_current_language()
        if not TRANSLATE_IS_PUBLISHED and self.published:
            return self.get_absolute_url(language)
        if (TRANSLATE_IS_PUBLISHED and \
                (self.safe_translation_getter('is_published_trans', language_code=language, any_language=False) or False) and \
                self.publishing_date <= now()):
            return self.get_absolute_url(language)
        return ''

    def get_search_data(self, language=None, request=None):
        """
        Provides an index for use with Haystack, or, for populating
        Event.translations.search_data.
        """
        if not self.pk:
            return ''
        if language is None:
            language = get_current_language()
        if request is None:
            request = get_request(language=language)
        title = self.safe_translation_getter('title', '')
        description = self.safe_translation_getter('lead_in', '')
        location = self.safe_translation_getter('location', '')
        text_bits = [title, strip_tags(description), strip_tags(location)]
        for category in self.categories.all():
            text_bits.append(
                force_unicode(category.safe_translation_getter('name')))
        for service in self.services.all():
            text_bits.append(
                force_unicode(service.safe_translation_getter('title')))
        if self.content:
            plugins = self.content.cmsplugin_set.filter(language=language)
            for base_plugin in plugins:
                plugin_text_content = ' '.join(
                    get_plugin_index_data(base_plugin, request))
                text_bits.append(plugin_text_content)
        return ' '.join(text_bits)

    def save(self, *args, **kwargs):
        # Update the search index
        if self.update_search_on_save:
            self.search_data = self.get_search_data()

        # slug would be generated by TranslatedAutoSlugifyMixin
        super(Event, self).save(*args, **kwargs)

    def __str__(self):
        return self.safe_translation_getter('title', any_language=True)

    def get_placeholders(self):
        return [
            self.content,
            self.registration_content,
            self.sidebar,
        ]

    def _get_related_qs(self, queryset):
        queryset = queryset.exclude(pk=self.pk).order_by('-event_start')
        if self.services.exists():
            return queryset.filter(services__in=self.services.all()).distinct()
        elif self.categories.exists():
            return queryset.filter(
                categories__in=self.categories.all()).distinct()
        else:
            return queryset.filter(app_config=self.app_config)

    def related_events(self):
        return self._get_related_qs(Event.objects.published())

    def related_upcoming_events(self):
        return self._get_related_qs(Event.objects.upcoming())

    def related_past_events(self):
        return self._get_related_qs(Event.objects.past())

    cached_related_events = cached_property(related_events,
                                            name='cached_related_events')

    cached_related_upcoming_events = cached_property(
        related_upcoming_events, name='cached_related_upcoming_events')

    cached_related_past_events = cached_property(
        related_past_events, name='cached_related_past_events')
class BlogCategory(BlogMetaMixin, TranslatableModel):
    """
    Blog category
    """
    parent = models.ForeignKey('self',
                               verbose_name=_('parent'),
                               null=True,
                               blank=True,
                               related_name='children',
                               on_delete=models.CASCADE)
    date_created = models.DateTimeField(_('created at'), auto_now_add=True)
    date_modified = models.DateTimeField(_('modified at'), auto_now=True)
    app_config = AppHookConfigField(BlogConfig,
                                    null=True,
                                    verbose_name=_('app. config'))

    translations = TranslatedFields(
        name=models.CharField(_('name'), max_length=752),
        slug=models.SlugField(_('slug'),
                              max_length=752,
                              blank=True,
                              db_index=True),
        meta_description=models.TextField(
            verbose_name=_('category meta description'),
            blank=True,
            default=''),
        meta={'unique_together': (('language_code', 'slug'), )})

    objects = AppHookConfigTranslatableManager()

    _metadata = {
        'title': 'get_title',
        'description': 'get_description',
        'og_description': 'get_description',
        'twitter_description': 'get_description',
        'gplus_description': 'get_description',
        'locale': 'get_locale',
        'object_type': 'get_meta_attribute',
        'og_type': 'get_meta_attribute',
        'og_app_id': 'get_meta_attribute',
        'og_profile_id': 'get_meta_attribute',
        'og_publisher': 'get_meta_attribute',
        'og_author_url': 'get_meta_attribute',
        'og_author': 'get_meta_attribute',
        'twitter_type': 'get_meta_attribute',
        'twitter_site': 'get_meta_attribute',
        'twitter_author': 'get_meta_attribute',
        'gplus_type': 'get_meta_attribute',
        'gplus_author': 'get_meta_attribute',
        'url': 'get_absolute_url',
    }

    class Meta:
        verbose_name = _('blog category')
        verbose_name_plural = _('blog categories')

    def descendants(self):
        children = []
        if self.children.exists():
            children.extend(self.children.all())
            for child in self.children.all():
                children.extend(child.descendants())
        return children

    @cached_property
    def linked_posts(self):
        return self.blog_posts.namespace(self.app_config.namespace)

    @cached_property
    def count(self):
        return self.linked_posts.published().count()

    @cached_property
    def count_all_sites(self):
        return self.linked_posts.published(current_site=False).count()

    def get_absolute_url(self, lang=None):
        if not lang or lang not in self.get_available_languages():
            lang = get_language()
        if not lang or lang not in self.get_available_languages():
            lang = self.get_current_language()
        if self.has_translation(lang):
            slug = self.safe_translation_getter('slug', language_code=lang)
            return reverse('%s:posts-category' % self.app_config.namespace,
                           kwargs={'category': slug},
                           current_app=self.app_config.namespace)
        # in case category doesn't exist in this language, gracefully fallback
        # to posts-latest
        return reverse('%s:posts-latest' % self.app_config.namespace,
                       current_app=self.app_config.namespace)

    def __str__(self):
        default = ugettext('BlogCategory (no translation)')
        return self.safe_translation_getter('name',
                                            any_language=True,
                                            default=default)

    def save(self, *args, **kwargs):
        super(BlogCategory, self).save(*args, **kwargs)
        for lang in self.get_available_languages():
            self.set_current_language(lang)
            if not self.slug and self.name:
                self.slug = slugify(force_text(self.name))
        self.save_translations()

    def get_title(self):
        title = self.safe_translation_getter('name', any_language=True)
        return title.strip()

    def get_description(self):
        description = self.safe_translation_getter('meta_description',
                                                   any_language=True)
        return escape(strip_tags(description)).strip()
Beispiel #20
0
class BlogCategory(BlogMetaMixin, TranslatableModel):
    """
    Blog category
    """

    parent = models.ForeignKey("self",
                               verbose_name=_("parent"),
                               null=True,
                               blank=True,
                               related_name="children",
                               on_delete=models.CASCADE)
    date_created = models.DateTimeField(_("created at"), auto_now_add=True)
    date_modified = models.DateTimeField(_("modified at"), auto_now=True)
    app_config = AppHookConfigField(BlogConfig,
                                    null=True,
                                    verbose_name=_("app. config"))
    order = models.PositiveIntegerField(default=0)

    translations = TranslatedFields(
        name=models.CharField(_("name"), max_length=752),
        slug=models.SlugField(_("slug"),
                              max_length=752,
                              blank=True,
                              db_index=True),
        meta_description=models.TextField(
            verbose_name=_("category meta description"),
            blank=True,
            default=""),
        meta={"unique_together": (("language_code", "slug"), )},
    )

    objects = AppHookConfigTranslatableManager()

    _metadata = {
        "title": "get_title",
        "description": "get_description",
        "og_description": "get_description",
        "twitter_description": "get_description",
        "gplus_description": "get_description",
        "locale": "get_locale",
        "object_type": "get_meta_attribute",
        "og_type": "get_meta_attribute",
        "og_app_id": "get_meta_attribute",
        "og_profile_id": "get_meta_attribute",
        "og_publisher": "get_meta_attribute",
        "og_author_url": "get_meta_attribute",
        "og_author": "get_meta_attribute",
        "twitter_type": "get_meta_attribute",
        "twitter_site": "get_meta_attribute",
        "twitter_author": "get_meta_attribute",
        "gplus_type": "get_meta_attribute",
        "gplus_author": "get_meta_attribute",
        "url": "get_absolute_url",
    }

    class Meta:
        verbose_name = _("blog category")
        verbose_name_plural = _("blog categories")
        ordering = ['order']

    def descendants(self):
        children = []
        if self.children.exists():
            children.extend(self.children.all())
            for child in self.children.all():
                children.extend(child.descendants())
        return children

    @cached_property
    def linked_posts(self):
        return self.blog_posts.namespace(self.app_config.namespace)

    @cached_property
    def count(self):
        return self.linked_posts.published().count()

    @cached_property
    def count_all_sites(self):
        return self.linked_posts.published(current_site=False).count()

    def get_absolute_url(self, lang=None):
        lang = _get_language(self, lang)
        if self.has_translation(lang):
            slug = self.safe_translation_getter("slug", language_code=lang)
            return reverse(
                "%s:posts-category" % self.app_config.namespace,
                kwargs={"category": slug},
                current_app=self.app_config.namespace,
            )
        # in case category doesn't exist in this language, gracefully fallback
        # to posts-latest
        return reverse("%s:posts-latest" % self.app_config.namespace,
                       current_app=self.app_config.namespace)

    def __str__(self):
        default = gettext("BlogCategory (no translation)")
        return self.safe_translation_getter("name",
                                            any_language=True,
                                            default=default)

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        for lang in self.get_available_languages():
            self.set_current_language(lang)
            if not self.slug and self.name:
                self.slug = slugify(force_str(self.name))
        self.save_translations()

    def get_title(self):
        title = self.safe_translation_getter("name", any_language=True)
        return title.strip()

    def get_description(self):
        description = self.safe_translation_getter("meta_description",
                                                   any_language=True)
        return escape(strip_tags(description)).strip()
Beispiel #21
0
class Article(TranslatedAutoSlugifyMixin, TranslationHelperMixin,
              TranslatableModel):

    # TranslatedAutoSlugifyMixin options
    slug_source_field_name = 'title'
    slug_default = _('untitled-article')
    # when True, updates the article's search_data field
    # whenever the article is saved or a plugin is saved
    # on the article's content placeholder.
    update_search_on_save = getattr(
        settings, 'ALDRYN_NEWSBLOG_UPDATE_SEARCH_DATA_ON_SAVE', False)

    translations = TranslatedFields(
        title=models.CharField(_('title'), max_length=234),
        slug=models.SlugField(
            verbose_name=_('slug'),
            max_length=255,
            db_index=True,
            blank=True,
            help_text=_('Used in the URL. If changed, the URL will change. '
                        'Clear it to have it re-created automatically.'),
        ),
        lead_in=HTMLField(
            verbose_name=_('lead'),
            default='',
            help_text=_(
                'The lead gives the reader the main idea of the story, this '
                'is useful in overviews, lists or as an introduction to your '
                'article.'),
            blank=True,
        ),
        meta_title=models.CharField(max_length=255,
                                    verbose_name=_('meta title'),
                                    blank=True,
                                    default=''),
        meta_description=models.TextField(verbose_name=_('meta description'),
                                          blank=True,
                                          default=''),
        meta_keywords=models.TextField(verbose_name=_('meta keywords'),
                                       blank=True,
                                       default=''),
        meta={'unique_together': ((
            'language_code',
            'slug',
        ), )},
        search_data=models.TextField(blank=True, editable=False))

    content = PlaceholderField('newsblog_article_content',
                               related_name='newsblog_article_content')
    author = models.ForeignKey(Person,
                               null=True,
                               blank=True,
                               verbose_name=_('author'))
    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              verbose_name=_('owner'))
    app_config = AppHookConfigField(NewsBlogConfig,
                                    verbose_name=_('Apphook configuration'))
    categories = CategoryManyToManyField('aldryn_categories.Category',
                                         verbose_name=_('categories'),
                                         blank=True)
    publishing_date = models.DateTimeField(_('publishing date'), default=now)
    is_published = models.BooleanField(_('is published'),
                                       default=False,
                                       db_index=True)
    is_featured = models.BooleanField(_('is featured'),
                                      default=False,
                                      db_index=True)
    featured_image = FilerImageField(null=True,
                                     blank=True,
                                     on_delete=models.SET_NULL)
    tags = TaggableManager(blank=True)

    related = SortedManyToManyField('self',
                                    verbose_name=_('related articles'),
                                    blank=True)

    objects = RelatedManager()

    class Meta:
        ordering = ['-publishing_date']

    @property
    def published(self):
        """
        Returns True only if the article (is_published == True) AND has a
        published_date that has passed.
        """
        return (self.is_published and self.publishing_date <= now())

    @property
    def future(self):
        """
        Returns True if the article is published but is scheduled for a
        future date/time.
        """
        return (self.is_published and self.publishing_date > now())

    def get_absolute_url(self, language=None):
        """Returns the url for this Article in the selected permalink format."""
        if not language:
            language = get_current_language()
        kwargs = {}
        permalink_type = self.app_config.permalink_type
        if 'y' in permalink_type:
            kwargs.update(year=self.publishing_date.year)
        if 'm' in permalink_type:
            kwargs.update(month="%02d" % self.publishing_date.month)
        if 'd' in permalink_type:
            kwargs.update(day="%02d" % self.publishing_date.day)
        if 'i' in permalink_type:
            kwargs.update(pk=self.pk)
        if 's' in permalink_type:
            slug, lang = self.known_translation_getter('slug',
                                                       default=None,
                                                       language_code=language)
            if slug and lang:
                site_id = getattr(settings, 'SITE_ID', None)
                if get_redirect_on_fallback(language, site_id):
                    language = lang
                kwargs.update(slug=slug)

        if self.app_config and self.app_config.namespace:
            namespace = '{0}:'.format(self.app_config.namespace)
        else:
            namespace = ''

        with override(language):
            return reverse('{0}article-detail'.format(namespace),
                           kwargs=kwargs)

    def get_search_data(self, language=None, request=None):
        """
        Provides an index for use with Haystack, or, for populating
        Article.translations.search_data.
        """
        if not self.pk:
            return ''
        if language is None:
            language = get_current_language()
        if request is None:
            request = get_request(language=language)
        description = self.safe_translation_getter('lead_in', '')
        text_bits = [strip_tags(description)]
        for category in self.categories.all():
            text_bits.append(
                force_unicode(category.safe_translation_getter('name')))
        for tag in self.tags.all():
            text_bits.append(force_unicode(tag.name))
        if self.content:
            plugins = self.content.cmsplugin_set.filter(language=language)
            for base_plugin in plugins:
                plugin_text_content = ' '.join(
                    get_plugin_index_data(base_plugin, request))
                text_bits.append(plugin_text_content)
        return ' '.join(text_bits)

    def save(self, *args, **kwargs):
        # Update the search index
        if self.update_search_on_save:
            self.search_data = self.get_search_data()

        # Ensure there is an owner.
        if self.app_config.create_authors and self.author is None:
            self.author = Person.objects.get_or_create(
                user=self.owner,
                defaults={
                    'name':
                    u' '.join((self.owner.first_name, self.owner.last_name))
                })[0]
        # slug would be generated by TranslatedAutoSlugifyMixin
        super(Article, self).save(*args, **kwargs)

    def __str__(self):
        return self.safe_translation_getter('title', any_language=True)
Beispiel #22
0
class Article(TranslatedAutoSlugifyMixin, TranslationHelperMixin,
              TranslatableModel):

    # TranslatedAutoSlugifyMixin options
    slug_source_field_name = 'title'
    slug_default = _('untitled-article')
    # when True, updates the article's search_data field
    # whenever the article is saved or a plugin is saved
    # on the article's content placeholder.
    update_search_on_save = getattr(
        settings, 'ALDRYN_NEWSBLOG_UPDATE_SEARCH_DATA_ON_SAVE', False)

    translations = TranslatedFields(
        title=models.CharField(_('title'), max_length=234),
        slug=models.SlugField(
            verbose_name=_('slug'),
            max_length=255,
            db_index=True,
            blank=True,
            help_text=_('Used in the URL. If changed, the URL will change. '
                        'Clear it to have it re-created automatically.'),
        ),
        lead_in=HTMLField(
            verbose_name=_('Summary'),
            default='',
            help_text=_(
                'The Summary gives the reader the main idea of the story, this '
                'is useful in overviews, lists or as an introduction to your '
                'article.'),
            blank=True,
        ),
        meta_title=models.CharField(max_length=255,
                                    verbose_name=_('meta title'),
                                    blank=True,
                                    default=''),
        meta_description=models.TextField(verbose_name=_('meta description'),
                                          blank=True,
                                          default=''),
        meta_keywords=models.TextField(verbose_name=_('meta keywords'),
                                       blank=True,
                                       default=''),
        meta={'unique_together': ((
            'language_code',
            'slug',
        ), )},
        search_data=models.TextField(blank=True, editable=False))

    content = PlaceholderField('newsblog_article_content',
                               related_name='newsblog_article_content')
    related_articles = PlaceholderField(
        'newsblog_related_articles', related_name='newsblog_related_articles')
    article_carousel = PlaceholderField(
        'newsblog_article_carousel', related_name='newsblog_article_carousel')
    article_sidebar = PlaceholderField('newsblog_article_sidebar',
                                       related_name='newsblog_article_sidebar')
    author = models.ForeignKey(Person,
                               null=True,
                               blank=True,
                               verbose_name=_('author'))
    author_2 = models.ForeignKey(Person,
                                 related_name='author_2',
                                 null=True,
                                 blank=True,
                                 verbose_name=_('second author'))
    author_3 = models.ForeignKey(Person,
                                 related_name='author_3',
                                 null=True,
                                 blank=True,
                                 verbose_name=_('third author'))
    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              verbose_name=_('owner'),
                              null=True,
                              blank=True)
    app_config = AppHookConfigField(
        NewsBlogConfig,
        verbose_name=_('Section'),
        help_text='',
    )
    categories = CategoryManyToManyField('aldryn_categories.Category',
                                         verbose_name=_('categories'),
                                         blank=True)
    services = SortedManyToManyField('js_services.Service',
                                     verbose_name=_('services'),
                                     blank=True)
    companies = SortedManyToManyField('js_companies.Company',
                                      verbose_name=_('companies'),
                                      blank=True)
    publishing_date = models.DateTimeField(_('publishing date'), default=now)
    is_published = models.BooleanField(_('is published'),
                                       default=False,
                                       db_index=True)
    is_featured = models.BooleanField(_('is featured'),
                                      default=False,
                                      db_index=True)
    featured_image = FilerImageField(
        verbose_name=_('featured image'),
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )
    tags = TaggableManager(blank=True)
    medium = models.ForeignKey(ArticleMedium,
                               verbose_name=_('medium'),
                               null=True,
                               blank=True)

    # Setting "symmetrical" to False since it's a bit unexpected that if you
    # set "B relates to A" you immediately have also "A relates to B". It have
    # to be forced to False because by default it's True if rel.to is "self":
    #
    # https://github.com/django/django/blob/1.8.4/django/db/models/fields/related.py#L2144
    #
    # which in the end causes to add reversed releted-to entry as well:
    #
    # https://github.com/django/django/blob/1.8.4/django/db/models/fields/related.py#L977
    related = SortedManyToManyField('self',
                                    verbose_name=_('specific articles'),
                                    blank=True,
                                    symmetrical=False)

    objects = RelatedManager()

    class Meta:
        ordering = ['-publishing_date']

    def get_class(self):
        '''Return class name'''
        return self.__class__.__name__

    @property
    def type(self):
        '''Article Type / Section.'''
        return self.app_config

    @property
    def type_slug(self):
        '''Article Type / Section Machine Name'''
        return self.app_config.namespace

    @property
    def published(self):
        """
        Returns True only if the article (is_published == True) AND has a
        published_date that has passed.
        """
        return (self.is_published and self.publishing_date <= now())

    @property
    def future(self):
        """
        Returns True if the article is published but is scheduled for a
        future date/time.
        """
        return (self.is_published and self.publishing_date > now())

    def get_absolute_url(self, language=None):
        """Returns the url for this Article in the selected permalink format."""
        if not language:
            language = get_current_language()
        kwargs = {}
        permalink_type = self.app_config.permalink_type
        if 'y' in permalink_type:
            kwargs.update(year=self.publishing_date.year)
        if 'm' in permalink_type:
            kwargs.update(month="%02d" % self.publishing_date.month)
        if 'd' in permalink_type:
            kwargs.update(day="%02d" % self.publishing_date.day)
        if 'i' in permalink_type:
            kwargs.update(pk=self.pk)
        if 's' in permalink_type:
            slug, lang = self.known_translation_getter('slug',
                                                       default=None,
                                                       language_code=language)
            if slug and lang:
                site_id = getattr(settings, 'SITE_ID', None)
                if get_redirect_on_fallback(language, site_id):
                    language = lang
                kwargs.update(slug=slug)

        if self.app_config and self.app_config.namespace:
            namespace = '{0}:'.format(self.app_config.namespace)
        else:
            namespace = ''

        with override(language):
            return reverse('{0}article-detail'.format(namespace),
                           kwargs=kwargs)

    def get_search_data(self, language=None, request=None):
        """
        Provides an index for use with Haystack, or, for populating
        Article.translations.search_data.
        """
        if not self.pk:
            return ''
        if language is None:
            language = get_current_language()
        if request is None:
            request = get_request(language=language)
        description = self.safe_translation_getter('lead_in', '')
        text_bits = [strip_tags(description)]
        for category in self.categories.all():
            text_bits.append(
                force_unicode(category.safe_translation_getter('name')))
        for service in self.services.all():
            text_bits.append(
                force_unicode(service.safe_translation_getter('name')))
        for tag in self.tags.all():
            text_bits.append(force_unicode(tag.name))
        if self.content:
            plugins = self.content.cmsplugin_set.filter(language=language)
            for base_plugin in plugins:
                plugin_text_content = ' '.join(
                    get_plugin_index_data(base_plugin, request))
                text_bits.append(plugin_text_content)
        return ' '.join(text_bits)

    def save(self, *args, **kwargs):
        # Update the search index
        if self.update_search_on_save:
            self.search_data = self.get_search_data()

        # Ensure there is an owner.
        if self.app_config.create_authors and self.owner and self.author is None:
            if hasattr(Person, 'first_name') and hasattr(Person, 'last_name'):
                defaults = {
                    'first_name': self.owner.first_name,
                    'last_name': self.owner.last_name,
                }
            else:
                defaults = {
                    'name':
                    ' '.join((
                        self.owner.first_name,
                        self.owner.last_name,
                    )),
                }
            self.author = Person.objects.get_or_create(user=self.owner,
                                                       defaults=defaults)[0]
        # slug would be generated by TranslatedAutoSlugifyMixin
        if not self.medium:
            self.medium = ArticleMedium.objects.first()
        super(Article, self).save(*args, **kwargs)

    def __str__(self):
        return self.safe_translation_getter('title', any_language=True)