Browse Source

Refactorement.

master
Erik Stein 7 years ago
parent
commit
b0dd3a272d
  1. 6
      content_plugins/__init__.py
  2. 107
      content_plugins/abstract_plugins.py
  3. 371
      content_plugins/content_plugins.py
  4. 13
      content_plugins/fields.py
  5. 4
      content_plugins/mixins.py
  6. 5
      content_plugins/renderer.py

6
content_plugins/__init__.py

@ -0,0 +1,6 @@
from django.conf import settings
USE_TRANSLATABLE_FIELDS = getattr(settings, 'CONTENT_USE_TRANSLATABLE_FIELDS', True)
# TODO Implement translatable AutoSlugField: USE_TRANSLATABLE_SLUG_FIELDS = getattr(settings, 'CONTENT_USE_TRANSLATABLE_SLUG_FIELDS', True)

107
content_plugins/abstract_plugins.py

@ -10,13 +10,14 @@ from django.utils.translation import ugettext_lazy as _
from feincms3.cleanse import CleansedRichTextField from feincms3.cleanse import CleansedRichTextField
from shared.utils.fields import AutoSlugField from shared.utils.fields import AutoSlugField
from shared.multilingual.utils.fields import TranslatableCharField, TranslatableTextField
from .admin import ContentInlineBase, RichTextInlineBase from .admin import ContentInlineBase, RichTextInlineBase
from .fields import TranslatableCleansedRichTextField
# FIXME Implement USE_TRANSLATABLE_FIELDS from . import USE_TRANSLATABLE_FIELDS
from .mixins import USE_TRANSLATABLE_FIELDS
if USE_TRANSLATABLE_FIELDS:
from shared.multilingual.utils.fields import TranslatableCharField
from .fields import TranslatableCleansedRichTextField
""" """
@ -31,8 +32,8 @@ TODO Class hierarchy should be
class BasePlugin(models.Model): class BasePlugin(models.Model):
class Meta: class Meta:
abstract = True abstract = True
verbose_name = _("Element") verbose_name = _("plugin")
verbose_name_plural = _("Elements") verbose_name_plural = _("plugins")
def __str__(self): def __str__(self):
return "{} ({})".format(self._meta.verbose_name, self.pk) return "{} ({})".format(self._meta.verbose_name, self.pk)
@ -57,14 +58,16 @@ class BasePlugin(models.Model):
or an instance with a "render" method (i.e. a Template instance). or an instance with a "render" method (i.e. a Template instance).
Default implementation is to return the result of self.get_template_names(). Default implementation is to return the result of self.get_template_names().
See rendering logic in feincms3.TemplateRendererPlugin.
""" """
return self.get_template_names() return self.get_template_names()
def get_context_data(self, request_context, **kwargs): def get_context_data(self, request_context, **kwargs):
context = kwargs.get('context', {}) context = kwargs.get('context', {})
context['parent'] = self.parent
context['request'] = request_context['request']
context['content'] = self context['content'] = self
context['parent'] = self.parent
context['request'] = getattr(request_context, 'request', None)
return context return context
# For rendering the template's render() method is used # For rendering the template's render() method is used
@ -82,19 +85,19 @@ class StringRendererPlugin(BasePlugin):
raise NotImplementedError raise NotImplementedError
class StyleField(models.CharField): class StyleMixin(models.Model):
# Allow overriding of STYLE_CHOICES constant in subclasses class StyleField(models.CharField):
# Allow overriding of STYLE_CHOICES in subclasses
def contribute_to_class(self, cls, name, **kwargs):
if hasattr(cls, 'STYLE_CHOICES'):
self.choices = cls.STYLE_CHOICES
super().contribute_to_class(cls, name, **kwargs)
def contribute_to_class(self, cls, name, **kwargs):
if hasattr(cls, 'STYLE_CHOICES'):
self.choices = cls.STYLE_CHOICES
super().contribute_to_class(cls, name, **kwargs)
class StyleMixin(models.Model):
STYLE_CHOICES = ( STYLE_CHOICES = (
('default', _("default")), ('default', _("default")),
) )
style = StyleField(_("style"), max_length=50, null=True, blank=True) style = StyleField(_("style"), max_length=50, null=True, blank=True)
class Meta: class Meta:
@ -103,42 +106,56 @@ class StyleMixin(models.Model):
def get_style_slug(self): def get_style_slug(self):
return getattr(self, 'style', None) or 'default' return getattr(self, 'style', None) or 'default'
def get_template_names(self):
# Should only be called by classes using filesystem templates
template_names = super().get_template_names() or []
template_names.extend([
"{path_prefix}style/_{style}.html".format(
path_prefix=self.get_path_prefix(),
style=self.get_style_slug()),
])
return template_names
class FilesystemTemplateRendererPlugin(BasePlugin):
# TODO Join FilesystemTemplateRendererPlugin code with BaseObjectElement code
class FilesystemTemplateRendererPlugin(BasePlugin):
template_name = None template_name = None
path_prefix = None # Potential values: "richtext", "image" path_prefix = 'plugins/' # TODO Rename to 'template_name_prefix'
class Meta: class Meta:
abstract = True abstract = True
def get_path_prefix(self): def get_path_prefix(self):
if self.path_prefix: return self.path_prefix
return "{}{}".format(self.path_prefix, os.path.sep)
else: def prefixed_path(self, path):
return "" return "{}{}".format(self.get_path_prefix(), path)
def get_template_names(self): def get_template_names(self):
# TODO Style related logic should be part of the StyleMixin: maybe get_template_names should call super() # Per default look first for absolute template_name path and
if hasattr(self, 'style'): # template_name path prefixed with path_prefix.
if getattr(self, 'template_name', False):
template_names = [ template_names = [
"curatorialcontent/elements/{path_prefix}style/_{style}.html".format( self.template_name,
path_prefix=self.get_path_prefix(), style=self.get_style_slug()), self.prefixed_path(self.template_name)
] ]
else: else:
template_names = [] template_names = []
template_names.extend(super().get_template_names() or [])
return template_names + [ return template_names + [
"curatorialcontent/elements/{path_prefix}_default.html".format( "{path_prefix}_default.html".format(
path_prefix=self.get_path_prefix()) path_prefix=self.get_path_prefix())
] + ([self.template_name] if getattr(self, 'template_name', None) else []) ]
class RichTextBase(StyleMixin, FilesystemTemplateRendererPlugin): class RichTextBase(StyleMixin, FilesystemTemplateRendererPlugin):
richtext = TranslatableCleansedRichTextField(_("text"), blank=True) if USE_TRANSLATABLE_FIELDS:
richtext = TranslatableCleansedRichTextField(_("text"), blank=True)
else:
richtext = CleansedRichTextField(_("text"), blank=True)
path_prefix = 'richtext' path_prefix = FilesystemTemplateRendererPlugin.path_prefix + 'richtext/'
class Meta: class Meta:
abstract = True abstract = True
@ -151,29 +168,29 @@ class RichTextBase(StyleMixin, FilesystemTemplateRendererPlugin):
class SectionBase(StyleMixin, BasePlugin): class SectionBase(StyleMixin, BasePlugin):
subheading = TranslatableCharField(_("subheading"), max_length=500) if USE_TRANSLATABLE_FIELDS:
subheading = TranslatableCharField(_("subheading"), max_length=500)
else:
subheading = models.CharField(_("subheading"), max_length=500)
slug = AutoSlugField(_("slug"), max_length=200, blank=True, populate_from='subheading', unique_slug=False) slug = AutoSlugField(_("slug"), max_length=200, blank=True, populate_from='subheading', unique_slug=False)
class Meta: class Meta:
abstract = True abstract = True
verbose_name = _("Abschnittswechsel") verbose_name = _("section")
verbose_name_plural = _("Abschnittswechsel") verbose_name_plural = _("section")
def get_template(self): def get_template(self):
return Template(""" return Template("""
</div>
</section> </section>
<section id="{{ slug }}"> <section id="{{ slug }}">
<h2>{{ heading }}</h2> <h2>{{ subheading }}</h2>
<div class="text">
""") """)
def get_context_data(self, request_context, **kwargs): def get_context_data(self, request_context, **kwargs):
context = super().get_context_data(request_context, **kwargs) context = super().get_context_data(request_context, **kwargs)
context['slug'] = self.slug context['slug'] = self.slug
context['heading'] = self.heading context['subheading'] = self.subheading
return context return context
@ -224,7 +241,10 @@ class DownloadBase(StyleMixin, BasePlugin):
class FootnoteBase(BasePlugin): class FootnoteBase(BasePlugin):
# TODO Validators: index might only contain alphanumeric characters # TODO Validators: index might only contain alphanumeric characters
index = models.CharField(_("footnote index"), max_length=10) index = models.CharField(_("footnote index"), max_length=10)
richtext = TranslatableCleansedRichTextField(_("footnote text")) if USE_TRANSLATABLE_FIELDS:
richtext = TranslatableCleansedRichTextField(_("footnote text"))
else:
richtext = CleansedRichTextField(_("footnote text"))
html_tag = '<li>' html_tag = '<li>'
@ -233,13 +253,12 @@ class FootnoteBase(BasePlugin):
verbose_name = _("footnote") verbose_name = _("footnote")
verbose_name_plural = _("footnote") verbose_name_plural = _("footnote")
# TODO Convert to Template
def render(self, html_tag=None): def render(self, html_tag=None):
template = """ template = """
{opening_tag} {opening_tag}
<div class="text"> <a id="fn{number}" class="footnote-index" href="#back{number}">{number}</a>
<a id="fn{number}" class="footnote-index" href="#back{number}">{number}</a> <div class="text">{text}</div>
{text}
</div>
{closing_tag} {closing_tag}
""" """
context = { context = {

371
content_plugins/content_plugins.py

@ -0,0 +1,371 @@
"""
Example instantiation of the abstract plugin base classes
"""
from django.db import models
from django.utils.translation import ugettext_lazy as _
from content_editor.models import create_plugin_base
from shared.utils.text import slugify_long
from shared.multilingual.utils.fields import TranslatableCharField
from ..collection.models import ItemBundle, Event, Collection
from ..content.admin import RichTextInlineBase
from ..content import plugins
from ..content.renderer import ContentPluginRenderer
from ..media.models import (
MediaImage,
MediaAudio,
MediaVideo,
MediaDocument,
)
from ..people.models import Person
from ..thesaurus.models import Term
from .models import Page
ContentPluginBase = create_plugin_base(Page)
renderer = ContentPluginRenderer()
@renderer.register()
class RichTextContentPlugin(plugins.RichTextFootnoteMixin, plugins.RichTextBase, ContentPluginBase):
regions = ['main', 'aside', 'intro']
@renderer.register()
class BlockquoteContentPlugin(plugins.RichTextFootnoteMixin, plugins.RichTextBase, ContentPluginBase):
STYLE_CHOICES = plugins.StyleMixin.STYLE_CHOICES + (
('blockquote', _("Blockquote")),
('pullquote', _("Pull Quote")),
('introquote', _("Introductory Quote")),
)
path_prefix = 'quote'
regions = ['main', 'intro']
class Meta:
verbose_name = _("quote")
verbose_name_plural = _("quotes")
@renderer.register()
class SectionContentPlugin(plugins.SectionBase, ContentPluginBase):
regions = ['main', 'aside']
# @renderer.register()
# class ImageContentPlugin(plugins.ImageBase, ContentPluginBase):
# regions = ['main', 'aside']
# @renderer.register()
# class DownloadContentPlugin(plugins.DownloadBase, ContentPluginBase):
# regions = ['main', 'aside']
@renderer.register()
class FootnoteContentPlugin(plugins.FootnoteBase, ContentPluginBase):
regions = ['references', 'footnotes']
class BaseObjectElement(plugins.BasePlugin, ContentPluginBase):
STYLE_CHOICES = plugins.StyleMixin.STYLE_CHOICES + (
('minimal', _("Minimal View")),
('extensive', _("Extensive View")),
)
class Meta:
abstract = True
verbose_name = _("object view")
verbose_name_plural = _("objects view")
fk_fieldname = None
regions = ['main', 'aside']
@property
def object(self):
assert self.fk_fieldname, "fk_fieldname not set."
return getattr(self, self.fk_fieldname)
def type_slug(self):
slug = ''
type = getattr(self.object, 'type', None)
if type:
slug = getattr(type, 'internal_slug', '')
return slug
def get_template_names(self):
assert self.fk_fieldname, "fk_fieldname not set."
type_slug = self.type_slug()
style_slug = getattr(self, 'style', None) or 'default'
# Append a potentially defined self.template_name
return [
"curatorialcontent/elements/{path_prefix}/pk/_{pk}.html".format(
path_prefix=self.fk_fieldname, pk=self.object.pk),
"curatorialcontent/elements/{path_prefix}/type/_{style}/_{type}.html".format(
path_prefix=self.fk_fieldname, style=style_slug, type=type_slug),
"curatorialcontent/elements/{path_prefix}/style/_{style}.html".format(
path_prefix=self.fk_fieldname, style=style_slug),
"curatorialcontent/elements/{path_prefix}/type/_{type}.html".format(
path_prefix=self.fk_fieldname, type=type_slug),
"curatorialcontent/elements/{path_prefix}/_default.html".format(
path_prefix=self.fk_fieldname)
] + ([self.template_name] if hasattr(self, 'template_name') else [])
@classmethod
def admin_inline(cls, base_class=None):
assert cls.fk_fieldname, "fk_fieldname not set."
inline = super().admin_inline(base_class=RichTextInlineBase) # TODO Do we need RichTextInlineBase here?
if not inline.raw_id_fields:
inline.raw_id_fields = []
inline.raw_id_fields += [cls.fk_fieldname]
return inline
@renderer.register()
class ItemBundleElement(BaseObjectElement, plugins.StyleMixin):
STYLE_CHOICES = BaseObjectElement.STYLE_CHOICES + (
('teaser', _("Big Top Teaser")),
)
itembundle = models.ForeignKey(ItemBundle, verbose_name=_("ItemBundle"))
fk_fieldname = 'itembundle'
regions = ['main', 'aside']
class Meta:
verbose_name = _("item bundle view")
verbose_name_plural = _("item bundle views")
@renderer.register()
class PersonElement(BaseObjectElement, plugins.StyleMixin):
person = models.ForeignKey(Person, verbose_name=_("Person"))
fk_fieldname = 'person'
regions = ['main', 'aside']
class Meta:
verbose_name = _("person view")
verbose_name_plural = _("person views")
@renderer.register()
class ContributorElement(BaseObjectElement):
person = models.ForeignKey(Person, verbose_name=_("Person"))
CONTRIBUTOR_ROLE_CHOICES = (
('author', _("Author")),
('contributor', _("Contributor")),
)
role = models.CharField(_("role"), max_length=50,
choices=CONTRIBUTOR_ROLE_CHOICES,
default=CONTRIBUTOR_ROLE_CHOICES[0][0], null=False, blank=False)
fk_fieldname = 'person'
regions = ['contributors']
class Meta:
verbose_name = _("contributor")
verbose_name_plural = _("contributors")
@renderer.register()
class TermElement(BaseObjectElement, plugins.StyleMixin):
term = models.ForeignKey(Term, verbose_name=_("Term"))
fk_fieldname = 'term'
regions = ['main', 'aside']
class Meta:
verbose_name = _("term view")
verbose_name_plural = _("term views")
@renderer.register()
class EventElement(BaseObjectElement, plugins.StyleMixin):
event = models.ForeignKey(Event, verbose_name=_("Event"))
fk_fieldname = 'event'
regions = ['main', 'aside']
class Meta:
verbose_name = _("event view")
verbose_name_plural = _("event views")
@renderer.register()
class SubcollectionElement(BaseObjectElement, plugins.StyleMixin):
STYLE_CHOICES = BaseObjectElement.STYLE_CHOICES + (
('index-nav', _("Index Page Navigation")),
)
collection = models.ForeignKey(Collection, verbose_name=_("Subcollection"))
fk_fieldname = 'collection'
regions = ['main', 'aside']
class Meta:
verbose_name = _("collection view")
verbose_name_plural = _("collection views")
@renderer.register()
class MediaImageElement(BaseObjectElement):
image = models.ForeignKey(MediaImage, verbose_name=_("Image"))
caption = TranslatableCharField(_("caption"), max_length=500, null=True, blank=True, help_text=_("image caption"))
fk_fieldname = 'image'
regions = ['main', 'aside']
class Meta:
verbose_name = _("image")
verbose_name_plural = _("images")
def type_slug(self):
return ''
@renderer.register()
class MediaAudioElement(BaseObjectElement):
audio = models.ForeignKey(MediaAudio, verbose_name=_("Audio"))
caption = TranslatableCharField(_("caption"), max_length=500, null=True, blank=True, help_text=_("audio caption"))
fk_fieldname = 'audio'
regions = ['main', 'aside']
class Meta:
verbose_name = _("audio")
verbose_name_plural = _("audio")
@renderer.register()
class MediaVideoElement(BaseObjectElement):
video = models.ForeignKey(MediaVideo, verbose_name=_("Video"))
caption = TranslatableCharField(_("caption"), max_length=500, null=True, blank=True, help_text=_("video caption"))
fk_fieldname = 'video'
regions = ['main', 'aside']
class Meta:
verbose_name = _("video")
verbose_name_plural = _("video")
@renderer.register()
class MediaDocumentElement(BaseObjectElement):
document = models.ForeignKey(MediaDocument, verbose_name=_("Document"))
caption = TranslatableCharField(_("caption"), max_length=500, null=True, blank=True, help_text=_("document caption"))
fk_fieldname = 'document'
regions = ['main', 'aside']
class Meta:
verbose_name = _("document")
verbose_name_plural = _("document")
@renderer.register()
class SubsectionsElement(plugins.StyleMixin, plugins.FilesystemTemplateRendererPlugin, ContentPluginBase):
regions = ['main', 'aside']
path_prefix = 'subsections_nav'
class Meta:
verbose_name = _("subsections navigation element")
verbose_name_plural = _("subsections navigation elements")
@renderer.register()
class TeamElement(plugins.StyleMixin, plugins.FilesystemTemplateRendererPlugin, ContentPluginBase):
regions = ['main', 'aside']
path_prefix = 'team'
class Meta:
verbose_name = _("team navigation element")
verbose_name_plural = _("team navigation elements")
@renderer.register()
class ActorsElement(plugins.StyleMixin, plugins.FilesystemTemplateRendererPlugin, ContentPluginBase):
regions = ['main', 'aside']
path_prefix = 'actors'
class Meta:
verbose_name = _("actors navigation element")
verbose_name_plural = _("actors navigation elements")
@renderer.register()
class ArticlesElement(plugins.StyleMixin, plugins.FilesystemTemplateRendererPlugin, ContentPluginBase):
regions = ['main', 'aside']
path_prefix = 'articles_nav'
class Meta:
verbose_name = _("articles navigation element")
verbose_name_plural = _("articles navigation elements")
#
# Slideshow Elements
class BaseSlideshowContentPlugin(plugins.StyleMixin, plugins.FilesystemTemplateRendererPlugin, ContentPluginBase):
STYLE_CHOICES = (
('black', _("black background")),
('yellow', _("yellow background")),
('white', _("white background")),
)
caption = plugins.TranslatableCleansedRichTextField(_("caption"), blank=True)
path_prefix = 'slide'
regions = ['slides']
class Meta:
abstract = True
@classmethod
def admin_inline(cls, base_class=None):
return super().admin_inline(base_class=RichTextInlineBase)
@renderer.register()
class TextSlideshowContentPlugin(BaseSlideshowContentPlugin):
path_prefix = 'slide/text/'
regions = ['slides']
class Meta:
verbose_name = _("text slide")
verbose_name_plural = _("text slides")
@renderer.register()
class ItembundleSlideshowContentPlugin(BaseSlideshowContentPlugin):
itembundle = models.ForeignKey(ItemBundle, verbose_name=_("ItemBundle"))
path_prefix = 'slide/itembundle/'
regions = ['slides']
class Meta:
verbose_name = _("item bundle slide")
verbose_name_plural = _("item bundle slides")
@classmethod
def admin_inline(cls, base_class=None):
inline = super().admin_inline(base_class=RichTextInlineBase)
if not inline.raw_id_fields:
inline.raw_id_fields = []
inline.raw_id_fields += ['itembundle']
return inline

13
content_plugins/fields.py

@ -1,8 +1,11 @@
from feincms3.cleanse import CleansedRichTextField from feincms3.cleanse import CleansedRichTextField
from shared.multilingual.utils.fields import TranslatableFieldMixin
from . import USE_TRANSLATABLE_FIELDS
class TranslatableCleansedRichTextField(TranslatableFieldMixin, CleansedRichTextField):
base_class = CleansedRichTextField if USE_TRANSLATABLE_FIELDS:
extra_parameter_names = ['config_name', 'extra_plugins', 'external_plugin_resources'] from shared.multilingual.utils.fields import TranslatableFieldMixin
# TODO Implement translatable rich text widget
class TranslatableCleansedRichTextField(TranslatableFieldMixin, CleansedRichTextField):
base_class = CleansedRichTextField
extra_parameter_names = ['config_name', 'extra_plugins', 'external_plugin_resources']

4
content_plugins/mixins.py

@ -9,9 +9,7 @@ from shared.utils.fields import AutoSlugField
from shared.utils.functional import firstof from shared.utils.functional import firstof
from shared.utils.text import slimdown from shared.utils.text import slimdown
from . import USE_TRANSLATABLE_FIELDS
USE_TRANSLATABLE_FIELDS = getattr(settings, 'CONTENT_USE_TRANSLATABLE_FIELDS', True)
# TODO Implement translatable AutoSlugField: USE_TRANSLATABLE_SLUG_FIELDS = getattr(settings, 'CONTENT_USE_TRANSLATABLE_SLUG_FIELDS', True)
if USE_TRANSLATABLE_FIELDS: if USE_TRANSLATABLE_FIELDS:

5
content_plugins/renderer.py

@ -8,10 +8,11 @@ class MultilingualRegions(Regions):
return '%s-%s' % (get_language(), super().cache_key(region)) return '%s-%s' % (get_language(), super().cache_key(region))
class PluginRenderer(TemplatePluginRenderer): class ContentPluginRenderer(TemplatePluginRenderer):
# Used as decorator
def register(self): def register(self):
""" """
Used as decorator
Usage: Usage:
@renderer.register() @renderer.register()
class TextPlugin(ModelPlugin): class TextPlugin(ModelPlugin):

Loading…
Cancel
Save