static pages

This commit is contained in:
bkfox 2020-05-26 16:51:09 +02:00
parent a59c4a3d5c
commit c4c1af2f2d
20 changed files with 145 additions and 124 deletions

View File

@ -91,16 +91,12 @@ class PageAdmin(BasePageAdmin):
@admin.register(StaticPage) @admin.register(StaticPage)
class StaticPageAdmin(BasePageAdmin): class StaticPageAdmin(BasePageAdmin):
list_display = BasePageAdmin.list_display + ('view','menu_title') list_display = BasePageAdmin.list_display + ('attach_to',)
list_editable = BasePageAdmin.list_editable + ('menu_title',)
fieldsets = deepcopy(BasePageAdmin.fieldsets) fieldsets = deepcopy(BasePageAdmin.fieldsets)
fieldsets[0][1]['fields'].insert(fieldsets[0][1]['fields'].index('slug') + 1, 'menu_title') fieldsets[1][1]['fields'] += ('attach_to',)
fieldsets[1][1]['fields'] += ('view',)
class NavItemInline(SortableInlineAdminMixin, admin.TabularInline): class NavItemInline(SortableInlineAdminMixin, admin.TabularInline):
model = NavItem model = NavItem

View File

@ -152,6 +152,10 @@ class Page(BasePage):
_('allow comments'), default=True, _('allow comments'), default=True,
) )
class Meta:
verbose_name = _('Publication')
verbose_name_plural = _('Publications')
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if self.is_published and self.pub_date is None: if self.is_published and self.pub_date is None:
self.pub_date = tz.now() self.pub_date = tz.now()
@ -160,43 +164,30 @@ class Page(BasePage):
super().save(*args, **kwargs) super().save(*args, **kwargs)
class StaticPage(Page): class StaticPage(BasePage):
""" Static page that eventually can be attached to a specific view. """ """ Static page that eventually can be attached to a specific view. """
VIEW_HOME = 0x00 detail_url_name = 'static-page-detail'
VIEW_SCHEDULE = 0x01
VIEW_LOG = 0x02
VIEW_PROGRAMS = 0x03
VIEW_EPISODES = 0x04
VIEW_ARTICLES = 0x05
VIEW_CHOICES = ( ATTACH_TO_HOME = 0x00
(VIEW_HOME, _('Home Page')), ATTACH_TO_DIFFUSIONS = 0x01
(VIEW_SCHEDULE, _('Schedule Page')), ATTACH_TO_LOGS = 0x02
(VIEW_LOG, _('Log Page')), ATTACH_TO_PROGRAMS = 0x03
(VIEW_PROGRAMS, _('Programs list')), ATTACH_TO_EPISODES = 0x04
(VIEW_EPISODES, _('Episodes list')), ATTACH_TO_ARTICLES = 0x05
(VIEW_ARTICLES, _('Articles list')),
ATTACH_TO_CHOICES = (
(ATTACH_TO_HOME, _('Home page')),
(ATTACH_TO_DIFFUSIONS, _('Diffusions page')),
(ATTACH_TO_LOGS, _('Logs page')),
(ATTACH_TO_PROGRAMS, _('Programs list')),
(ATTACH_TO_EPISODES, _('Episodes list')),
(ATTACH_TO_ARTICLES, _('Articles list')),
) )
view = models.SmallIntegerField( attach_to = models.SmallIntegerField(
_('attach to'), choices=VIEW_CHOICES, blank=True, null=True, _('attach to'), choices=ATTACH_TO_CHOICES, blank=True, null=True,
help_text=_('display this page content to related element'), help_text=_('display this page content to related element'),
) )
menu_title = models.CharField(_('menu title'), max_length=64, blank=True, null=True)
is_active = False
def render_menu_item(self, request, css_class='', active_class=''):
if active_class and request.path.startswith(self.url):
css_class += ' ' + active_class
if not self.url:
return self.text
elif not css_class:
return format_html('<a href="{}">{}</a>', self.url, self.text)
else:
return format_html('<a href="{}" class="{}">{}</a>', self.url,
css_class, self.text)
class Comment(models.Model): class Comment(models.Model):
@ -218,6 +209,9 @@ class NavItem(models.Model):
order = models.PositiveSmallIntegerField(_('order')) order = models.PositiveSmallIntegerField(_('order'))
text = models.CharField(_('title'), max_length=64) text = models.CharField(_('title'), max_length=64)
url = models.CharField(_('url'), max_length=256, blank=True, null=True) url = models.CharField(_('url'), max_length=256, blank=True, null=True)
page = models.ForeignKey(StaticPage, models.CASCADE,
verbose_name=_('page'), blank=True, null=True,
limit_choices_to={'attach_to__isnull': True})
#target_type = models.ForeignKey( #target_type = models.ForeignKey(
# ContentType, models.CASCADE, blank=True, null=True) # ContentType, models.CASCADE, blank=True, null=True)
#target_id = models.PositiveSmallIntegerField(blank=True, null=True) #target_id = models.PositiveSmallIntegerField(blank=True, null=True)
@ -227,12 +221,6 @@ class NavItem(models.Model):
verbose_name = _('Menu item') verbose_name = _('Menu item')
ordering = ('order', 'pk') ordering = ('order', 'pk')
is_active = False
def get_is_active(self, url):
""" Return True if navigation item is active for this url. """
return self.url and url.startswith(self.url)
def render(self, request, css_class='', active_class=''): def render(self, request, css_class='', active_class=''):
if active_class and request.path.startswith(self.url): if active_class and request.path.startswith(self.url):
css_class += ' ' + active_class css_class += ' ' + active_class

View File

@ -1,3 +0,0 @@
{% extends "aircox/page_list.html" %}
{% comment %}List of articles{% endcomment %}

View File

@ -45,7 +45,11 @@ Blocks:
{% endblock %} {% endblock %}
<title> <title>
{% block head_title %}{{ station.name }}{% endblock %} {% block head_title %}
{% if page and page.title %}{{ page.title }} &mdash; {{ station.name }}
{% else %}{{ station.name }}
{% endif %}
{% endblock %}
</title> </title>
{% block head_extra %}{% endblock %} {% block head_extra %}{% endblock %}
@ -77,7 +81,13 @@ Blocks:
<main class="column page"> <main class="column page">
<header class="header"> <header class="header">
{% block header %} {% block header %}
<h1 class="title is-1">{% block title %}{% endblock %}</h1> <h1 class="title is-1">
{% block title %}
{% if page and page.title %}
{{ page.title }}
{% endif %}
{% endblock %}
</h1>
<h3 class="subtitle is-3"> <h3 class="subtitle is-3">
{% block subtitle %}{% endblock %} {% block subtitle %}{% endblock %}
@ -96,6 +106,12 @@ Blocks:
</header> </header>
{% block main %} {% block main %}
{% block content %}
{% if page and page.content %}
{{ page.content|safe }}
{% endif %}
{% endblock %}
{% if has_filters %} {% if has_filters %}
{% comment %}Translators: extra toolbar displayed on the top of page lists {% endcomment %} {% comment %}Translators: extra toolbar displayed on the top of page lists {% endcomment %}
<nav class="navbar toolbar" <nav class="navbar toolbar"
@ -103,6 +119,8 @@ Blocks:
{% block filters %}{% endblock %} {% block filters %}{% endblock %}
</nav> </nav>
{% endif %} {% endif %}
{% endblock main %} {% endblock main %}
</main> </main>
@ -111,8 +129,8 @@ Blocks:
<aside class="column is-one-third-desktop"> <aside class="column is-one-third-desktop">
{# FIXME: block cover into sidebar one #} {# FIXME: block cover into sidebar one #}
{% block cover %} {% block cover %}
{% if cover is not None %} {% if page and page.cover %}
<img class="cover" src="{{ cover.url }}" class="cover"/> <img class="cover" src="{{ page.cover.url }}" class="cover"/>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -3,9 +3,13 @@
{% load i18n aircox humanize %} {% load i18n aircox humanize %}
{% block title %} {% block title %}
{% if not page or not page.title %}
{% with station.name as station %} {% with station.name as station %}
{% blocktrans %}Today on {{ station }}{% endblocktrans %} {% blocktrans %}This week on {{ station }}{% endblocktrans %}
{% endwith %} {% endwith %}
{% else %}
{{ block.super }}
{% endif %}
{% endblock %} {% endblock %}
{% block subtitle %}{{ date|date:"l d F Y" }}{% endblock %} {% block subtitle %}{{ date|date:"l d F Y" }}{% endblock %}

View File

@ -1,3 +0,0 @@
{% extends "aircox/page_list.html" %}
{% comment %}List of episodes pages{% endcomment %}

View File

@ -1,17 +1,17 @@
{% extends "aircox/page_list.html" %} {% extends "aircox/page_list.html" %}
{% comment %}
Context:
-
- main:
- focused
- nav to 'publications' view
{% endcomment %}
{% load i18n %} {% load i18n %}
{% block head_title %}{% block title %}{{ station.name }}{% endblock %}{% endblock %} {% block head_title %}{{ station.name }}{% endblock %}
{% block main %} {% block title %}
{% if not page or not page.title %}{{ station.name }}
{% else %}{{ block.super }}
{% endif %}
{% endblock %}
{% block pages_list %}
{% if page and page.content %}<hr/>{% endif %}
<div class="columns"> <div class="columns">
{% with render_card=True %} {% with render_card=True %}
{% for object in top_diffs %} {% for object in top_diffs %}

View File

@ -3,9 +3,13 @@
{% load i18n humanize aircox %} {% load i18n humanize aircox %}
{% block title %} {% block title %}
{% if not page or not page.title %}
{% with station.name as station %} {% with station.name as station %}
{% blocktrans %}That happened on {{ station }}{% endblocktrans %} {% blocktrans %}That happened on {{ station }}{% endblocktrans %}
{% endwith %} {% endwith %}
{% else %}
{{ block.super }}
{% endif %}
{% endblock %} {% endblock %}

View File

@ -1,20 +1,12 @@
{% extends "aircox/base.html" %} {% extends "aircox/base.html" %}
{% load static i18n thumbnail %}
{% comment %} {% comment %}
Base template to display pages (list, detail, whatever). By default extend to This template is only used in order to have correct page <title></title>,
this one instead of "base.html" otherwise use base.html
Context:
- cover: cover image
- title: title
- page: page
{% endcomment %} {% endcomment %}
{% block head_title %} {% block head_title %}
{% comment %}Hack to include the page title into the <title> tag.{% endcomment %} {% block title %}{{ page.title }}{% endblock %}
{% block title %}{{ title }}{% endblock %}
&mdash; &mdash;
{{ station.name }} {{ station.name }}
{% endblock %} {% endblock %}

View File

@ -18,9 +18,7 @@ Context:
{% endblock %} {% endblock %}
{% block main %} {% block main %}
{% block content %} {{ block.super }}
{{ object.content|default_if_none:''|safe }}
{% endblock %}
{% block comments %} {% block comments %}
{% if comments or comment_form %} {% if comments or comment_form %}

View File

@ -3,6 +3,7 @@
{% load i18n aircox %} {% load i18n aircox %}
{% block title %} {% block title %}
{% if not page or not page.title %}
{% if not parent %}{{ view.model|verbose_name:True|title }} {% if not parent %}{{ view.model|verbose_name:True|title }}
{% else %} {% else %}
{% with parent.title as title %} {% with parent.title as title %}
@ -12,6 +13,8 @@
{% endwith %} {% endwith %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{% else %}{{ block.super }}
{% endif %}
{% endblock %} {% endblock %}
{% block filters %} {% block filters %}
@ -61,14 +64,19 @@
{% block main %}{{ block.super }} {% block main %}{{ block.super }}
<section role="list"> <section role="list">
{% block pages_list %}
{% with has_headline=True %} {% with has_headline=True %}
{% for object in object_list %} {% for object in object_list %}
{% block list_object %} {% block list_object %}
{% include object.item_template_name|default:item_template_name %} {% include object.item_template_name|default:item_template_name %}
{% endblock %} {% endblock %}
{% empty %}
{% blocktrans %}There is nothing published here...{% endblocktrans %}
{% endfor %} {% endfor %}
{% endwith %} {% endwith %}
{% endblock %}
</section> </section>
{% if is_paginated %} {% if is_paginated %}

View File

@ -60,13 +60,20 @@ urls = [
path(_('publications/'), path(_('publications/'),
views.PageListView.as_view(model=models.Page), name='page-list'), views.PageListView.as_view(model=models.Page), name='page-list'),
path(_('pages/'), path(_('pages/'), views.PageListView.as_view(
views.PageListView.as_view(model=models.StaticPage), name='static-page-list'), model=models.StaticPage,
queryset=models.StaticPage.objects.filter(attach_to__isnull=True),
),
name='static-page-list'
),
path(_('pages/<slug:slug>/'), views.PageDetailView.as_view(
model=models.StaticPage,
queryset=models.StaticPage.objects.filter(attach_to__isnull=True),
),
name='static-page-detail'
),
path(_('pages/<slug:slug>/'), path(_('programs/'), views.ProgramListView.as_view(),
views.PageDetailView.as_view(), name='page-detail'),
path(_('programs/'), views.ProgramListView.as_view(model=models.Program),
name='program-list'), name='program-list'),
path(_('programs/<slug:slug>/'), path(_('programs/<slug:slug>/'),
views.ProgramDetailView.as_view(), name='program-detail'), views.ProgramDetailView.as_view(), name='program-detail'),

View File

@ -1,5 +1,4 @@
from ..models import Article, Program from ..models import Article, Program, StaticPage
from .mixins import ParentMixin
from .page import PageDetailView, PageListView from .page import PageDetailView, PageListView
@ -17,12 +16,12 @@ class ArticleDetailView(PageDetailView):
return qs return qs
class ArticleListView(ParentMixin, PageListView): class ArticleListView(PageListView):
model = Article model = Article
template_name = 'aircox/article_list.html'
has_headline = True has_headline = True
is_static = False is_static = False
parent_model = Program parent_model = Program
attach_to_value = StaticPage.ATTACH_TO_ARTICLES
def get_queryset(self): def get_queryset(self):
return super().get_queryset().filter(is_static=self.is_static) return super().get_queryset().filter(is_static=self.is_static)

View File

@ -12,11 +12,6 @@ __all__ = ['BaseView']
class BaseView(TemplateResponseMixin, ContextMixin): class BaseView(TemplateResponseMixin, ContextMixin):
title = None
""" Page title """
cover = None
""" Page cover """
has_sidebar = True has_sidebar = True
""" Show side navigation """ """ Show side navigation """
has_filters = False has_filters = False
@ -39,9 +34,12 @@ class BaseView(TemplateResponseMixin, ContextMixin):
def get_sidebar_url(self): def get_sidebar_url(self):
return reverse('page-list') return reverse('page-list')
def get_page(self):
return None
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs.setdefault('station', self.station) kwargs.setdefault('station', self.station)
kwargs.setdefault('cover', self.cover) kwargs.setdefault('page', self.get_page())
kwargs.setdefault('has_filters', self.has_filters) kwargs.setdefault('has_filters', self.has_filters)
has_sidebar = kwargs.setdefault('has_sidebar', self.has_sidebar) has_sidebar = kwargs.setdefault('has_sidebar', self.has_sidebar)
@ -61,7 +59,6 @@ class BaseView(TemplateResponseMixin, ContextMixin):
type(self.object) type(self.object)
kwargs['model'] = model kwargs['model'] = model
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)

View File

@ -4,11 +4,11 @@ import datetime
from django.views.generic import ListView from django.views.generic import ListView
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from ..models import Diffusion, Episode, Program, Sound from ..models import Diffusion, Episode, Program, StaticPage, Sound
from .base import BaseView from .base import BaseView
from .program import ProgramPageDetailView from .program import ProgramPageDetailView
from .page import PageListView from .page import PageListView
from .mixins import GetDateMixin, ParentMixin from .mixins import AttachedToMixin, GetDateMixin, ParentMixin
__all__ = ['EpisodeDetailView', 'EpisodeListView', 'DiffusionListView'] __all__ = ['EpisodeDetailView', 'EpisodeListView', 'DiffusionListView']
@ -28,18 +28,20 @@ class EpisodeDetailView(ProgramPageDetailView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class EpisodeListView(ParentMixin, PageListView): class EpisodeListView(PageListView):
model = Episode model = Episode
item_template_name = 'aircox/widgets/episode_item.html' item_template_name = 'aircox/widgets/episode_item.html'
has_headline = True has_headline = True
parent_model = Program parent_model = Program
attach_to_value = StaticPage.ATTACH_TO_EPISODES
class DiffusionListView(GetDateMixin, BaseView, ListView): class DiffusionListView(GetDateMixin, AttachedToMixin, BaseView, ListView):
""" View for timetables """ """ View for timetables """
model = Diffusion model = Diffusion
has_filters = True has_filters = True
redirect_date_url = 'diffusion-list' redirect_date_url = 'diffusion-list'
attach_to_value = StaticPage.ATTACH_TO_DIFFUSIONS
def get_date(self): def get_date(self):
date = super().get_date() date = super().get_date()

View File

@ -3,9 +3,10 @@ import datetime
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.utils import timezone as tz from django.utils import timezone as tz
from ..models import Diffusion, Log, Page from ..models import Diffusion, Log, Page, StaticPage
from .page import PageListView from .page import PageListView
class HomeView(PageListView): class HomeView(PageListView):
template_name = 'aircox/home.html' template_name = 'aircox/home.html'
model = Page model = Page
@ -14,6 +15,7 @@ class HomeView(PageListView):
list_count = 40 list_count = 40
logs_count = 5 logs_count = 5
has_filters = False has_filters = False
attach_to_value = StaticPage.ATTACH_TO_HOME
def get_logs(self): def get_logs(self):
today = datetime.date.today() today = datetime.date.today()

View File

@ -8,10 +8,10 @@ from rest_framework.generics import ListAPIView
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.decorators import action from rest_framework.decorators import action
from ..models import Diffusion, Log from ..models import Diffusion, Log, StaticPage
from ..serializers import LogInfo, LogInfoSerializer from ..serializers import LogInfo, LogInfoSerializer
from .base import BaseView, BaseAPIView from .base import BaseView, BaseAPIView
from .mixins import GetDateMixin from .mixins import GetDateMixin, AttachedToMixin
__all__ = ['LogListMixin', 'LogListView'] __all__ = ['LogListMixin', 'LogListView']
@ -52,13 +52,14 @@ class LogListMixin(GetDateMixin):
return Log.merge_diffusions(logs, diffs) return Log.merge_diffusions(logs, diffs)
class LogListView(BaseView, LogListMixin, ListView): class LogListView(AttachedToMixin, BaseView, LogListMixin, ListView):
""" """
Return list of logs for the provided date (from `kwargs` or Return list of logs for the provided date (from `kwargs` or
`request.GET`, defaults to today). `request.GET`, defaults to today).
""" """
redirect_date_url = 'log-list' redirect_date_url = 'log-list'
has_filters = True has_filters = True
attach_to_value = StaticPage.ATTACH_TO_LOGS
def get_date(self): def get_date(self):
date = super().get_date() date = super().get_date()

View File

@ -2,9 +2,10 @@ from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse from django.urls import reverse
from ..utils import str_to_date from ..utils import str_to_date
from ..models import StaticPage
__all__ = ['GetDateMixin', 'ParentMixin'] __all__ = ['GetDateMixin', 'ParentMixin', 'AttachedToMixin']
class GetDateMixin: class GetDateMixin:
@ -68,3 +69,14 @@ class ParentMixin:
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class AttachedToMixin:
""" Mixin for views that can have a static page attached to it. """
attach_to_value = None
""" Value of StaticPage.attach_to """
def get_page(self):
if self.attach_to_value is not None:
return StaticPage.objects.filter(attach_to=self.attach_to_value) \
.published().first()
return super().get_page()

View File

@ -6,15 +6,16 @@ from django.views.generic import DetailView, ListView
from honeypot.decorators import check_honeypot from honeypot.decorators import check_honeypot
from ..forms import CommentForm from ..forms import CommentForm
from ..models import Category, Comment, Page from ..models import Category, Comment
from ..utils import Redirect from ..utils import Redirect
from .base import BaseView from .base import BaseView
from .mixins import AttachedToMixin, ParentMixin
__all__ = ['PageDetailView', 'PageListView'] __all__ = ['PageDetailView', 'PageListView']
class PageListView(BaseView, ListView): class PageListView(AttachedToMixin, ParentMixin, BaseView, ListView):
template_name = 'aircox/page_list.html' template_name = 'aircox/page_list.html'
item_template_name = 'aircox/widgets/page_item.html' item_template_name = 'aircox/widgets/page_item.html'
has_sidebar = True has_sidebar = True
@ -81,14 +82,12 @@ class PageDetailView(BaseView, DetailView):
raise Http404('%s not found' % self.model._meta.verbose_name) raise Http404('%s not found' % self.model._meta.verbose_name)
return obj return obj
def get_context_data(self, **kwargs): def get_page(self):
page = kwargs.setdefault('page', self.object) return self.object
kwargs.setdefault('title', page.title)
kwargs.setdefault('cover', page.cover)
def get_context_data(self, **kwargs):
if self.object.allow_comments and not 'comment_form' in kwargs: if self.object.allow_comments and not 'comment_form' in kwargs:
kwargs['comment_form'] = CommentForm() kwargs['comment_form'] = CommentForm()
kwargs['comments'] = Comment.objects.filter(page=self.object) \ kwargs['comments'] = Comment.objects.filter(page=self.object) \
.order_by('-date') .order_by('-date')
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
@ -112,5 +111,3 @@ class PageDetailView(BaseView, DetailView):

View File

@ -3,8 +3,8 @@ from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.urls import reverse from django.urls import reverse
from ..models import Episode, Program, Page from ..models import Episode, Program, Page, StaticPage
from .mixins import ParentMixin from .mixins import ParentMixin, AttachedToMixin
from .page import PageDetailView, PageListView from .page import PageDetailView, PageListView
@ -34,8 +34,10 @@ class ProgramDetailView(BaseProgramMixin, PageDetailView):
class ProgramListView(PageListView): class ProgramListView(PageListView):
model = Program model = Program
attach_to_value = StaticPage.ATTACH_TO_PROGRAMS
# FIXME: not used
class ProgramPageDetailView(BaseProgramMixin, ParentMixin, PageDetailView): class ProgramPageDetailView(BaseProgramMixin, ParentMixin, PageDetailView):
""" """
Base view class for a page that is displayed as a program's child page. Base view class for a page that is displayed as a program's child page.
@ -50,7 +52,7 @@ class ProgramPageDetailView(BaseProgramMixin, ParentMixin, PageDetailView):
return super().get_sidebar_queryset().filter(parent=self.program) return super().get_sidebar_queryset().filter(parent=self.program)
class ProgramPageListView(BaseProgramMixin, ParentMixin, PageListView): class ProgramPageListView(BaseProgramMixin, PageListView):
model = Page model = Page
parent_model = Program parent_model = Program
queryset = Page.objects.select_subclasses() queryset = Page.objects.select_subclasses()