class Video(File): # the icon it will use _icon = "video" # declare the file_type for the list template file_type = 'Video' categories = CategoryManyToManyField() metadata = models.CharField(_('metadata'), max_length=255, null=True, blank=True) @classmethod def matches_file_type(cls, iname, ifile, request): # the extensions we'll recognise for this file type filename_extensions = [ '.dv', '.mov', '.mp4', '.avi', 'mpeg', '.wmv', '.flv', ] ext = os.path.splitext(iname)[1].lower() return ext in filename_extensions class Meta: app_label = 'filer'
class Course(models.Model): session_id = models.ForeignKey('Session') course_name = models.CharField(blank=False, null=False, max_length=250) slug = models.SlugField(unique=True) description = models.TextField(blank=False, null=False) is_workshop = models.BooleanField(default=False) all_day = models.BooleanField(default=False) start_time = models.TimeField(blank=True, null=True) end_time = models.TimeField(blank=True, null=True) days_of_week = models.CharField(max_length=1, choices=DOW, blank=True) custom_start_date = models.DateField(blank=True, null=True) custom_end_date = models.DateField(blank=True, null=True) is_private = models.BooleanField(default=False) spaces = models.IntegerField(default=15) instructor = models.ForeignKey('auth.User') image = FilerImageField(null=True, blank=True) price = models.IntegerField(blank=True, null=True) location = models.ForeignKey('location.Location', null=True) category = CategoryManyToManyField() dropins = models.BooleanField(default=True) dropin_price = models.IntegerField(default=20) ticket_link = models.CharField(blank=True, max_length=250) create_date = models.DateTimeField(blank=True, null=False, auto_now=True) publish_date = models.DateField(blank=False, default=datetime.datetime.now) is_active = models.BooleanField(default=True) def __str__(self): return self.course_name
class NewsBlogCMSPlugin(CMSPlugin): """AppHookConfig aware abstract CMSPlugin class for Aldryn Newsblog""" # avoid reverse relation name clashes by not adding a related_name # to the parent plugin cmsplugin_ptr = models.OneToOneField( CMSPlugin, related_name='+', parent_link=True) app_config = models.ForeignKey(NewsBlogConfig, verbose_name=_('Apphook configuration')) dynamic_category_config = models.BooleanField(default=False, verbose_name=_('Use the ALDRYN_NEWSBLOG_CATEGORIES_FROM_REQUEST setting instead of category configuration.')) category_config = CategoryManyToManyField(Category, blank=True, verbose_name=_('Category configuration'), help_text=_('Will be applied if you do not use the ALDRYN_NEWSBLOG_CATEGORIES_FROM_REQUEST setting. If left empty, it will not use any filter on categories.')) class Meta: abstract = True def copy_relations(self, old_instance): self.app_config = old_instance.app_config
class NewsBlogLatestArticleByCategory(NewsBlogCMSPlugin): latest_articles = models.IntegerField( default=5, help_text=_('The maximum number of latest articles to display.')) categories = CategoryManyToManyField('aldryn_categories.Category', verbose_name=_('categories'), blank=True) # copy manytomany and foreignkey relationship from edited plugin to published plugin def copy_relations(self, oldinstance): self.categories = oldinstance.categories.all() def get_articles(self, request): """ Returns a queryset of the latest N articles in one or more of the M categories. N is the plugin setting: latest_articles. M is the plugin setting: categories """ queryset = Article.objects.published() queryset = queryset.all().filter(categories__in=self.categories.all()) if not self.latest_articles: return Article.objects.none() languages = get_valid_languages_from_request(self.app_config.namespace, request) if self.language not in languages: return queryset.none() queryset = queryset.translated(*languages).filter( app_config=self.app_config) return queryset[:self.latest_articles] def __str__(self): return ugettext( '%(app_title)s latest articles by category: %(latest_articles)s' ) % { 'app_title': self.app_config.get_app_title(), 'latest_articles': self.latest_articles, }
class NewsBlogCategoryRelatedPlugin(PluginEditModeMixin, PluginStyleMixin, AdjustableCacheModelMixin, CMSPlugin): # NOTE: This one does NOT subclass NewsBlogCMSPlugin. This is because this # plugin can really only be placed on the article detail view in an apphook. exclude_categories = CategoryManyToManyField( verbose_name=_('excluded categories'), blank=True) article_count = models.PositiveIntegerField( default=10, help_text=_( 'The maximum number of related articles to display (0 for all).'), ) def get_queryset(self, article, request): """ Returns a queryset of articles that have common categories with the given article. """ languages = get_valid_languages_from_request( article.app_config.namespace, request) if self.language not in languages: return Article.objects.none() filter_categories = article.categories.exclude( pk__in=models.Subquery(self.exclude_categories.values('pk'))) queryset = Article.objects.filter( categories__in=filter_categories).exclude( pk=article.pk).translated(*languages) if not self.get_edit_mode(request): queryset = queryset.published() return queryset.distinct() def get_articles(self, article, request): queryset = self.get_queryset(article, request) if self.article_count > 0: queryset = queryset[:self.article_count] return queryset def __str__(self): return ugettext('Category-related articles')
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( 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) 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 Service(CustomServiceMixin, TranslatedAutoSlugifyMixin, TranslationHelperMixin, TranslatableModel): # TranslatedAutoSlugifyMixin options slug_source_field_name = 'title' slug_default = _('untitled-service') # when True, updates the service's search_data field # whenever the service is saved or a plugin is saved # on the service's content placeholder. update_search_on_save = getattr( settings, 'SERVICES_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 ' 'service.' ), 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), layout_trans = models.CharField( blank=True, default='', max_length=60, verbose_name=_('layout') ), ) content = PlaceholderField('service_content', related_name='service_content') sidebar = PlaceholderField('service_sidebar', related_name='service_sidebar') related_articles_placeholder = PlaceholderField('service_related_articles', related_name='service_related_articles') banner = PlaceholderField('service_banner', related_name='service_banner') sections = models.ManyToManyField( ServicesConfig, verbose_name=_('Sections'), related_name='services', ) 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) featured_image = FilerImageField( verbose_name=_('featured image'), null=True, blank=True, on_delete=models.SET_NULL, ) 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=_('related services'), blank=True, symmetrical=True) objects = RelatedManager() class Meta: ordering = ['-publishing_date'] def get_class(self): '''Return class name''' return self.__class__.__name__ @cached_property def app_config(self): app_config = ServicesConfig.objects.get(namespace=ServicesConfig.default_namespace) if self.pk: for section in self.sections.all(): try: reverse('{0}:service-list'.format(section.namespace)) except: pass else: app_config = section break return app_config @property def type(self): '''Service Type / Section.''' return self.app_config @property def type_slug(self): '''Service Type / Section Machine Name''' return self.app_config.namespace @property def published(self): """ Returns True only if the service (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): is_published = self.is_published if TRANSLATE_IS_PUBLISHED: is_published = self.safe_translation_getter('is_published_trans', language_code=language, any_language=False) or False if SERVICES_ENABLE_PUBDATE: return (is_published and self.publishing_date <= now()) return is_published @property def future(self): """ Returns True if the service 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 Service 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 = ServicesConfig.default_namespace with override(language): return reverse('{0}service-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 Service.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)] if ADD_CATEGORIES_TO_SEARCH_DATA: for category in self.categories.all(): text_bits.append( force_unicode(category.safe_translation_getter('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() if hasattr(self, 'app_config'): delattr(self, 'app_config') # slug would be generated by TranslatedAutoSlugifyMixin super(Service, self).save(*args, **kwargs) def __str__(self): return self.safe_translation_getter('title', any_language=True) if self.pk else '' def get_placeholders(self): return [ self.content, self.sidebar, self.related_articles_placeholder, self.banner, ] def services_by_category(self, category=None): if category: categories = Category.objects.filter(translations__slug=category) return Service.objects.published().filter(categories__in=categories[0].get_descendants()).distinct().exclude(pk=self.pk) if categories.count() else [] return Service.objects.published().exclude(pk=self.pk) def related_categories(self, category=None): if category: categories = Category.objects.filter(translations__slug=category) return categories[0].get_descendants().filter(pk__in=self.categories.all()) if categories.count() else [] return self.categories.all() def related_articles(self, article_category=None): articles = self.article_set.published() if article_category: return articles.namespace(article_category) return articles def services(self, service_category=None): if service_category: return Service.objects.published().filter(sections__namespace=service_category) return Service.objects.published() def people(self): return Person.objects.published().filter(services=self) def related_people(self): return Person.objects.published().filter(services__in=self.related.all()).distinct() def __getattr__(cls, name): if not hasattr(Service, name): if name.startswith('related_articles_'): category = name.split('related_articles_')[1].replace('_', '-') def wrapper(self): return self.related_articles(category) setattr(Service, name, wrapper) return getattr(cls, name) elif name.startswith('services_by_category_'): category = name.split('services_by_category_')[1].replace('_', '-') def wrapper(self): return self.services_by_category(category) setattr(Service, name, wrapper) return getattr(cls, name) elif name.startswith('related_categories_'): category = name.split('related_categories_')[1].replace('_', '-') def wrapper(self): return self.related_categories(category) setattr(Service, name, wrapper) return getattr(cls, name) elif name.startswith('services_'): category = name.split('services_')[1].replace('_', '-') def wrapper(self): return self.services(category) setattr(Service, name, wrapper) return getattr(cls, name) raise AttributeError
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 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 Person(CustomPersonMixin, TranslationHelperMixin, TranslatedAutoSlugifyMixin, TranslatableModel): update_search_on_save = UPDATE_SEARCH_DATA_ON_SAVE translations = TranslatedFields( first_name_trans=models.CharField( _('first name'), max_length=255, blank=False, default='', help_text=_("Provide this person's first name.")), last_name_trans=models.CharField( _('last name'), max_length=255, blank=False, default='', help_text=_("Provide this person's last name.")), suffix=models.CharField( _('suffix'), max_length=60, blank=True, default='', help_text=_("Provide this person's suffix.")), slug=models.SlugField( _('unique slug'), max_length=255, blank=True, default='', help_text=_("Leave blank to auto-generate a unique slug.")), function=models.CharField(_('role'), max_length=255, blank=True, default=''), description=HTMLField(_('description'), blank=True, default=''), search_data=models.TextField(blank=True, editable=False), is_published_trans=models.BooleanField( verbose_name=_('show on website'), default=True) ) first_name = models.CharField( _('first name'), max_length=255, blank=False, default='', help_text=_("Provide this person's first name.")) last_name = models.CharField( _('last name'), max_length=255, blank=False, default='', help_text=_("Provide this person's last name.")) phone = models.CharField( verbose_name=_('phone'), null=True, blank=True, max_length=100) second_phone = models.CharField( verbose_name=_('secondary phone'), null=True, blank=True, max_length=100) mobile = models.CharField( verbose_name=_('mobile'), null=True, blank=True, max_length=100) fax = models.CharField( verbose_name=_('fax'), null=True, blank=True, max_length=100) email = models.EmailField( verbose_name=_("email"), blank=True, default='') facebook = models.URLField( verbose_name=_('facebook'), null=True, blank=True, max_length=200) twitter = models.CharField( verbose_name=_('twitter'), null=True, blank=True, max_length=100) linkedin = models.URLField( verbose_name=_('linkedin'), null=True, blank=True, max_length=200) xing = models.URLField( verbose_name=_('xing'), null=True, blank=True, max_length=200) location = models.ForeignKey('js_locations.Location', on_delete=models.SET_NULL, verbose_name=_('location'), null=True, blank=True) website = models.URLField( verbose_name=_('website'), null=True, blank=True) groups = SortedManyToManyField( 'aldryn_people.Group', default=None, blank=True, related_name='people', help_text=_('Choose and order the groups for this person, the first ' 'will be the "primary group".')) visual = FilerImageField( null=True, blank=True, default=None, on_delete=models.SET_NULL) second_visual = FilerImageField( null=True, blank=True, default=None, on_delete=models.SET_NULL, related_name='second_person_visual') vcard_enabled = models.BooleanField( verbose_name=_('enable vCard download'), default=True) details_enabled = models.BooleanField( verbose_name=_('enable details'), default=True) is_published = models.BooleanField( verbose_name=_('show on website'), default=True) user = models.OneToOneField( getattr(settings, 'AUTH_USER_MODEL', 'auth.User'), on_delete=models.SET_NULL, null=True, blank=True, related_name='persons') categories = CategoryManyToManyField('aldryn_categories.Category', verbose_name=_('categories'), blank=True) services = SortedManyToManyField('js_services.Service', verbose_name=_('services'), blank=True) content = PlaceholderField('content', related_name='person_content') placeholder_sidebar = PlaceholderField('sidebar') banner = PlaceholderField('person_banner', related_name='person_banner') 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) objects = PeopleManager() class Meta: verbose_name = _('Person') verbose_name_plural = _('People') def __str__(self): pkstr = str(self.pk) if six.PY2: pkstr = six.u(pkstr) # name = ' '.join(( # self.safe_translation_getter( # 'first_name', # default='', # any_language=True # ), # self.safe_translation_getter( # 'last_name', # default='', # any_language=True # ) # )).strip() # name = 'TESTING' #DEBUG name = ' '.join((self.first_name, self.last_name)).strip() return name if len(name) > 0 else pkstr @property def primary_group(self): """Simply returns the first in `groups`, if any, else None.""" return self.groups.first() @property def primary_company(self): if IS_THERE_COMPANIES: return self.companies.first() @property def comment(self): return self.safe_translation_getter('description', '') @property def sorted_companies(self): if IS_THERE_COMPANIES: return [obj.company for obj in self.companies.through.objects.filter(person=self).order_by('sort_value')] else: [] @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() if TRANSLATE_IS_PUBLISHED: return self.safe_translation_getter('is_published_trans', language_code=language, any_language=False) or False return self.is_published 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) text_bits = [self.first_name] text_bits.append(self.last_name) text_bits.append(self.safe_translation_getter('function', '')) description = self.safe_translation_getter('description', '') text_bits.append(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'))) 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): if self.update_search_on_save: self.search_data = self.get_search_data() super(Person, self).save(*args, **kwargs) def get_absolute_url(self, language=None): if not language: language = get_current_language() slug, language = self.known_translation_getter( 'slug', None, language_code=language) if slug: kwargs = {'slug': slug} else: kwargs = {'pk': self.pk} with override(language): # do not fail with 500 error so that if detail view can't be # resolved we still can use plugins. try: url = reverse('%s:person-detail' % DEFAULT_APP_NAMESPACE, kwargs=kwargs) except NoReverseMatch: url = '' return url def get_public_url(self, language=None): if not language: language = get_current_language() if not TRANSLATE_IS_PUBLISHED and self.is_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)): return self.get_absolute_url(language) return '' def get_vcard_url(self, language=None): if not language: language = get_current_language() slug = self.safe_translation_getter( 'slug', None, language_code=language, any_language=False) if slug: kwargs = {'slug': slug} else: kwargs = {'pk': self.pk} with override(language): return reverse('%s:download_vcard' % DEFAULT_APP_NAMESPACE, kwargs=kwargs) def get_vcard(self, request=None): vcard = Vcard() function = self.safe_translation_getter('function') safe_name = self.name() vcard.add_line('FN', safe_name) vcard.add_line('N', [None, safe_name, None, None, None]) if self.visual: ext = self.visual.extension.upper() try: with open(self.visual.path, 'rb') as f: data = force_text(base64.b64encode(f.read())) vcard.add_line('PHOTO', data, TYPE=ext, ENCODING='b') except IOError: if request: url = urlparse.urljoin(request.build_absolute_uri(), self.visual.url), vcard.add_line('PHOTO', url, TYPE=ext) if self.email: vcard.add_line('EMAIL', self.email) if function: vcard.add_line('TITLE', self.function) if self.phone: vcard.add_line('TEL', self.phone, TYPE='WORK') if self.mobile: vcard.add_line('TEL', self.mobile, TYPE='CELL') if self.fax: vcard.add_line('TEL', self.fax, TYPE='FAX') if self.website: vcard.add_line('URL', self.website) if self.primary_company: vcard.add_line('ORG', self.primary_company.name) if self.primary_group: group_name = self.primary_group.safe_translation_getter( 'name', default="Group: {0}".format(self.primary_group.pk)) if group_name and not self.primary_company: vcard.add_line('ORG', group_name) if (self.primary_group.address or self.primary_group.city or self.primary_group.postal_code): vcard.add_line('ADR', ( None, None, self.primary_group.address, self.primary_group.city, None, self.primary_group.postal_code, None, ), TYPE='WORK') if self.primary_group.phone: vcard.add_line('TEL', self.primary_group.phone, TYPE='WORK') if self.primary_group.fax: vcard.add_line('TEL', self.primary_group.fax, TYPE='FAX') if self.primary_group.website: vcard.add_line('URL', self.primary_group.website) return six.b('{}'.format(vcard)) def get_slug_source(self): return self.__str__() def name(self): return self.__str__() def get_placeholders(self): return [ self.content, self.placeholder_sidebar, self.banner, ] def related_articles(self, article_category=None): qs = self.article_set.published() | self.author_2.published() | self.author_3.published() if article_category: return qs.filter(app_config__namespace=article_category).distinct() return qs.distinct() def related_events(self, event_category=None): if hasattr(self, 'event_set'): qs = self.event_set.published() | self.host_2.published() | self.host_3.published() if event_category: return qs.filter(app_config__namespace=event_category).distinct() return qs.distinct().order_by('-event_start') def related_upcoming_events(self, event_category=None): if hasattr(self, 'event_set'): qs = self.event_set.upcoming() | self.host_2.upcoming() | self.host_3.upcoming() if event_category: return qs.filter(app_config__namespace=event_category).distinct() return qs.distinct().order_by('event_start') def related_past_events(self, event_category=None): if hasattr(self, 'event_set'): qs = self.event_set.past() | self.host_2.past() | self.host_3.past() if event_category: return qs.filter(app_config__namespace=event_category).distinct() return qs.distinct().order_by('-event_start') def related_services(self, service_category=None): if service_category: return self.services.published().filter(sections__namespace=service_category) return self.services.published().all()
class Article(TranslatableModel): 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=_('Optional lead-in'), default='', help_text=_('Will be displayed in lists, and at the start of the ' 'detail page (in bold)'), 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('aldryn_newsblog_article_content', related_name='aldryn_newsblog_articles', unique=True) author = models.ForeignKey(Person, null=True, blank=True, verbose_name=_('author')) owner = models.ForeignKey(User, verbose_name=_('owner')) app_config = models.ForeignKey(NewsBlogConfig, verbose_name=_('app. config')) categories = CategoryManyToManyField('aldryn_categories.Category', verbose_name=_('categories'), blank=True) publishing_date = models.DateTimeField(_('publishing date'), default=datetime.datetime.now) is_published = models.BooleanField(_('is published'), default=True, db_index=True) is_featured = models.BooleanField(_('is featured'), default=False, db_index=True) featured_image = FilerImageField(null=True, blank=True) tags = TaggableManager(blank=True) related = SortedManyToManyField('self', verbose_name=_('related articles'), blank=True) objects = RelatedManager() class Meta: ordering = ['-publishing_date'] def __str__(self): return self.safe_translation_getter('title', any_language=True) def get_absolute_url(self): return reverse('{namespace}:article-detail'.format( namespace=self.app_config.namespace), kwargs={ 'slug': self.safe_translation_getter('slug', any_language=True) }) def slugify(self, source_text, i=None): slug = default_slugify(source_text) if i is not None: slug += "_%d" % i return slug def get_search_data(self, language, request=None): """ Provides an index for use with Haystack, or, for populating Article.translations.search_data. """ 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: for base_plugin in self.content.cmsplugin_set.filter( language=language): 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): # 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] # Start with a naïve approach, if none provided. if not self.slug: self.slug = default_slugify(self.title) # Ensure we aren't colliding with an existing slug *for this language*. if Article.objects.translated(slug=self.slug).exclude( id=self.id).count() == 0: return super(Article, self).save(*args, **kwargs) for lang in LANGUAGE_CODES: # # We'd much rather just do something like: # Article.objects.translated(lang, # slug__startswith=self.slug) # But sadly, this isn't supported by Parler/Django, see: # http://django-parler.readthedocs.org/en/latest/api/\ # parler.managers.html#the-translatablequeryset-class # slugs = [] all_slugs = Article.objects.language(lang).exclude( id=self.id).values_list('translations__slug', flat=True) for slug in all_slugs: if slug and slug.startswith(self.slug): slugs.append(slug) i = 1 while True: slug = self.slugify(self.title, i) if slug not in slugs: self.slug = slug return super(Article, self).save(*args, **kwargs) i += 1
def test_category_many_to_many_field(self): field = CategoryManyToManyField(Category) form_field = field.formfield() self.assertTrue(isinstance(form_field, CategoryMultipleChoiceField)) field_type = field.get_internal_type() self.assertEquals(field_type, 'ManyToManyField')
def test_category_many_to_many_field(self): field = CategoryManyToManyField(Category) form_field = field.formfield() self.assertTrue(isinstance(form_field, CategoryMultipleChoiceField)) field_type = field.get_internal_type() self.assertEquals(field_type, 'ManyToManyField')
class Article(TranslationHelperMixin, BaseModel, TranslatableModel): # TranslatedAutoSlugifyMixin options slug_source_field_name = 'title' slug_default = _('untitled-article') update_search_on_save = getattr( settings, 'ALDRYN_NEWSBLOG_UPDATE_SEARCH_DATA_ON_SAVE', False) # foreign keys created_by = models.ForeignKey( 'users.User', verbose_name=_('created by'), null=True, on_delete=models.SET_NULL, related_name='articles', ) 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=''), is_published=models.BooleanField(_('is published'), default=False, db_index=True), is_featured=models.BooleanField(_('is featured'), default=False, db_index=True), is_dirty=models.BooleanField( default=False, editable=False, ), draft=models.BooleanField( default=True, editable=False, db_index=True, ), meta={'unique_together': (('language_code', 'slug', 'draft'), )}, search_data=models.TextField(blank=True, editable=False)) content = PlaceholderField( 'article_content', on_delete=models.SET_NULL, related_name='articles', ) categories = CategoryManyToManyField( 'aldryn_categories.Category', verbose_name=_('categories'), blank=True, related_name='articles', ) publishing_date = models.DateTimeField(_('publishing date'), default=timezone.now) featured_image = FilerImageField( verbose_name=_('featured image'), null=True, blank=True, on_delete=models.SET_NULL, related_name='articles', ) tags = TaggableManager( blank=True, related_name='articles', ) # 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) is_draft = models.BooleanField( default=True, editable=False, db_index=True, ) public = models.OneToOneField('self', related_name='draft_article', null=True, editable=False) objects = RelatedManager() exclude_on_on_delete_test = ('public', 'content') class Meta: verbose_name = _('article') verbose_name_plural = _('articles') ordering = ('-publishing_date', ) permissions = (('view_article', _('Can view article')), ) # private methods def __str__(self): return self.title def _copy_attributes(self, target, language): """ Copy all page data to the target. This excludes parent and other values that are specific to an exact instance. :param target: The Article to copy the attributes to """ translation = self.translations.get(language_code=language) # copy translations try: new_translation = ArticleTranslation.objects.get( master_id=target.pk, language_code=language, ) except ArticleTranslation.DoesNotExist: ArticleTranslation.objects.create( master_id=target.pk, language_code=language, title=translation.title, slug=translation.slug, lead_in=translation.lead_in, meta_title=translation.meta_title, meta_description=translation.meta_description, meta_keywords=translation.meta_keywords, search_data=translation.search_data, draft=False, is_featured=translation.is_featured, ) else: new_translation.title = translation.title new_translation.draft = False new_translation.slug = translation.slug new_translation.lead_in = translation.lead_in new_translation.meta_title = translation.meta_title new_translation.meta_description = translation.meta_description new_translation.meta_keywords = translation.meta_keywords new_translation.search_data = translation.search_data new_translation.is_featured = translation.is_featured new_translation.save() target.featured_image = self.featured_image target.publishing_date = self.publishing_date target.is_featured = self.is_featured target.tags.clear() for tag in self.tags.all(): target.tags.add(tag.name) def _copy_contents(self, target, language): """ Copy all the plugins to a new article. :param target: The page where the new content should be stored """ # TODO: Make this into a "graceful" copy instead of deleting and # overwriting copy the placeholders # (and plugins on those placeholders!) plugins = CMSPlugin.objects.filter( placeholder=target.content, language=language).order_by('-depth') for plugin in plugins: instance, cls = plugin.get_plugin_instance() if instance and getattr(instance, 'cmsplugin_ptr_id', False): instance.cmsplugin_ptr = plugin instance.cmsplugin_ptr._no_reorder = True instance.delete(no_mp=True) else: plugin._no_reorder = True plugin.delete(no_mp=True) plugins = self.content.get_plugins_list(language) # update the page copy if plugins: copy_plugins_to(plugins, target.content) # django methods def clean(self): values = super().clean() # check for existing slugs if not self.slug: self.slug = slugify(self.title) not_this_article_list = Article.objects.exclude_article(self) if not_this_article_list.translated(slug=self.slug).exists(): raise ValidationError({ 'slug': _('Slug "%s" is repeated') % self.slug, }) return values def get_absolute_url(self): url = reverse('articles:article_detail', args=(self.slug, )) if self.is_draft: return url + '?edit' else: return url def get_index_url(self): return reverse('articles:article_detail', args=(self.slug, )) def save(self, *args, **kwargs): if self.is_draft: if not self.slug: self.slug = slugify(self.title) new = not self.id return_value = super(Article, self).save(*args, **kwargs) # Index if appropiate (if the document already exists it replaces it) # Otherwise delete the document indexed # Called after saving because the id is needed if not new: if self.is_published and not self.is_draft: self.index_in_elasticsearch(1) else: self.deindex_in_elasticsearch() return return_value # custom methods @property def draft_pk(self): if self.is_draft: return self.pk # the 'public' field is badly defined, since it's the draft when the # article is public return self.public_id def publish(self, language): """ :returns: the publicated Article. """ # Publish only be called on draft articles if not self.is_draft: raise PublicIsUnmodifiable( 'The public instance cannot be published. Use draft.') if not self.pk: self.save() # be sure we have the newest data including tree information self.refresh_from_db() if self.public_id: # Ensure we have up to date mptt properties public_article = self.public else: public_article = Article(created_by=self.created_by) if not self.publishing_date: self.publishing_date = timezone.now() # we need to set relate this new public copy to its draft page (self) public_article.public = self public_article.is_draft = False public_article.save() # store the relationship between draft and public self.public = public_article self.save() self._copy_attributes(public_article, language) public_article.is_published = True public_article.save() # The target page now has a pk, so can be used as a target self._copy_contents(public_article, language) return public_article def get_elasticsearch_kwargs(self): kwargs = super(Article, self).get_elasticsearch_kwargs() if hasattr(self, 'lead_in'): kwargs['description'] = remove_tags(self.lead_in) kwargs['lead_in'] = remove_tags(self.lead_in) kwargs['url'] = self.get_index_url() kwargs['detail'] = date_format(self.publishing_date) if self.tags: tags = self.tags.values_list('name', flat=True) kwargs['tags'] = ', '.join(tags), if self.categories: categories = self.categories.values_list('translations__name', flat=True) categories_slug = self.categories.values_list('translations__slug', flat=True) kwargs['categories'] = ', '.join(categories), kwargs['categories_slug'] = ', '.join(categories_slug) return kwargs def unpublish(self, language): # Unpublish only be called on non draft articles if self.is_draft: raise PublicIsUnmodifiable( 'The draft instance cannot be published. Use public.') self.is_published = False self.save()