update sections, work a bit on style
This commit is contained in:
parent
cfdc9b6de0
commit
cde58334bd
|
@ -3,8 +3,8 @@ Platform to manage radio programs, schedules, cms, etc. -- main test repo
|
|||
|
||||
# Applications
|
||||
* **programs**: programs, episodes, schedules, sounds and tracks;
|
||||
* **streams**: streams and diffusions, links with LiquidSoap;
|
||||
* **website**: website rendering, using models defined by the previous apps;
|
||||
* **cms**: cms renderer
|
||||
* **website**: the website using the cms and the programs
|
||||
|
||||
|
||||
# Code and names conventions and uses
|
||||
|
|
|
@ -12,57 +12,18 @@ from django.dispatch import receiver
|
|||
|
||||
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):
|
||||
thread = models.ForeignKey(
|
||||
Thread,
|
||||
thread_type = models.ForeignKey(
|
||||
ContentType,
|
||||
on_delete=models.SET_NULL,
|
||||
blank = True, null = True,
|
||||
help_text = _('the publication is posted on this thread'),
|
||||
blank = True, null = True
|
||||
)
|
||||
thread_pk = models.PositiveIntegerField(
|
||||
blank = True, null = True
|
||||
)
|
||||
thread = GenericForeignKey('thread_type', 'thread_pk')
|
||||
|
||||
author = models.ForeignKey(
|
||||
User,
|
||||
verbose_name = _('author'),
|
||||
|
@ -94,7 +55,7 @@ class Post (models.Model):
|
|||
)
|
||||
|
||||
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,
|
||||
'slug': slugify(self.title) })
|
||||
|
||||
|
@ -151,7 +112,6 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
|
|||
mapping = None # dict of related mapping values
|
||||
bind_mapping = False # update fields of related data on save
|
||||
|
||||
|
||||
def get_attribute (self, attr):
|
||||
attr = self._relation.mappings.get(attr)
|
||||
return self.related.__dict__[attr] if attr else None
|
||||
|
@ -163,32 +123,10 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
|
|||
if self._relation.bind_mapping:
|
||||
self.related.__dict__.update({
|
||||
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()
|
||||
|
||||
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
|
||||
|
||||
|
|
4
aircox_cms/requirements.txt
Normal file
4
aircox_cms/requirements.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
Django>=1.9.0
|
||||
django-taggit>=0.12.1
|
||||
easy_thumbnails
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
from django.conf.urls import url
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils import timezone
|
||||
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
|
||||
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).
|
||||
|
||||
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),... ]
|
||||
|
||||
@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
|
||||
"""
|
||||
pass
|
||||
|
||||
@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
|
||||
"""
|
||||
|
@ -56,10 +57,8 @@ class Route:
|
|||
return ''
|
||||
|
||||
@classmethod
|
||||
def as_url (cl, model, view, view_kwargs = None):
|
||||
base_name = model._meta.verbose_name_plural.lower()
|
||||
|
||||
pattern = '^{}/{}'.format(base_name, cl.name)
|
||||
def as_url (cl, model, model_name, view, view_kwargs = None):
|
||||
pattern = '^{}/{}'.format(model_name, cl.name)
|
||||
if cl.url_args:
|
||||
url_args = '/'.join([
|
||||
'(?P<{}>{}){}'.format(
|
||||
|
@ -78,7 +77,7 @@ class Route:
|
|||
kwargs.update(view_kwargs)
|
||||
|
||||
return url(pattern, view, kwargs = kwargs,
|
||||
name = base_name + '_' + cl.name)
|
||||
name = model_name + '_' + cl.name)
|
||||
|
||||
|
||||
class DetailRoute (Route):
|
||||
|
@ -89,7 +88,7 @@ class DetailRoute (Route):
|
|||
]
|
||||
|
||||
@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))
|
||||
|
||||
|
||||
|
@ -97,7 +96,7 @@ class AllRoute (Route):
|
|||
name = 'all'
|
||||
|
||||
@classmethod
|
||||
def get_queryset (cl, model, request, **kwargs):
|
||||
def get_queryset (cl, website, model, request, **kwargs):
|
||||
return model.objects.all()
|
||||
|
||||
@classmethod
|
||||
|
@ -108,14 +107,32 @@ class AllRoute (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'
|
||||
url_args = [
|
||||
('thread_model', '(\w|_|-)+'),
|
||||
('pk', '[0-9]+'),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def get_queryset (cl, model, request, pk, **kwargs):
|
||||
return model.objects.filter(thread__pk = int(pk))
|
||||
def get_queryset (cl, website, model, request, thread_model, pk, **kwargs):
|
||||
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):
|
||||
|
@ -127,7 +144,7 @@ class DateRoute (Route):
|
|||
]
|
||||
|
||||
@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(
|
||||
date__year = int(year),
|
||||
date__month = int(month),
|
||||
|
@ -139,7 +156,7 @@ class SearchRoute (Route):
|
|||
name = 'search'
|
||||
|
||||
@classmethod
|
||||
def get_queryset (cl, model, request, **kwargs):
|
||||
def get_queryset (cl, website, model, request, **kwargs):
|
||||
q = request.GET.get('q') or ''
|
||||
qs = model.objects
|
||||
for search_field in model.search_fields or []:
|
||||
|
@ -151,4 +168,3 @@ class SearchRoute (Route):
|
|||
return qs
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -20,9 +20,13 @@ body {
|
|||
}
|
||||
|
||||
|
||||
.section {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
main .section {
|
||||
width: calc(50% - 1em);
|
||||
float: left;
|
||||
width: calc(50% - 2em);
|
||||
display: inline-block;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
|
@ -39,3 +43,8 @@ main .section {
|
|||
}
|
||||
|
||||
|
||||
.post_item {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -12,23 +12,23 @@
|
|||
<time datetime="{{ post.date }}" class="post_datetime">
|
||||
{% if 'date' in view.fields %}
|
||||
<span class="post_date">
|
||||
{{ post.date|date:'D. d F' }},
|
||||
{{ post.date|date:'D. d F' }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if 'time' in view.fields %}
|
||||
<span class="post_time">
|
||||
{{ post.date|date:'H:i' }},
|
||||
{{ post.date|date:'H:i' }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</time>
|
||||
{% endif %}
|
||||
|
||||
{% 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 %}
|
||||
|
||||
{% if 'title' in view.fields %}
|
||||
<h4 class="post_title">{{ post.title }}</h4>
|
||||
<h3 class="post_title">{{ post.title }}</h3>
|
||||
{% endif %}
|
||||
|
||||
{% if 'content' in view.fields %}
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
<ul style="padding:0; margin:0">
|
||||
{% for item in object_list %}
|
||||
<li>
|
||||
{% if item.url %}
|
||||
<a href="{{item.url}}">
|
||||
{% endif %}
|
||||
{% if use_icons and item.icon %}
|
||||
<img src="{% thumbnail item.icon icon_size crop %}" class="icon">
|
||||
{% endif %}
|
||||
|
@ -15,6 +18,9 @@
|
|||
{% if item.text %}
|
||||
<small>{{ item.text }}</small>
|
||||
{% endif %}
|
||||
{% if item.url %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import re
|
||||
|
||||
|
||||
from django.templatetags.static import static
|
||||
from django.shortcuts import render
|
||||
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
|
||||
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
|
||||
to self.
|
||||
|
@ -32,7 +31,7 @@ class PostBaseView:
|
|||
|
||||
if not self.embed:
|
||||
context['menus'] = {
|
||||
k: v.get(self.request)
|
||||
k: v.get(self.request, **kwargs)
|
||||
for k, v in {
|
||||
k: self.website.get_menu(k)
|
||||
for k in self.website.menu_layouts
|
||||
|
@ -70,10 +69,12 @@ class PostListView (PostBaseView, ListView):
|
|||
|
||||
template_name = 'aircox_cms/list.html'
|
||||
allow_empty = True
|
||||
model = None
|
||||
|
||||
route = None
|
||||
query = None
|
||||
fields = [ 'date', 'time', 'image', 'title', 'content' ]
|
||||
icon_size = '64x64'
|
||||
|
||||
def __init__ (self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -84,7 +85,11 @@ class PostListView (PostBaseView, ListView):
|
|||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
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.update(self.request.GET)
|
||||
|
@ -156,7 +161,7 @@ class PostDetailView (DetailView, PostBaseView):
|
|||
context.update(self.get_base_context())
|
||||
context.update({
|
||||
'sections': [
|
||||
section.get(self.request, object = self.object)
|
||||
section.get(self.request, **kwargs)
|
||||
for section in self.sections
|
||||
]
|
||||
})
|
||||
|
@ -184,7 +189,7 @@ class Menu (View):
|
|||
'classes': self.classes,
|
||||
'position': self.position,
|
||||
'sections': [
|
||||
section.get(self.request, object = None)
|
||||
section.get(self.request, object = None, **kwargs)
|
||||
for section in self.sections
|
||||
]
|
||||
}
|
||||
|
@ -202,36 +207,48 @@ class BaseSection (View):
|
|||
in order to have extra content about a post, or in menus.
|
||||
"""
|
||||
template_name = 'aircox_cms/base_section.html'
|
||||
tag = 'div' # container tags
|
||||
kwargs = None # kwargs argument passed to get
|
||||
tag = 'div' # container tags
|
||||
classes = '' # container classes
|
||||
attrs = '' # container extra attributes
|
||||
content = '' # content
|
||||
visible = True # if false renders an empty string
|
||||
|
||||
|
||||
def get_context_data (self, **kwargs):
|
||||
def get_context_data (self):
|
||||
return {
|
||||
'view': self,
|
||||
'tag': self.tag,
|
||||
'classes': self.classes,
|
||||
'attrs': self.attrs,
|
||||
'visible': self.visible,
|
||||
'content': self.content,
|
||||
}
|
||||
|
||||
def get (self, request, **kwargs):
|
||||
self.request = request
|
||||
context = self.get_context_data(**kwargs)
|
||||
return render_to_string(self.template_name, context)
|
||||
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 ''
|
||||
|
||||
|
||||
class Section (BaseSection):
|
||||
"""
|
||||
A Section that can be related to an object.
|
||||
"""
|
||||
template_name = 'aircox_cms/section.html'
|
||||
require_object = False
|
||||
object = None
|
||||
object_required = False
|
||||
title = ''
|
||||
header = ''
|
||||
bottom = ''
|
||||
|
||||
def get_context_data (self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
def get_context_data (self):
|
||||
context = super().get_context_data()
|
||||
context.update({
|
||||
'title': self.title,
|
||||
'header': self.header,
|
||||
|
@ -239,14 +256,20 @@ class Section (BaseSection):
|
|||
})
|
||||
return context
|
||||
|
||||
def get (self, request, **kwargs):
|
||||
self.object = kwargs.get('object') or self.object
|
||||
def get (self, request, object = None, **kwargs):
|
||||
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)
|
||||
|
||||
|
||||
class Sections:
|
||||
class Image (BaseSection):
|
||||
url = None # relative url to the image
|
||||
"""
|
||||
Render an image with the given relative url.
|
||||
"""
|
||||
url = None
|
||||
|
||||
@property
|
||||
def content (self):
|
||||
|
@ -256,6 +279,10 @@ class Sections:
|
|||
|
||||
|
||||
class PostContent (Section):
|
||||
"""
|
||||
Render the content of the Post (format the text a bit and escape HTML
|
||||
tags).
|
||||
"""
|
||||
@property
|
||||
def content (self):
|
||||
content = escape(self.object.content)
|
||||
|
@ -265,6 +292,9 @@ class Sections:
|
|||
|
||||
|
||||
class PostImage (Section):
|
||||
"""
|
||||
Render the image of the Post
|
||||
"""
|
||||
@property
|
||||
def content (self):
|
||||
return '<img src="{}" class="post_image">'.format(
|
||||
|
@ -281,59 +311,78 @@ class Sections:
|
|||
icon = None
|
||||
title = 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.title = title
|
||||
self.text = text
|
||||
|
||||
use_icons = True
|
||||
icon_size = '32x32'
|
||||
hide_empty = False # hides the section if the list is empty
|
||||
use_icons = True # print icons
|
||||
icon_size = '32x32' # icons size
|
||||
template_name = 'aircox_cms/section_list.html'
|
||||
|
||||
def get_object_list (self):
|
||||
return []
|
||||
|
||||
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.update({
|
||||
'classes': context.get('classes') + ' section_list',
|
||||
'icon_size': self.icon_size,
|
||||
'object_list': self.get_object_list(),
|
||||
'object_list': object_list,
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class UrlList (List):
|
||||
class Urls (List):
|
||||
"""
|
||||
Render a list of urls of targets that are Posts
|
||||
"""
|
||||
classes = 'section_urls'
|
||||
targets = None
|
||||
|
||||
def get_object_list (self, request, **kwargs):
|
||||
def get_object_list (self):
|
||||
return [
|
||||
List.Item(
|
||||
target.image or None,
|
||||
'<a href="{}">{}</a>'.format(target.detail_url(), target.title)
|
||||
target.title,
|
||||
url = target.detail_url(),
|
||||
)
|
||||
for target in self.targets
|
||||
]
|
||||
|
||||
|
||||
class PostList (PostListView):
|
||||
route = None
|
||||
model = None
|
||||
class Posts (PostBaseView, Section):
|
||||
"""
|
||||
Render a list using PostListView's template.
|
||||
"""
|
||||
embed = True
|
||||
icon_size = '64x64'
|
||||
fields = [ 'date', 'time', 'image', 'title', 'content' ]
|
||||
|
||||
def __init__ (self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def get_object_list (self):
|
||||
return []
|
||||
|
||||
def get_kwargs (self, request, **kwargs):
|
||||
return kwargs
|
||||
|
||||
def dispatch (self, request, *args, **kwargs):
|
||||
kwargs = self.get_kwargs(kwargs)
|
||||
response = super().dispatch(request, *args, **kwargs)
|
||||
return str(response.content)
|
||||
def render_list (self):
|
||||
self.embed = True
|
||||
context = self.get_base_context(**self.kwargs)
|
||||
context.update({
|
||||
'object_list': self.get_object_list(),
|
||||
'embed': True,
|
||||
})
|
||||
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:
|
||||
|
@ -348,15 +397,25 @@ class ViewSet:
|
|||
|
||||
detail_view = PostDetailView
|
||||
detail_sections = [
|
||||
Sections.PostContent,
|
||||
Sections.PostImage,
|
||||
Sections.PostContent(),
|
||||
Sections.PostImage(),
|
||||
]
|
||||
|
||||
def __init__ (self, website = None):
|
||||
self.detail_sections = [
|
||||
section()
|
||||
for section in self.detail_sections
|
||||
]
|
||||
|
||||
def get_list_name (self):
|
||||
"""
|
||||
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(
|
||||
model = self.model,
|
||||
sections = self.detail_sections,
|
||||
|
@ -367,8 +426,9 @@ class ViewSet:
|
|||
website = website,
|
||||
)
|
||||
|
||||
self.urls = [ route.as_url(self.model, self.list_view)
|
||||
for route in self.list_routes ] + \
|
||||
[ routes.DetailRoute.as_url(self.model, self.detail_view ) ]
|
||||
self.urls = [ route.as_url(self.model, self.get_list_name(),
|
||||
self.list_view) for route in self.list_routes ] + \
|
||||
[ routes.DetailRoute.as_url(self.model,
|
||||
self.get_detail_name(), self.detail_view ) ]
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ class Website:
|
|||
'right', 'bottom',
|
||||
'header', 'footer']
|
||||
router = None
|
||||
registry = {}
|
||||
|
||||
@property
|
||||
def urls (self):
|
||||
|
@ -27,7 +28,9 @@ class Website:
|
|||
Register a ViewSet (or subclass) to the router,
|
||||
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)
|
||||
|
||||
def get_menu (self, position):
|
||||
|
|
|
@ -71,7 +71,7 @@ class ProgramAdmin (NameableAdmin):
|
|||
@admin.register(Episode)
|
||||
class EpisodeAdmin (NameableAdmin):
|
||||
list_filter = ['program'] + NameableAdmin.list_filter
|
||||
fields = NameableAdmin.fields + ['sounds']
|
||||
fields = NameableAdmin.fields + ['sounds', 'program']
|
||||
|
||||
inlines = (TrackInline, DiffusionInline)
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ def add_inline (base_model, post_model, prepend = False):
|
|||
'fields': ['title', 'content', 'image', 'tags']
|
||||
}),
|
||||
(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.Episode, Episode, True)
|
||||
|
||||
admin.site.register(Program)
|
||||
admin.site.register(Episode)
|
||||
|
||||
#class ArticleAdmin (DescriptionAdmin):
|
||||
# fieldsets = copy.deepcopy(DescriptionAdmin.fieldsets)
|
||||
|
|
|
@ -6,6 +6,7 @@ import aircox_programs.models as programs
|
|||
class Program (RelatedPost):
|
||||
class Relation:
|
||||
related_model = programs.Program
|
||||
bind_mapping = True
|
||||
mapping = {
|
||||
'title': 'name',
|
||||
'content': 'description',
|
||||
|
@ -14,6 +15,7 @@ class Program (RelatedPost):
|
|||
class Episode (RelatedPost):
|
||||
class Relation:
|
||||
related_model = programs.Episode
|
||||
bind_mapping = True
|
||||
mapping = {
|
||||
'title': 'name',
|
||||
'content': 'description',
|
||||
|
|
|
@ -9,36 +9,87 @@ h1, h2, h3 {
|
|||
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 {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
nav.menu_top {
|
||||
background-color: #212121;
|
||||
color: #007EDF;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
nav.menu_top {
|
||||
background-color: #212121;
|
||||
color: #007EDF;
|
||||
font-size: 1.1em;
|
||||
box-shadow: 0em 0.2em 0.5em 0.1em black
|
||||
}
|
||||
|
||||
|
||||
header {
|
||||
header.menu {
|
||||
padding: 0.2em;
|
||||
height: 9em;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 100%;
|
||||
float: left;
|
||||
}
|
||||
header.menu img {
|
||||
height: 100%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
header .colony {
|
||||
position: fixed;
|
||||
top: 0em;
|
||||
right: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
#colony img {
|
||||
height: auto;
|
||||
position: fixed;
|
||||
top: 1em;
|
||||
right: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.page {
|
||||
width: 100%;
|
||||
padding: 1.5em 0em;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@ class ProgramSet (ViewSet):
|
|||
]
|
||||
|
||||
detail_sections = ViewSet.detail_sections + [
|
||||
ScheduleSection,
|
||||
ScheduleSection(),
|
||||
EpisodesSection(),
|
||||
]
|
||||
|
||||
class EpisodeSet (ViewSet):
|
||||
|
@ -49,7 +50,7 @@ website = Website(
|
|||
position = 'header',
|
||||
sections = [
|
||||
Sections.Image(url = 'website/logo.png'),
|
||||
Sections.Image(url = 'website/colony.png', classes='colony'),
|
||||
Sections.Image(url = 'website/colony.png', attrs = { 'id': 'colony' }),
|
||||
]
|
||||
),
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ from django.utils.translation import ugettext as _, ugettext_lazy
|
|||
import aircox_programs.models as programs
|
||||
from aircox_cms.views import Sections
|
||||
|
||||
from website.models import *
|
||||
|
||||
|
||||
|
||||
class PlayListSection (Sections.List):
|
||||
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)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user