update sections, work a bit on style

This commit is contained in:
bkfox 2015-10-06 18:01:19 +02:00
parent cfdc9b6de0
commit cde58334bd
15 changed files with 264 additions and 163 deletions

View File

@ -3,8 +3,8 @@ Platform to manage radio programs, schedules, cms, etc. -- main test repo
# Applications # Applications
* **programs**: programs, episodes, schedules, sounds and tracks; * **programs**: programs, episodes, schedules, sounds and tracks;
* **streams**: streams and diffusions, links with LiquidSoap; * **cms**: cms renderer
* **website**: website rendering, using models defined by the previous apps; * **website**: the website using the cms and the programs
# Code and names conventions and uses # Code and names conventions and uses

View File

@ -12,57 +12,18 @@ from django.dispatch import receiver
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
class Thread (models.Model):
"""
Object assigned to any Post and children that can be used to have parent and
children relationship between posts of different kind.
We use this system instead of having directly a GenericForeignKey into the
Post because it avoids having to define the relationship with two models for
routing (one for the parent and one for the children).
"""
post_type = models.ForeignKey(ContentType)
post_id = models.PositiveIntegerField()
post = GenericForeignKey('post_type', 'post_id')
__initial_post = None
@classmethod
def __get_query_set (cl, function, model, post, kwargs):
if post:
model = type(post)
kwargs['post_id'] = post.id
kwargs['post_type'] = ContentType.objects.get_for_model(model)
return getattr(cl.objects, function)(**kwargs)
@classmethod
def get (cl, model = None, post = None, **kwargs):
return cl.__get_query_set('get', model, post, kwargs)
@classmethod
def filter (cl, model = None, post = None, **kwargs):
return self.__get_query_set('filter', model, post, kwargs)
@classmethod
def exclude (cl, model = None, post = None, **kwargs):
return self.__get_query_set('exclude', model, post, kwargs)
def save (self, *args, **kwargs):
self.post = self.__initial_post or self.post
super().save(*args, **kwargs)
def __str__ (self):
return self.post_type.name + ': ' + str(self.post)
class Post (models.Model): class Post (models.Model):
thread = models.ForeignKey( thread_type = models.ForeignKey(
Thread, ContentType,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
blank = True, null = True, blank = True, null = True
help_text = _('the publication is posted on this thread'),
) )
thread_pk = models.PositiveIntegerField(
blank = True, null = True
)
thread = GenericForeignKey('thread_type', 'thread_pk')
author = models.ForeignKey( author = models.ForeignKey(
User, User,
verbose_name = _('author'), verbose_name = _('author'),
@ -94,7 +55,7 @@ class Post (models.Model):
) )
def detail_url (self): def detail_url (self):
return reverse(self._meta.verbose_name_plural.lower() + '_detail', return reverse(self._meta.verbose_name.lower() + '_detail',
kwargs = { 'pk': self.pk, kwargs = { 'pk': self.pk,
'slug': slugify(self.title) }) 'slug': slugify(self.title) })
@ -151,7 +112,6 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
mapping = None # dict of related mapping values mapping = None # dict of related mapping values
bind_mapping = False # update fields of related data on save bind_mapping = False # update fields of related data on save
def get_attribute (self, attr): def get_attribute (self, attr):
attr = self._relation.mappings.get(attr) attr = self._relation.mappings.get(attr)
return self.related.__dict__[attr] if attr else None return self.related.__dict__[attr] if attr else None
@ -163,32 +123,10 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
if self._relation.bind_mapping: if self._relation.bind_mapping:
self.related.__dict__.update({ self.related.__dict__.update({
rel_attr: self.__dict__[attr] rel_attr: self.__dict__[attr]
for attr, rel_attr in self.Relation.mapping for attr, rel_attr in self.Relation.mapping.items()
}) })
self.related.save() self.related.save()
super().save(*args, **kwargs) super().save(*args, **kwargs)
@receiver(post_init)
def on_thread_init (sender, instance, **kwargs):
if not issubclass(Thread, sender):
return
instance.__initial_post = instance.post
@receiver(post_save)
def on_post_save (sender, instance, created, *args, **kwargs):
if not issubclass(sender, Post) or not created:
return
thread = Thread(post = instance)
thread.save()
@receiver(post_delete)
def on_post_delete (sender, instance, using, *args, **kwargs):
try:
Thread.get(sender, post = instance).delete()
except:
pass

View File

@ -0,0 +1,4 @@
Django>=1.9.0
django-taggit>=0.12.1
easy_thumbnails

View File

@ -1,4 +1,5 @@
from django.conf.urls import url from django.conf.urls import url
from django.contrib.contenttypes.models import ContentType
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext as _, ugettext_lazy from django.utils.translation import ugettext as _, ugettext_lazy
@ -27,9 +28,9 @@ class Route:
""" """
Base class for routing. Given a model, we generate url specific for each Base class for routing. Given a model, we generate url specific for each
route type. The generated url takes this form: route type. The generated url takes this form:
base_name + '/' + route_name + '/' + '/'.join(route_url_args) model_name + '/' + route_name + '/' + '/'.join(route_url_args)
Where base_name by default is the given model's verbose_name (uses plural if Where model_name by default is the given model's verbose_name (uses plural if
Route is for a list). Route is for a list).
The given view is considered as a django class view, and has view_ The given view is considered as a django class view, and has view_
@ -38,14 +39,14 @@ class Route:
url_args = [] # arguments passed from the url [ (name : regex),... ] url_args = [] # arguments passed from the url [ (name : regex),... ]
@classmethod @classmethod
def get_queryset (cl, model, request, **kwargs): def get_queryset (cl, website, model, request, **kwargs):
""" """
Called by the view to get the queryset when it is needed Called by the view to get the queryset when it is needed
""" """
pass pass
@classmethod @classmethod
def get_object (cl, model, request, **kwargs): def get_object (cl, website, model, request, **kwargs):
""" """
Called by the view to get the object when it is needed Called by the view to get the object when it is needed
""" """
@ -56,10 +57,8 @@ class Route:
return '' return ''
@classmethod @classmethod
def as_url (cl, model, view, view_kwargs = None): def as_url (cl, model, model_name, view, view_kwargs = None):
base_name = model._meta.verbose_name_plural.lower() pattern = '^{}/{}'.format(model_name, cl.name)
pattern = '^{}/{}'.format(base_name, cl.name)
if cl.url_args: if cl.url_args:
url_args = '/'.join([ url_args = '/'.join([
'(?P<{}>{}){}'.format( '(?P<{}>{}){}'.format(
@ -78,7 +77,7 @@ class Route:
kwargs.update(view_kwargs) kwargs.update(view_kwargs)
return url(pattern, view, kwargs = kwargs, return url(pattern, view, kwargs = kwargs,
name = base_name + '_' + cl.name) name = model_name + '_' + cl.name)
class DetailRoute (Route): class DetailRoute (Route):
@ -89,7 +88,7 @@ class DetailRoute (Route):
] ]
@classmethod @classmethod
def get_object (cl, model, request, pk, **kwargs): def get_object (cl, website, model, request, pk, **kwargs):
return model.objects.get(pk = int(pk)) return model.objects.get(pk = int(pk))
@ -97,7 +96,7 @@ class AllRoute (Route):
name = 'all' name = 'all'
@classmethod @classmethod
def get_queryset (cl, model, request, **kwargs): def get_queryset (cl, website, model, request, **kwargs):
return model.objects.all() return model.objects.all()
@classmethod @classmethod
@ -108,14 +107,32 @@ class AllRoute (Route):
class ThreadRoute (Route): class ThreadRoute (Route):
"""
Select posts using by their assigned thread.
- "thread_model" can be a string with the name of a registered item on
website or a model.
- "pk" is the pk of the thread item.
"""
name = 'thread' name = 'thread'
url_args = [ url_args = [
('thread_model', '(\w|_|-)+'),
('pk', '[0-9]+'), ('pk', '[0-9]+'),
] ]
@classmethod @classmethod
def get_queryset (cl, model, request, pk, **kwargs): def get_queryset (cl, website, model, request, thread_model, pk, **kwargs):
return model.objects.filter(thread__pk = int(pk)) if type(thread_model) is str:
thread_model = website.registry.get(thread_model).model
if not thread_model:
return
thread_model = ContentType.objects.get_for_model(thread_model)
return model.objects.filter(
thread_type = thread_model,
thread_pk = int(pk)
)
class DateRoute (Route): class DateRoute (Route):
@ -127,7 +144,7 @@ class DateRoute (Route):
] ]
@classmethod @classmethod
def get_queryset (cl, model, request, year, month, day, **kwargs): def get_queryset (cl, website, model, request, year, month, day, **kwargs):
return model.objects.filter( return model.objects.filter(
date__year = int(year), date__year = int(year),
date__month = int(month), date__month = int(month),
@ -139,7 +156,7 @@ class SearchRoute (Route):
name = 'search' name = 'search'
@classmethod @classmethod
def get_queryset (cl, model, request, **kwargs): def get_queryset (cl, website, model, request, **kwargs):
q = request.GET.get('q') or '' q = request.GET.get('q') or ''
qs = model.objects qs = model.objects
for search_field in model.search_fields or []: for search_field in model.search_fields or []:
@ -151,4 +168,3 @@ class SearchRoute (Route):
return qs return qs

View File

@ -20,9 +20,13 @@ body {
} }
.section {
vertical-align: top;
}
main .section { main .section {
width: calc(50% - 1em); width: calc(50% - 2em);
float: left; display: inline-block;
padding: 0.5em; padding: 0.5em;
} }
@ -39,3 +43,8 @@ main .section {
} }
.post_item {
display: block;
}

View File

@ -12,23 +12,23 @@
<time datetime="{{ post.date }}" class="post_datetime"> <time datetime="{{ post.date }}" class="post_datetime">
{% if 'date' in view.fields %} {% if 'date' in view.fields %}
<span class="post_date"> <span class="post_date">
{{ post.date|date:'D. d F' }}, {{ post.date|date:'D. d F' }}
</span> </span>
{% endif %} {% endif %}
{% if 'time' in view.fields %} {% if 'time' in view.fields %}
<span class="post_time"> <span class="post_time">
{{ post.date|date:'H:i' }}, {{ post.date|date:'H:i' }}
</span> </span>
{% endif %} {% endif %}
</time> </time>
{% endif %} {% endif %}
{% if 'image' in view.fields %} {% if 'image' in view.fields %}
<img src="{% thumbnail post.image "64x64" crop %}" class="post_image"> <img src="{% thumbnail post.image view.icon_size crop %}" class="post_image">
{% endif %} {% endif %}
{% if 'title' in view.fields %} {% if 'title' in view.fields %}
<h4 class="post_title">{{ post.title }}</h4> <h3 class="post_title">{{ post.title }}</h3>
{% endif %} {% endif %}
{% if 'content' in view.fields %} {% if 'content' in view.fields %}

View File

@ -6,6 +6,9 @@
<ul style="padding:0; margin:0"> <ul style="padding:0; margin:0">
{% for item in object_list %} {% for item in object_list %}
<li> <li>
{% if item.url %}
<a href="{{item.url}}">
{% endif %}
{% if use_icons and item.icon %} {% if use_icons and item.icon %}
<img src="{% thumbnail item.icon icon_size crop %}" class="icon"> <img src="{% thumbnail item.icon icon_size crop %}" class="icon">
{% endif %} {% endif %}
@ -15,6 +18,9 @@
{% if item.text %} {% if item.text %}
<small>{{ item.text }}</small> <small>{{ item.text }}</small>
{% endif %} {% endif %}
{% if item.url %}
</a>
{% endif %}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@ -1,6 +1,5 @@
import re import re
from django.templatetags.static import static from django.templatetags.static import static
from django.shortcuts import render from django.shortcuts import render
from django.template.loader import render_to_string from django.template.loader import render_to_string
@ -19,7 +18,7 @@ class PostBaseView:
embed = False # page is embed (if True, only post content is printed embed = False # page is embed (if True, only post content is printed
classes = '' # extra classes for the content classes = '' # extra classes for the content
def get_base_context (self): def get_base_context (self, **kwargs):
""" """
Return a context with all attributes of this classe plus 'view' set Return a context with all attributes of this classe plus 'view' set
to self. to self.
@ -32,7 +31,7 @@ class PostBaseView:
if not self.embed: if not self.embed:
context['menus'] = { context['menus'] = {
k: v.get(self.request) k: v.get(self.request, **kwargs)
for k, v in { for k, v in {
k: self.website.get_menu(k) k: self.website.get_menu(k)
for k in self.website.menu_layouts for k in self.website.menu_layouts
@ -70,10 +69,12 @@ class PostListView (PostBaseView, ListView):
template_name = 'aircox_cms/list.html' template_name = 'aircox_cms/list.html'
allow_empty = True allow_empty = True
model = None
route = None route = None
query = None query = None
fields = [ 'date', 'time', 'image', 'title', 'content' ] fields = [ 'date', 'time', 'image', 'title', 'content' ]
icon_size = '64x64'
def __init__ (self, *args, **kwargs): def __init__ (self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -84,7 +85,11 @@ class PostListView (PostBaseView, ListView):
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def get_queryset (self): def get_queryset (self):
qs = self.route.get_queryset(self.model, self.request, **self.kwargs) if self.route:
qs = self.route.get_queryset(self.website, self.model, self.request,
**self.kwargs)
else:
qs = self.queryset or self.model.objects.all()
query = self.query query = self.query
query.update(self.request.GET) query.update(self.request.GET)
@ -156,7 +161,7 @@ class PostDetailView (DetailView, PostBaseView):
context.update(self.get_base_context()) context.update(self.get_base_context())
context.update({ context.update({
'sections': [ 'sections': [
section.get(self.request, object = self.object) section.get(self.request, **kwargs)
for section in self.sections for section in self.sections
] ]
}) })
@ -184,7 +189,7 @@ class Menu (View):
'classes': self.classes, 'classes': self.classes,
'position': self.position, 'position': self.position,
'sections': [ 'sections': [
section.get(self.request, object = None) section.get(self.request, object = None, **kwargs)
for section in self.sections for section in self.sections
] ]
} }
@ -202,36 +207,48 @@ class BaseSection (View):
in order to have extra content about a post, or in menus. in order to have extra content about a post, or in menus.
""" """
template_name = 'aircox_cms/base_section.html' template_name = 'aircox_cms/base_section.html'
kwargs = None # kwargs argument passed to get
tag = 'div' # container tags tag = 'div' # container tags
classes = '' # container classes classes = '' # container classes
attrs = '' # container extra attributes attrs = '' # container extra attributes
content = '' # content content = '' # content
visible = True # if false renders an empty string
def get_context_data (self, **kwargs): def get_context_data (self):
return { return {
'view': self,
'tag': self.tag, 'tag': self.tag,
'classes': self.classes, 'classes': self.classes,
'attrs': self.attrs, 'attrs': self.attrs,
'visible': self.visible,
'content': self.content, 'content': self.content,
} }
def get (self, request, **kwargs): def get (self, request, **kwargs):
self.request = request self.request = request
context = self.get_context_data(**kwargs) self.kwargs = kwargs
context = self.get_context_data()
# get_context_data may call extra function that can change visibility
if self.visible:
return render_to_string(self.template_name, context) return render_to_string(self.template_name, context)
return ''
class Section (BaseSection): class Section (BaseSection):
"""
A Section that can be related to an object.
"""
template_name = 'aircox_cms/section.html' template_name = 'aircox_cms/section.html'
require_object = False
object = None object = None
object_required = False
title = '' title = ''
header = '' header = ''
bottom = '' bottom = ''
def get_context_data (self, **kwargs): def get_context_data (self):
context = super().get_context_data(**kwargs) context = super().get_context_data()
context.update({ context.update({
'title': self.title, 'title': self.title,
'header': self.header, 'header': self.header,
@ -239,14 +256,20 @@ class Section (BaseSection):
}) })
return context return context
def get (self, request, **kwargs): def get (self, request, object = None, **kwargs):
self.object = kwargs.get('object') or self.object self.object = object or self.object
if self.object_required and not self.object:
raise ValueError('object is required by this Section but not given')
return super().get(request, **kwargs) return super().get(request, **kwargs)
class Sections: class Sections:
class Image (BaseSection): class Image (BaseSection):
url = None # relative url to the image """
Render an image with the given relative url.
"""
url = None
@property @property
def content (self): def content (self):
@ -256,6 +279,10 @@ class Sections:
class PostContent (Section): class PostContent (Section):
"""
Render the content of the Post (format the text a bit and escape HTML
tags).
"""
@property @property
def content (self): def content (self):
content = escape(self.object.content) content = escape(self.object.content)
@ -265,6 +292,9 @@ class Sections:
class PostImage (Section): class PostImage (Section):
"""
Render the image of the Post
"""
@property @property
def content (self): def content (self):
return '<img src="{}" class="post_image">'.format( return '<img src="{}" class="post_image">'.format(
@ -281,59 +311,78 @@ class Sections:
icon = None icon = None
title = None title = None
text = None text = None
url = None
def __init__ (self, icon, title = None, text = None): def __init__ (self, icon, title = None, text = None, url = None):
self.icon = icon self.icon = icon
self.title = title self.title = title
self.text = text self.text = text
use_icons = True hide_empty = False # hides the section if the list is empty
icon_size = '32x32' use_icons = True # print icons
icon_size = '32x32' # icons size
template_name = 'aircox_cms/section_list.html' template_name = 'aircox_cms/section_list.html'
def get_object_list (self): def get_object_list (self):
return [] return []
def get_context_data (self, **kwargs): def get_context_data (self, **kwargs):
object_list = self.get_object_list()
self.visibility = True
if not object_list and hide_empty:
self.visibility = False
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context.update({ context.update({
'classes': context.get('classes') + ' section_list', 'classes': context.get('classes') + ' section_list',
'icon_size': self.icon_size, 'icon_size': self.icon_size,
'object_list': self.get_object_list(), 'object_list': object_list,
}) })
return context return context
class UrlList (List): class Urls (List):
"""
Render a list of urls of targets that are Posts
"""
classes = 'section_urls' classes = 'section_urls'
targets = None targets = None
def get_object_list (self, request, **kwargs): def get_object_list (self):
return [ return [
List.Item( List.Item(
target.image or None, target.image or None,
'<a href="{}">{}</a>'.format(target.detail_url(), target.title) target.title,
url = target.detail_url(),
) )
for target in self.targets for target in self.targets
] ]
class Posts (PostBaseView, Section):
class PostList (PostListView): """
route = None Render a list using PostListView's template.
model = None """
embed = True embed = True
icon_size = '64x64'
fields = [ 'date', 'time', 'image', 'title', 'content' ]
def __init__ (self, *args, **kwargs): def get_object_list (self):
super().__init__(*args, **kwargs) return []
def get_kwargs (self, request, **kwargs): def render_list (self):
return kwargs self.embed = True
context = self.get_base_context(**self.kwargs)
def dispatch (self, request, *args, **kwargs): context.update({
kwargs = self.get_kwargs(kwargs) 'object_list': self.get_object_list(),
response = super().dispatch(request, *args, **kwargs) 'embed': True,
return str(response.content) })
print(context['object_list'][0].image)
return render_to_string(PostListView.template_name, context)
def get_context_data (self, **kwargs):
context = super().get_context_data(**kwargs)
context['content'] = self.render_list()
return context
class ViewSet: class ViewSet:
@ -348,15 +397,25 @@ class ViewSet:
detail_view = PostDetailView detail_view = PostDetailView
detail_sections = [ detail_sections = [
Sections.PostContent, Sections.PostContent(),
Sections.PostImage, Sections.PostImage(),
] ]
def __init__ (self, website = None):
self.detail_sections = [ def get_list_name (self):
section() """
for section in self.detail_sections Return a string with the name to use in the route for the lists
] """
return self.model._meta.verbose_name_plural.lower()
@classmethod
def get_detail_name (cl):
"""
Return a string with the name to use in the route for the details
"""
return cl.model._meta.verbose_name.lower()
def connect (self, website = None):
self.detail_view = self.detail_view.as_view( self.detail_view = self.detail_view.as_view(
model = self.model, model = self.model,
sections = self.detail_sections, sections = self.detail_sections,
@ -367,8 +426,9 @@ class ViewSet:
website = website, website = website,
) )
self.urls = [ route.as_url(self.model, self.list_view) self.urls = [ route.as_url(self.model, self.get_list_name(),
for route in self.list_routes ] + \ self.list_view) for route in self.list_routes ] + \
[ routes.DetailRoute.as_url(self.model, self.detail_view ) ] [ routes.DetailRoute.as_url(self.model,
self.get_detail_name(), self.detail_view ) ]

View File

@ -12,6 +12,7 @@ class Website:
'right', 'bottom', 'right', 'bottom',
'header', 'footer'] 'header', 'footer']
router = None router = None
registry = {}
@property @property
def urls (self): def urls (self):
@ -27,7 +28,9 @@ class Website:
Register a ViewSet (or subclass) to the router, Register a ViewSet (or subclass) to the router,
and connect it to self. and connect it to self.
""" """
view_set = view_set(website = self) view_set = view_set()
view_set.connect(website = self)
self.registry[view_set.get_detail_name()] = view_set
self.router.register_set(view_set) self.router.register_set(view_set)
def get_menu (self, position): def get_menu (self, position):

View File

@ -71,7 +71,7 @@ class ProgramAdmin (NameableAdmin):
@admin.register(Episode) @admin.register(Episode)
class EpisodeAdmin (NameableAdmin): class EpisodeAdmin (NameableAdmin):
list_filter = ['program'] + NameableAdmin.list_filter list_filter = ['program'] + NameableAdmin.list_filter
fields = NameableAdmin.fields + ['sounds'] fields = NameableAdmin.fields + ['sounds', 'program']
inlines = (TrackInline, DiffusionInline) inlines = (TrackInline, DiffusionInline)

View File

@ -20,7 +20,7 @@ def add_inline (base_model, post_model, prepend = False):
'fields': ['title', 'content', 'image', 'tags'] 'fields': ['title', 'content', 'image', 'tags']
}), }),
(None, { (None, {
'fields': ['date', 'published', 'author', 'thread'] 'fields': ['date', 'published', 'author', 'thread_pk', 'thread_type']
}) })
] ]
@ -40,6 +40,8 @@ def add_inline (base_model, post_model, prepend = False):
add_inline(programs.Program, Program, True) add_inline(programs.Program, Program, True)
add_inline(programs.Episode, Episode, True) add_inline(programs.Episode, Episode, True)
admin.site.register(Program)
admin.site.register(Episode)
#class ArticleAdmin (DescriptionAdmin): #class ArticleAdmin (DescriptionAdmin):
# fieldsets = copy.deepcopy(DescriptionAdmin.fieldsets) # fieldsets = copy.deepcopy(DescriptionAdmin.fieldsets)

View File

@ -6,6 +6,7 @@ import aircox_programs.models as programs
class Program (RelatedPost): class Program (RelatedPost):
class Relation: class Relation:
related_model = programs.Program related_model = programs.Program
bind_mapping = True
mapping = { mapping = {
'title': 'name', 'title': 'name',
'content': 'description', 'content': 'description',
@ -14,6 +15,7 @@ class Program (RelatedPost):
class Episode (RelatedPost): class Episode (RelatedPost):
class Relation: class Relation:
related_model = programs.Episode related_model = programs.Episode
bind_mapping = True
mapping = { mapping = {
'title': 'name', 'title': 'name',
'content': 'description', 'content': 'description',

View File

@ -9,6 +9,20 @@ h1, h2, h3 {
font-family: "Myriad Pro",Calibri,Helvetica,Arial,sans-serif font-family: "Myriad Pro",Calibri,Helvetica,Arial,sans-serif
} }
time {
font-size: 0.9em;
color: #616161;
}
a {
text-decoration: none;
color: #616161;
}
a:hover {
color: #818181;
}
nav.menu { nav.menu {
padding: 0.5em; padding: 0.5em;
@ -18,27 +32,64 @@ nav.menu_top {
background-color: #212121; background-color: #212121;
color: #007EDF; color: #007EDF;
font-size: 1.1em; font-size: 1.1em;
box-shadow: 0em 0.2em 0.5em 0.1em black
} }
header { header.menu {
padding: 0.2em;
height: 9em; height: 9em;
} }
header img { header.menu img {
height: 100%; height: 100%;
float: left; float: left;
} }
header .colony { #colony img {
height: auto;
position: fixed; position: fixed;
top: 0em; top: 1em;
right: 0; right: 0;
z-index: -1; z-index: -1;
} }
.page { .page {
width: 100%; width: 100%;
padding: 1.5em 0em;
background-color: rgba(255, 255, 255, 0.8); background-color: rgba(255, 255, 255, 0.8);
} }
.page img {
box-shadow: 0em 0em 0.2em 0.01em black;
border-radius: 0.2em;
}
.post_list {
background-color: #F2F2F2;
box-shadow: inset 0.2em 0.2em 0.2em 0.01em black;
}
.post_list .post_item {
min-height: 64px;
padding: 0.2em;
}
.post_list .post_item:hover {
}
.post_list h3 {
margin: 0.2em;
}
.post_list time {
float: right;
}
.post_list img {
float: left;
margin-right: 0.5em;
}

View File

@ -18,7 +18,8 @@ class ProgramSet (ViewSet):
] ]
detail_sections = ViewSet.detail_sections + [ detail_sections = ViewSet.detail_sections + [
ScheduleSection, ScheduleSection(),
EpisodesSection(),
] ]
class EpisodeSet (ViewSet): class EpisodeSet (ViewSet):
@ -49,7 +50,7 @@ website = Website(
position = 'header', position = 'header',
sections = [ sections = [
Sections.Image(url = 'website/logo.png'), Sections.Image(url = 'website/logo.png'),
Sections.Image(url = 'website/colony.png', classes='colony'), Sections.Image(url = 'website/colony.png', attrs = { 'id': 'colony' }),
] ]
), ),

View File

@ -8,6 +8,9 @@ from django.utils.translation import ugettext as _, ugettext_lazy
import aircox_programs.models as programs import aircox_programs.models as programs
from aircox_cms.views import Sections from aircox_cms.views import Sections
from website.models import *
class PlayListSection (Sections.List): class PlayListSection (Sections.List):
title = _('Playlist') title = _('Playlist')
@ -34,4 +37,10 @@ class ScheduleSection (Sections.List):
] ]
class EpisodesSection (Sections.Posts):
title = _('Episodes')
def get_object_list (self):
return Episode.objects.filter(related__program = self.object.pk)