class MarkupItem(ContentItem): """ A snippet of markup (restructuredtext, markdown, or textile) to display at a page. """ text = models.TextField(_("markup")) # Store the language to keep rendering intact while switching settings. language = models.CharField( _("Language"), max_length=30, editable=False, db_index=True, choices=backend.LANGUAGE_CHOICES, ) objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = _("Markup code") verbose_name_plural = _("Markup code") def __str__(self): return Truncator(self.text).words(20) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Extra polymorphic, in case the base class content ID was stored. ProxyModelClass = LANGUAGE_MODEL_CLASSES.get(self.language, None) if ProxyModelClass: self.__class__ = ProxyModelClass
class GistItem(ContentItem): """ A reference to a gist item (gist.github.com) that is rendered as source code. """ gist_id = models.CharField( _("Gist number"), max_length=128, help_text= _('Go to <a href="https://gist.github.com/" target="_blank">https://gist.github.com/</a> and copy the number of the Gist snippet you want to display.' )) filename = models.CharField( _("Gist filename"), max_length=128, blank=True, help_text=_( 'Leave the filename empty to display all files in the Gist.')) objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = _('GitHub Gist snippet') verbose_name_plural = _('GitHub Gist snippets') def __str__(self): return str(self.gist_id)
class TwitterRecentEntriesItem(ContentItem): """ Content item to display recent entries of a twitter user. """ title = models.CharField( _('Title'), max_length=200, blank=True, help_text=_( 'You may use Twitter markup here, such as a #hashtag or @username.' )) twitter_user = models.CharField(_('Twitter user'), max_length=75) amount = models.PositiveSmallIntegerField(_('Number of results'), default=5) footer_text = models.CharField( _('Footer text'), max_length=200, blank=True, help_text=_( 'You may use Twitter markup here, such as a #hashtag or @username.' )) include_retweets = models.BooleanField(_("Include retweets"), default=False) include_replies = models.BooleanField(_("Include replies"), default=False) objects = ContentItemManager() # Avoid Django 1.10 migrations def __str__(self): return self.title or self.twitter_user class Meta: verbose_name = _('Recent twitter entries') verbose_name_plural = _('Recent twitter entries')
class GoogleDocsViewerItem(ContentItem): """ A Google Docs viewer that is displayed at the page. """ url = models.URLField( _("File URL"), help_text= _("Specify the URL of an online document, for example a PDF or DOCX file." )) width = models.CharField( _("Width"), max_length=10, validators=[validate_html_size], default="100%", help_text= _("Specify the size in pixels, or a percentage of the container area size." )) height = models.CharField(_("Height"), max_length=10, validators=[validate_html_size], default="600", help_text=_("Specify the size in pixels.")) objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = _("Embedded document") verbose_name_plural = _("Embedded document") def __str__(self): return self.url
class TextItem(ContentItem): """ A snippet of HTML text to display on a page. """ text = PluginHtmlField(_("text"), blank=True) text_final = models.TextField(editable=False, blank=True, null=True) objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = _("Text") verbose_name_plural = _("Text") def __str__(self): return Truncator(strip_tags(self.text)).words(20) def full_clean(self, *args, **kwargs): # This is called by the form when all values are assigned. # The pre filters are applied here, so any errors also appear as ValidationError. super().full_clean(*args, **kwargs) self.text, self.text_final = apply_filters(self, self.text, field_name="text") if self.text_final == self.text: # No need to store duplicate content: self.text_final = None
class IframeItem(ContentItem): """ An ``<iframe>`` that is displayed at the page.. """ src = models.URLField(_("Page URL")) width = models.CharField( _("Width"), max_length=10, validators=[validate_html_size], default="100%", help_text=_( "Specify the size in pixels, or a percentage of the container area size." ), ) height = models.CharField( _("Height"), max_length=10, validators=[validate_html_size], default="600", help_text=_("Specify the size in pixels."), ) objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = _("Iframe") verbose_name_plural = _("Iframes") def __str__(self): return self.src
class OEmbedItem(AbstractOEmbedItem): """ Embedded media via OEmbed """ objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = _("Online media") verbose_name_plural = _("Online media")
class FormDesignerLink(ContentItem): form_definition = models.ForeignKey(FormDefinition, verbose_name=_('Form')) objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = _('Form link') verbose_name_plural = _('Form links') def __str__(self): return self.form_definition.title if self.form_definition else u''
class DisqusCommentsAreaItem(ContentItem): allow_new = models.BooleanField(_("Allow posting new comments"), default=True) objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = _('Disqus comments area') verbose_name_plural = _('Disqus comments areas') def __str__(self): return u''
class FormDesignerLink(ContentItem): form_definition = models.ForeignKey(FormDefinition, on_delete=models.PROTECT, verbose_name=_("Form")) objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = _("Form link") verbose_name_plural = _("Form links") def __str__(self): return self.form_definition.title if self.form_definition else ""
class ArticleTextItem(ContentItem): """ This model can be placed on every placeholder field / page. """ text = models.TextField("Text") objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = "Article text item" verbose_name_plural = "Article text items" def __str__(self): return self.text
class SharedContentItem(ContentItem): """ The contentitem to include in a page. """ shared_content = models.ForeignKey(SharedContent, on_delete=models.CASCADE, verbose_name=_('Shared content'), related_name='shared_content_items') objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = _('Shared content') verbose_name_plural = _('Shared content') def __str__(self): return str(self.shared_content)
class RawHtmlItem(ContentItem): html = models.TextField( _('HTML code'), help_text= _("Enter the HTML code to display, like the embed code of an online widget." )) objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = _('HTML code') verbose_name_plural = _('HTML code') def __str__(self): return strip_tags(self.html).strip()
class PictureItem(ContentItem): """ Display a picture """ ALIGN_LEFT = "left" ALIGN_CENTER = "center" ALIGN_RIGHT = "right" ALIGN_CHOICES = ( (ALIGN_LEFT, _("Left")), (ALIGN_CENTER, _("Center")), (ALIGN_RIGHT, _("Right")), ) image = PluginImageField(_("Image"), upload_to=appsettings.FLUENT_PICTURE_UPLOAD_TO) caption = models.TextField(_("Caption"), blank=True) align = models.CharField(_("Align"), max_length=10, choices=ALIGN_CHOICES, blank=True) url = PluginUrlField(_("URL"), blank=True) in_new_window = models.BooleanField(_("Open in a new window"), default=False, blank=True) objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = _("Picture") verbose_name_plural = _("Pictures") def __str__(self): return self.caption or str(self.image) @property def align_class(self): """ The CSS class for alignment. """ if self.align == self.ALIGN_LEFT: return "align-left" elif self.align == self.ALIGN_CENTER: return "align-center" elif self.align == self.ALIGN_RIGHT: return "align-right" else: return ""
class TwitterSearchItem(ContentItem): """ Content item to display recent entries of a twitter user. """ title = models.CharField( _("Title"), max_length=200, blank=True, help_text=_( "You may use Twitter markup here, such as a #hashtag or @username." ), ) query = models.CharField( _("Search for"), max_length=200, default="", help_text= _('<a href="https://support.twitter.com/articles/71577" target="_blank">Twitter search syntax</a> is allowed.' ), ) amount = models.PositiveSmallIntegerField(_("Number of results"), default=5) footer_text = models.CharField( _("Footer text"), max_length=200, blank=True, help_text=_( "You may use Twitter markup here, such as a #hashtag or @username." ), ) include_retweets = models.BooleanField(_("Include retweets"), default=False) include_replies = models.BooleanField(_("Include replies"), default=False) objects = ContentItemManager() # Avoid Django 1.10 migrations def __str__(self): return self.title or self.query class Meta: verbose_name = _("Twitter search feed") verbose_name_plural = _("Twitter search feed")
class CodeItem(ContentItem): """ A snippet of source code, formatted with syntax highlighting. """ language = HideChoicesCharField( _('Language'), max_length=50, choices=backend.LANGUAGE_CHOICES, default=appsettings.FLUENT_CODE_DEFAULT_LANGUAGE) code = models.TextField(_('Code')) linenumbers = models.BooleanField( _('Show line numbers'), default=appsettings.FLUENT_CODE_DEFAULT_LINE_NUMBERS) objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: verbose_name = _('Code snippet') verbose_name_plural = _('Code snippets') def __str__(self): return Truncator(self.code).words(20)
class AbstractOEmbedItem(ContentItem): """ The base class for an OEmbedItem, This allows to create custom models easily. .. versionadded:: 1.0 """ TYPE_PHOTO = "photo" TYPE_VIDEO = "video" TYPE_RICH = "rich" # HTML TYPE_LINK = "link" # Fetch parameters embed_url = OEmbedUrlField(_("URL to embed")) embed_max_width = models.PositiveIntegerField(_("Max width"), blank=True, null=True) embed_max_height = models.PositiveIntegerField(_("Max height"), blank=True, null=True) # The cached response: type = models.CharField(editable=False, max_length=20, null=True, blank=True) url = models.URLField(editable=False, null=True, blank=True) title = models.CharField(editable=False, max_length=512, null=True, blank=True) description = models.TextField(editable=False, null=True, blank=True) author_name = models.CharField(editable=False, max_length=255, null=True, blank=True) author_url = models.URLField(editable=False, null=True, blank=True) provider_name = models.CharField(editable=False, max_length=255, null=True, blank=True) provider_url = models.URLField(editable=False, null=True, blank=True) thumbnail_url = models.URLField(editable=False, null=True, blank=True) thumbnail_height = models.IntegerField(editable=False, null=True, blank=True) thumbnail_width = models.IntegerField(editable=False, null=True, blank=True) height = models.IntegerField(editable=False, null=True, blank=True) width = models.IntegerField(editable=False, null=True, blank=True) html = models.TextField(editable=False, null=True, blank=True) objects = ContentItemManager() # Avoid Django 1.10 migrations class Meta: abstract = True verbose_name = _("Online media") verbose_name_plural = _("Online media") def __str__(self): return self.title or self.embed_url def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._old_embed_url = self.embed_url self._old_embed_max_width = self.embed_max_width self._old_embed_max_height = self.embed_max_height def save(self, *args, **kwargs): self.update_oembed_data( ) # if clean() did not run, still update the oembed super().save(*args, **kwargs) def clean(self): # Avoid getting server errors when the URL is not valid. try: self.update_oembed_data() except ProviderException as e: raise ValidationError(str(e)) def update_oembed_data(self, force=False, **backend_params): """ Update the OEmbeddata if needed. .. versionadded:: 1.0 Added force and backend_params parameters. """ if appsettings.FLUENT_OEMBED_FORCE_HTTPS and self.embed_url.startswith( "http://"): self.embed_url = "https://" + self.embed_url[7:] if force or self._input_changed(): # Fetch new embed code params = self.get_oembed_params(self.embed_url) params.update(backend_params) response = backend.get_oembed_data(self.embed_url, **params) # Save it self.store_response(response) # Track field changes self._old_embed_url = self.embed_url self._old_embed_max_width = self.embed_max_width self._old_embed_max_height = self.embed_max_height def get_oembed_params(self, embed_url): """ .. versionadded:: 1.0 Allow to define the parameters that are passed to the backend to fetch the current URL. """ return { "maxwidth": self.embed_max_width, "maxheight": self.embed_max_height } def _input_changed(self): return (not self.type or self._old_embed_url != self.embed_url or self._old_embed_max_width != self.embed_max_width or self._old_embed_max_height != self.embed_max_height) def store_response(self, response): # Store the OEmbed response # Unused: cache_age # Security considerations: URLs are checked by Django for http:// or ftp:// KEYS = ( "type", "title", "description", "author_name", "author_url", "provider_url", "provider_name", "thumbnail_width", "thumbnail_height", "thumbnail_url", "height", "width", "html", "url", ) for key in KEYS: if key in response: setattr(self, key, response[key])