Browse Source

Refactorement.

master
Erik Stein 7 years ago
parent
commit
b0dd3a272d
  1. 6
      content_plugins/__init__.py
  2. 89
      content_plugins/abstract_plugins.py
  3. 371
      content_plugins/content_plugins.py
  4. 7
      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)

89
content_plugins/abstract_plugins.py

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

7
content_plugins/fields.py

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

4
content_plugins/mixins.py

@ -9,9 +9,7 @@ from shared.utils.fields import AutoSlugField
from shared.utils.functional import firstof
from shared.utils.text import slimdown
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)
from . import 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))
class PluginRenderer(TemplatePluginRenderer):
# Used as decorator
class ContentPluginRenderer(TemplatePluginRenderer):
def register(self):
"""
Used as decorator
Usage:
@renderer.register()
class TextPlugin(ModelPlugin):

Loading…
Cancel
Save