sections rendering + tag + minor changes; rename Base* to *Base for more consistency in naming convention

This commit is contained in:
bkfox 2016-07-24 14:50:56 +02:00
parent 74e4f42ca5
commit 807e864f71
4 changed files with 134 additions and 42 deletions

View File

@ -143,7 +143,7 @@ class Comment(models.Model):
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
class RelatedLink(BaseRelatedLink): class RelatedLink(RelatedLinkBase):
parent = ParentalKey('Publication', related_name='related_links') parent = ParentalKey('Publication', related_name='related_links')
class PublicationTag(TaggedItemBase): class PublicationTag(TaggedItemBase):
@ -487,14 +487,18 @@ class ListPage(Page):
help_text = _('add an extra description for this list') 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): def get_context(self, request, *args, **kwargs):
context = super().get_context(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 context['object_list'] = qs
return context return context
class DatedListPage(BaseDatedList,Page): class DatedListPage(DatedListBase,Page):
body = RichTextField( body = RichTextField(
_('body'), _('body'),
blank = True, null = True, blank = True, null = True,
@ -509,7 +513,7 @@ class DatedListPage(BaseDatedList,Page):
FieldPanel('title'), FieldPanel('title'),
FieldPanel('body'), FieldPanel('body'),
], heading=_('Content')), ], heading=_('Content')),
] + BaseDatedList.panels ] + DatedListBase.panels
def get_queryset(self, request, context): def get_queryset(self, request, context):
""" """
@ -554,7 +558,11 @@ class LogsPage(DatedListPage):
'0 means no limit') '0 means no limit')
) )
content_panels = BaseDatedList.panels + [ class Meta:
verbose_name = _('Logs')
verbose_name_plural = _('Logs')
content_panels = DatedListBase.panels + [
MultiFieldPanel([ MultiFieldPanel([
FieldPanel('station'), FieldPanel('station'),
FieldPanel('age_max'), FieldPanel('age_max'),
@ -612,6 +620,7 @@ class TimetablePage(DatedListPage):
class Meta: class Meta:
verbose_name = _('Timetable') verbose_name = _('Timetable')
verbose_name_plural = _('Timetable')
def get_queryset(self, request, context): def get_queryset(self, request, context):
diffs = [] diffs = []

View File

@ -4,11 +4,11 @@ from enum import Enum, IntEnum
from django.db import models 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.contrib.contenttypes.models import ContentType
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger 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.models import Page, Orderable
from wagtail.wagtailcore.fields import RichTextField from wagtail.wagtailcore.fields import RichTextField
@ -17,6 +17,8 @@ from wagtail.wagtailadmin.edit_handlers import FieldPanel, FieldRowPanel, \
MultiFieldPanel, InlinePanel, PageChooserPanel, StreamFieldPanel MultiFieldPanel, InlinePanel, PageChooserPanel, StreamFieldPanel
from wagtail.wagtailsearch import index from wagtail.wagtailsearch import index
from wagtail.wagtailcore.utils import camelcase_to_underscore
# snippets # snippets
from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel
from wagtail.wagtailsnippets.models import register_snippet from wagtail.wagtailsnippets.models import register_snippet
@ -28,6 +30,28 @@ from modelcluster.tags import ClusterTaggableManager
from taggit.models import TaggedItemBase 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: class ListItem:
""" """
@ -48,7 +72,7 @@ class ListItem:
# #
# Base # Base
# #
class BaseRelatedLink(Orderable): class RelatedLinkBase(Orderable):
url = models.URLField( url = models.URLField(
_('url'), _('url'),
null=True, blank=True, null=True, blank=True,
@ -90,7 +114,7 @@ class BaseRelatedLink(Orderable):
] ]
class BaseList(models.Model): class ListBase(models.Model):
""" """
Generic list Generic list
""" """
@ -112,10 +136,7 @@ class BaseList(models.Model):
verbose_name = _('filter by type'), verbose_name = _('filter by type'),
blank = True, null = True, blank = True, null = True,
help_text = _('if set, select only elements that are of this type'), help_text = _('if set, select only elements that are of this type'),
limit_choices_to = { limit_choices_to = related_pages_filter,
'model__in': ('publication','programpage','diffusionpage',
'eventpage'),
}
) )
filter_related = models.ForeignKey( filter_related = models.ForeignKey(
'Publication', 'Publication',
@ -212,7 +233,7 @@ class BaseList(models.Model):
Context's fields: Context's fields:
* object_list: the final queryset * object_list: the final queryset
* list_selector: dict of { 'tag_query', 'search_query' } plus * 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 * paginator: paginator object
""" """
model = request.GET.get('model') model = request.GET.get('model')
@ -255,7 +276,7 @@ class BaseList(models.Model):
return qs return qs
class BaseDatedList(models.Model): class DatedListBase(models.Model):
""" """
List that display items per days. Renders a navigation section on the List that display items per days. Renders a navigation section on the
top. top.
@ -342,18 +363,20 @@ class BaseDatedList(models.Model):
# #
@register_snippet @register_snippet
class Section(ClusterableModel): 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 = models.CharField(
_('name'), _('name'),
max_length=32, max_length=32,
blank = True, null = True, blank = True, null = True,
help_text=_('name of this section (not displayed)'), 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 = models.CharField(
_('position'), _('position'),
max_length=16, max_length=16,
@ -361,45 +384,59 @@ class Section(ClusterableModel):
help_text = _('name of the template block in which the section must ' help_text = _('name of the template block in which the section must '
'be set'), '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 = [ panels = [
MultiFieldPanel([ MultiFieldPanel([
FieldPanel('name'), FieldPanel('name'),
FieldPanel('css_class'),
FieldPanel('position'), FieldPanel('position'),
FieldPanel('model'),
], heading=_('General')), ], heading=_('General')),
InlinePanel('section_items', label=_('section items')), InlinePanel('places', label=_('Section Items')),
] ]
def __str__(self): def __str__(self):
return '{}: {}'.format(self.__class__.__name__, self.name or self.pk) 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): class SectionPlace(Orderable):
section = ParentalKey(Section, related_name='section_items') section = ParentalKey(Section, related_name='places')
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'),
}
)
item = models.ForeignKey( item = models.ForeignKey(
'SectionItem', 'SectionItem',
verbose_name=_('item') verbose_name=_('item')
) )
panels = [ panels = [
SnippetChooserPanel('item'), SnippetChooserPanel('item'),
FieldPanel('related'), FieldPanel('model'),
] ]
def __str__(self): def __str__(self):
return '{}: {}'.format(self.__class__.__name__, self.title or self.pk) 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 @register_snippet
class SectionItem(models.Model): class SectionItem(models.Model):
real_type = models.CharField( real_type = models.CharField(
@ -416,6 +453,13 @@ class SectionItem(models.Model):
default = False, default = False,
help_text=_('if set show a title at the head of the section'), 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 = models.CharField(
_('CSS class'), _('CSS class'),
max_length=64, max_length=64,
@ -430,12 +474,15 @@ class SectionItem(models.Model):
], heading=_('General')), ], heading=_('General')),
] ]
def model_name(self):
return self.__class__.__name__.lower()
def specific(self): def specific(self):
""" """
Return a downcasted version of the post if it is from another Return a downcasted version of the post if it is from another
model, or itself model, or itself
""" """
if not self.real_type or type(self) != Post: if not self.real_type or type(self) != SectionItem:
return self return self
return getattr(self, self.real_type) return getattr(self, self.real_type)
@ -444,6 +491,20 @@ class SectionItem(models.Model):
self.real_type = type(self).__name__.lower() self.real_type = type(self).__name__.lower()
return super().save(*args, **kwargs) 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): def __str__(self):
return '{}: {}'.format( return '{}: {}'.format(
@ -473,13 +534,13 @@ class SectionImage(SectionItem):
@register_snippet @register_snippet
class SectionLink(BaseRelatedLink,SectionItem): class SectionLink(RelatedLinkBase,SectionItem):
""" """
Can either be used standalone or in a SectionLinkList Can either be used standalone or in a SectionLinkList
""" """
parent = ParentalKey('SectionLinkList', related_name='links', parent = ParentalKey('SectionLinkList', related_name='links',
blank=True, null=True) blank=True, null=True)
panels = SectionItem.panels + BaseRelatedLink.panels panels = SectionItem.panels + RelatedLinkBase.panels
@register_snippet @register_snippet
@ -490,14 +551,14 @@ class SectionLinkList(SectionItem,ClusterableModel):
@register_snippet @register_snippet
class SectionPublicationList(BaseList,SectionItem): class SectionPublicationList(ListBase,SectionItem):
focus_available = models.BooleanField( focus_available = models.BooleanField(
_('focus available'), _('focus available'),
default = False, default = False,
help_text = _('if true, highlight the first focused article found') help_text = _('if true, highlight the first focused article found')
) )
panels = SectionItem.panels + [ FieldPanel('focus_available') ] +\ panels = SectionItem.panels + [ FieldPanel('focus_available') ] +\
BaseList.panels ListBase.panels

View File

@ -1,11 +1,33 @@
from django import template from django import template
from django.db.models import Q
import aircox.cms.sections as sections
register = template.Library() register = template.Library()
@register.filter(name='around') @register.filter
def around(page_num, n): def around(page_num, n):
""" """
Return a range of value around a given number. Return a range of value around a given number.
""" """
return range(page_num-n, page_num+n+1) 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
)