import os import re from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.db import models from django.utils.html import mark_safe, strip_tags from django.utils.text import Truncator from django.utils.translation import ugettext_lazy as _ from shared.utils.models.slugs import DowngradingSlugField # TODO Rename ContentInlineBase to PluginInlineBase from .admin import ContentInlineBase, RichTextInlineBase from .plugins.mixins import StyleMixin # Make available for import # noqa from . import USE_TRANSLATABLE_FIELDS if USE_TRANSLATABLE_FIELDS: from shared.multilingual.utils.fields import TranslatableCharField from .fields import TranslatableCleansedRichTextField else: from feincms3.cleanse import CleansedRichTextField TranslatableCharField = models.CharField TranslatableCleansedRichTextField = CleansedRichTextField class BasePlugin(models.Model): admin_inline_baseclass = ContentInlineBase class Meta: abstract = True verbose_name = _("plugin") verbose_name_plural = _("plugins") @classmethod def register_with_renderer(cls, renderer): pass def __str__(self): return "{} ({})".format(self._meta.verbose_name, self.pk) @classmethod def admin_inline(cls, base_class=None): class Inline(base_class or cls.admin_inline_baseclass): model = cls regions = cls.regions return Inline def get_plugin_context(self, context=None, **kwargs): """ Returns a dict. """ plugin_context = {} plugin_context['content'] = self plugin_context['parent'] = self.parent if 'request_context' in kwargs: plugin_context['request'] = getattr(kwargs['request_context'], 'request', None) return plugin_context class StringRendererPlugin(BasePlugin): class Meta: abstract = True @classmethod def register_with_renderer(cls, renderer): renderer.register_string_renderer(cls, cls.render) def render(self): raise NotImplementedError("render method must be implemented by subclass") class TemplateRendererPlugin(BasePlugin): class Meta: abstract = True @classmethod def register_with_renderer(cls, renderer): renderer.register_template_renderer(cls, cls.get_template, cls.get_plugin_context) def get_template_names(self): t = getattr(self, 'template_name', None) if t: return [t] else: return [] def get_template(self): """ Might return a single template name, a list of template names or an instance with a "render" method (i.e. a Template instance). Default implementation is to return the result of self.get_template_names(). See rendering logic in feincms3.TemplateRendererPlugin. """ return self.get_template_names() # For rendering the template's render() method is used class FilesystemTemplateRendererPlugin(TemplateRendererPlugin): # Don't define template_name_prefix here, so that a sibling class takes precedence # template_name_prefix = '' # template_name = '' class Meta: abstract = True def get_template_name_prefix(self): return getattr(self, 'template_name_prefix', '') def prefixed_path(self, path): # TODO Use posixpath return "{}{}".format(self.get_template_name_prefix(), path) def get_template_names(self): """ Look first for template_name, second for prefixed template_name, then super's template names, finally prefixed _default.html. """ if not getattr(self, 'template_name', False): raise ImproperlyConfigured( "FilesystemTemplateRendererPlugin requires either a definition of " "'template_name' or an implementation of 'get_template_names()'") else: return [ self.prefixed_path(self.template_name), ] class PrepareRichtextMixin: @property def prepared_richtext(self): return mark_safe(self.get_prepared_richtext(self.richtext)) def get_prepared_richtext(self, richtext): return richtext class RichTextBase(PrepareRichtextMixin, FilesystemTemplateRendererPlugin): richtext = TranslatableCleansedRichTextField(_("text"), blank=True) admin_inline_baseclass = RichTextInlineBase template_name = 'plugins/_richtext.html' class Meta: abstract = True verbose_name = _("text") verbose_name_plural = _("texts") def __str__(self): return Truncator(strip_tags(self.richtext)).words(10, truncate=" ...") class SectionBreakBase(FilesystemTemplateRendererPlugin): subheading = TranslatableCharField(_("subheading"), null=True, blank=True, max_length=500) slug = DowngradingSlugField(_("slug"), max_length=200, blank=True, populate_from='subheading', unique_slug=False) template_name = 'plugins/_sectionbreak.html' class Meta: abstract = True verbose_name = _("section break") verbose_name_plural = _("section break") def __str__(self): return Truncator(strip_tags(self.subheading)).words(10, truncate=" ...") # FIXME Not needed, members are accessible through {{ content.slug }} etc. def get_plugin_context(self, context=None, **kwargs): context = super().get_plugin_context(context=context, **kwargs) context['slug'] = self.slug context['subheading'] = self.subheading return context SectionBase = SectionBreakBase class ObjectPluginBase(FilesystemTemplateRendererPlugin): fk_fieldname = None regions = None class Meta: abstract = True def __str__(self): return str(getattr(self, self.fk_fieldname, "")) @property def object(self): assert self.fk_fieldname, "fk_fieldname not set." return getattr(self, self.fk_fieldname) def get_type_slug(self): type = getattr(self.object, 'type', None) if type: return getattr(type, 'internal_slug', "") return "" def get_template_names(self): """" __/_