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()
class TranslatableArticle(TranslatableModel): translations = TranslatedFields(title=models.CharField('title', max_length=234), slug=models.SlugField()) section = AppHookConfigField(ExampleConfig, verbose_name='section') objects = AppHookConfigTranslatableManager()
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()
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()
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'
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)
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')
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')
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()
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)
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())
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)
class News(Article): config = AppHookConfigField(AnotherExampleConfig, verbose_name='config') objects = AppHookConfigManager()
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), )
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()
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()
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)
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)