redesign default layout; add new sections type (Timetable, Logs, Search); fix multiple things & minors changes in some sections; new settings for referencing; new templates for date_list's items; etc

This commit is contained in:
bkfox 2016-07-25 22:31:00 +02:00
parent 960fcab65d
commit bce84af7f3
21 changed files with 630 additions and 249 deletions

View File

@ -40,20 +40,22 @@ class WebsiteSettings(BaseSetting):
# exist. Update all dependent code such as signal handling
# general website information
logo = models.ForeignKey(
'wagtailimages.Image',
verbose_name = _('logo'),
null=True, blank=True, on_delete=models.SET_NULL,
related_name='+',
help_text = _('logo of the website'),
)
favicon = models.ForeignKey(
'wagtailimages.Image',
favicon = models.ImageField(
verbose_name = _('favicon'),
null=True, blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text = _('favicon for the website'),
help_text = _('small logo for the website displayed in the browser'),
)
tags = models.CharField(
_('tags'),
max_length=256,
null=True, blank=True,
help_text = _('tags describing the website; used for referencing'),
)
description = models.CharField(
_('public description'),
max_length=256,
null=True, blank=True,
help_text = _('public description of the website; used for referencing'),
)
# comments
@ -107,8 +109,11 @@ class WebsiteSettings(BaseSetting):
panels = [
ImageChooserPanel('logo'),
ImageChooserPanel('favicon'),
MultiFieldPanel([
FieldPanel('favicon'),
FieldPanel('tags'),
FieldPanel('description'),
], heading=_('promotion')),
MultiFieldPanel([
FieldPanel('allow_comments'),
FieldPanel('accept_comments'),
@ -252,7 +257,8 @@ class Publication(Page):
FieldPanel('allow_comments'),
]
search_fields = [
index.SearchField('body'),
index.SearchField('title', partial_match=True),
index.SearchField('body', partial_match=True),
index.FilterField('live'),
index.FilterField('show_in_menus'),
]
@ -285,7 +291,7 @@ class Publication(Page):
context['comment_form'] = CommentForm()
if view == 'list':
context['object_list'] = ListPage.get_queryset(
context['object_list'] = ListBase.from_request(
request, context = context, related = self
)
return context
@ -448,11 +454,17 @@ class DiffusionPage(Publication):
item = cl.from_diffusion(diff, ListItem)
item.live = True
item.info = []
if diff.initial:
item.info = _('Rerun of %(date)s') % {
item.info.append(_('Rerun of %(date)s') % {
'date': diff.initial.start.strftime('%A %d')
}
diff.css_class = 'diffusion'
})
if diff.type == diff.Type.canceled:
item.info.append(_('Cancelled'))
item.info = '; '.join(item.info)
item.date = diff.start
item.css_class = 'diffusion'
return item
def save(self, *args, **kwargs):
@ -460,6 +472,7 @@ class DiffusionPage(Publication):
self.date = self.diffusion.start
super().save(*args, **kwargs)
class EventPageQuerySet(PageQuerySet):
def upcoming(self):
now = tz.now().date()
@ -587,8 +600,7 @@ class LogsPage(DatedListPage):
verbose_name = _('station'),
null = True,
on_delete=models.SET_NULL,
help_text = _('(required for logs) the station on which the logs '
'happened')
help_text = _('(required) the station on which the logs happened')
)
age_max = models.IntegerField(
_('maximum age'),
@ -608,24 +620,6 @@ class LogsPage(DatedListPage):
], heading=_('Configuration')),
]
def as_item(cl, log):
"""
Return a log object as a DiffusionPage or ListItem.
Supports: Log/Track, Diffusion
"""
if type(log) == programs.Diffusion:
return DiffusionPage.as_item(log)
return ListItem(
title = '{artist} -- {title}'.format(
artist = log.related.artist,
title = log.related.title,
),
summary = log.related.info,
date = log.date,
info = '',
css_class = 'track'
)
def get_nav_dates(self, date):
"""
Return a list of dates availables for the navigation
@ -648,8 +642,8 @@ class LogsPage(DatedListPage):
logs = []
for date in context['nav_dates']['dates']:
items = self.station.on_air(date = date)
items = [ self.as_item(item) for item in items ]
items = [ SectionLogsList.as_item(item)
for item in self.station.on_air(date = date) ]
logs.append((date, items))
return logs

View File

@ -34,7 +34,7 @@ from taggit.models import TaggedItemBase
# aircox
import aircox.programs.models as programs
import aircox.controllers.models as controllers
def related_pages_filter(reset_cache=False):
@ -102,6 +102,11 @@ class RelatedLinkBase(Orderable):
related_name='+',
help_text = _('icon to display before the url'),
)
# icon = models.ImageField(
# verbose_name = _('icon'),
# null=True, blank=True,
# help_text = _('icon to display before the url'),
#)
text = models.CharField(
_('text'),
max_length = 64,
@ -327,7 +332,9 @@ class ListBase(models.Model):
search = request.GET.get('search')
if search:
kwargs['terms'] = search
print(search, qs)
qs = qs.search(search)
print(qs.count())
set('list_selector', kwargs)
@ -534,7 +541,6 @@ class SectionItemMeta(models.base.ModelBase):
cl.template = 'cms/sections/section_item.html'
return cl
@register_snippet
class SectionItem(models.Model,metaclass=SectionItemMeta):
"""
@ -554,15 +560,6 @@ class SectionItem(models.Model,metaclass=SectionItemMeta):
default = False,
help_text=_('if set show a title at the head of the section'),
)
is_related = models.BooleanField(
_('is related'),
default = False,
help_text=_(
'if set, section is related to the page being processed '
'e.g rendering a list of links will use thoses of the '
'publication instead of an assigned one.'
)
)
css_class = models.CharField(
_('CSS class'),
max_length=64,
@ -574,7 +571,6 @@ class SectionItem(models.Model,metaclass=SectionItemMeta):
FieldPanel('title'),
FieldPanel('show_title'),
FieldPanel('css_class'),
FieldPanel('is_related'),
], heading=_('General')),
]
@ -593,12 +589,7 @@ class SectionItem(models.Model,metaclass=SectionItemMeta):
self.real_type = type(self).__name__.lower()
return super().save(*args, **kwargs)
def related_page_attr(self, page, attr):
return self.is_related and hasattr(page, attr) \
and getattr(page, attr)
def get_context(self, request, page, *args, **kwargs):
def get_context(self, request, page):
"""
Default context attributes:
* self: section being rendered
@ -636,6 +627,33 @@ class SectionItem(models.Model,metaclass=SectionItemMeta):
self.title or self.pk
)
class SectionRelativeItem(SectionItem):
is_related = models.BooleanField(
_('is related'),
default = False,
help_text=_(
'if set, section is related to the page being processed '
'e.g rendering a list of links will use thoses of the '
'publication instead of an assigned one.'
)
)
class Meta:
abstract=True
panels = SectionItem.panels.copy()
panels[-1] = MultiFieldPanel(
panels[-1].children + [ FieldPanel('is_related') ],
heading = panels[-1].heading
)
def related_attr(self, page, attr):
"""
Return an attribute from the given page if self.is_related,
otherwise retrieve the attribute from self.
"""
return self.is_related and hasattr(page, attr) \
and getattr(page, attr)
@register_snippet
class SectionText(SectionItem):
@ -644,14 +662,14 @@ class SectionText(SectionItem):
FieldPanel('body'),
]
def get_context(self, request, page, *args, **kwargs):
def get_context(self, request, page):
from wagtail.wagtailcore.rich_text import expand_db_html
context = super().get_context(request, page, *args, **kwargs)
context = super().get_context(request, page)
context['content'] = expand_db_html(self.body)
return context
@register_snippet
class SectionImage(SectionItem):
class SectionImage(SectionRelativeItem):
class ResizeMode(IntEnum):
max = 0x00
min = 0x01
@ -685,7 +703,10 @@ class SectionImage(SectionItem):
)
panels = SectionItem.panels + [
ImageChooserPanel('image'),
MultiFieldPanel([
ImageChooserPanel('image'),
FieldPanel('image'),
], heading=_('Source')),
MultiFieldPanel([
FieldPanel('width'),
FieldPanel('height'),
@ -693,21 +714,25 @@ class SectionImage(SectionItem):
], heading=_('Resizing'))
]
def get_context(self, request, page, *args, **kwargs):
context = super().get_context(request, page, *args, **kwargs)
def get_filter(self):
return \
'original' if not (self.height or self.width) else \
'width-{}'.format(self.width) if not self.height else \
'height-{}'.format(self.height) if not self.width else \
'{}-{}x{}'.format(
self.get_resize_mode_display(),
self.width, self.height
)
image = self.related_page_attr(page, 'cover') or self.image
def get_context(self, request, page):
context = super().get_context(request, page)
image = self.related_attr(page, 'cover') or self.image
if not image:
return context
if self.width or self.height:
filter_spec = \
'width-{}'.format(self.width) if not self.height else \
'height-{}'.format(self.height) if not self.width else \
'{}-{}x{}'.format(
self.get_resize_mode_display(),
self.width, self.height
)
filter_spec = self.get_filter()
filter_spec = (image.id, filter_spec)
url = reverse(
'wagtailimages_serve',
@ -732,7 +757,7 @@ class SectionLink(RelatedLinkBase, SectionItem):
@register_snippet
class SectionLinkList(SectionItem, ClusterableModel):
class SectionLinkList(SectionRelativeItem, ClusterableModel):
"""
Render a list of links. If related to the current page, print
the page's links otherwise, the assigned link list.
@ -746,16 +771,15 @@ class SectionLinkList(SectionItem, ClusterableModel):
))
]
def get_context(self, request, page, *args, **kwargs):
context = super().get_context(*args, **kwargs)
links = self.related_page_attr(page, 'related_link') or self.links
context['object_list'] = links
def get_context(self, request, page):
context = super().get_context(request, page)
links = self.related_attr(page, 'related_link') or self.links
context['object_list'] = links.all()
return context
@register_snippet
class SectionList(ListBase, SectionItem):
class SectionList(ListBase, SectionRelativeItem):
"""
This one is quite badass, but needed: render a list of pages
using given parameters (cf. ListBase).
@ -789,18 +813,17 @@ class SectionList(ListBase, SectionItem):
], heading=_('Rendering')),
] + ListBase.panels
def get_context(self, request, page, *args, **kwargs):
def get_context(self, request, page):
from aircox.cms.models import Publication
context = super().get_context(request, page, *args, **kwargs)
context = super().get_context(request, page)
qs = self.get_queryset()
qs = qs.live()
if self.focus_available:
focus = qs.type(Publication).filter(focus = True).first()
if focus:
focus.css_class = \
focus.css_class + ' focus' if focus.css_class else 'focus'
qs = qs.exclude(focus.page)
focus.css_class = 'focus'
qs = qs.exclude(pk = focus.pk)
else:
focus = None
pages = qs[:self.count - (focus != None)]
@ -814,8 +837,65 @@ class SectionList(ListBase, SectionItem):
return context
@register_snippet
class SectionLogsList(SectionItem):
station = models.ForeignKey(
controllers.Station,
verbose_name = _('station'),
null = True,
on_delete=models.SET_NULL,
help_text = _('(required) the station on which the logs happened')
)
count = models.SmallIntegerField(
_('count'),
default = 5,
help_text = _('number of items to display in the list (max 100)'),
)
class Meta:
verbose_name = _('list of logs')
verbose_name_plural = _('lists of logs')
panels = SectionItem.panels + [
FieldPanel('station'),
FieldPanel('count'),
]
@staticmethod
def as_item(log):
"""
Return a log object as a DiffusionPage or ListItem.
Supports: Log/Track, Diffusion
"""
from aircox.cms.models import DiffusionPage
if type(log) == programs.Diffusion:
return DiffusionPage.as_item(log)
return ListItem(
title = '{artist} -- {title}'.format(
artist = log.related.artist,
title = log.related.title,
),
summary = log.related.info,
date = log.date,
info = '',
css_class = 'track'
)
def get_context(self, request, page):
context = super().get_context(request, page)
context['object_list'] = [
self.as_item(item)
for item in self.station.on_air(count = min(self.count, 100))
]
return context
@register_snippet
class SectionTimetable(SectionItem,DatedListBase):
class Meta:
verbose_name = _('timetable')
verbose_name_plural = _('timetable')
panels = SectionItem.panels + DatedListBase.panels
def get_queryset(self, context):
@ -827,27 +907,48 @@ class SectionTimetable(SectionItem,DatedListBase):
diffs.append((date, items))
return diffs
def get_context(self, request, page, *args, **kwargs):
context = super().get_context(request, page, *args, **kwargs)
def get_context(self, request, page):
context = super().get_context(request, page)
context.update(self.get_date_context())
context['object_list'] = self.get_queryset(context)
return context
@register_snippet
class SectionLogs(SectionItem):
count = models.SmallIntegerField(
_('count'),
default = 5,
help_text = _('number of items to display in the list'),
class SectionPublicationInfo(SectionItem):
class Meta:
verbose_name = _('section with publication\'s info')
verbose_name = _('sections with publication\'s info')
@register_snippet
class SectionSearchField(SectionItem):
page = models.ForeignKey(
'cms.ListPage',
verbose_name = _('search page'),
blank = True, null = True,
help_text=_('page used to display the results'),
)
default_text = models.CharField(
_('default text'),
max_length=32,
default=_('search'),
help_text=_('text to display when the search field is empty'),
)
class Meta:
verbose_name = _('search field')
verbose_name_plural = _('search fields')
panels = SectionItem.panels + [
FieldPanel('count'),
PageChooserPanel('page'),
FieldPanel('default_text'),
]
def get_context(self, request, page, *args, **kwargs):
pass
def get_context(self, request, page):
from aircox.cms.models import ListPage
context = super().get_context(request, page)
list_page = self.page or ListPage.objects.live().first()
context['list_page'] = list_page
print(context, self.template)
return context

View File

@ -0,0 +1,104 @@
/**
* Define rules for the default layouts, and some useful classes
*/
body {
margin: 0em;
padding: 0em;
}
h1, h2, h3, h4, h5 {
margin: 0.4em 0em;
}
/** classes: flex **/
.flex_row {
display: -webkit-flex;
display: flex;
-webkit-flex-direction: row;
flex-direction: row;
}
.flex_column {
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
}
.flex_row > .flex_item,
.flex_column > .flex_item {
-webkit-flex: auto;
flex: auto;
}
/** content: list & items **/
.list {
width: 100%;
padding: 0.4em;
}
.list_item {
width: inherit;
margin: 0.4em 0;
}
.list_item > *:not(:last-child) {
margin-right: 0.4em;
}
.list_item img.cover.big {
display: block;
}
.list_item img.cover.small {
margin-right: 0.4em;
border-radius: 0.4em;
float: left;
min-height: 64px;
}
/** content: date list **/
.date_list nav {
text-align:center;
}
.date_list nav a {
display: inline-block;
width: 4em;
}
.date_list nav a[selected] {
color: #007EDF;
border-bottom: 0.2em #007EDF dotted;
}
.date_list ul:not([selected]) {
display: none;
}
.date_list ul:target {
display: block;
}
.date_list h2 {
display: none;
}
.date_list_item .cover.small {
width: 64px;
margin: 0.4em;
}
.date_list_item h3 {
width: 100%;
}
/** content: publication **/

View File

@ -0,0 +1,74 @@
/*
* Define a default theme, that is the one for RadioCampus
*
* Colors:
* - light:
* - background: #F2F2F2
* - color: #000
*
* - dark:
* - background: #212121
* - color: #007EDF
*
* - info:
* - generic (time,url,...): #616161
* - additional: #007EDF
* - active: #007EDF
*/
/** main **/
body {
background-color: #F2F2F2;
font-family: "Myriad Pro",Calibri,Helvetica,Arial,sans-serif;
margin: 0em;
padding: 0em;
}
h1, h2, h3 {
font-family: "Myriad Pro",Calibri,Helvetica,Arial,sans-serif;
}
h1:first-letter, h2:first-letter, h3:first-letter {
text-transform: capitalize;
}
h1 { font-size: 1.4em; }
h2 { font-size: 1.2em; }
h3 { font-size: 1.0em; }
/** info **/
time {
font-size: 0.9em;
color: #616161;
}
.info {
font-size: 0.9em;
color: #007EDF;
}
a {
cursor: pointer;
text-decoration: none;
color: #616161;
}
a:hover {
color: #007EDF;
}
.error { color: red; }
.warning { color: orange; }
.success { color: green; }
/** page **/
.page > nav {
width: 20em;
overflow: hidden;
}

View File

@ -10,6 +10,14 @@
<html>
<head>
<meta charset="utf-8">
<meta name="application-name" content="aircox-cms">
<meta name="description" content="{{ settings.cms.WebsiteSettings.description }}">
<meta name="keywords" content="{{ page.tags.all|default:settings.cms.WebsiteSettings.tags }}">
{% with favicon=settings.cms.WebsiteSettings.favicon %}
<link rel="icon" href="{{ favicon.url }}" />
{% endwith %}
{% block css %}
<link rel="stylesheet" href="{% static 'cms/css/layout.css' %}" type="text/css" />
<link rel="stylesheet" href="{% static 'cms/css/theme.css' %}" type="text/css" />
@ -21,19 +29,19 @@
</head>
<body>
<div class="top">
<nav>
<a href="">Grille Horaire</a>
<a href="">Programmes</a>
<a href="">Contact</a>
</nav>
{% render_sections position="top" %}
</div>
<div class="middle">
<nav class="left">
{% render_sections position="menu_left" %}
<header class="header">
{% render_sections position="header" %}
</header>
<div class="page flex_row">
<nav class="page_left flex_item">
{% render_sections position="page_left" %}
</nav>
<main>
<main class="flex_item">
{% if messages %}
<ul class="messages">
{% for message in messages %}
@ -50,6 +58,14 @@
{% block content %}
{% endblock %}
</main>
<nav class="page_right flex_item">
{% render_sections position="page_right" %}
</nav>
</div>
<footer class="footer">
{% render_sections position="footer" %}
</footer>
</body>
</html>

View File

@ -1,8 +1,11 @@
{% extends "cms/base_site.html" %}
{% load i18n %}
{% load wagtailcore_tags %}
{% load wagtailimages_tags %}
{% load aircox_cms %}
{% block content %}
{% if object_list %}
{# list view #}
@ -18,75 +21,21 @@
{# detail view #}
<div class="content">
{% if page.cover %}
<img class="cover" src="{{ page.cover.file.url }}">
{% image page.cover max-600x480 class="cover" height="" width="" %}
{% endif %}
<div class="body">
<section class="body">
{{ page.body|richtext}}
{% block content_extras %}{% endblock %}
</section>
<div class="post_content">
{% render_sections position="post_content" %}
</div>
{% block content_extras %}
{% endblock %}
{% if page.related_links.all %}
<ul class="related">
<h3>{% trans "Related links" %}</h3>
{% for link in page.related_links.all %}
<li>
<a href="{{ link.url }}">
{% if link.icon %}{% image link.icon fill-size-32x32 %}{% endif %}
{{ link.title|default:link.url }}
</a>
</li>
{% endfor %}
</ul>
{% endif %}
<div class="comments">
<section class="comments">
{% include "cms/snippets/comments.html" %}
</div>
</section>
</div>
{% block page_nav %}
<nav class="page_nav">
{% block metadata %}
<div class="meta">
<div class="author">
{% if page.publish_as %}
{% with page.publish_as as item %}
{% include "cms/snippets/list_item.html" %}
{% endwith %}
{% else %}
{{ page.owner|default:'' }}
{% endif %}
</div>
<time datetime="{{ page.specific.date }}">
{% trans "Published on " %}
{{ page.specific.date|date:'l d F, H:i' }}
</time>
<div class="tags">
{% for tag in page.tags.all %}
{# <a href="{% pageurl page.blog_index %}?tag={{ tag }}">{{ tag }}</a> #}
{{ tag }}
{% endfor %}
</div>
</div>
{% endblock %}
{% block page_nav_extras %}
{% endblock %}
{% if page.recents %}
<div>
<h2>{% trans "Last Publications" %}</h2>
{% with object_list=page.recents %}
{% include "cms/snippets/list.html" %}
{% endwith %}
{# TODO: url to complete list of publications #}
</div>
{% endif %}
</nav>
{% endblock %}
{% endif %}
{% endblock %}

View File

@ -1,8 +1,8 @@
<div class="section_item {{ self.css_class }}">
<section class="section_item {{ self.css_class }}">
{% block title %}
{% if self.show_title %}<h2>{{ self.title }}</h2>{% endif %}
{% endblock %}
{% block content %}{{ content|safe }}{% endblock %}
</div>
</section>

View File

@ -4,7 +4,7 @@
{% block content %}
{% with link=self.as_dict %}
<a href="{{ link.url }}">
{% if link.icon %}{% image link.icon fill-32x32 class="icon" %}{% endif %}
{% if link.icon %}{% image link.icon fill-32x32 class="icon link_icon" height='' width='' %}{% endif %}
{{ link.text }}
</a>
{% endwith %}

View File

@ -6,7 +6,7 @@
{% with link=item.as_dict %}
<a href="{{ link.url }}"
{% if item.css_class %}class="{{ item.css_class }}"{% endif %}>
{% if link.icon %}{% image link.icon fill-32x32 class="icon" %}{% endif %}
{% if link.icon %}{% image link.icon fill-24x24 class="icon" %}{% endif %}
{{ link.text }}
</a>
{% endwith %}

View File

@ -0,0 +1,8 @@
{% extends "cms/sections/section_item.html" %}
{% block content %}
{% with item_date_format="H:i" list_css_class="date_list" %}
{% include "cms/snippets/list.html" %}
{% endwith %}
{% endblock %}

View File

@ -0,0 +1,27 @@
{% extends "cms/sections/section_item.html" %}
{% load i18n %}
<div class="meta">
<div class="author">
{% if page.publish_as %}
{% trans "Published by" %}
{% with item=page.publish_as item_date_format='' %}
{% include "cms/snippets/list_item.html" %}
{% endwith %}
{% elif page.owner %}
{% trans "Published by" %}
{{ page.owner }}
{% endif %}
</div>
<time datetime="{{ page.specific.date }}">
{% trans "Published on " %}
{{ page.specific.date|date:'l d F, H:i' }}
</time>
<div class="tags">
{% for tag in page.tags.all %}
{# <a href="{% pageurl page.blog_index %}?tag={{ tag }}">{{ tag }}</a> #}
{{ tag }}
{% endfor %}
</div>
</div>

View File

@ -0,0 +1,10 @@
{% extends "cms/sections/section_item.html" %}
{% load i18n %}
{% block content %}
<form action="{{ list_page.url }}" method="GET">
<input type="text" name="search" placeholder="{{ self.text }}">
<input type="submit" style="display: none;">
</form>
{% endblock %}

View File

@ -0,0 +1,6 @@
{% extends "cms/sections/section_item.html" %}
{% block content %}
{% include "cms/snippets/date_list.html" %}
{% endblock %}

View File

@ -1,34 +1,58 @@
{% load i18n %}
<script>
/// Function used to select a panel on a tab selection.
/// The tab should be at max level -2 of the main container
/// The panel must have a class "panel"
function select_tab(target) {
parent = target.parentNode.parentNode;
var date = target.dataset.date;
panel = parent.querySelector('.panel[data-date="' + date + '"]');
// unselect
qs = parent.querySelectorAll('*[selected]');
for(var i = 0; i < qs.length; i++)
if(qs[i].dataset.date != date)
qs[i].removeAttribute('selected');
console.log(panel, target, date);
panel.setAttribute('selected', 'true');
target.setAttribute('selected', 'true');
}
</script>
{# FIXME: get current complete URL #}
<div class="list dated_list">
<div class="list date_list">
{% if nav_dates %}
<nav class="nav_dates">
{% if nav_dates.prev %}
<a href="?date={{ nav_dates.prev|date:"Y-m-d" }}" title="{% trans "previous days" %}">&lt;</a>
<a href="?date={{ nav_dates.prev|date:"Y-m-d" }}" title="{% trans "previous days" %}"></a>
{% endif %}
{% for day in nav_dates.dates %}
<a href="#day_{{day|date:"Y-m-d"}}"
{% if day == nav_dates.date %}class="today"{% endif %}>
<a onclick="select_tab(this);" data-date="day_{{day|date:"Y-m-d"}}"
{% if day == nav_dates.date %}selected{% endif %}
class="tab {% if day == nav_dates.date %}today{% endif %}">
{{ day|date:'D. d' }}
</a>
{% endfor %}
{% if nav_dates.next %}
<a href="?date={{ nav_dates.next|date:"Y-m-d" }}" title="{% trans "next days" %}">&gt;</a>
<a href="?date={{ nav_dates.next|date:"Y-m-d" }}" title="{% trans "next days" %}"></a>
{% endif %}
</nav>
{% endif %}
{% for day, list in object_list %}
<ul id="day_{{day|date:"Y-m-d"}}"
{% if day == nav_dates.date %}class="today"{% endif %}>
<ul class="panel {% if day == nav_dates.date %}class="today"{% endif %}"
{% if day == nav_dates.date %}selected{% endif %}
data-date="day_{{day|date:"Y-m-d"}}">
{# you might like to hide it by default -- this more for sections #}
<h2>{{ day|date:'l d F' }}</h2>
{% with object_list=list item_date_format="H:i" %}
{% for item in list %}
{% include "cms/snippets/list_item.html" %}
{% include "cms/snippets/date_list_item.html" %}
{% endfor %}
{% endwith %}
</ul>

View File

@ -0,0 +1,43 @@
{% comment %}
Configurable item to be put in a dated list. Work like list_item, the layout
is just a bit different.
{% endcomment %}
{% load wagtailimages_tags %}
<a {% if item.url %}href="{{ item.url }}" {% endif %}
class="list_item date_list_item {% if not item_big_cover %}flex_row {% endif %}{% if item.css_class %}{{ item.css_class }}{% endif %}">
{% if not item.show_in_menus and item.date and item_date_format != '' %}
{% with date_format=item_date_format|default_if_none:'l d F, H:i' %}
<time datetime="{{ item.date }}">
{{ item.date|date:date_format }}
</time>
{% endwith %}
{% endif %}
{% if item_big_cover %}
{% image item.cover max-640x480 class="cover big" height="" width="" %}
{% elif item.cover %}
{% image item.cover fill-64x64 class="cover small" %}
{% else %}
<div class="cover small"></div>
{% endif %}
<div class="flex_item">
<h3>{{ item.title }}</h3>
{% if item.summary %}<div class="summary">{{ item.summary }}</div>{% endif %}
{% if item.info %}
<span class="info">{{ item.info|safe }}</span>
{% endif %}
{% if item.extra %}
<div class="extra"></div>
{% endif %}
</div>
</a>

View File

@ -1,15 +1,19 @@
{% comment %}
Options:
- list_css_class: extra class for the main list container
- list_paginator: paginator object to display pagination at the bottom;
{% endcomment %}
{% load i18n %}
{% load aircox_cms %}
<div class="list">
<ul>
<ul class="list {{ list_css_class|default:'' }}">
{% for page in object_list %}
{% with item=page.specific %}
{% include "cms/snippets/list_item.html" %}
{% endwith %}
{% endfor %}
</ul>
{# we use list_paginator to avoid conflicts when there are multiple lists #}
{% if list_paginator and list_paginator.num_pages > 1 %}
@ -54,6 +58,6 @@
</nav>
{% endif %}
</div>
</ul>

View File

@ -4,30 +4,41 @@ ListItem instance.
Options:
* item: item to render. Fields: title, summary, cover, url, date, info, css_class
* item_date_format: format passed to the date filter instead of default one
* item_date_format: format passed to the date filter instead of default one. If
it is an empty string, do not print the date.
* item_big_cover: cover should is big instead of thumbnail (width: 600)
{% endcomment %}
{% load wagtailimages_tags %}
<a {% if item.url %}href="{{ item.url }}" {% endif %}
class="item page_item{% if item.css_class %}{{ item.css_class }}{% endif %}">
{% if item_big_cover %}
{% image item.cover max-600 class="cover item_cover" %}
{% else %}
{% image item.cover fill-64x64 class="cover item_cover" %}
class="list_item {% if not item_big_cover %}flex_row {% endif %}{% if item.css_class %}{{ item.css_class }}{% endif %}">
{% if item.cover %}
{% if item_big_cover %}
{% image item.cover max-640x480 class="cover big" height="" width="" %}
{% else %}
{% image item.cover fill-64x64 class="cover small" %}
{% endif %}
{% endif %}
<h3>{{ item.title }}</h3>
{% if item.summary %}<div class="summary">{{ item.summary }}</div>{% endif %}
{% if not item.show_in_menus and item.date %}
{% with date_format=list_date_format|default:'l d F, H:i' %}
<time datetime="{{ item.date }}">
{{ item.date|date:date_format }}
</time>
{% endwith %}
{% endif %}
{% if item.info %}
<span class="info">{{ item.info|safe }}</span>
<div class="flex_item">
<h3>{{ item.title }}</h3>
{% if item.info %}
<span class="info">{{ item.info|safe }}</span>
{% endif %}
{% if item.summary %}<div class="summary">{{ item.summary }}</div>{% endif %}
{% if not item.show_in_menus and item.date and item_date_format != '' %}
{% with date_format=item_date_format|default_if_none:'l d F, H:i' %}
<time datetime="{{ item.date }}">
{{ item.date|date:date_format }}
</time>
{% endwith %}
{% endif %}
</div>
{% if item.extra %}
<div class="extra"></div>
{% endif %}
</a>

View File

@ -45,6 +45,31 @@ class GenericMenu(Menu):
return self._registered_menu_items
class DiffusionsMenu(GenericMenu):
"""
Menu to display diffusions of today
"""
def get_queryset(self):
return programs.Diffusion.objects.filter(
type = programs.Diffusion.Type.normal,
start__contains = tz.now().date(),
).order_by('start')
def get_title(self, item):
return item.program.name
def get_parent(self, item):
return item.program.page.first()
@hooks.register('register_admin_menu_item')
def register_programs_menu_item():
return SubmenuMenuItem(
_('Today\'s Diffusions'), DiffusionsMenu(),
classnames='icon icon-folder-open-inverse', order=10
)
class ProgramsMenu(GenericMenu):
"""
Menu to display all active programs.
@ -67,36 +92,12 @@ class ProgramsMenu(GenericMenu):
return settings.default_program_parent_page
class DiffusionsMenu(GenericMenu):
"""
Menu to display diffusions of today
"""
def get_queryset(self):
return programs.Diffusion.objects.filter(
type = programs.Diffusion.Type.normal,
start__contains = tz.now().date(),
).order_by('start')
def get_title(self, item):
return item.program.name
def get_parent(self, item):
return item.program.page.first()
@hooks.register('register_admin_menu_item')
def register_programs_menu_item():
return SubmenuMenuItem(
_('Programs'), ProgramsMenu(),
classnames='icon icon-folder-open-inverse', order=100
)
@hooks.register('register_admin_menu_item')
def register_programs_menu_item():
return SubmenuMenuItem(
_('Today\'s Diffusions'), DiffusionsMenu(),
classnames='icon icon-folder-open-inverse', order=100
classnames='icon icon-folder-open-inverse', order=10
)

View File

@ -20,6 +20,7 @@ from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext as _, ugettext_lazy
from django.utils import timezone as tz
import aircox.programs.models as programs
from aircox.programs.utils import to_timedelta
@ -176,14 +177,15 @@ class Station(programs.Nameable):
for diff in diffs:
logs_ = \
logs.filter(date__gt = diff.end, date__lt = diff_.start) \
if diff_ else logs.filter(date__gt = diff.end)
if diff_ else \
logs.filter(date__gt = diff.end)
diff_ = diff
items.extends(logs_)
items.extend(logs_)
items.append(diff)
if count and len(items) >= count:
break
if diff_ and not count or len(items) <= count:
if diff_ and (not count or len(items) <= count):
logs_ = logs.filter(date__lt = diff_.end)
items.extend(logs_)
@ -207,19 +209,22 @@ class Station(programs.Nameable):
# FIXME: as an iterator?
# TODO argument to get sound instead of tracks
# TODO #Station
date = date or tz.now().date()
if date > datetime.date.today():
if date and date > datetime.date.today():
return []
logs = Log.objects.get_for(model = programs.Track) \
.filter(station = self) \
.order_by('-date')
diffs = programs.Diffusion.objects \
.filter(type = Diffusion.Type.normal) \
.order_by('-date')
if date:
logs = logs.filter(date__contains = date)
diffs = diffs.get_at(date)
diffs = programs.Diffusion.objects.get_at(date)
else:
diffs = programs.Diffusion.objects
diffs = diffs.filter(type = programs.Diffusion.Type.normal) \
.filter(start__lte = tz.now()) \
.order_by('-start')
return self.__mix_logs_and_diff(diffs, logs, count)

View File

@ -33,25 +33,29 @@ This file is used as a reminder, can be used as crappy documentation too.
- config generation and sound diffusion
- cms:
- command to sync programs with cms's
- player:
- mixcloud
- remove from playing playlist -> stop
- filter choices on DiffusionPage and ProgramPage related objects
# Long term TODO
- automatic cancel of passed diffusion based on logs?
- archives can be set afterwards for rerun, so check must be done
at the same time we monitor
- sounds monitor: max_size of path, take in account
- logs: archive functionnality
- track stats for diffusions
- debug/prod configuration
- player support diffusions with multiple archive files
- view as grid
- actions -> noscript case, think of accessibility
- comments -> remove/edit by the author
- integrate logs for tracks + in on air
- rename controllers.Station into controllers.Streamer -> keep Station for sth else
programs:
- sounds monitor: max_size of path, take in account
controllers:
- automatic cancel of passed diffusion based on logs?
- archives can be set afterwards for rerun, so check must be done
at the same time we monitor
- logs: archive functionnality
- tools:
- track stats for diffusions
- rename controllers.Station into controllers.Streamer -> keep Station for sth else
cms:
- player support diffusions with multiple archive files
- comments -> remove/edit by the author