clean-up list system a bit (still some fixes left); work on design (cover, lists rendering)
This commit is contained in:
		@ -162,14 +162,11 @@ class WebsiteSettings(BaseSetting):
 | 
				
			|||||||
        verbose_name = _('website settings')
 | 
					        verbose_name = _('website settings')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Publications
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
@register_snippet
 | 
					@register_snippet
 | 
				
			||||||
class Comment(models.Model):
 | 
					class Comment(models.Model):
 | 
				
			||||||
    publication = models.ForeignKey(
 | 
					    publication = models.ForeignKey(
 | 
				
			||||||
        'Publication',
 | 
					        Page,
 | 
				
			||||||
        verbose_name = _('publication')
 | 
					        verbose_name = _('page')
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    published = models.BooleanField(
 | 
					    published = models.BooleanField(
 | 
				
			||||||
        verbose_name = _('published'),
 | 
					        verbose_name = _('published'),
 | 
				
			||||||
@ -227,16 +224,114 @@ class Comment(models.Model):
 | 
				
			|||||||
        return super().save(*args, **kwargs)
 | 
					        return super().save(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BasePage(Page):
 | 
				
			||||||
 | 
					    body = RichTextField(
 | 
				
			||||||
 | 
					        _('body'),
 | 
				
			||||||
 | 
					        null = True, blank = True,
 | 
				
			||||||
 | 
					        help_text = _('the publication itself')
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    cover = models.ForeignKey(
 | 
				
			||||||
 | 
					        'wagtailimages.Image',
 | 
				
			||||||
 | 
					        verbose_name = _('cover'),
 | 
				
			||||||
 | 
					        null=True, blank=True,
 | 
				
			||||||
 | 
					        on_delete=models.SET_NULL,
 | 
				
			||||||
 | 
					        related_name='+',
 | 
				
			||||||
 | 
					        help_text = _('image to use as cover of the publication'),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    allow_comments = models.BooleanField(
 | 
				
			||||||
 | 
					        _('allow comments'),
 | 
				
			||||||
 | 
					        default = True,
 | 
				
			||||||
 | 
					        help_text = _('allow comments')
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # panels
 | 
				
			||||||
 | 
					    content_panels = [
 | 
				
			||||||
 | 
					        MultiFieldPanel([
 | 
				
			||||||
 | 
					            FieldPanel('title'),
 | 
				
			||||||
 | 
					            ImageChooserPanel('cover'),
 | 
				
			||||||
 | 
					            FieldPanel('body', classname='full'),
 | 
				
			||||||
 | 
					        ], heading=_('Content'))
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    settings_panels = Page.settings_panels + [
 | 
				
			||||||
 | 
					        FieldPanel('allow_comments'),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    search_fields = [
 | 
				
			||||||
 | 
					        index.SearchField('title', partial_match=True),
 | 
				
			||||||
 | 
					        index.SearchField('body', partial_match=True),
 | 
				
			||||||
 | 
					        index.FilterField('live'),
 | 
				
			||||||
 | 
					        index.FilterField('show_in_menus'),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # properties
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def url(self):
 | 
				
			||||||
 | 
					        if not self.live:
 | 
				
			||||||
 | 
					            parent = self.get_parent().specific
 | 
				
			||||||
 | 
					            return parent and parent.url
 | 
				
			||||||
 | 
					        return super().url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def icon(self):
 | 
				
			||||||
 | 
					        return image_url(self.cover, 'fill-64x64')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def small_icon(self):
 | 
				
			||||||
 | 
					        return image_url(self.cover, 'fill-32x32')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def comments(self):
 | 
				
			||||||
 | 
					        return Comment.objects.filter(
 | 
				
			||||||
 | 
					            publication = self,
 | 
				
			||||||
 | 
					            published = True,
 | 
				
			||||||
 | 
					        ).order_by('-date')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # methods
 | 
				
			||||||
 | 
					    def get_context(self, request, *args, **kwargs):
 | 
				
			||||||
 | 
					        from aircox_cms.forms import CommentForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        context = super().get_context(request, *args, **kwargs)
 | 
				
			||||||
 | 
					        if self.allow_comments and \
 | 
				
			||||||
 | 
					                WebsiteSettings.for_site(request.site).allow_comments:
 | 
				
			||||||
 | 
					            context['comment_form'] = CommentForm()
 | 
				
			||||||
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def serve(self, request):
 | 
				
			||||||
 | 
					        from aircox_cms.forms import CommentForm
 | 
				
			||||||
 | 
					        if request.POST and 'comment' in request.POST['type']:
 | 
				
			||||||
 | 
					            settings = WebsiteSettings.for_site(request.site)
 | 
				
			||||||
 | 
					            comment_form = CommentForm(request.POST)
 | 
				
			||||||
 | 
					            if comment_form.is_valid():
 | 
				
			||||||
 | 
					                comment = comment_form.save(commit=False)
 | 
				
			||||||
 | 
					                comment.publication = self
 | 
				
			||||||
 | 
					                comment.published = settings.accept_comments
 | 
				
			||||||
 | 
					                comment.save()
 | 
				
			||||||
 | 
					                messages.success(request,
 | 
				
			||||||
 | 
					                    settings.comment_success_message
 | 
				
			||||||
 | 
					                        if comment.published else
 | 
				
			||||||
 | 
					                    settings.comment_wait_message,
 | 
				
			||||||
 | 
					                    fail_silently=True,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                messages.error(
 | 
				
			||||||
 | 
					                    request, settings.comment_error_message, fail_silently=True
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					        return super().serve(request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Publications
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
class PublicationRelatedLink(RelatedLinkBase,TemplateMixin):
 | 
					class PublicationRelatedLink(RelatedLinkBase,TemplateMixin):
 | 
				
			||||||
    template = 'aircox_cms/snippets/link.html'
 | 
					    template = 'aircox_cms/snippets/link.html'
 | 
				
			||||||
    parent = ParentalKey('Publication', related_name='links')
 | 
					    parent = ParentalKey('Publication', related_name='links')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
class PublicationTag(TaggedItemBase):
 | 
					class PublicationTag(TaggedItemBase):
 | 
				
			||||||
    content_object = ParentalKey('Publication', related_name='tagged_items')
 | 
					    content_object = ParentalKey('Publication', related_name='tagged_items')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Publication(BasePage):
 | 
				
			||||||
class Publication(Page):
 | 
					 | 
				
			||||||
    order_field = 'date'
 | 
					    order_field = 'date'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    date = models.DateTimeField(
 | 
					    date = models.DateTimeField(
 | 
				
			||||||
@ -262,19 +357,6 @@ class Publication(Page):
 | 
				
			|||||||
        help_text = _('allow comments')
 | 
					        help_text = _('allow comments')
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    body = RichTextField(
 | 
					 | 
				
			||||||
        _('body'),
 | 
					 | 
				
			||||||
        blank=True,
 | 
					 | 
				
			||||||
        help_text = _('the publication itself')
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    cover = models.ForeignKey(
 | 
					 | 
				
			||||||
        'wagtailimages.Image',
 | 
					 | 
				
			||||||
        verbose_name = _('cover'),
 | 
					 | 
				
			||||||
        null=True, blank=True,
 | 
					 | 
				
			||||||
        on_delete=models.SET_NULL,
 | 
					 | 
				
			||||||
        related_name='+',
 | 
					 | 
				
			||||||
        help_text = _('image to use as cover of the publication'),
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    headline = models.TextField(
 | 
					    headline = models.TextField(
 | 
				
			||||||
        _('headline'),
 | 
					        _('headline'),
 | 
				
			||||||
        blank = True, null = True,
 | 
					        blank = True, null = True,
 | 
				
			||||||
@ -309,82 +391,32 @@ class Publication(Page):
 | 
				
			|||||||
        FieldPanel('publish_as'),
 | 
					        FieldPanel('publish_as'),
 | 
				
			||||||
        FieldPanel('allow_comments'),
 | 
					        FieldPanel('allow_comments'),
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    search_fields = [
 | 
					    search_fields = BasePage.search_fields + [
 | 
				
			||||||
        index.SearchField('title', partial_match=True),
 | 
					        index.SearchField('headline', partial_match=True),
 | 
				
			||||||
        index.SearchField('body', partial_match=True),
 | 
					 | 
				
			||||||
        index.FilterField('live'),
 | 
					 | 
				
			||||||
        index.FilterField('show_in_menus'),
 | 
					 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					 | 
				
			||||||
    def url(self):
 | 
					 | 
				
			||||||
        if not self.live:
 | 
					 | 
				
			||||||
            parent = self.get_parent().specific
 | 
					 | 
				
			||||||
            return parent and parent.url
 | 
					 | 
				
			||||||
        return super().url
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property
 | 
					 | 
				
			||||||
    def icon(self):
 | 
					 | 
				
			||||||
        return image_url(self.cover, 'fill-64x64')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property
 | 
					 | 
				
			||||||
    def small_icon(self):
 | 
					 | 
				
			||||||
        return image_url(self.cover, 'fill-32x32')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def recents(self):
 | 
					    def recents(self):
 | 
				
			||||||
        return self.get_children().type(Publication).not_in_menu().live() \
 | 
					        return self.get_children().type(Publication).not_in_menu().live() \
 | 
				
			||||||
                   .order_by('-publication__date')
 | 
					                   .order_by('-publication__date')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    def get_context(self, request, *args, **kwargs):
 | 
				
			||||||
    def comments(self):
 | 
					        context = super().get_context(request, *args, **kwargs)
 | 
				
			||||||
        return Comment.objects.filter(
 | 
					        view = request.GET.get('view')
 | 
				
			||||||
            publication = self,
 | 
					        context.update({
 | 
				
			||||||
            published = True,
 | 
					            'view': view,
 | 
				
			||||||
        ).order_by('-date')
 | 
					            'page': self,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        if view == 'list':
 | 
				
			||||||
 | 
					            context.update(BaseList.from_request(request, related = self))
 | 
				
			||||||
 | 
					            context['list_url_args'] += '&view=list'
 | 
				
			||||||
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def save(self, *args, **kwargs):
 | 
					    def save(self, *args, **kwargs):
 | 
				
			||||||
        if not self.date and self.first_published_at:
 | 
					        if not self.date and self.first_published_at:
 | 
				
			||||||
            self.date = self.first_published_at
 | 
					            self.date = self.first_published_at
 | 
				
			||||||
        super().save(*args, **kwargs)
 | 
					        return super().save(*args, **kwargs)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_context(self, request, *args, **kwargs):
 | 
					 | 
				
			||||||
        from aircox_cms.forms import CommentForm
 | 
					 | 
				
			||||||
        context = super().get_context(request, *args, **kwargs)
 | 
					 | 
				
			||||||
        view = request.GET.get('view')
 | 
					 | 
				
			||||||
        page = request.GET.get('page')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if self.allow_comments and \
 | 
					 | 
				
			||||||
                WebsiteSettings.for_site(request.site).allow_comments:
 | 
					 | 
				
			||||||
            context['comment_form'] = CommentForm()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if view == 'list':
 | 
					 | 
				
			||||||
            context['object_list'] = ListBase.from_request(
 | 
					 | 
				
			||||||
                request, context = context, related = self
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        return context
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def serve(self, request):
 | 
					 | 
				
			||||||
        from aircox_cms.forms import CommentForm
 | 
					 | 
				
			||||||
        if request.POST and 'comment' in request.POST['type']:
 | 
					 | 
				
			||||||
            settings = WebsiteSettings.for_site(request.site)
 | 
					 | 
				
			||||||
            comment_form = CommentForm(request.POST)
 | 
					 | 
				
			||||||
            if comment_form.is_valid():
 | 
					 | 
				
			||||||
                comment = comment_form.save(commit=False)
 | 
					 | 
				
			||||||
                comment.publication = self
 | 
					 | 
				
			||||||
                comment.published = settings.accept_comments
 | 
					 | 
				
			||||||
                comment.save()
 | 
					 | 
				
			||||||
                messages.success(request,
 | 
					 | 
				
			||||||
                    settings.comment_success_message
 | 
					 | 
				
			||||||
                        if comment.published else
 | 
					 | 
				
			||||||
                    settings.comment_wait_message,
 | 
					 | 
				
			||||||
                    fail_silently=True,
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                messages.error(
 | 
					 | 
				
			||||||
                    request, settings.comment_error_message, fail_silently=True
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
        return super().serve(request)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ProgramPage(Publication):
 | 
					class ProgramPage(Publication):
 | 
				
			||||||
@ -393,7 +425,6 @@ class ProgramPage(Publication):
 | 
				
			|||||||
        verbose_name = _('program'),
 | 
					        verbose_name = _('program'),
 | 
				
			||||||
        related_name = 'page',
 | 
					        related_name = 'page',
 | 
				
			||||||
        on_delete=models.SET_NULL,
 | 
					        on_delete=models.SET_NULL,
 | 
				
			||||||
        unique = True,
 | 
					 | 
				
			||||||
        blank=True, null=True,
 | 
					        blank=True, null=True,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    # rss = models.URLField()
 | 
					    # rss = models.URLField()
 | 
				
			||||||
@ -488,7 +519,6 @@ class DiffusionPage(Publication):
 | 
				
			|||||||
        aircox.models.Diffusion,
 | 
					        aircox.models.Diffusion,
 | 
				
			||||||
        verbose_name = _('diffusion'),
 | 
					        verbose_name = _('diffusion'),
 | 
				
			||||||
        related_name = 'page',
 | 
					        related_name = 'page',
 | 
				
			||||||
        unique = True,
 | 
					 | 
				
			||||||
        null=True,
 | 
					        null=True,
 | 
				
			||||||
        # not blank because we enforce the connection to a diffusion
 | 
					        # not blank because we enforce the connection to a diffusion
 | 
				
			||||||
        #   (still users always tend to break sth)
 | 
					        #   (still users always tend to break sth)
 | 
				
			||||||
@ -619,7 +649,18 @@ class DiffusionPage(Publication):
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
# Others types of pages
 | 
					# Others types of pages
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
class DynamicListPage(Page):
 | 
					
 | 
				
			||||||
 | 
					class CategoryPage(BasePage, BaseList):
 | 
				
			||||||
 | 
					    content_panels = BasePage.content_panels + BaseList.panels
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_context(self, request, *args, **kwargs):
 | 
				
			||||||
 | 
					        context = super().get_context(request, *args, **kwargs)
 | 
				
			||||||
 | 
					        context.update(BaseList.get_context(self, request, paginate = True))
 | 
				
			||||||
 | 
					        context['view'] = 'list'
 | 
				
			||||||
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DynamicListPage(BasePage):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Displays a list of publications using query passed by the url.
 | 
					    Displays a list of publications using query passed by the url.
 | 
				
			||||||
    This can be used for search/tags page, and generally only one
 | 
					    This can be used for search/tags page, and generally only one
 | 
				
			||||||
@ -628,47 +669,21 @@ class DynamicListPage(Page):
 | 
				
			|||||||
    If a title is given, use it instead of the generated one.
 | 
					    If a title is given, use it instead of the generated one.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    # FIXME/TODO: title in template <title></title>
 | 
					    # FIXME/TODO: title in template <title></title>
 | 
				
			||||||
    body = RichTextField(
 | 
					    # TODO: personnalized titles depending on request
 | 
				
			||||||
        _('body'),
 | 
					 | 
				
			||||||
        blank = True, null = True,
 | 
					 | 
				
			||||||
        help_text = _('add an extra description for this list')
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    content_panels = [
 | 
					 | 
				
			||||||
        MultiFieldPanel([
 | 
					 | 
				
			||||||
            FieldPanel('title'),
 | 
					 | 
				
			||||||
            FieldPanel('body'),
 | 
					 | 
				
			||||||
        ], heading=_('Content'))
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        verbose_name = _('Dynamic List Page')
 | 
					        verbose_name = _('Dynamic List Page')
 | 
				
			||||||
        verbose_name_plural = _('Dynamic List Pages')
 | 
					        verbose_name_plural = _('Dynamic List Pages')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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 = ListBase.from_request(request, context=context)
 | 
					        context.update(BaseList.from_request(request))
 | 
				
			||||||
        context['object_list'] = qs
 | 
					 | 
				
			||||||
        return context
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DatedListPage(DatedListBase,Page):
 | 
					class DatedListPage(DatedBaseList,BasePage):
 | 
				
			||||||
    body = RichTextField(
 | 
					 | 
				
			||||||
        _('body'),
 | 
					 | 
				
			||||||
        blank = True, null = True,
 | 
					 | 
				
			||||||
        help_text = _('add an extra description for this list')
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        abstract = True
 | 
					        abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    content_panels = [
 | 
					 | 
				
			||||||
        MultiFieldPanel([
 | 
					 | 
				
			||||||
            FieldPanel('title'),
 | 
					 | 
				
			||||||
            FieldPanel('body'),
 | 
					 | 
				
			||||||
        ], heading=_('Content')),
 | 
					 | 
				
			||||||
    ] + DatedListBase.panels
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_queryset(self, request, context):
 | 
					    def get_queryset(self, request, context):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Must be implemented by the child
 | 
					        Must be implemented by the child
 | 
				
			||||||
@ -702,6 +717,8 @@ class LogsPage(DatedListPage):
 | 
				
			|||||||
    station = models.ForeignKey(
 | 
					    station = models.ForeignKey(
 | 
				
			||||||
        aircox.models.Station,
 | 
					        aircox.models.Station,
 | 
				
			||||||
        verbose_name = _('station'),
 | 
					        verbose_name = _('station'),
 | 
				
			||||||
 | 
					        null = True, blank = True,
 | 
				
			||||||
 | 
					        on_delete = models.SET_NULL,
 | 
				
			||||||
        help_text = _('(required) related station')
 | 
					        help_text = _('(required) related station')
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    age_max = models.IntegerField(
 | 
					    age_max = models.IntegerField(
 | 
				
			||||||
@ -763,6 +780,8 @@ class TimetablePage(DatedListPage):
 | 
				
			|||||||
    station = models.ForeignKey(
 | 
					    station = models.ForeignKey(
 | 
				
			||||||
        aircox.models.Station,
 | 
					        aircox.models.Station,
 | 
				
			||||||
        verbose_name = _('station'),
 | 
					        verbose_name = _('station'),
 | 
				
			||||||
 | 
					        on_delete = models.SET_NULL,
 | 
				
			||||||
 | 
					        null = True, blank = True,
 | 
				
			||||||
        help_text = _('(required) related station')
 | 
					        help_text = _('(required) related station')
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -167,7 +167,7 @@ class RelatedLinkBase(Orderable):
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ListBase(models.Model):
 | 
					class BaseList(models.Model):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Generic list
 | 
					    Generic list
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
@ -175,37 +175,24 @@ class ListBase(models.Model):
 | 
				
			|||||||
        none = 0x00
 | 
					        none = 0x00
 | 
				
			||||||
        previous = 0x01
 | 
					        previous = 0x01
 | 
				
			||||||
        next = 0x02
 | 
					        next = 0x02
 | 
				
			||||||
        before_related = 0x03,
 | 
					        before_related = 0x03
 | 
				
			||||||
        after_related = 0x04,
 | 
					        after_related = 0x04
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    date_filter = models.SmallIntegerField(
 | 
					    class RelationFilter(IntEnum):
 | 
				
			||||||
        verbose_name = _('filter by date'),
 | 
					        none = 0x00
 | 
				
			||||||
        choices = [ (int(y), _(x.replace('_', ' ')))
 | 
					        subpages = 0x01
 | 
				
			||||||
                        for x,y in DateFilter.__members__.items() ],
 | 
					        siblings = 0x02
 | 
				
			||||||
        blank = True, null = True,
 | 
					
 | 
				
			||||||
    )
 | 
					    # rendering
 | 
				
			||||||
    model = models.ForeignKey(
 | 
					    use_focus = models.BooleanField(
 | 
				
			||||||
        ContentType,
 | 
					        _('focus available'),
 | 
				
			||||||
        verbose_name = _('filter by type'),
 | 
					 | 
				
			||||||
        blank = True, null = True,
 | 
					 | 
				
			||||||
        on_delete=models.SET_NULL,
 | 
					 | 
				
			||||||
        help_text = _('if set, select only elements that are of this type'),
 | 
					 | 
				
			||||||
        limit_choices_to = related_pages_filter,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    related = models.ForeignKey(
 | 
					 | 
				
			||||||
        Page,
 | 
					 | 
				
			||||||
        verbose_name = _('filter by a related page'),
 | 
					 | 
				
			||||||
        blank = True, null = True,
 | 
					 | 
				
			||||||
        on_delete=models.SET_NULL,
 | 
					 | 
				
			||||||
        help_text = _('if set, select children or siblings related to this page'),
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    siblings = models.BooleanField(
 | 
					 | 
				
			||||||
        verbose_name = _('select siblings of related'),
 | 
					 | 
				
			||||||
        default = False,
 | 
					        default = False,
 | 
				
			||||||
        help_text = _(
 | 
					        help_text = _('if true, highlight the first focused article found')
 | 
				
			||||||
            'if checked, related publications are siblings instead of '
 | 
					    )
 | 
				
			||||||
            'the children.'
 | 
					    count = models.SmallIntegerField(
 | 
				
			||||||
        ),
 | 
					        _('count'),
 | 
				
			||||||
 | 
					        default = 30,
 | 
				
			||||||
 | 
					        help_text = _('number of items to display in the list'),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    asc = models.BooleanField(
 | 
					    asc = models.BooleanField(
 | 
				
			||||||
        verbose_name = _('ascending order'),
 | 
					        verbose_name = _('ascending order'),
 | 
				
			||||||
@ -213,27 +200,87 @@ class ListBase(models.Model):
 | 
				
			|||||||
        help_text = _('if selected sort list in the ascending order by date')
 | 
					        help_text = _('if selected sort list in the ascending order by date')
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    # selectors
 | 
				
			||||||
        abstract = True
 | 
					    date_filter = models.SmallIntegerField(
 | 
				
			||||||
 | 
					        verbose_name = _('filter on date'),
 | 
				
			||||||
 | 
					        choices = [ (int(y), _(x.replace('_', ' ')))
 | 
				
			||||||
 | 
					                        for x,y in DateFilter.__members__.items() ],
 | 
				
			||||||
 | 
					        blank = True, null = True,
 | 
				
			||||||
 | 
					        help_text = _(
 | 
				
			||||||
 | 
					            'select pages whose date follows the given constraint'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    model = models.ForeignKey(
 | 
				
			||||||
 | 
					        ContentType,
 | 
				
			||||||
 | 
					        verbose_name = _('filter on page type'),
 | 
				
			||||||
 | 
					        blank = True, null = True,
 | 
				
			||||||
 | 
					        on_delete=models.SET_NULL,
 | 
				
			||||||
 | 
					        help_text = _('if set, select only elements that are of this type'),
 | 
				
			||||||
 | 
					        limit_choices_to = related_pages_filter,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    related = models.ForeignKey(
 | 
				
			||||||
 | 
					        Page,
 | 
				
			||||||
 | 
					        verbose_name = _('related page'),
 | 
				
			||||||
 | 
					        blank = True, null = True,
 | 
				
			||||||
 | 
					        on_delete=models.SET_NULL,
 | 
				
			||||||
 | 
					        help_text = _(
 | 
				
			||||||
 | 
					            'if set, select children or siblings of this page'
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        related_name = '+'
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    relation = models.BooleanField(
 | 
				
			||||||
 | 
					        verbose_name = _('relation'),
 | 
				
			||||||
 | 
					        choices = [ (int(y), _(x.replace('_', ' ')))
 | 
				
			||||||
 | 
					                        for x,y in RelationFilter.__members__.items() ],
 | 
				
			||||||
 | 
					        default = 1,
 | 
				
			||||||
 | 
					        help_text = _(
 | 
				
			||||||
 | 
					            'when the list is related to a page, only select pages that '
 | 
				
			||||||
 | 
					            'correspond to this relationship'
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    search = models.CharField(
 | 
				
			||||||
 | 
					        verbose_name = _('filter on search'),
 | 
				
			||||||
 | 
					        blank = True, null = True,
 | 
				
			||||||
 | 
					        max_length = 128,
 | 
				
			||||||
 | 
					        help_text = _(
 | 
				
			||||||
 | 
					            'keep only pages that matches the given search'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    tags = models.CharField(
 | 
				
			||||||
 | 
					        verbose_name = _('filter on tag'),
 | 
				
			||||||
 | 
					        blank = True, null = True,
 | 
				
			||||||
 | 
					        max_length = 128,
 | 
				
			||||||
 | 
					        help_text = _(
 | 
				
			||||||
 | 
					            'keep only pages with the given tags (separated by a colon)'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    panels = [
 | 
					    panels = [
 | 
				
			||||||
        MultiFieldPanel([
 | 
					        MultiFieldPanel([
 | 
				
			||||||
            FieldPanel('model'),
 | 
					            FieldPanel('count'),
 | 
				
			||||||
            PageChooserPanel('related'),
 | 
					            FieldPanel('use_focus'),
 | 
				
			||||||
            FieldPanel('siblings'),
 | 
					            FieldPanel('asc'),
 | 
				
			||||||
        ], heading=_('filters')),
 | 
					        ], heading=_('rendering')),
 | 
				
			||||||
        MultiFieldPanel([
 | 
					        MultiFieldPanel([
 | 
				
			||||||
            FieldPanel('date_filter'),
 | 
					            FieldPanel('date_filter'),
 | 
				
			||||||
            FieldPanel('asc'),
 | 
					            FieldPanel('model'),
 | 
				
			||||||
        ], heading=_('sorting'))
 | 
					            PageChooserPanel('related'),
 | 
				
			||||||
 | 
					            FieldPanel('relation'),
 | 
				
			||||||
 | 
					            FieldPanel('search'),
 | 
				
			||||||
 | 
					            FieldPanel('tags'),
 | 
				
			||||||
 | 
					        ], heading=_('filters'))
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __get_related(self, qs):
 | 
					    def __get_related(self, qs):
 | 
				
			||||||
        related = self.related and self.related.specific
 | 
					        related = self.related and self.related.specific
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.siblings:
 | 
					        if self.relation == self.RelationFilter.siblings:
 | 
				
			||||||
            qs = qs.sibling_of(related)
 | 
					            qs = qs.sibling_of(related)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
 | 
					        # elif self.relation == RelatedFilter.subpages:
 | 
				
			||||||
            qs = qs.descendant_of(related)
 | 
					            qs = qs.descendant_of(related)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        date = related.date if hasattr(related, 'date') else \
 | 
					        date = related.date if hasattr(related, 'date') else \
 | 
				
			||||||
@ -250,7 +297,6 @@ class ListBase(models.Model):
 | 
				
			|||||||
        reusable by other classes if needed.
 | 
					        reusable by other classes if needed.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        from aircox_cms.models import Publication
 | 
					        from aircox_cms.models import Publication
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # model
 | 
					        # model
 | 
				
			||||||
        if self.model:
 | 
					        if self.model:
 | 
				
			||||||
            qs = self.model.model_class().objects.all()
 | 
					            qs = self.model.model_class().objects.all()
 | 
				
			||||||
@ -262,7 +308,7 @@ class ListBase(models.Model):
 | 
				
			|||||||
        if self.related:
 | 
					        if self.related:
 | 
				
			||||||
            qs = self.__get_related(qs)
 | 
					            qs = self.__get_related(qs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # date
 | 
					        # date_filter
 | 
				
			||||||
        date = tz.now()
 | 
					        date = tz.now()
 | 
				
			||||||
        if self.date_filter == self.DateFilter.previous:
 | 
					        if self.date_filter == self.DateFilter.previous:
 | 
				
			||||||
            qs = qs.filter(date__lt = date)
 | 
					            qs = qs.filter(date__lt = date)
 | 
				
			||||||
@ -270,77 +316,138 @@ class ListBase(models.Model):
 | 
				
			|||||||
            qs = qs.filter(date__gte = date)
 | 
					            qs = qs.filter(date__gte = date)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # sort
 | 
					        # sort
 | 
				
			||||||
        if self.asc:
 | 
					            qs = qs.order_by('date', 'pk') \
 | 
				
			||||||
            return qs.order_by('date', 'pk')
 | 
					                    if self.asc else qs.order_by('-date', '-pk')
 | 
				
			||||||
        return qs.order_by('-date', '-pk')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def to_url(self, list_page = None, **kwargs):
 | 
					        # tags
 | 
				
			||||||
 | 
					        if self.tags:
 | 
				
			||||||
 | 
					            qs = qs.filter(tags__name__in = ','.split(self.tags))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # search
 | 
				
			||||||
 | 
					        if self.search:
 | 
				
			||||||
 | 
					            # this qs.search does not return a queryset
 | 
				
			||||||
 | 
					            qs = qs.search(self.search)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return qs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_context(self, request, qs = None, paginate = True):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Return a url parameters from self. Extra named parameters are used
 | 
					        Return a context object using the given request and arguments.
 | 
				
			||||||
        to override values of self or add some to the parameters.
 | 
					        @param paginate: paginate and include paginator into context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        If there is related field use it to get the page, otherwise use the
 | 
					        Context arguments:
 | 
				
			||||||
        given list_page or the first DynamicListPage it finds.
 | 
					            - object_list: queryset of the list's objects
 | 
				
			||||||
 | 
					            - paginator: [if paginate] paginator object for this list
 | 
				
			||||||
 | 
					            - list_url_args: GET arguments of the url as string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ! Note: BaseList does not inherit from Wagtail.Page, and calling
 | 
				
			||||||
 | 
					                this method won't call other super() get_context.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        import aircox_cms.models as models
 | 
					        qs = qs or self.get_queryset()
 | 
				
			||||||
 | 
					        paginator = None
 | 
				
			||||||
 | 
					        context = {}
 | 
				
			||||||
 | 
					        if qs.count():
 | 
				
			||||||
 | 
					            if paginate:
 | 
				
			||||||
 | 
					                context.update(self.paginate(request, qs))
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                context['object_list'] = qs[:self.count]
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            # keep empty queryset
 | 
				
			||||||
 | 
					            context['object_list'] = qs
 | 
				
			||||||
 | 
					        context['list_url_args'] = self.to_url(full_url = False)
 | 
				
			||||||
 | 
					        #context['list_selector'] = {
 | 
				
			||||||
 | 
					        #    attr: getattr(self, attr) for attr in (
 | 
				
			||||||
 | 
					        #        'asc', 'date_filter', 'model', 'related', 'relation',
 | 
				
			||||||
 | 
					        #        'tags', 'search',
 | 
				
			||||||
 | 
					        #    )
 | 
				
			||||||
 | 
					        #}
 | 
				
			||||||
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def paginate(self, request, qs):
 | 
				
			||||||
 | 
					        # paginator
 | 
				
			||||||
 | 
					        paginator = Paginator(qs, self.count)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            qs = paginator.page(request.GET.get('page') or 1)
 | 
				
			||||||
 | 
					        except PageNotAnInteger:
 | 
				
			||||||
 | 
					            qs = paginator.page(1)
 | 
				
			||||||
 | 
					        except EmptyPage:
 | 
				
			||||||
 | 
					            qs = paginator.page(paginator.num_pages)
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            'paginator': paginator,
 | 
				
			||||||
 | 
					            'object_list': qs
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_url(self, page = None, full_url = True, **kwargs):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Return a url to a given page with GET corresponding to this
 | 
				
			||||||
 | 
					        list's parameters.
 | 
				
			||||||
 | 
					        @param page: if given use it to prepend url with page's url instead of giving only
 | 
				
			||||||
 | 
					                        GET parameters
 | 
				
			||||||
 | 
					        @param **kwargs: override list parameters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If there is related field use it to get the page, otherwise use
 | 
				
			||||||
 | 
					        the given list_page or the first BaseListPage it finds.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        params = {
 | 
					        params = {
 | 
				
			||||||
            'view': 'list',
 | 
					            'asc': self.asc,
 | 
				
			||||||
            'date_filter': self.get_date_filter_display(),
 | 
					            'date_filter': self.get_date_filter_display(),
 | 
				
			||||||
            'model': self.model and self.model.model,
 | 
					            'model': self.model and self.model.model,
 | 
				
			||||||
            'asc': self.asc,
 | 
					            'relation': self.get_relation_display(),
 | 
				
			||||||
            'related': self.related,
 | 
					            'search': self.search,
 | 
				
			||||||
            'siblings': self.siblings,
 | 
					            'tags': self.tags
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        params.update(kwargs)
 | 
					        params.update(kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        page = params.get('related') or list_page or \
 | 
					 | 
				
			||||||
                models.DynamicListPage.objects.all().first()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if params.get('related'):
 | 
					        if params.get('related'):
 | 
				
			||||||
            params['related'] = True
 | 
					            params['related'] = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        params = '&'.join([
 | 
					        params = '&'.join([
 | 
				
			||||||
            key if value == True else '{}={}'.format(key, value)
 | 
					            key if value == True else '{}={}'.format(key, value)
 | 
				
			||||||
            for key, value in params.items()
 | 
					            for key, value in params.items() if value
 | 
				
			||||||
            if value
 | 
					 | 
				
			||||||
        ])
 | 
					        ])
 | 
				
			||||||
 | 
					        if not full_url:
 | 
				
			||||||
 | 
					            return params
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        page = page or self.page
 | 
				
			||||||
 | 
					        if not page:
 | 
				
			||||||
 | 
					            raise ValueError(
 | 
				
			||||||
 | 
					                "full_url = True requires either list.related or "
 | 
				
			||||||
 | 
					                "method's argument `page` to be given"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
        return page.url + '?' + params
 | 
					        return page.url + '?' + params
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def from_request(cl, request, related = None, context = None,
 | 
					    def from_request(cl, request, related = None):
 | 
				
			||||||
                     *args, **kwargs):
 | 
					 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Return a queryset from the request's GET parameters. Context
 | 
					        Return a context from the request's GET parameters. Context
 | 
				
			||||||
        can be used to update relative informations.
 | 
					        can be used to update relative informations, more information
 | 
				
			||||||
 | 
					        on this object from BaseList.get_context()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @param request: get params from this request
 | 
				
			||||||
 | 
					        @param related: reference page for a related list
 | 
				
			||||||
 | 
					        @return context object from BaseList.get_context()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        This function can be used by other views if needed
 | 
					        This function can be used by other views if needed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Parameters:
 | 
					        Parameters:
 | 
				
			||||||
 | 
					        * asc:      if present, sort ascending instead of descending
 | 
				
			||||||
        * date_filter: one of DateFilter attribute's key.
 | 
					        * date_filter: one of DateFilter attribute's key.
 | 
				
			||||||
        * model:    ['program','diffusion','event'] type of the publication
 | 
					        * model:    ['program','diffusion','event'] type of the publication
 | 
				
			||||||
        * asc:      if present, sort ascending instead of descending
 | 
					        * relation: one of RelationFilter attribute's key
 | 
				
			||||||
        * related:  children of the thread passed in arguments only
 | 
					        * related:  list is related to the method's argument `related`
 | 
				
			||||||
        * siblings: sibling of the related instead of children
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        * tag:      tag to search for
 | 
					        * tag:      tag to search for
 | 
				
			||||||
        * search:   query to search in the publications
 | 
					        * search:   query to search in the publications
 | 
				
			||||||
        * page:     page number
 | 
					        * page:     page number
 | 
				
			||||||
 | 
					 | 
				
			||||||
        Context's fields:
 | 
					 | 
				
			||||||
        * object_list:      the final queryset
 | 
					 | 
				
			||||||
        * list_selector:    dict of { 'tag_query', 'search_query' } plus
 | 
					 | 
				
			||||||
                            arguments passed to ListBase.get_base_queryset
 | 
					 | 
				
			||||||
        * paginator:        paginator object
 | 
					 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        def set(key, value):
 | 
					        # FIXME: page argument to select a page
 | 
				
			||||||
            if context is not None:
 | 
					        # FIXME: related
 | 
				
			||||||
                context[key] = value
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        date_filter = request.GET.get('date_filter')
 | 
					        date_filter = request.GET.get('date_filter')
 | 
				
			||||||
        model = request.GET.get('model')
 | 
					        model = request.GET.get('model')
 | 
				
			||||||
 | 
					        relation = request.GET.get('relation')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        kwargs = {
 | 
					        kwargs = {
 | 
				
			||||||
 | 
					            'asc': 'asc' in request.GET,
 | 
				
			||||||
            'date_filter':
 | 
					            'date_filter':
 | 
				
			||||||
                int(getattr(cl.DateFilter, date_filter))
 | 
					                int(getattr(cl.DateFilter, date_filter))
 | 
				
			||||||
                if date_filter and hasattr(cl.DateFilter, date_filter)
 | 
					                if date_filter and hasattr(cl.DateFilter, date_filter)
 | 
				
			||||||
@ -350,42 +457,21 @@ class ListBase(models.Model):
 | 
				
			|||||||
                DiffusionPage if model == 'diffusion' else
 | 
					                DiffusionPage if model == 'diffusion' else
 | 
				
			||||||
                EventPage if model == 'event' else None,
 | 
					                EventPage if model == 'event' else None,
 | 
				
			||||||
            'related': 'related' in request.GET and related,
 | 
					            'related': 'related' in request.GET and related,
 | 
				
			||||||
            'siblings': 'siblings' in request.GET,
 | 
					            'relation':
 | 
				
			||||||
            'asc': 'asc' in request.GET,
 | 
					                int(getattr(cl.RelationFilter, relation))
 | 
				
			||||||
 | 
					                if relation and hasattr(cl.RelationFilter, relation)
 | 
				
			||||||
 | 
					                else None,
 | 
				
			||||||
 | 
					            'tags': request.GET.get('tags'),
 | 
				
			||||||
 | 
					            'search': request.GET.get('search'),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        base_list = cl(**{ k:v for k,v in kwargs.items() if v })
 | 
					        base_list = cl(
 | 
				
			||||||
        qs = base_list.get_queryset()
 | 
					            count = 30, **{ k:v for k,v in kwargs.items() if v }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        # filter by tag
 | 
					        return base_list.get_context(request)
 | 
				
			||||||
        tag = request.GET.get('tag')
 | 
					 | 
				
			||||||
        if tag:
 | 
					 | 
				
			||||||
            kwargs['terms'] = tag
 | 
					 | 
				
			||||||
            qs = qs.filter(tags__name = tag)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # search
 | 
					 | 
				
			||||||
        search = request.GET.get('search')
 | 
					 | 
				
			||||||
        if search:
 | 
					 | 
				
			||||||
            kwargs['terms'] = search
 | 
					 | 
				
			||||||
            qs = qs.search(search)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        set('list_selector', kwargs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # paginator
 | 
					 | 
				
			||||||
        if qs:
 | 
					 | 
				
			||||||
            paginator = Paginator(qs, 30)
 | 
					 | 
				
			||||||
            try:
 | 
					 | 
				
			||||||
                qs = paginator.page(request.GET.get('page') or 1)
 | 
					 | 
				
			||||||
            except PageNotAnInteger:
 | 
					 | 
				
			||||||
                qs = paginator.page(1)
 | 
					 | 
				
			||||||
            except EmptyPage:
 | 
					 | 
				
			||||||
                qs = parginator.page(paginator.num_pages)
 | 
					 | 
				
			||||||
            set('paginator', paginator)
 | 
					 | 
				
			||||||
        set('object_list', qs)
 | 
					 | 
				
			||||||
        return qs
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DatedListBase(models.Model):
 | 
					class DatedBaseList(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.
 | 
				
			||||||
@ -794,70 +880,47 @@ class SectionLink(RelatedLinkBase,TemplateMixin):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_snippet
 | 
					@register_snippet
 | 
				
			||||||
class SectionList(ListBase, SectionRelativeItem):
 | 
					class SectionList(BaseList, SectionRelativeItem):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    This one is quite badass, but needed: render a list of pages
 | 
					    This one is quite badass, but needed: render a list of pages
 | 
				
			||||||
    using given parameters (cf. ListBase).
 | 
					    using given parameters (cf. BaseList).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    If focus_available, the first article in the list will be the last
 | 
					    If focus_available, the first article in the list will be the last
 | 
				
			||||||
    article with a focus, and will be rendered in a bigger size.
 | 
					    article with a focus, and will be rendered in a bigger size.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    focus_available = models.BooleanField(
 | 
					    # TODO/FIXME: focus, quid?
 | 
				
			||||||
        _('focus available'),
 | 
					    # TODO: logs in menu show headline???
 | 
				
			||||||
        default = False,
 | 
					 | 
				
			||||||
        help_text = _('if true, highlight the first focused article found')
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    count = models.SmallIntegerField(
 | 
					 | 
				
			||||||
        _('count'),
 | 
					 | 
				
			||||||
        default = 5,
 | 
					 | 
				
			||||||
        help_text = _('number of items to display in the list'),
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    url_text = models.CharField(
 | 
					    url_text = models.CharField(
 | 
				
			||||||
        _('text of the url'),
 | 
					        _('text of the url'),
 | 
				
			||||||
        max_length=32,
 | 
					        max_length=32,
 | 
				
			||||||
        blank = True, null = True,
 | 
					        blank = True, null = True,
 | 
				
			||||||
        help_text = _('use this text to display an URL to the complete '
 | 
					        help_text = _('use this text to display an URL to the complete '
 | 
				
			||||||
                      'list. If empty, does not print an address'),
 | 
					                      'list. If empty, no link is displayed'),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    panels = SectionRelativeItem.panels + [
 | 
					    panels = SectionRelativeItem.panels + [
 | 
				
			||||||
        MultiFieldPanel([
 | 
					 | 
				
			||||||
        FieldPanel('focus_available'),
 | 
					 | 
				
			||||||
        FieldPanel('count'),
 | 
					 | 
				
			||||||
        FieldPanel('url_text'),
 | 
					        FieldPanel('url_text'),
 | 
				
			||||||
        ], heading=_('Rendering')),
 | 
					    ] + BaseList.panels
 | 
				
			||||||
    ] + ListBase.panels
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_context(self, request, page):
 | 
					    def get_context(self, request, page):
 | 
				
			||||||
        from aircox_cms.models import Publication
 | 
					        import aircox_cms.models as cms
 | 
				
			||||||
        context = super().get_context(request, page)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if self.is_related:
 | 
					        if self.is_related:
 | 
				
			||||||
            self.related = page
 | 
					            self.related = page
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        qs = self.get_queryset()
 | 
					        context = BaseList.get_context(self, request, paginate = False)
 | 
				
			||||||
        qs = qs.live()
 | 
					        if not context['object_list'].count():
 | 
				
			||||||
        if self.focus_available:
 | 
					 | 
				
			||||||
            focus = qs.type(Publication).filter(focus = True).first()
 | 
					 | 
				
			||||||
            if focus:
 | 
					 | 
				
			||||||
                focus.css_class = 'focus'
 | 
					 | 
				
			||||||
                qs = qs.exclude(pk = focus.pk)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            focus = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if not qs.count():
 | 
					 | 
				
			||||||
            return { 'hide': True }
 | 
					            return { 'hide': True }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pages = qs[:self.count - (focus != None)]
 | 
					        context.update(SectionRelativeItem.get_context(self, request, page))
 | 
				
			||||||
 | 
					 | 
				
			||||||
        context['focus'] = focus
 | 
					 | 
				
			||||||
        context['object_list'] = pages
 | 
					 | 
				
			||||||
        if self.url_text:
 | 
					        if self.url_text:
 | 
				
			||||||
            context['url'] = self.to_url(
 | 
					            if not self.is_related or not page:
 | 
				
			||||||
                list_page = self.is_related and page
 | 
					                settings = cms.WebsiteSettings.for_site(request.site)
 | 
				
			||||||
            )
 | 
					                page = settings.list_page
 | 
				
			||||||
 | 
					            context['url'] = self.to_url(page = page) + '&view=list'
 | 
				
			||||||
        return context
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SectionList._meta.get_field('count').default = 5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_snippet
 | 
					@register_snippet
 | 
				
			||||||
class SectionLogsList(SectionItem):
 | 
					class SectionLogsList(SectionItem):
 | 
				
			||||||
@ -915,7 +978,7 @@ class SectionLogsList(SectionItem):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_snippet
 | 
					@register_snippet
 | 
				
			||||||
class SectionTimetable(SectionItem,DatedListBase):
 | 
					class SectionTimetable(SectionItem,DatedBaseList):
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        verbose_name = _('Section: Timetable')
 | 
					        verbose_name = _('Section: Timetable')
 | 
				
			||||||
        verbose_name_plural = _('Sections: Timetable')
 | 
					        verbose_name_plural = _('Sections: Timetable')
 | 
				
			||||||
@ -937,8 +1000,8 @@ class SectionTimetable(SectionItem,DatedListBase):
 | 
				
			|||||||
        help_text = _('if checked, navigation dates will be shown')
 | 
					        help_text = _('if checked, navigation dates will be shown')
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # TODO: put in multi-field panel of datedlistbase
 | 
					    # TODO: put in multi-field panel of DatedBaseList
 | 
				
			||||||
    panels = SectionItem.panels + DatedListBase.panels + [
 | 
					    panels = SectionItem.panels + DatedBaseList.panels + [
 | 
				
			||||||
        MultiFieldPanel([
 | 
					        MultiFieldPanel([
 | 
				
			||||||
            FieldPanel('nav_visible'),
 | 
					            FieldPanel('nav_visible'),
 | 
				
			||||||
            FieldPanel('target'),
 | 
					            FieldPanel('target'),
 | 
				
			||||||
@ -987,12 +1050,6 @@ class SectionSearchField(SectionItem):
 | 
				
			|||||||
        FieldPanel('default_text'),
 | 
					        FieldPanel('default_text'),
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_context(self, request, page):
 | 
					 | 
				
			||||||
        # FIXME ?????
 | 
					 | 
				
			||||||
        from aircox_cms.models import DynamicListPage
 | 
					 | 
				
			||||||
        context = super().get_context(request, page)
 | 
					 | 
				
			||||||
        return context
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_snippet
 | 
					@register_snippet
 | 
				
			||||||
class SectionPlayer(SectionItem):
 | 
					class SectionPlayer(SectionItem):
 | 
				
			||||||
 | 
				
			|||||||
@ -148,7 +148,7 @@ body section ul {
 | 
				
			|||||||
    width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ul.list {
 | 
					ul.list, .list > ul {
 | 
				
			||||||
    padding: 0.4em;
 | 
					    padding: 0.4em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -186,6 +186,21 @@ ul.list {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** content: list items in full page **/
 | 
				
			||||||
 | 
					.content > .list .list_item {
 | 
				
			||||||
 | 
					    min-width: 20em;
 | 
				
			||||||
 | 
					    display: inline-block;
 | 
				
			||||||
 | 
					    min-height: 2.5em;
 | 
				
			||||||
 | 
					    margin: 0.4em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.content > .list .dated_list_item time {
 | 
				
			||||||
 | 
					    color: #007EDF;
 | 
				
			||||||
 | 
					    display: block;
 | 
				
			||||||
 | 
					    margin-left: -0.5em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** content: date list **/
 | 
					/** content: date list **/
 | 
				
			||||||
.date_list nav {
 | 
					.date_list nav {
 | 
				
			||||||
    text-align:center;
 | 
					    text-align:center;
 | 
				
			||||||
 | 
				
			|||||||
@ -82,12 +82,15 @@ a:hover > .small_icon {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
main {
 | 
					main {
 | 
				
			||||||
    background-color: rgba(255,255,255,0.9);
 | 
					    background-color: rgba(255,255,255,0.9);
 | 
				
			||||||
    padding: 1em;
 | 
					 | 
				
			||||||
    margin: 0em 2em;
 | 
					    margin: 0em 2em;
 | 
				
			||||||
    box-shadow: 0em 0em 0.2em black;
 | 
					    box-shadow: 0em 0em 0.2em black;
 | 
				
			||||||
    width: 60%;
 | 
					    width: 60%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    main > .content {
 | 
				
			||||||
 | 
					        /*! margin: 1em; */
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    main:not(.detail) h1 {
 | 
					    main:not(.detail) h1 {
 | 
				
			||||||
        margin: 0em 0em 0.4em 0em;
 | 
					        margin: 0em 0em 0.4em 0em;
 | 
				
			||||||
@ -109,68 +112,65 @@ main.detail {
 | 
				
			|||||||
    padding: 0em;
 | 
					    padding: 0em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    main.detail > .content {
 | 
					    main > .content {
 | 
				
			||||||
        padding: 1em;
 | 
					        padding: 1em;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    main.detail > header {
 | 
					    main > header {
 | 
				
			||||||
        padding: 0em;
 | 
					 | 
				
			||||||
        margin: 0em;
 | 
					        margin: 0em;
 | 
				
			||||||
    }
 | 
					        padding: 1em;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    main.detail > header h1.title,
 | 
					 | 
				
			||||||
    main.detail > header .headline {
 | 
					 | 
				
			||||||
        display: block;
 | 
					 | 
				
			||||||
        padding: 0.4em;
 | 
					 | 
				
			||||||
        vertical-align: middle;
 | 
					 | 
				
			||||||
        transition: opacity 1.5s;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    main.detail > header:hover h1.title,
 | 
					 | 
				
			||||||
    main.detail > header:hover .headline {
 | 
					 | 
				
			||||||
        opacity: 0.0;
 | 
					 | 
				
			||||||
        transition: opacity 1.5s 1s;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    main.detail > header h1.title {
 | 
					 | 
				
			||||||
        position: relative;
 | 
					        position: relative;
 | 
				
			||||||
        z-index: 1000;
 | 
					    }
 | 
				
			||||||
        height: 1.2em;
 | 
					
 | 
				
			||||||
 | 
					    main > header .foreground {
 | 
				
			||||||
 | 
					        position: absolute;
 | 
				
			||||||
 | 
					        left: 0em;
 | 
				
			||||||
 | 
					        top: 0em;
 | 
				
			||||||
 | 
					        width: calc(100% - 2em);
 | 
				
			||||||
 | 
					        padding: 1em;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    main > header h1 {
 | 
				
			||||||
 | 
					        width: calc(100% - 2em);
 | 
				
			||||||
        margin: 0em;
 | 
					        margin: 0em;
 | 
				
			||||||
        background-color: rgba(255,255,255,0.8);
 | 
					        margin-bottom: 0.8em;
 | 
				
			||||||
        /*! padding-top: 0em; */
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    main.detail > header h1.title + section {
 | 
					    main header .headline {
 | 
				
			||||||
        margin-top: 2em;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    main.detail header .headline {
 | 
					 | 
				
			||||||
        display: inline-block;
 | 
					        display: inline-block;
 | 
				
			||||||
        width: calc(100% - 0.8em);
 | 
					        width: calc(60% - 0.8em);
 | 
				
			||||||
        min-height: 1.2em;
 | 
					        min-height: 1.2em;
 | 
				
			||||||
        font-size: 1.2em;
 | 
					        font-size: 1.2em;
 | 
				
			||||||
        font-weight: bold;
 | 
					        font-weight: bold;
 | 
				
			||||||
        background-color: rgba(255,255,255,0.8);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    main.detail > header .cover_container,
 | 
					    main > header .background {
 | 
				
			||||||
    main.detail > header img.cover {
 | 
					        margin: -1em;
 | 
				
			||||||
       display: block;
 | 
					        height: 17em;
 | 
				
			||||||
       width: 100%;
 | 
					        overflow: hidden;
 | 
				
			||||||
 | 
					        position: relative;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    main.detail > header .cover_container {
 | 
					    main > header .background img {
 | 
				
			||||||
       max-height: 450px;
 | 
					        position: absolute;
 | 
				
			||||||
       overflow: hidden;
 | 
					        top: -40%;
 | 
				
			||||||
       margin-top: -2.8em;
 | 
					        left: -40%;
 | 
				
			||||||
       margin-bottom: -2.4em;
 | 
					        width: 250%;
 | 
				
			||||||
 | 
					        min-height: 250%;
 | 
				
			||||||
 | 
					        filter: blur(20px);
 | 
				
			||||||
 | 
					        opacity: 0.3;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    main.detail > header img.cover {
 | 
					    main > header .cover {
 | 
				
			||||||
        height: auto;
 | 
					        right: 0em;
 | 
				
			||||||
        margin: auto;
 | 
					        bottom: 0em;
 | 
				
			||||||
        vertical-align: middle;
 | 
					        width: auto;
 | 
				
			||||||
 | 
					        max-height: calc(100% - 4em);
 | 
				
			||||||
 | 
					        max-width: 40%;
 | 
				
			||||||
 | 
					        margin: 1em;
 | 
				
			||||||
 | 
					        position: absolute;
 | 
				
			||||||
 | 
					        box-shadow: 0em 0em 4em rgba(0, 0, 0, 0.3);
 | 
				
			||||||
 | 
					        border: 1em rgba(255, 255, 255, 0.1) solid;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -5296,7 +5296,7 @@ body.explorer-open .explorer-close {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  li.submenu-active .nav-submenu {
 | 
					  li.submenu-active .nav-submenu {
 | 
				
			||||||
    box-shadow: 2px 0 2px rgba(0, 0, 0, 0.35);
 | 
					    box-shadow: 2px 0 2px rgba(0, 0, 0, 0.35);
 | 
				
			||||||
    width: 220px;
 | 
					    width: 230px;
 | 
				
			||||||
    padding: 0 0 0.5em;
 | 
					    padding: 0 0 0.5em;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  body.ready li.submenu-active .nav-submenu {
 | 
					  body.ready li.submenu-active .nav-submenu {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
{% load staticfiles %}
 | 
					{% load staticfiles %}
 | 
				
			||||||
{% load i18n %}
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% load wagtailcore_tags %}
 | 
				
			||||||
{% load wagtailimages_tags %}
 | 
					{% load wagtailimages_tags %}
 | 
				
			||||||
{% load wagtailsettings_tags %}
 | 
					{% load wagtailsettings_tags %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -60,12 +61,47 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            <header>
 | 
					            <header>
 | 
				
			||||||
            {% block title %}
 | 
					            {% block title %}
 | 
				
			||||||
            <h1>{{ page.title }}</h1>
 | 
					                {% if page.cover %}
 | 
				
			||||||
 | 
					                <div class="background">
 | 
				
			||||||
 | 
					                {% image page.cover max-600x480 class="background-cover" height="" width="" %}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                {% image page.cover max-600x480 class="cover" height="" width="" %}
 | 
				
			||||||
 | 
					                <div class="foreground">
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
 | 
					                    <h1 class="title"><a href="{{ page.url }}">{{ page.title }}</a></h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    {% if page.headline %}
 | 
				
			||||||
 | 
					                    <section class="headline">
 | 
				
			||||||
 | 
					                    {{ page.headline }}
 | 
				
			||||||
 | 
					                    </section>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                {% if page.cover %}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
            {% endblock %}
 | 
					            {% endblock %}
 | 
				
			||||||
            </header>
 | 
					            </header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            {% block content %}
 | 
					            <div class="content">
 | 
				
			||||||
            {% endblock %}
 | 
					                {% block content %}
 | 
				
			||||||
 | 
					                {% if page.body %}
 | 
				
			||||||
 | 
					                <section class="body">
 | 
				
			||||||
 | 
					                {{ page.body|richtext}}
 | 
				
			||||||
 | 
					                </section>
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
 | 
					                {% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                {% block content_extras %}
 | 
				
			||||||
 | 
					                {% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <div class="post_content">
 | 
				
			||||||
 | 
					                {% render_sections position="post_content" %}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <section class="comments">
 | 
				
			||||||
 | 
					                {% include "aircox_cms/snippets/comments.html" %}
 | 
				
			||||||
 | 
					                </section>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            </main>
 | 
					            </main>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <nav class="menu page_right flex_item">
 | 
					            <nav class="menu page_right flex_item">
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										3
									
								
								aircox_cms/templates/aircox_cms/category_page.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								aircox_cms/templates/aircox_cms/category_page.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					{% extends "aircox_cms/publication.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -6,40 +6,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
{% load aircox_cms %}
 | 
					{% load aircox_cms %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% if not object_list %}
 | 
					 | 
				
			||||||
{% block title %}
 | 
					 | 
				
			||||||
<h1 class="title">{{ page.title }}</h1>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% if page.cover %}
 | 
					 | 
				
			||||||
<span class="cover_container">
 | 
					 | 
				
			||||||
{% image page.cover max-600x480 class="cover" height="" width="" %}
 | 
					 | 
				
			||||||
</span>
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<section class="headline">
 | 
					 | 
				
			||||||
    {% if page.headline %}
 | 
					 | 
				
			||||||
        {{ page.headline }}
 | 
					 | 
				
			||||||
    {% else %}
 | 
					 | 
				
			||||||
        {{ page.body|truncatewords:24|richtext }}
 | 
					 | 
				
			||||||
    {% endif %}
 | 
					 | 
				
			||||||
</section>
 | 
					 | 
				
			||||||
{% endblock %}
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
{% if object_list %}
 | 
					 | 
				
			||||||
{# list view #}
 | 
					 | 
				
			||||||
    <section class="body headline">
 | 
					 | 
				
			||||||
    {{ page.headline }}
 | 
					 | 
				
			||||||
    <a href="?" class="go_back float_right">{% trans "Go back to the publication" %}</a>
 | 
					 | 
				
			||||||
    </section>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    {% with list_paginator=paginator %}
 | 
					 | 
				
			||||||
    {% include "aircox_cms/snippets/list.html" %}
 | 
					 | 
				
			||||||
    {% endwith %}
 | 
					 | 
				
			||||||
{% else %}
 | 
					 | 
				
			||||||
{# detail view #}
 | 
					 | 
				
			||||||
<div class="content">
 | 
					<div class="content">
 | 
				
			||||||
    {% if page.body %}
 | 
					    {% if page.body %}
 | 
				
			||||||
    <section class="body">
 | 
					    <section class="body">
 | 
				
			||||||
@ -47,21 +15,18 @@
 | 
				
			|||||||
    </section>
 | 
					    </section>
 | 
				
			||||||
    {% endif %}
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {% if page.links.count %}
 | 
					    {% if object_list %}
 | 
				
			||||||
    {% include "aircox_cms/sections/section_link_list.html" %}
 | 
					    {# list view #}
 | 
				
			||||||
 | 
					        {% with list_paginator=paginator %}
 | 
				
			||||||
 | 
					        {% include "aircox_cms/snippets/list.html" %}
 | 
				
			||||||
 | 
					        {% endwith %}
 | 
				
			||||||
 | 
					    {% else %}
 | 
				
			||||||
 | 
					    {# detail view #}
 | 
				
			||||||
 | 
					        {% if page.links.count %}
 | 
				
			||||||
 | 
					        {% include "aircox_cms/sections/section_link_list.html" %}
 | 
				
			||||||
 | 
					        {% endif %}
 | 
				
			||||||
    {% endif %}
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					 | 
				
			||||||
    {% block content_extras %}{% endblock %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <div class="post_content">
 | 
					 | 
				
			||||||
    {% render_sections position="post_content" %}
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <section class="comments">
 | 
					 | 
				
			||||||
    {% include "aircox_cms/snippets/comments.html" %}
 | 
					 | 
				
			||||||
    </section>
 | 
					 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,9 @@
 | 
				
			|||||||
{% extends "aircox_cms/sections/section_item.html" %}
 | 
					{% extends "aircox_cms/sections/section_item.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
{% if focus %}
 | 
					 | 
				
			||||||
{% with item=focus item_big_cover=True %}
 | 
					 | 
				
			||||||
{% include "aircox_cms/snippets/list_item.html" %}
 | 
					 | 
				
			||||||
{% endwith %}
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% with url=url url_text=self.url_text %}
 | 
					{% with url=url url_text=self.url_text %}
 | 
				
			||||||
{% include "aircox_cms/snippets/list.html" %}
 | 
					{% include "aircox_cms/snippets/list.html" %}
 | 
				
			||||||
{% endwith %}
 | 
					{% endwith %}
 | 
				
			||||||
 | 
					 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
{% extends "aircox_cms/sections/section_item.html" %}
 | 
					{% extends "aircox_cms/sections/section_item.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
{% with item_date_format="H:i" list_css_class="date_list" list_no_cover=True %}
 | 
					{% with item_date_format="H:i" list_css_class="date_list" list_no_cover=True list_no_headline=True %}
 | 
				
			||||||
{% for item in object_list %}
 | 
					{% for item in object_list %}
 | 
				
			||||||
{% include "aircox_cms/snippets/date_list_item.html" %}
 | 
					{% include "aircox_cms/snippets/date_list_item.html" %}
 | 
				
			||||||
{% endfor %}
 | 
					{% endfor %}
 | 
				
			||||||
 | 
				
			|||||||
@ -31,7 +31,9 @@ is just a bit different.
 | 
				
			|||||||
    <div class="flex_item">
 | 
					    <div class="flex_item">
 | 
				
			||||||
        <h3 class="title">{{ item.title }}</h3>
 | 
					        <h3 class="title">{{ item.title }}</h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        {% if item.headline %}<div class="headline">{{ item.headline }}</div>{% endif %}
 | 
					        {% if not list_no_headline and item.headline %}
 | 
				
			||||||
 | 
					        <div class="headline">{{ item.headline }}</div>
 | 
				
			||||||
 | 
					        {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        {% if item.info %}
 | 
					        {% if item.info %}
 | 
				
			||||||
        <span class="info">{{ item.info|safe }}</span>
 | 
					        <span class="info">{{ item.info|safe }}</span>
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,11 @@ Options:
 | 
				
			|||||||
{% load i18n %}
 | 
					{% load i18n %}
 | 
				
			||||||
{% load aircox_cms %}
 | 
					{% load aircox_cms %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% if focus %}
 | 
				
			||||||
 | 
					{% with item=focus item_big_cover=True %}
 | 
				
			||||||
 | 
					{% include "aircox_cms/snippets/list_item.html" %}
 | 
				
			||||||
 | 
					{% endwith %}
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<ul class="list {{ list_css_class|default:'' }}">
 | 
					<ul class="list {{ list_css_class|default:'' }}">
 | 
				
			||||||
{% for page in object_list %}
 | 
					{% for page in object_list %}
 | 
				
			||||||
@ -20,13 +25,13 @@ Options:
 | 
				
			|||||||
<nav>
 | 
					<nav>
 | 
				
			||||||
{% with list_paginator.num_pages as num_pages %}
 | 
					{% with list_paginator.num_pages as num_pages %}
 | 
				
			||||||
    {% if object_list.has_previous %}
 | 
					    {% if object_list.has_previous %}
 | 
				
			||||||
        <a href="?page={{ object_list.previous_page_number }}">
 | 
					    <a href="?{{ list_url_args }}&page={{ object_list.previous_page_number }}">
 | 
				
			||||||
            {% trans "previous page" %}
 | 
					            {% trans "previous page" %}
 | 
				
			||||||
        </a>
 | 
					        </a>
 | 
				
			||||||
    {% endif %}
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {% if object_list.number > 3 %}
 | 
					    {% if object_list.number > 3 %}
 | 
				
			||||||
        <a href="?page=1">1</a>
 | 
					        <a href="?{{ list_url_args }}&page=1">1</a>
 | 
				
			||||||
        {% if object_list.number > 4 %}
 | 
					        {% if object_list.number > 4 %}
 | 
				
			||||||
        …
 | 
					        …
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
@ -36,7 +41,7 @@ Options:
 | 
				
			|||||||
        {% if i == object_list.number %}
 | 
					        {% if i == object_list.number %}
 | 
				
			||||||
            {{ object_list.number }}
 | 
					            {{ object_list.number }}
 | 
				
			||||||
        {% elif i > 0 and i <= num_pages %}
 | 
					        {% elif i > 0 and i <= num_pages %}
 | 
				
			||||||
            <a href="?page={{ i }}">{{ i }}</a>
 | 
					            <a href="?{{ list_url_args }}&page={{ i }}">{{ i }}</a>
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
    {% endfor %}
 | 
					    {% endfor %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -45,7 +50,7 @@ Options:
 | 
				
			|||||||
        {% if max|add:"1" < num_pages %}
 | 
					        {% if max|add:"1" < num_pages %}
 | 
				
			||||||
        …
 | 
					        …
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
        <a href="?page={{ num_pages }}">{{ num_pages }}</a>
 | 
					        <a href="?{{ list_url_args }}&page={{ num_pages }}">{{ num_pages }}</a>
 | 
				
			||||||
    {% endif %}
 | 
					    {% endif %}
 | 
				
			||||||
    {% endwith %}
 | 
					    {% endwith %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,32 +1,32 @@
 | 
				
			|||||||
from django.shortcuts import render
 | 
					#from django.shortcuts import render
 | 
				
			||||||
import django.views.generic as generic
 | 
					#import django.views.generic as generic
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
import foxcms.views as Views
 | 
					#import foxcms.views as Views
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
import aircox.sections as sections
 | 
					#import aircox.sections as sections
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
class DynamicListView(Views.View, generic.list.ListView):
 | 
					#class DynamicListView(Views.View, generic.list.ListView):
 | 
				
			||||||
    list_info = None
 | 
					#    list_info = None
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
    def get_queryset(self):
 | 
					#    def get_queryset(self):
 | 
				
			||||||
        self.list_info = {}
 | 
					#        self.list_info = {}
 | 
				
			||||||
        return sections.ListBase.from_request(request, context = self.list_info)
 | 
					#        return sections.ListBase.from_request(request, context = self.list_info)
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
    #def get_ordering(self):
 | 
					#    #def get_ordering(self):
 | 
				
			||||||
    #    order = self.request.GET.get('order_by')
 | 
					#    #    order = self.request.GET.get('order_by')
 | 
				
			||||||
    #    if order:
 | 
					#    #    if order:
 | 
				
			||||||
    #        field = order[1:] if order['-'] else order
 | 
					#    #        field = order[1:] if order['-'] else order
 | 
				
			||||||
    #    else:
 | 
					#    #    else:
 | 
				
			||||||
    #        field = 'pk'
 | 
					#    #        field = 'pk'
 | 
				
			||||||
    #    if field not in self.model.ordering_fields:
 | 
					#    #    if field not in self.model.ordering_fields:
 | 
				
			||||||
    #        return super().get_ordering()
 | 
					#    #        return super().get_ordering()
 | 
				
			||||||
    # TODO replace 'asc' in ListBase into sorting field
 | 
					#    # TODO replace 'asc' in ListBase into sorting field
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
    def get_context_data(self, *args, **kwargs
 | 
					#    def get_context_data(self, *args, **kwargs
 | 
				
			||||||
        context = super().get_context_data(*args, **kwargs)
 | 
					#        context = super().get_context_data(*args, **kwargs)
 | 
				
			||||||
        if self.list_info:
 | 
					#        if self.list_info:
 | 
				
			||||||
            context.update(self.list_info)
 | 
					#            context.update(self.list_info)
 | 
				
			||||||
        return context
 | 
					#        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										5
									
								
								notes.md
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								notes.md
									
									
									
									
									
								
							@ -16,6 +16,11 @@ This file is used as a reminder, can be used as crappy documentation too.
 | 
				
			|||||||
    \--> move liquidsoap control on commands/streamer.py
 | 
					    \--> move liquidsoap control on commands/streamer.py
 | 
				
			||||||
- cms:
 | 
					- cms:
 | 
				
			||||||
    x button to select the current station
 | 
					    x button to select the current station
 | 
				
			||||||
 | 
					    - section's title: format with current page info e.g. "The diffusions of {program.name}" -- perhaps use pass **context
 | 
				
			||||||
 | 
					    - section exclude: exclude a section for a given page type
 | 
				
			||||||
 | 
					    - category page
 | 
				
			||||||
 | 
					    - for timetable, logs, etc. make station optional
 | 
				
			||||||
 | 
					    - django's message css displayed on pages when element is modified (remove bullet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# conventions
 | 
					# conventions
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user