From 807e864f7199baaa76b793d0ecd05ceece86ceec Mon Sep 17 00:00:00 2001 From: bkfox Date: Sun, 24 Jul 2016 14:50:56 +0200 Subject: [PATCH] sections rendering + tag + minor changes; rename Base* to *Base for more consistency in naming convention --- cms/models.py | 19 ++- cms/sections.py | 133 +++++++++++++----- cms/templatetags/aircox_cms.py | 24 +++- .../__pycache__/__init__.cpython-35.pyc | Bin 166 -> 166 bytes 4 files changed, 134 insertions(+), 42 deletions(-) diff --git a/cms/models.py b/cms/models.py index 743235e..c2bbeb0 100644 --- a/cms/models.py +++ b/cms/models.py @@ -143,7 +143,7 @@ class Comment(models.Model): return super().save(*args, **kwargs) -class RelatedLink(BaseRelatedLink): +class RelatedLink(RelatedLinkBase): parent = ParentalKey('Publication', related_name='related_links') class PublicationTag(TaggedItemBase): @@ -487,14 +487,18 @@ class ListPage(Page): help_text = _('add an extra description for this list') ) + class Meta: + verbose_name = _('List') + verbose_name_plural = _('List') + def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) - qs = BaseList.get_queryset_from_request(request, context=context) + qs = ListBase.get_queryset_from_request(request, context=context) context['object_list'] = qs return context -class DatedListPage(BaseDatedList,Page): +class DatedListPage(DatedListBase,Page): body = RichTextField( _('body'), blank = True, null = True, @@ -509,7 +513,7 @@ class DatedListPage(BaseDatedList,Page): FieldPanel('title'), FieldPanel('body'), ], heading=_('Content')), - ] + BaseDatedList.panels + ] + DatedListBase.panels def get_queryset(self, request, context): """ @@ -554,7 +558,11 @@ class LogsPage(DatedListPage): '0 means no limit') ) - content_panels = BaseDatedList.panels + [ + class Meta: + verbose_name = _('Logs') + verbose_name_plural = _('Logs') + + content_panels = DatedListBase.panels + [ MultiFieldPanel([ FieldPanel('station'), FieldPanel('age_max'), @@ -612,6 +620,7 @@ class TimetablePage(DatedListPage): class Meta: verbose_name = _('Timetable') + verbose_name_plural = _('Timetable') def get_queryset(self, request, context): diffs = [] diff --git a/cms/sections.py b/cms/sections.py index 404683f..a74b5f0 100644 --- a/cms/sections.py +++ b/cms/sections.py @@ -4,11 +4,11 @@ from enum import Enum, IntEnum from django.db import models +from django.utils.translation import ugettext as _, ugettext_lazy +from django.utils import timezone as tz +from django.template.loader import render_to_string from django.contrib.contenttypes.models import ContentType from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from django.utils import timezone as tz -from django.utils.text import slugify -from django.utils.translation import ugettext as _, ugettext_lazy from wagtail.wagtailcore.models import Page, Orderable from wagtail.wagtailcore.fields import RichTextField @@ -17,6 +17,8 @@ from wagtail.wagtailadmin.edit_handlers import FieldPanel, FieldRowPanel, \ MultiFieldPanel, InlinePanel, PageChooserPanel, StreamFieldPanel from wagtail.wagtailsearch import index +from wagtail.wagtailcore.utils import camelcase_to_underscore + # snippets from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel from wagtail.wagtailsnippets.models import register_snippet @@ -28,6 +30,28 @@ from modelcluster.tags import ClusterTaggableManager from taggit.models import TaggedItemBase +def related_pages_filter(): + """ + Return a dict that can be used to filter foreignkey to pages' + subtype declared in aircox.cms.models. + + Note: the filter is calculated at the first call of the function. + """ + if hasattr(related_pages_filter, 'filter'): + return related_pages_filter.filter + + import aircox.cms.models as cms + import inspect + related_pages_filter.filter = { + 'model__in': (name.lower() for name, member in + inspect.getmembers(cms, + lambda x: inspect.isclass(x) and issubclass(x, Page) + ) + if member != Page + ), + } + return related_pages_filter.filter + class ListItem: """ @@ -48,7 +72,7 @@ class ListItem: # # Base # -class BaseRelatedLink(Orderable): +class RelatedLinkBase(Orderable): url = models.URLField( _('url'), null=True, blank=True, @@ -90,7 +114,7 @@ class BaseRelatedLink(Orderable): ] -class BaseList(models.Model): +class ListBase(models.Model): """ Generic list """ @@ -112,10 +136,7 @@ class BaseList(models.Model): verbose_name = _('filter by type'), blank = True, null = True, help_text = _('if set, select only elements that are of this type'), - limit_choices_to = { - 'model__in': ('publication','programpage','diffusionpage', - 'eventpage'), - } + limit_choices_to = related_pages_filter, ) filter_related = models.ForeignKey( 'Publication', @@ -212,7 +233,7 @@ class BaseList(models.Model): Context's fields: * object_list: the final queryset * list_selector: dict of { 'tag_query', 'search_query' } plus - arguments passed to BaseList.get_base_queryset + arguments passed to ListBase.get_base_queryset * paginator: paginator object """ model = request.GET.get('model') @@ -255,7 +276,7 @@ class BaseList(models.Model): return qs -class BaseDatedList(models.Model): +class DatedListBase(models.Model): """ List that display items per days. Renders a navigation section on the top. @@ -342,18 +363,20 @@ class BaseDatedList(models.Model): # @register_snippet class Section(ClusterableModel): + """ + Section is a container of multiple items of different types + that are used to render extra content related or not the current + page. + + A section has an assigned position in the page, and can be restrained + to a given type of page. + """ name = models.CharField( _('name'), max_length=32, blank = True, null = True, help_text=_('name of this section (not displayed)'), ) - css_class = models.CharField( - _('CSS class'), - max_length=64, - blank = True, null = True, - help_text=_('section container\'s "class" attribute') - ) position = models.CharField( _('position'), max_length=16, @@ -361,45 +384,59 @@ class Section(ClusterableModel): help_text = _('name of the template block in which the section must ' 'be set'), ) + model = models.ForeignKey( + ContentType, + verbose_name = _('model'), + blank = True, null = True, + help_text=_('this section is displayed only when the current ' + 'page or publication is of this type'), + limit_choices_to = related_pages_filter, + ) panels = [ MultiFieldPanel([ FieldPanel('name'), - FieldPanel('css_class'), FieldPanel('position'), + FieldPanel('model'), ], heading=_('General')), - InlinePanel('section_items', label=_('section items')), + InlinePanel('places', label=_('Section Items')), ] def __str__(self): return '{}: {}'.format(self.__class__.__name__, self.name or self.pk) + def render(self, request, page = None, *args, **kwargs): + return ''.join([ + place.item.render(request, page, *args, **kwargs) + for place in self.places + ]) -class SectionItemItem(Orderable): - section = ParentalKey(Section, related_name='section_items') - related = models.ForeignKey( - ContentType, - blank = True, null = True, - help_text=_('this section is displayed only for this model'), - limit_choices_to = { - 'model__in': ('publication','programpage','diffusionpage', - 'eventpage'), - } - ) +class SectionPlace(Orderable): + section = ParentalKey(Section, related_name='places') item = models.ForeignKey( 'SectionItem', verbose_name=_('item') ) + panels = [ SnippetChooserPanel('item'), - FieldPanel('related'), + FieldPanel('model'), ] def __str__(self): return '{}: {}'.format(self.__class__.__name__, self.title or self.pk) +class SectionItemMeta(models.base.ModelBase): + def __new__(cls, name, bases, attrs): + cl = super().__new__(name, bases, attrs) + if not 'template' in attrs: + attrs['template'] = 'cms/sections/{}.html' \ + .format(camelcase_to_underscore(name)) + return cl + + @register_snippet class SectionItem(models.Model): real_type = models.CharField( @@ -416,6 +453,13 @@ class SectionItem(models.Model): default = False, help_text=_('if set show a title at the head of the section'), ) + related = models.BooleanField( + _('related'), + default = False, + help_text=_('if set, section is related to the current publication. ' + 'e.g rendering a list related to it instead of to all ' + 'publications'), + ) css_class = models.CharField( _('CSS class'), max_length=64, @@ -430,12 +474,15 @@ class SectionItem(models.Model): ], heading=_('General')), ] + def model_name(self): + return self.__class__.__name__.lower() + def specific(self): """ Return a downcasted version of the post if it is from another model, or itself """ - if not self.real_type or type(self) != Post: + if not self.real_type or type(self) != SectionItem: return self return getattr(self, self.real_type) @@ -444,6 +491,20 @@ class SectionItem(models.Model): self.real_type = type(self).__name__.lower() return super().save(*args, **kwargs) + def get_context(self, request, page, *args, **kwargs): + return { + 'self': self, + 'page': page, + } + + def render(self, request, page, *args, **kwargs): + """ + Render the section. Page is the current publication being rendered. + """ + return render_to_string( + request, self.template, + self.get_context(request, *args, page=page, **kwargs) + ) def __str__(self): return '{}: {}'.format( @@ -473,13 +534,13 @@ class SectionImage(SectionItem): @register_snippet -class SectionLink(BaseRelatedLink,SectionItem): +class SectionLink(RelatedLinkBase,SectionItem): """ Can either be used standalone or in a SectionLinkList """ parent = ParentalKey('SectionLinkList', related_name='links', blank=True, null=True) - panels = SectionItem.panels + BaseRelatedLink.panels + panels = SectionItem.panels + RelatedLinkBase.panels @register_snippet @@ -490,14 +551,14 @@ class SectionLinkList(SectionItem,ClusterableModel): @register_snippet -class SectionPublicationList(BaseList,SectionItem): +class SectionPublicationList(ListBase,SectionItem): focus_available = models.BooleanField( _('focus available'), default = False, help_text = _('if true, highlight the first focused article found') ) panels = SectionItem.panels + [ FieldPanel('focus_available') ] +\ - BaseList.panels + ListBase.panels diff --git a/cms/templatetags/aircox_cms.py b/cms/templatetags/aircox_cms.py index d6a18d7..6d4851d 100644 --- a/cms/templatetags/aircox_cms.py +++ b/cms/templatetags/aircox_cms.py @@ -1,11 +1,33 @@ from django import template +from django.db.models import Q + +import aircox.cms.sections as sections + register = template.Library() -@register.filter(name='around') +@register.filter def around(page_num, n): """ Return a range of value around a given number. """ return range(page_num-n, page_num+n+1) +@register.simple_tag(takes_context=True) +def render_section(position = None, section = None, context = None): + """ + If section is not given, render all sections at the given + position (filter out base on page models' too, cf. Section.model). + """ + request = context.get('request') + page = context.get('page') + + if section: + return section.render(request, page=page) + + sections = Section.objects \ + .filter( Q(model__isnull = True) | Q(model = type(page)) ) \ + .filter(position = position) + return '\n'.join( + section.render(request, page=page) for section in sections + ) diff --git a/programs/management/__pycache__/__init__.cpython-35.pyc b/programs/management/__pycache__/__init__.cpython-35.pyc index 5797616ed42dc30e963dee0ed4459ec0ad8ca90b..44f0f68fcdfbdc93a926f31c031e515e0a405094 100644 GIT binary patch delta 15 WcmZ3+xQvlqjF*=yVA_<4?DGL08w5cB delta 15 WcmZ3+xQvlqjF*?|?wbCI?DGL3!vx;|