class CmsCourse(Course): icon = models.CharField(max_length=100, default='fas fa-graduation-cap') flow_content = PlaceholderField( 'flow_content', related_name='flow_content', help_text=_('Content shown on the flow index page')) common_content = PlaceholderField( 'lesson_common_content', related_name='lesson_common_content', help_text=_('Content shown in each course lesson')) class Meta: verbose_name = _('Course') verbose_name_plural = _('Courses') def save(self, force_insert=False, force_update=False, using=None, update_fields=None): is_create = self.pk is None super().save(force_insert, force_update, using, update_fields) if is_create: cms.api.add_plugin( placeholder=self.flow_content, plugin_type='FlowLessonsCMSPlugin', language='ru', ) cms.api.add_plugin( placeholder=self.common_content, plugin_type='CommentsCMSPlugin', language='ru', )
class Post(models.Model): section = models.ForeignKey(Section, default=None, on_delete=models.SET_DEFAULT, related_name="posts", verbose_name=_("Section")) categories = models.ManyToManyField(Category, related_name="posts", verbose_name=_("Category"), blank=True) title = models.CharField(_("Title"), default="", max_length=255) slug = models.SlugField(_("Slug"), max_length=255) image = PlaceholderField(slotname='post_image', related_name='featured_image') author = models.CharField(_("Author"), default="", max_length=255, blank=True) date = models.DateTimeField(_("Publish date"), default=get_now) summary = models.TextField(_("Summary"), blank=True, null=True) content = PlaceholderField(slotname='post_content') published = models.BooleanField(_("Published"), default=False) template = models.CharField(_("Template"), max_length=255, default=POST_DETAIL_TEMPLATE, choices=post_templates) created = models.DateTimeField(_("Created"), auto_now_add=True, null=True) updated = models.DateTimeField(_("Edited"), auto_now=True) typo3_uid = models.IntegerField(_("Typo3 ID", ), null=True) typo3_pid = models.CharField(_("Typo3 Related", ), null=True, max_length=255) def __str__(self): return "%s" % self.title def get_absolute_url(self): return reverse('%s:%s' % (self.section.namespace, POST_DETAIL_URL), args=[self.id, self.slug]) def get_formatted_date(self): return self.date.strftime('%d.%m.%Y.') @property def cms_edit_text(self): return _('Edit information') class Meta: ordering = ("-date", ) verbose_name = _("Post") verbose_name_plural = _("Posts")
class ReadingSection(Section): class Meta: verbose_name_plural = 'Sections (Reading)' manager_inheritance_from_future = True def absolute_url(self): return reverse('core:section_detail', kwargs={ 'module_slug': self.lesson.topic.module.slug, 'topic_slug': self.lesson.topic.slug, 'lesson_slug': self.lesson.slug, 'slug': self.slug }) ''' define method to clear placeholderfield to be signaled on predelete ''' def delete(self, *args, **kwargs): print("----- in ReadingSection overridden delete") # self.cleanup_placeholders() placeholders = [self.content] super(ReadingSection, self).delete(*args, **kwargs) for ph in placeholders: ph.clear() ph.delete() content = PlaceholderField('reading_content')
class ActivitySection(Section): class Meta: verbose_name_plural = 'Sections (Activity)' manager_inheritance_from_future = True def absolute_url(self): return reverse('core:section_detail', kwargs={ 'module_slug': self.lesson.topic.module.slug, 'topic_slug': self.lesson.topic.slug, 'lesson_slug': self.lesson.slug, 'slug': self.slug }) def delete(self, *args, **kwargs): print("----- in ActivitySection overridden delete") # self.cleanup_placeholders() placeholders = [self.content] super(ActivitySection, self).delete(*args, **kwargs) for ph in placeholders: ph.clear() ph.delete() content = PlaceholderField('activity_content')
class Question(models.Model): def composition(): outline = { 'findings': u'<p>Add Your Findings here<p><p>Identify Your Audience here<p>' u'<p>Add Your Findings Description here<p><p>Add Your Conclusion and Recommendations here<p>', } return outline # f_default = composition() # defaults = f_default.values() number = models.IntegerField(null=True, blank=True, help_text=(_('Use numbers <small>e.g</small> 1, 2 or 3'))) question = models.CharField(max_length=128, null=True, blank=True) findings = RichTextField(null=True, blank=True, default=composition()['findings'], help_text=_( 'Do not delete the tags <pre><code><p> ... <p></code></pre>' )) image = models.ImageField(max_length=128000, null=True, blank=True, upload_to='media/project') project = models.ForeignKey('Project', on_delete=models.CASCADE, null=True, related_name='projects_question') add_findings_placeholder = PlaceholderField(slotname='add_findings') class Meta: permissions = ( ('can_add_question', 'Can add Question'), ('can_edit_question', 'Can edit Question') ) def __str__(self): return '{} for {}'.format(self.number, self.project)
class PostPlaceholderExtension(models.Model): post = models.OneToOneField(Post, on_delete=models.CASCADE, related_name="placeholder") some_placeholder = PlaceholderField("some_placeholder", related_name="some_placeholder") def delete(self): print("delete") super().delete()
class UserProfile(models.Model): ''' ''' user = models.OneToOneField(User) profile_content = PlaceholderField(profile_content, null=True, blank=True, help_text='Content Plugin') def __unicode__(self): return self.user.username
class CmsLesson(Lesson): lesson_content = PlaceholderField('lesson_content', related_name='lesson_content') support_content = PlaceholderField('support_content', related_name='support_content') show_common_content = models.BooleanField( default=True, help_text= _('Display content common for all lessons across course at the bottom of lesson content' )) class Meta: verbose_name = _('Lesson') verbose_name_plural = _('Lessons') def save(self, force_insert=False, force_update=False, using=None, update_fields=None): is_create = self.pk is None super().save(force_insert, force_update, using, update_fields) if is_create: cms.api.add_plugin( placeholder=self.lesson_content, plugin_type='PageTitleCMSPlugin', language='ru', ) cms.api.add_plugin( placeholder=self.lesson_content, plugin_type='TextCMSPlugin', language='ru', body=f'<h2>В этом уроке:</h2>{self.brief}', ) cms.api.add_plugin( placeholder=self.lesson_content, plugin_type='VideoYoutubeCMSPlugin', language='ru', )
class CMSPlaceholder(models.Model): """ Model to hold content for placeholders on pages outside the CMS hierarchy. """ cmscontent_placeholder = PlaceholderField('cmscontent_placeholder') page_identifier = models.CharField(max_length=255, choices=VALID_IDENTIFIER_CHOICES, unique=True) class Meta: ordering = ('page_identifier', ) def __unicode__(self): return self.page_identifier
class PublisherItem(EditModeAndChangePermissionMixin, PublisherParlerAutoSlugifyModel): # TranslatedAutoSlugifyMixin options slug_source_field_name = "text" translations = TranslatedFields( text=models.CharField(max_length=255), slug=models.SlugField(max_length=255, db_index=True, blank=True), ) content = PlaceholderField(slotname="item_content") #-------------------------------------------------------------------------- def get_absolute_url(self, language=None): language = language or self.get_current_language() slug = self.safe_translation_getter('slug', language_code=language) with force_language(language): if not slug: return reverse('%s:publisher-list' % constants.LIST_APPHOOK_NAMESPACE) else: return reverse( '%s:publisher-detail' % constants.LIST_APPHOOK_NAMESPACE, kwargs={"slug": slug}, ) def __str__(self): """ str() used in relation choice fields, wo we used the published fields, if available """ if self.publisher_linked is None: obj = self else: obj = self.publisher_linked text = obj.safe_translation_getter(field="text", any_language=True) or "none" if self.publisher_is_draft: info = "draft," if self.publisher_linked is None: info += "not published" else: info += "is published" else: info = "published" return "%s (pk:%r, %s)" % (text, self.pk, info)
class Post(models.Model): title = models.CharField( _('Title'), max_length=255, help_text=_('The post title.')) slug = models.SlugField( _('slug'), max_length=255, unique=True, help_text=_('The name (slug) for the post, used in URLs.')) excerpt = models.TextField(_('Excerpt'), blank=True) featured_image = FilerImageField( verbose_name=_('Featured Image'), blank=True, null=True, help_text=_('Featured image for this post'), related_name='blog_post_featured_images') content = PlaceholderField( 'post content', related_name='post_content', help_text=_('The post content.')) author = models.ForeignKey( settings.AUTH_USER_MODEL, verbose_name=_('Author'), related_name='posts', help_text=_('The author of the post.')) creation_date = models.DateTimeField( auto_now_add=True, help_text=_('The post\'s creation time.')) publication_date = models.DateTimeField( _('Publication date'), default=timezone.now, db_index=True, help_text=_('Used in the URL. If changed, the URL will change.')) class Meta: ordering = ('-publication_date',) get_latest_by = 'publication_date' def __str__(self): return self.title def get_absolute_url(self): kwargs = { 'year': self.publication_date.year, 'month': self.publication_date.month, 'day': self.publication_date.day, 'slug': self.slug } return reverse('blog:post_detail', kwargs=kwargs)
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(auto_now_add=True) date_modified = models.DateTimeField(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(BlogCategory, verbose_name=_('category'), related_name='blog_posts',) main_image = FilerImageField(verbose_name=_('Main image'), blank=True, null=True, related_name='djangocms_blog_post_image') main_image_thumbnail = models.ForeignKey(ThumbnailOption, verbose_name=_('Main image thumbnail'), related_name='djangocms_blog_post_thumbnail', blank=True, null=True) main_image_full = models.ForeignKey(ThumbnailOption, verbose_name=_('Main image full'), related_name='djangocms_blog_post_full', blank=True, null=True) enable_comments = models.BooleanField( verbose_name=_(u'Enable comments on post'), default=settings.BLOG_ENABLE_COMMENTS ) translations = TranslatedFields( title=models.CharField(_('Title'), max_length=255), slug=models.SlugField(_('slug'), blank=True, db_index=True), abstract=HTMLField(_('Abstract')), meta_description=models.TextField(verbose_name=_(u'Post meta description'), blank=True, default=''), meta_keywords=models.TextField(verbose_name=_(u'Post meta keywords'), blank=True, default=''), meta_title=models.CharField(verbose_name=_(u'Post meta title'), help_text=_(u'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') objects = GenericDateTaggedManager() tags = TaggableManager(blank=True, related_name='djangocms_blog_tags') _metadata = { 'title': 'get_title', 'description': 'get_description', 'og_description': 'get_description', 'twitter_description': 'get_description', 'gplus_description': 'get_description', 'keywords': 'get_keywords', 'locale': None, 'image': 'get_image_full_url', 'object_type': settings.BLOG_TYPE, 'og_type': settings.BLOG_FB_TYPE, 'og_app_id': settings.BLOG_FB_APPID, 'og_profile_id': settings.BLOG_FB_PROFILE_ID, 'og_publisher': settings.BLOG_FB_PUBLISHER, 'og_author_url': settings.BLOG_FB_AUTHOR_URL, 'twitter_type': settings.BLOG_TWITTER_TYPE, 'twitter_site': settings.BLOG_TWITTER_SITE, 'twitter_author': settings.BLOG_TWITTER_AUTHOR, 'gplus_type': settings.BLOG_GPLUS_TYPE, 'gplus_author': settings.BLOG_GPLUS_AUTHOR, 'published_time': 'date_published', 'modified_time': 'date_modified', 'expiration_time': 'date_published_end', 'tag': 'get_tags', 'url': 'get_absolute_url', } 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_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 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 class Meta: verbose_name = _('blog article') verbose_name_plural = _('blog articles') ordering = ('-date_published', '-date_created') get_latest_by = 'date_published' def __unicode__(self): return self.safe_translation_getter('title') def save(self, *args, **kwargs): super(Post, self).save(*args, **kwargs) for lang in self.get_available_languages(): self.set_current_language(lang) if not self.slug and self.title: self.slug = slugify(self.title) self.save_translations() def get_absolute_url(self): kwargs = {'year': self.date_published.year, 'month': self.date_published.month, 'day': self.date_published.day, 'slug': self.safe_translation_getter('slug', any_language=True)} return reverse('djangocms_blog:post-detail', kwargs=kwargs) def thumbnail_options(self): if self.main_image_thumbnail_id: return self.main_image_thumbnail.as_dict else: return settings.BLOG_IMAGE_THUMBNAIL_SIZE def full_image_options(self): if self.main_image_full_id: return self.main_image_full.as_dict else: return settings.BLOG_IMAGE_FULL_SIZE def get_full_url(self): return self.make_full_url(self.get_absolute_url())
class CaseStudy(models.Model): REGIONS = ( ('americas', 'The Americas and Caribbean'), ('europe-asia-c', 'Europe and Central Asia'), ('pacific-asia-e', 'East Asia and the Pacific'), ('africa-e-s', 'Eastern and Southern Africa'), ('middle-east-africa', 'Middle East and North Africa'), ('asia-s', 'South Asia'), ('africa-w-c', 'West and Central Africa'), ) region = models.CharField(max_length=20, choices=REGIONS) countries = CountryField(multiple=True) heading = models.CharField(max_length=128) slug = AutoSlugField(populate_from='heading', unique=True, max_length=128) featured_image = FilerImageField(verbose_name='Featured Image', blank=True, null=True, related_name='+', on_delete=models.SET_NULL) lead_content = PlaceholderField(slotname='case_study_lead_content', related_name='lead_case_study', verbose_name='Case Study Lead Content') main_content = PlaceholderField(slotname='case_study_main_content', related_name='main_case_study', verbose_name='Case Study Main Content') stats_content = PlaceholderField(slotname='case_study_stats_content', related_name='stats_case_study', verbose_name='Case Study Stats Content') sidebar_content = PlaceholderField( slotname='case_study_sidebar_content', related_name='sidebar_case_study', verbose_name='Case Study Sidebar Content') published = models.BooleanField( 'Published', default=False, help_text='Indicates if this Case Study is pubilc or still a draft.') last_modified = models.DateTimeField(auto_now=True) use_cases = models.ManyToManyField(Page, limit_choices_to=use_case_choices) class Meta: verbose_name_plural = 'Case Studies' def __str__(self): return self.heading def get_absolute_url(self): """ Absolute URL for this object. Expects a given Application name to reverse the URL to the correct location. """ return reverse('case_studies:detail', kwargs={'slug': self.slug}) def get_cms_change_url(self): return '{}?edit'.format(self.get_absolute_url()) def similar(self, count=4, only_published=True): qs = CaseStudy.objects.exclude(pk=self.pk) if only_published: qs = qs.filter(published=True) qs = qs similar_cases = list( qs.filter( use_cases__in=self.use_cases.all()).order_by().distinct()) if len(similar_cases) > count: similar_cases = random.sample(similar_cases, count) else: random.shuffle(similar_cases) print(similar_cases) remaining = count - len(similar_cases) if remaining > 0: similar_cases.extend( list( qs.exclude(pk__in=[case.pk for case in similar_cases ]).order_by('?')[:remaining])) return similar_cases
class SellerProfileInstructionsPlaceholder(models.Model): """Django CMS placeholder for use in seller profile to add cms content.""" seller_profile_instructions = PlaceholderField( slotname='seller_profile_instructions', related_name='seller_profile_instructions')
class Module(CreationTrackingBaseModel): objects = IterativeDeletion_Manager() class Meta: app_label = 'core' ordering = ('name',) verbose_name_plural = 'Modules' # path to the core module view def absolute_url(self): return reverse('core:module_detail', kwargs={ 'slug': self.slug, }) # path to the manage page for a module def manage_url(self): return reverse('manage:module_content', kwargs={ 'slug': self.slug, }) # path to the viewer 'module' page for a module def viewer_url(self): return reverse('modules:module_detail', kwargs={ 'slug': self.slug, }) # path to the viewer 'module' page for a module def reference_url(self): return reverse('modules:module_ref', kwargs={ 'ref_id': self.ref_id, }) # added for wizard support. potentially this can be used to redirect between core/manage/module def get_absolute_url(self): return self.absolute_url() def __unicode__(self): return self.name # needed to show the name in the admin interface (otherwise will show 'Module Object' for all entries) def __str__(self): return self.name def copy_relations(self, oldinstance): # Before copying related objects from the old instance, the ones # on the current one need to be deleted. Otherwise, duplicates may # appear on the public version of the page self.topic_item.all().delete() for topic_item in oldinstance.topic_item.all(): # instance.pk = None; instance.pk.save() is the slightly odd but # standard Django way of copying a saved model instance topic_item.pk = None topic_item.plugin = self topic_item.save() def delete(self, *args, **kwargs): print("----- in module overridden delete") # self.cleanup_placeholders() placeholders = [self.intro] # get all child Topics and delete them # Topic.objects.filter(module=self).delete() self.topics.delete() super(Module, self).delete(*args, **kwargs) for ph in placeholders: ph.clear() ph.delete() name = models.CharField(u'Module Name', blank=False, default='', help_text=u'Please enter a name for this module', max_length=250, unique=True, ) ref_id = RandomCharField(unique=True, length=8, include_punctuation=False, ) slug = AutoSlugField(u'slug', blank=False, default='', max_length=64, unique=True, # populate_from=('name',), populate_from=('ref_id',), help_text=u'Please enter a unique slug for this module (can autogenerate from name field)', ) # shared_with = models.ManyToManyField(Person, through='ShareMapping') tags = TaggableManager(blank=True) intro = PlaceholderField('module_intro')
class Lesson(CreationTrackingBaseModel): objects = IterativeDeletion_Manager() class Meta: app_label = 'core' unique_together = ('topic', 'name') # enforce only unique topic names within a module ordering = ('position',) verbose_name_plural = 'Lessons' def absolute_url(self): return reverse('core:lesson_detail', kwargs={ 'module_slug': self.topic.module.slug, 'topic_slug': self.topic.slug, 'slug': self.slug }) # path to the manage page for a topic def manage_url(self): return reverse('manage:lesson_content', kwargs={ 'module_slug': self.topic.module.slug, 'topic_slug': self.topic.slug, 'slug': self.slug }) # path to the viewer page for a topic def viewer_url(self): return reverse('modules:lesson_detail', kwargs={ 'module_slug': self.topic.module.slug, 'topic_slug': self.topic.slug, 'slug': self.slug }) def __unicode__(self): return self.name # needed to show the name in the admin interface (otherwise will show 'Module Object' for all entries) def __str__(self): return "%s:%s:%s" % (self.topic.module.name, self.topic.name, self.name) def copy_relations(self, oldinstance): # Before copying related objects from the old instance, the ones # on the current one need to be deleted. Otherwise, duplicates may # appear on the public version of the page self.section_item.all().delete() for section_item in oldinstance.section_item.all(): # instance.pk = None; instance.pk.save() is the slightly odd but # standard Django way of copying a saved model instance section_item.pk = None section_item.plugin = self section_item.save() def delete(self, *args, **kwargs): print("----- in Lesson overridden delete") # self.cleanup_placeholders() placeholders = [self.summary] self.sections.delete() super(Lesson, self).delete(*args, **kwargs) for ph in placeholders: ph.clear() ph.delete() topic = models.ForeignKey('core.Topic', related_name="lessons", blank=False, default=None, help_text=u'Please specify the Topic for this Lesson.', null=False, on_delete=models.CASCADE, ) parent = 'topic' # position = models.PositiveIntegerField(default=0, editable=False, db_index=True) position = models.PositiveIntegerField(default=0, blank=False, null=False) ref_id = RandomCharField(unique=True, length=8, include_punctuation=False, ) name = models.CharField(u'Lesson Name', blank=False, default='', help_text=u'Please enter a name for this Lesson', max_length=250, unique=False, ) short_name = models.CharField(u'Lesson Short Name', blank=True, default='', help_text=u'(OPTIONAL) A shortened version of this lesson\'s name for use in lesson listings', max_length=250, unique=False, ) slug = AutoSlugField(u'slug', blank=False, default='', max_length=64, unique=True, # populate_from=('name',), populate_from=('ref_id',), help_text=u'Please enter a unique slug for this Lesson (can autogenerate from name field)', ) tags = TaggableManager(blank=True) summary = PlaceholderField('lesson_summary')
class Post(TranslatableModel): """ Blog post """ author = models.ForeignKey(User, verbose_name=_('Author'), null=True, blank=True) date_created = models.DateTimeField(auto_now_add=True) date_modified = models.DateTimeField(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(BlogCategory, verbose_name=_('category'), related_name='blog_posts',) main_image = FilerImageField(verbose_name=_('Main image'), blank=True, null=True) main_image_thumbnail = models.ForeignKey(ThumbnailOption, verbose_name=_('Main image thumbnail'), related_name='blog_post_thumbnail', blank=True, null=True) main_image_full = models.ForeignKey(ThumbnailOption, verbose_name=_('Main image full'), related_name='blog_post_full', blank=True, null=True) translations = TranslatedFields( title=models.CharField(_('Title'), max_length=255), slug=models.SlugField(_('slug'), blank=True, db_index=True), abstract=HTMLField(_('Text')), meta={'unique_together': (('language_code', 'slug'),)} ) content = PlaceholderField("post_content") objects = GenericDateTaggedManager() tags = TaggableManager(blank=True) class Meta: verbose_name = _('blog article') verbose_name_plural = _('blog articles') ordering = ("-date_published", "-date_created") def __unicode__(self): return self.safe_translation_getter('title') def save(self, *args, **kwargs): super(Post, self).save(*args, **kwargs) for lang in self.get_available_languages(): self.set_current_language(lang) if not self.slug and self.title: self.slug = slugify(self.title) self.save_translations() def get_absolute_url(self): kwargs = {'year': self.date_published.year, 'month': self.date_published.month, 'day': self.date_published.day, 'slug': self.slug} return reverse('djangocms_blog:post-detail', kwargs=kwargs) def thumbnail_options(self): if self.main_image_thumbnail_id: return self.main_image_thumbnail.as_dict else: return settings.BLOG_IMAGE_THUMBNAIL_SIZE def get_full_url(self): s = Site.objects.get_current() if s.domain.find('http') > -1: return "%s%s" % (s.domain, self.get_absolute_url()) else: return "http://%s%s" % (s.domain, self.get_absolute_url()) def full_image_options(self): if self.main_image_fulll_id: return self.main_image_full.as_dict else: return settings.BLOG_IMAGE_FULL_SIZE
class Lesson(Publication): # TODO: if needed for publishable, can inherit parent's meta # class Meta(Publishable.Meta): class Meta: app_label = 'core' #unique_together = ('parent_lesson', 'name') # enforce only unique topic names within a module #ordering = ('name',) ordering = ('position', ) verbose_name_plural = 'Lessons' ######################################## # Fields ######################################## # boolean field representing if a lesson is (soft) deleted # TODO: this has not been factored into the system yet, implementation will require revision of delete method is_deleted = models.BooleanField(default=False) # the reference id for a lesson (used in slug generation) # this reference id will be maintained for all copies of this lesson ref_id = models.UUIDField(default=uuid.uuid4, editable=False) # marks the parent lesson for a lesson # this field will be auto-populated by the generated forms # it from the dynamic interface parent_lesson = models.ForeignKey( 'self', related_name="sub_lessons", blank=True, default=None, help_text=u'Specify a Parent Lesson for this Sub-Lesson.', null=True, on_delete=models.CASCADE, ) # Being that lessons will be clonable # maintain a linkage to the lesson copied from # used for giving credit to original creator # derived_from_lesson = models.ForeignKey('self', # related_name="derivations", # blank=True, # default=None, # help_text=u'Lesson this lesson was copied from.', # null=True, # on_delete=models.SET_NULL, # ) # position amongst siblings, siblings can be of type Lessons or Sections position = models.PositiveIntegerField(default=0, blank=False, null=False) # zero based depth level of lesson # exclusively set by backend depth = models.PositiveIntegerField(default=0, blank=False, null=False) # depth Identifier # exclusively set by backend depth_label = models.CharField( u'Depth Label', blank=False, default='Module', help_text=u'The depth-level label for this lesson', max_length=10, unique=False, ) name = models.CharField( u'Lesson Name', blank=False, default='', help_text=u'Please enter a name for this Lesson', max_length=250, unique=False, ) short_name = models.CharField( u'Lesson Short Name', blank=True, default='', help_text= u'(OPTIONAL) A shortened version of this lesson\'s name for use in lesson listings', max_length=250, unique=False, ) slug = AutoSlugField( u'slug', blank=False, default='', max_length=8, unique=True, populate_from=('ref_id', ), help_text= u'Please enter a unique slug for this Lesson (can autogenerate from name field)', ) tags = TaggableManager(blank=True) # many to many relationship for collaborators # allowed to make edits to the draft of a publication collaborators = models.ManyToManyField(User, related_name="collaborations", through='Collaboration') # the content of this lesson summary = PlaceholderField('lesson_summary') ######################################## # Cloning references ######################################## # the date this lesson was cloned from a published lesson derived_date = models.DateTimeField(null=True) # the published lesson this lesson was derived from's ref_id derived_lesson_slug = models.CharField(null=True, default=None, editable=False, max_length=8) # the user that created the lesson this was derived from derived_lesson_creator = models.ForeignKey(User, null=True, blank=True, related_name='inspired_lessons') # the default related name for this many-to-many field is lesson_set # # learning_objectives = models.ManyToManyField('core.LearningObjective') # the default related name for this many-to-many field is lesson_set # these will potentially be polymorphic to account for different # resource types potentially needing different attributes # # resources = models.ManyToManyField('core.Resource') # TODO: potentially add 1-to-1 relationship to a publishable (instead of direct inheritance) # this will allow for a lesson to be a child and root # e.g. root.publishable = [publishable object], child.publishable = None # ______________________________ # parent_link # When True and used in a model which inherits from another # concrete model, indicates that this field should be used as # the link back to the parent class, rather than the extra # OneToOneField which would normally be implicitly created # by subclassing. # # publishable = models.OneToOneField('core.Publishable', default=None, on_delete=modeld.SET_NULL, parent_link=True) def __str__(self): return self.name ######################################## # URL Methods ######################################## # define for use by FormMixin # (calls this method specifically, but isn't defined by default... right...) def get_absolute_url(self): return self.absolute_url() def absolute_url(self): return reverse('core:lesson_detail', kwargs={'slug': self.slug}) # path to the manage page for a topic def manage_url(self): return reverse('manage:lesson_content', kwargs={'slug': self.slug}) # path to the edit page for a topic def edit_url(self): return reverse('editor:lesson_edit', kwargs={'slug': self.slug}) # path to the viewer page for a topic def viewer_url(self): return reverse('modules:lesson_detail', kwargs={'slug': self.slug}) # path to the viewer 'module' page for a module def reference_url(self): return reverse('modules:module_ref', kwargs={ 'ref_id': self.ref_id, }) ######################################## # Query Methods/properties ######################################## # TODO: Watch for this, as formsets may not access this with update def save(self, **kwargs): # print('---- in custom lesson save') # set depth level on save if self.parent_lesson: self.depth = self.parent_lesson.depth + 1 else: self.depth = 0 # TODO: this needs to be flipped # based on depth level set the depth label self.depth_label = { 0: 'Module', 1: 'Topic', 2: 'Lesson', }.get(self.depth, "INVALID") super(Lesson, self).save(**kwargs) def delete(self, *args, **kwargs): # self.cleanup_placeholders() placeholders = [self.summary] self.sections.delete() self.sub_lessons.delete() super(Lesson, self).delete(*args, **kwargs) for ph in placeholders: ph.clear() ph.delete() def validate_unique(self, exclude=None): # add a conditional unique constraint to prevent # creation of multiple drafts with the same name # this is only valid if a base lesson so check that it's not a root lesson too # TODO: watch this, it could be inadequate when 'lesson-copy' becomes enabled later in development # if not self.parent_lesson and self.is_draft and Lesson.objects.exclude(pk=self.pk).filter(name=self.name, is_draft=True).exists(): # raise ValidationError('A Draft-Lesson with this name already exists') return super(Lesson, self).validate_unique(exclude) @property def total_depth(self): ''' method to return the total depth of this lesson's structure (the max level of nested children) :return: integer representation of child depth ''' if self.sub_lessons: max_depth = 0 for sub_lesson in self.sub_lessons.all(): max_depth = max(max_depth, sub_lesson.total_depth) return max_depth + 1 else: return 1 @property def num_children(self): return self.num_sections + self.num_sub_lessons @property def num_sections(self): return self.sections.count() @property def num_sub_lessons(self): return self.sub_lessons.count() def derivation(self): ''' Method to copy a published lesson instance and set derivation attributes to point to this lesson and it's creator :return: new lesson instance with attributes set to link to derived lesson ''' derivation = self.copy() derivation.derived_date = now() derivation.derived_lesson_slug = self.slug derivation.derived_lesson_creator = self.created_by return derivation def derive_children_from(self, from_lesson): self.sections.delete() self.sub_lessons.delete() for section_item in from_lesson.sections.all(): # copy the section items and set their linked lesson to this new instance new_section = section_item.copy() new_section.lesson = self new_section.position = section_item.position # save the copied section instance new_section.save() new_section.copy_content(section_item) new_section.copy_children(section_item) for sub_lesson in from_lesson.sub_lessons.all(): # copy the sub-lesson items and set their linked parent_lesson to this new instance new_lesson = sub_lesson.derivation() new_lesson.parent_lesson = self new_lesson.position = sub_lesson.position # save the copied sub-lesson instance new_lesson.save() new_lesson.copy_content(sub_lesson) new_lesson.derive_children_from(sub_lesson) ######################################## # Publication Method overrides ######################################## def copy(self, maintain_ref=False): ''' generate a new (unsaved) lesson instance based on this lesson, with a fresh ref_id if specified. Notes: The newly generated instance: - removes reference to parent - marks 'position' as 0 - and sets 'is_deleted' to False Additionally, this method does not copy placeholder(content), tags, collaborators, or child-objects (use copy_content (or copy_children for children) after save to do this) :return: a new (unsaved) copy of this lesson ''' new_instance = Lesson( parent_lesson=None, position=0, is_deleted=False, name=self.name, short_name=self.short_name, ) # if specified, mark this new instance as the same lesson # typically only used in publication methods if maintain_ref: new_instance.ref_id = self.ref_id if self.derived_date: new_instance.derived_date = self.derived_date new_instance.derived_lesson_slug = self.derived_lesson_slug new_instance.derived_lesson_creator = self.derived_lesson_creator return new_instance def copy_children(self, from_instance, maintain_ref=False): ''' Copy child relations (sub_lessons/sections) from a passed lesson, with the option of specifying if the ref_id should be maintained. this should only happen during publishing. :param from_instance: Lesson instance from which the child relations are provided. :param maintain_ref: Boolean representing if the ref_id should be maintained on the child objects, this should only be true in the case of publication. :return: None ''' # copy over the content #self.copy_content(from_instance) # Before copying related objects from the old instance, the ones # on the current one need to be deleted. Otherwise, duplicates may # appear on the public version of the page self.sections.delete() self.sub_lessons.delete() #self.app_refs.delete() for section_item in from_instance.sections.all(): # copy the section items and set their linked lesson to this new instance new_section = section_item.copy(maintain_ref) new_section.lesson = self new_section.position = section_item.position # save the copied section instance new_section.save() new_section.copy_content(section_item) new_section.copy_children(section_item, maintain_ref) for sub_lesson in from_instance.sub_lessons.all(): # copy the sub-lesson items and set their linked parent_lesson to this new instance new_lesson = sub_lesson.copy(maintain_ref) new_lesson.parent_lesson = self new_lesson.position = sub_lesson.position # save the copied sub-lesson instance new_lesson.save() new_lesson.copy_content(sub_lesson) new_lesson.copy_children(sub_lesson, maintain_ref) for app_ref in from_instance.app_refs.all(): new_ref = AppReference( app_name=app_ref.app_name, app_link=app_ref.app_link, lesson=self, ) new_ref.save() for learning_obj in from_instance.learning_objectives.all(): new_lo = Learning_Objective( lesson=self, condition=learning_obj.condition, task=learning_obj.task, degree=learning_obj.degree, verb=learning_obj.verb, ) new_lo.save() for outcome in learning_obj.outcomes.all(): new_lo.outcomes.add(outcome) def copy_content(self, from_instance): ''' copy content including tags, and placeholder plugins to this instance from a passed Lesson :param from_instance: a Lesson object the content/tags are being copied from :return: None ''' # add any tags from the 'from_instance' self.tags.add(*list(from_instance.tags.names())) # clear any existing plugins self.summary.clear() # get the list of plugins in the 'from_instance's intro plugins = from_instance.summary.get_plugins_list() # copy 'from_instance's intro plugins to this object's intro copy_plugins_to(plugins, self.summary, no_signals=True) def get_Publishable_parent(self): # return self.topic.module # if this isn't the parent lesson traverse up until root is reached. if self.parent_lesson: return self.parent_lesson.get_Publishable_parent() else: return self @property def is_dirty(self): # if this is the root lesson if not self.parent_lesson: # if this root lesson is not published, return dirty #if not self.is_published(): return True # TODO: need to check if this is the draft copy or not # if this is the draft instance, and not published return true if self.publish_status == Publication.DRAFT_ONLY: return True # get the publication date to check against pub_date = None if self.publish_status == Publication.PUBLISHED: # if this is a published draft-copy pub_date = self.published_copy.published_date else: # if this is the current publication pub_date = self.published_date # if this is a published copy result = any([ super(Lesson, self).is_dirty, self.summary.cmsplugin_set.filter( changed_date__gt=pub_date).exists(), ]) if result: return result # if this lesson is clean, check it's children for t in self.sub_lessons.all(): if t.is_dirty: return True for t in self.sections.all(): if t.is_dirty: return True else: # if this lesson is a child lesson, check if it's root lesson has # a published copy, if not mark dirty parent_publication = self.get_Publishable_parent() if parent_publication.publish_status == Publication.DRAFT_ONLY: return True pub_date = None if parent_publication.publish_status == Publication.PUBLISHED: # if this is a published draft-copy pub_date = parent_publication.published_copy.published_date else: # if this is the current publication pub_date = parent_publication.published_date # otherwise, check that this lesson has no plugins saved # after the publication date result = any([ #super(Lesson, self).is_dirty, self.changed_date > pub_date, self.summary.cmsplugin_set.filter( changed_date__gt=pub_date).exists(), ]) if result: return result # if not check children for t in self.sub_lessons.all(): if t.is_dirty: return True for t in self.sections.all(): if t.is_dirty: return True return False def has_draft_access(self, user): # if passes the default permission check (owner/admin) return true if (super(Lesson, self).has_draft_access(user)): return True else: # else check if the current user is a collaborator for this lesson # or is a collaborator on the root object access_conditions = [ self.collaborators.filter(pk=user.pk).exists(), self.get_Publishable_parent().collaborators.filter( pk=user.pk).exists(), ] return any(access_conditions) def has_edit_access(self, user): # user can edit if they are the owner or have been marked as a collaborator with # edit access on either this lesson or the parent publication access_conditions = [ self.get_owner() == user, Collaboration.objects.filter(publication=self, collaborator=user, can_edit=True).exists(), Collaboration.objects.filter( publication=self.get_Publishable_parent(), collaborator=user, can_edit=True).exists(), ] return any(access_conditions) def get_owner(self): ''' get the owner of the lesson (created-by), if this is a child lesson return the owner of it's parent :return: user who created the root lesson ''' if self.parent_lesson: return self.parent_lesson.get_Publishable_parent().get_owner() else: return self.created_by
class Article(PolymorphicModel): namespace = models.CharField(_('Application instance'), max_length=200) title = models.CharField(_('Title'), max_length=250) slug = models.SlugField(_('Slug'), max_length=250, db_index=True, unique=False) pub_date = models.DateField(_('Publication date'), editable=True) perex = HTMLField(_('Perex'), blank=True, default='') text = PlaceholderField('article_text') page_title = models.CharField( _('Page title'), max_length=250, blank=True, null=True, help_text=_('Overwrite the title (html title tag)')) menu_title = models.CharField( _('Menu title'), max_length=250, blank=True, null=True, help_text=_('Overwrite the title in the menu')) meta_desc = models.TextField( _('Meta description'), blank=True, default='', help_text=_('The text displayed in search engines.')) public = models.BooleanField(default=False, verbose_name=_('Public')) class Meta: ordering = ('-pub_date', ) unique_together = [('pub_date', 'slug')] verbose_name = _('Article') verbose_name_plural = _('Articles') def __str__(self): return self.title def save(self, *args, **kwargs): errors = self._perform_unique_checks([(Article, ('pub_date', 'slug'))]) if errors: raise ValidationError(errors) super(Article, self).save(*args, **kwargs) def get_absolute_url(self): return reverse( '{}:detail'.format(self.namespace), kwargs={ 'year': '{:%Y}'.format(self.pub_date), 'month': '{:%m}'.format(self.pub_date), 'day': '{:%d}'.format(self.pub_date), 'slug': self.slug, }, ) def get_edit_url(self): return reverse('admin:{}_{}_change'.format(self._meta.app_label, self._meta.model_name), args=(self.id, )) def get_page(self): return Page.objects.get(application_namespace=self.namespace, publisher_is_draft=False) def get_title(self): return self.title def get_page_title(self): return self.page_title or self.title def get_menu_title(self): return self.menu_title or self.title
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 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 ActivitySection(Section): class Meta: verbose_name = "Activity Section" verbose_name_plural = 'Activity Sections' manager_inheritance_from_future = True content = PlaceholderField('activity_content') def absolute_url(self): return reverse('core:section_detail', kwargs={ 'lesson_slug': self.lesson.slug, 'slug': self.slug }) def delete(self, *args, **kwargs): #print("----- in ActivitySection overridden delete") # self.cleanup_placeholders() placeholders = [self.content] super(ActivitySection, self).delete(*args, **kwargs) for ph in placeholders: ph.clear() ph.delete() ######################################## # Publication Method overrides ######################################## def copy(self, maintain_ref=False): ''' generate a new Activity instance based on this Activitiy instance with a fresh ref_id and no parent :return: a new lesson with a fresh reference id ''' new_instance = ActivitySection( lesson=None, is_deleted=False, position=0, name=self.name, short_name=self.short_name, duration=self.duration, ) if maintain_ref: new_instance.ref_id = self.ref_id return new_instance def copy_children(self, from_instance, maintain_ref=False): # copy over the resources for this activity self.resources.all().delete() for resource in from_instance.resources.all(): new_resource = Resource( display_text=resource.display_text, resource_link=resource.resource_link, resource_type=resource.resource_type, activity=self, ) new_resource.save() def copy_content(self, from_instance): # add any tags from the 'from_instance' self.tags.add(*list(from_instance.tags.names())) # clear any existing plugins self.content.clear() # get the list of plugins in the 'from_instance's intro plugins = from_instance.content.get_plugins_list() # copy 'from_instance's intro plugins to this object's intro copy_plugins_to(plugins, self.content, no_signals=True) @property def is_dirty(self): # a module is considered dirty if it's pub_status is pending, or if it contains any plugins # edited after the most recent change date. #result = super(ActivitySection, self).is_dirty() or self.content.cmsplugin_set.filter(changed_date__gt=self.changed_date).exists() result = any([ super(ActivitySection, self).is_dirty, self.content.cmsplugin_set.filter( changed_date__gt=self.get_Publishable_parent( ).published_copy.creation_date).exists() ]) return result
class Doctor(TranslationHelperMixin, TranslatedAutoSlugifyMixin, TranslatableModel): slug_source_field_name = 'name' translations = TranslatedFields( name=models.CharField(_('name'), max_length=255, blank=False, default='', help_text=_("Provide this person's name.")), slug=models.SlugField( _('unique slug'), max_length=255, blank=True, default='', help_text=_("Leave blank to auto-generate a unique slug.")), speciality=models.CharField(_('speciality'), max_length=255), sub_speciality=models.CharField(_('sub speciality'), max_length=255, blank=True), function=models.CharField(_('role'), max_length=255, blank=True, default=''), ) phone = models.CharField(verbose_name=_('phone'), null=True, blank=True, max_length=100) mobile = models.CharField(verbose_name=_('mobile'), null=True, blank=True, max_length=100) email = models.EmailField(verbose_name=_("email"), blank=True, default='') website = models.URLField(verbose_name=_('website'), null=True, blank=True) section = models.ForeignKey(Section, verbose_name=_('Section'), related_name='doctors', null=True, blank=True, on_delete=models.SET_NULL) visual = FilerImageField(null=True, blank=True, default=None, on_delete=models.SET_NULL) facebook = models.URLField(verbose_name=_('Facebook'), blank=True) twitter = models.URLField(verbose_name=_('Twitter'), blank=True) linkedin = models.URLField(verbose_name=_('LinkedIn'), blank=True) content = PlaceholderField('doctor_content', related_name='doctor_content') content_bottom = PlaceholderField('doctor_content_bottom', related_name='doctor_content_bottom') sidebar = PlaceholderField('doctor_sidebar', related_name='doctor_sidebar') sidebar_bottom = PlaceholderField('doctor_sidebar_bottom', related_name='doctor_sidebar_bottom') class Meta: verbose_name = _('Doctor') verbose_name_plural = _('Doctors') def __str__(self): pkstr = str(self.pk) name = self.safe_translation_getter('name', default='', any_language=True).strip() return name if len(name) > 0 else pkstr 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('nnuh_doctors:doctor-detail', kwargs=kwargs) except NoReverseMatch: url = '' return url
class Terms(ModelMeta, TranslatableModel): """ Terms Agreement Model """ author = models.ForeignKey( dj_settings.AUTH_USER_MODEL, verbose_name=_(u'author'), null=True, blank=True, related_name='app_terms_author', ) date_created = models.DateTimeField(_(u'created'), auto_now_add=True) date_modified = models.DateTimeField(_(u'last modified'), auto_now=True) date_published = models.DateTimeField(_(u'published Since'), default=timezone.now) date_published_end = models.DateTimeField(_(u'published Until'), null=True, blank=True) publish = models.BooleanField(_(u'publish'), default=False) categories = models.ManyToManyField( 'app.AppCategory', verbose_name=_(u'category'), related_name='app_terms_categories', ) main_image = FilerImageField(verbose_name=_(u'main image'), blank=True, null=True, on_delete=models.SET_NULL, related_name='app_terms_image') main_image_thumbnail = models.ForeignKey( 'cmsplugin_filer_image.ThumbnailOption', verbose_name=_(u'main image thumbnail'), related_name='app_terms_thumbnail', on_delete=models.SET_NULL, blank=True, null=True) main_image_full = models.ForeignKey( 'cmsplugin_filer_image.ThumbnailOption', verbose_name=_(u'main image full'), related_name='app_terms_full', on_delete=models.SET_NULL, blank=True, null=True) enable_comments = models.BooleanField( verbose_name=_(u'enable comments on terms agreement'), default=get_setting('ENABLE_COMMENTS')) sites = models.ManyToManyField( 'sites.Site', verbose_name=_(u'Site(s)'), blank=True, null=True, help_text=_(u'Select sites in which to show the terms agreement ' u'If none is set it will be ' u'visible in all the configured sites.')) translations = TranslatedFields( title=models.CharField(_(u'title'), max_length=255), slug=models.SlugField(_(u'slug'), blank=True, db_index=True), abstract=HTMLField(_(u'abstract'), blank=True, default=''), meta_description=models.TextField( verbose_name=_(u'terms meta description'), blank=True, default=''), meta_keywords=models.TextField(verbose_name=_(u'terms meta keywords'), blank=True, default=''), meta_title=models.CharField( verbose_name=_(u'terms meta title'), help_text=_(u'used in title tag and social sharing'), max_length=255, blank=True, default=''), terms_text=HTMLField(_(u'text'), default='', blank=True), meta={'unique_together': [ ( 'slug', 'language_code', ), ]}, ) content = PlaceholderField('terms_content', related_name='app_terms_content') objects = GenericDateTaggedManager() tags = TaggableManager(blank=True, related_name='app_tags') _metadata = { 'title': 'get_title', 'description': 'get_description', 'og_description': 'get_description', 'twitter_description': 'get_description', 'gplus_description': 'get_description', 'keywords': 'get_keywords', 'locale': None, #'image': 'get_image_full_url', 'object_type': get_setting('TYPE'), 'og_type': get_setting('FB_TYPE'), 'og_app_id': get_setting('FB_APPID'), 'og_profile_id': get_setting('FB_PROFILE_ID'), 'og_publisher': get_setting('FB_PUBLISHER'), 'og_author_url': get_setting('FB_AUTHOR_URL'), 'twitter_type': get_setting('TWITTER_TYPE'), 'twitter_site': get_setting('TWITTER_SITE'), 'twitter_author': get_setting('TWITTER_AUTHOR'), 'gplus_type': get_setting('GPLUS_TYPE'), 'gplus_author': get_setting('GPLUS_AUTHOR'), 'published_time': 'date_published', 'modified_time': 'date_modified', 'expiration_time': 'date_published_end', 'tag': 'get_tags', 'url': 'get_absolute_url', } class Meta: verbose_name = _('app terms') verbose_name_plural = _('app terms agreements') ordering = ('-date_published', '-date_created') get_latest_by = 'date_published' def __str__(self): return self.safe_translation_getter('title') def get_absolute_url(self): #import pdb; pdb.set_trace() kwargs = { 'year': self.date_published.year, 'month': '%02d' % self.date_published.month, 'day': '%02d' % self.date_published.day, 'article_slug': self.safe_translation_getter('slug', language_code=get_language(), any_language=True) } return reverse('app:terms-detail', kwargs=kwargs) def save(self, *args, **kwargs): super(Terms, self).save(*args, **kwargs) main_lang = self.get_current_language() for lang in self.get_available_languages(): self.set_current_language(lang) if not self.slug and self.title: self.slug = slugify(self.title) self.set_current_language(main_lang) self.save_translations() 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_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 ReadingSection(Section): class Meta: verbose_name = "Reading Section" verbose_name_plural = 'Reading Sections' manager_inheritance_from_future = True content = PlaceholderField('reading_content') def absolute_url(self): return reverse('core:section_detail', kwargs={ 'lesson_slug': self.lesson.slug, 'slug': self.slug }) def delete(self, *args, **kwargs): #print("----- in ReadingSection overridden delete") # self.cleanup_placeholders() placeholders = [self.content] super(ReadingSection, self).delete(*args, **kwargs) for ph in placeholders: ph.clear() ph.delete() ######################################## # Publication Method overrides ######################################## def copy(self, maintain_ref=False): ''' generate a new ReadingSection instance based on this ReadingSection instance with a fresh ref_id and no parent :return: a new lesson with a fresh reference id ''' new_instance = ReadingSection( lesson=None, position=0, is_deleted=False, name=self.name, short_name=self.short_name, duration=self.duration, ) if maintain_ref: new_instance.ref_id = self.ref_id return new_instance def copy_children(self, from_instance, maintain_ref=False): # copy over the content #self.copy_content(from_instance) pass def copy_content(self, from_instance): # add any tags from the 'from_instance' self.tags.add(*list(from_instance.tags.names())) # clear any existing plugins self.content.clear() # get the list of plugins in the 'from_instance's intro plugins = from_instance.content.get_plugins_list() # copy 'from_instance's intro plugins to this object's intro copy_plugins_to(plugins, self.content, no_signals=True) @property def is_dirty(self): # a module is considered dirty if it's pub_status is pending, or if it contains any plugins # edited after the most recent change date. # get parent publication object parent_publication = self.get_Publishable_parent() if parent_publication.publish_status == Publication.DRAFT_ONLY: return True pub_date = None if parent_publication.publish_status == Publication.PUBLISHED: # if this is a published draft-copy pub_date = parent_publication.published_copy.published_date else: # if this is the current publication pub_date = parent_publication.published_date result = any([ super(ReadingSection, self).is_dirty, self.content.cmsplugin_set.filter( changed_date__gt=pub_date).exists() ]) return result
class Migration(migrations.Migration): dependencies = [ ('cms', '__latest__'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('filer', '__latest__'), ] operations = [ migrations.CreateModel( name='Post', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('title', models.CharField(help_text='The post title.', max_length=255, verbose_name='Title')), ('slug', models.SlugField( help_text='The name (slug) for the post, used in URLs.', unique=True, max_length=255, verbose_name='slug')), ('excerpt', models.TextField(verbose_name='Excerpt', blank=True)), ('creation_date', models.DateTimeField(help_text="The post's creation time.", auto_now_add=True)), ('publication_date', models.DateTimeField( default=django.utils.timezone.now, help_text= 'Used in the URL. If changed, the URL will change.', verbose_name='Publication date', db_index=True)), ('author', models.ForeignKey(related_name='posts', verbose_name='Author', to=settings.AUTH_USER_MODEL, help_text='The author of the post.')), ('content', PlaceholderField(related_name='post_content', slotname='post content', editable=False, to='cms.Placeholder', help_text='The post content.', null=True)), ('featured_image', FilerImageField(related_name='blog_post_featured_images', verbose_name='Featured Image', blank=True, null=True, to='filer.Image', help_text='Featured image for this post')), ], options={ 'ordering': ('-publication_date', ), 'get_latest_by': 'publication_date', }, ), ]
class Project(CMSPlugin): objects = models.Manager() slug = models.SlugField(_('slug'), max_length=255, blank=True, db_index=True) title = models.CharField(max_length=250, null=True, blank=True) sub_title = models.CharField(max_length=250, null=True, blank=True) short_description = models.TextField(default='') description = HTMLField(_('description'), blank=True, default='') skills = models.CharField(max_length=255, null=True, blank=True) roles = models.CharField(max_length=255, null=True, blank=True) client = models.CharField(max_length=255, null=True, blank=True) length = models.CharField(max_length=50, null=True, blank=True) demo_url = models.URLField(max_length=255, null=True, blank=True) android_demo_url = models.URLField(max_length=500, null=True, blank=True, default='') ios_demo_url = models.URLField(max_length=500, null=True, blank=True, default='') youtube_embed_url = models.CharField(max_length=255, null=True, blank=True, default='') top_image = models.ImageField(upload_to='portfolio/img/top_image/', max_length=500, null=True, blank=True, default='') # published_date = models.DateTimeField(null=True, blank=True) # Placeholder property use_case = PlaceholderField('use_case') is_published = models.BooleanField(null=False, blank=False, default=True) is_homepage_display = models.BooleanField(null=False, blank=False, default=True) is_list_display = models.BooleanField(null=False, blank=False, default=True) tags = models.ManyToManyField(Tag, verbose_name=_('tag'), related_name='project') created_at = models.DateTimeField(auto_now_add=True, auto_now=False, editable=False) updated_at = models.DateTimeField(auto_now=True, editable=True) def __str__(self): return self.title 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(Project, self).save(*args, **kwargs) def test_function(self): return '{}: test'.format(self.title) @property def sorted_mediaaset_set(self): return self.mediaasset_set.order_by('id')
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 AbstractArticle(TimestampMixin, PublishingMixin): """ Model for creating and storing and Article object """ title = models.CharField(max_length=255) slug = models.SlugField(max_length=255, unique=True) author = models.ForeignKey(to=Author, on_delete=models.CASCADE, related_name="%(app_label)s_articles") photo = FilerImageField( related_name="%(app_label)s_%(class)s_images", on_delete=models.SET_NULL, null=True, blank=True, ) intro = models.CharField(max_length=255) content = PlaceholderField(slotname="article_content", related_name="%(app_label)s_article_content") tags = models.ManyToManyField(to=Tag, verbose_name="Tags", related_name="%(app_label)s_%(class)s_tags") category = models.ForeignKey(to=Category, on_delete=models.CASCADE, related_name="%(app_label)s_articles") meta_title = models.CharField(max_length=160, blank=True) meta_description = models.CharField(max_length=255, blank=True) plugin_text = models.TextField(blank=True, editable=False) objects = ArticleQuerySet.as_manager() class Meta: abstract = True ordering = ["-publish_at"] def __str__(self): """ Returns the string representation of the article object """ return f"{self.title} article by {self.author}" def save(self, *args, **kwargs): """ Override save method so we can store the plugin text """ if self.content: self.plugin_text = self.plain_text super().save(*args, **kwargs) def get_absolute_url(self): """ Builds the url for the article object """ return reverse("news:detail", kwargs={"slug": self.slug}) @cached_property def plain_text(self): """ Renders all the plaintext plugins from the placeholder field """ # We need to use this weird ContentRenderer in order to render the plugins renderer = ContentRenderer(request=RequestFactory()) text = "" for plugin in self.content.cmsplugin_set.all(): html = renderer.render_plugin(plugin, {}) text += strip_tags(html) return text.strip() @cached_property def read_time(self): """ Return estimated article reading time """ word_count = len(self.plain_text.split()) mins = round(word_count / 240.0) if word_count and mins < 1: return 1 return mins
class News(models.Model): """ News """ author = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_('author'), null=True, blank=True, related_name='news_author') category = models.ForeignKey("NewsCategory", verbose_name=_('category'), related_name="news_category", null=True, blank=True, default=None) meta_description = models.TextField( verbose_name=_('news meta description'), blank=True, default='') meta_keywords = models.TextField(verbose_name=_('news meta keywords'), blank=True, default='') meta_title = models.CharField( verbose_name=_('news meta title'), help_text=_('used in title tag and social sharing'), max_length=255, blank=True, default='') title = models.CharField(_('Title'), max_length=255) slug = AutoSlugField(_('Slug'), max_length=128, unique=True, editable=True, populate_from='title', help_text=_('A slug is a short name which uniquely' ' identifies the news item')) description = HTMLField(_('Description'), blank=True, configuration='CKEDITOR_SETTINGS_NEWS') content = PlaceholderField('news_content', related_name='news_content') publish = models.BooleanField(_('Published'), default=False) 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) enable_comments = models.BooleanField( verbose_name=_('enable comments on post'), default=ENABLE_COMMENTS) images = models.ManyToManyField( 'filer.Image', through='NewsImages', verbose_name=_("News images"), ) # Oscar links linked_products = models.ManyToManyField( 'catalogue.Product', blank=True, verbose_name=_("Linked products"), help_text=_( "These are products that can be shown with news post " "or news post can be shown on the specific product's page.")) linked_categories = models.ManyToManyField( 'catalogue.Category', blank=True, verbose_name=_("Linked product's categories"), help_text=_("Show news for that categories " "or display news on the category page")) linked_classes = models.ManyToManyField( 'catalogue.ProductClass', blank=True, verbose_name=_("Linked product's classes"), help_text=_("Show news for that classes " "or display news on the specific class product's pages")) 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.')) tags = TaggableManager(blank=True, related_name='news_tags') objects = NewsManager() published = PublishedNewsManager() class Meta: app_label = 'oscar_news' verbose_name = _('News') verbose_name_plural = _('News') ordering = ('-date_published', ) def __unicode__(self): return self.title @property def is_published(self): """ Checks whether the news entry 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 _set_default_author(self, current_user): if not self.author_id: if AUTHOR_DEFAULT is True: user = current_user else: user = get_user_model().objects.get(username=AUTHOR_DEFAULT) self.author = user def get_absolute_url(self): """ method below inherited and slightly customized """ cache_key = 'NEWS_ENTRY_URL_%s' % self.pk url = cache.get(cache_key) if not url: # temporarily use link to news detail url = reverse('oscar_news:entry-detail', kwargs={'slug': self.slug}) cache.set(cache_key, url) return url def get_tags(self, queryset=None): """ :return: the list of object's tags annotated with counters. Tags are limited by published news. """ queryset = queryset or News.published.get_queryset() return get_tag_cloud(self.__class__, queryset, set(self.tags.all())) def get_all_tags(self): """ :return: List of all object's tags including unpublished """ return self.get_tags(News.objects.all()) def _get_next_or_previous_published(self, is_next): if not self.pk: raise ValueError( "get_next/get_previous cannot be used on unsaved objects.") op = 'gt' if is_next else 'lt' order = '' if is_next else '-' field = 'date_published' param = force_text(getattr(self, field)) q = Q(**{'%s__%s' % (field, op): param}) q = q | Q(**{field: param, 'pk__%s' % op: self.pk}) qs = self.__class__.published.using(self._state.db).filter(q).order_by( '%s%s' % (order, field), '%spk' % order) try: return qs[0] except IndexError: raise self.DoesNotExist("%s matching query does not exist." % self.__class__._meta.object_name) def get_next_published(self): return self._get_next_or_previous_published(is_next=True) def get_previous_published(self): return self._get_next_or_previous_published(is_next=False)