work on sections

This commit is contained in:
bkfox 2015-10-04 22:05:10 +02:00
parent 60478aaf88
commit cfdc9b6de0
12 changed files with 372 additions and 211 deletions

View File

@ -1,10 +1,41 @@
body { padding: 0; margin: 0; } body {
padding: 0;
nav.menu_top { margin: 0;
width: 100%
position: absolute;
border-bottom: 1px grey solid;
} }
.page {
display: flex;
}
.page .menu {
width: 15em;
}
.page .menu_left { margin-right: 0.5em; }
.page .menu_right { margin-left: 0.5em; }
.page main {
flex-grow: 1;
}
main .section {
width: calc(50% - 1em);
float: left;
padding: 0.5em;
}
main .section .section_content {
font-size: 0.95em;
}
main .section h1 {
font-size: 1.2em;
}
main .section * {
max-width: 100%;
}

View File

@ -0,0 +1,9 @@
<{{ tag }} class="section {{ classes }}"
{% for key, value in attrs.items %}{{ key }} = "{{ value|addslashes }}"
{% endfor %} >
{% block content %}
{{ content|safe }}
{% endblock %}
</{{ tag }}>

View File

@ -8,22 +8,23 @@
<meta name="description" content="{{ website.description }}"> <meta name="description" content="{{ website.description }}">
<meta name="keywords" content="{{ website.tags }}"> <meta name="keywords" content="{{ website.tags }}">
<link rel="stylesheet" href="{% static "aircox_aircox_cms/styles.css" %}" type="text/css"> <link rel="stylesheet" href="{% static "aircox_cms/styles.css" %}" type="text/css">
{% if website.styles %}
<link rel="stylesheet" href="{% static website.styles %}" type="text/css">
{% endif %}
<title>{{ website.name }} {% if title %}- {{ title }} {% endif %}</title> <title>{{ website.name }} {% if title %}- {{ title }} {% endif %}</title>
</head> </head>
<body> <body>
{% block header %}
{% if menus.header %}
{{ menus.header|safe }}
{% endif %}
{% endblock %}
{% if menus.top %} {% if menus.top %}
{{ menus.top|safe }} {{ menus.top|safe }}
{% endif %} {% endif %}
{% block header %}
{% if menus.header %}
<header>
{{ menus.header|safe }}
</header>
{% endif %}
{% endblock %}
<div class="page"> <div class="page">
{% if menus.left %} {% if menus.left %}
{{ menus.left|safe }} {{ menus.left|safe }}
@ -50,11 +51,13 @@
{% endif %} {% endif %}
</div> </div>
{% if menus.page_bottom %}
{{ menus.page_bottom|safe }}
{% endif %}
{% block footer %} {% block footer %}
{% if menus.footer %} {% if menus.footer %}
<footer> {{ menus.footer|safe }}
{{ menus.footer|safe }}
</footer>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -1,10 +1,10 @@
<nav class="menu menu_{{ position }} {{ classes }}" {% if name %} <{{ tag }} class="menu menu_{{ position }} {{ classes }}" {% if name %}
name="menu_{{ name }}" name="{{ name }}"
id="menu_{{ name }}" id="{{ name }}"
{% endif %}> {% endif %}>
{% for section in sections %} {% for section in sections %}
{{ section|safe }} {{ section|safe }}
{% endfor %} {% endfor %}
</nav> </{{ tag }}>

View File

@ -1,33 +1,34 @@
<div class="section {{ classes }}"> {% extends "aircox_cms/base_section.html" %}
{% if title %}
<h1>
{% block section_title %}
{{ title }}
{% endblock %}
</h1>
{% endif %}
{% if header %} {% block content %}
<header class="section_header"> {% if title %}
{% block section_header %} <h1>
{{ header }} {% block section_title %}
{% endblock %} {{ title }}
</header> {% endblock %}
{% endif %} </h1>
{% endif %}
<div class="section_content"> {% if header %}
{% block section_content %} <header class="section_header">
{{ content|safe }} {% block section_header %}
{% endblock %} {{ header }}
</div> {% endblock %}
</header>
{% endif %}
{% if bottom %} <div class="section_content">
<div class="section_bottom"> {% block section_content %}
{% block section_bottom %} {{ content|safe }}
{{ bottom }} {% endblock %}
{% endblock %}
</div>
{% endif %}
</div> </div>
{% if bottom %}
<div class="section_bottom">
{% block section_bottom %}
{{ bottom }}
{% endblock %}
</div>
{% endif %}
{% endblock %}

View File

@ -1,9 +1,14 @@
import re
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
from django.views.generic import ListView, DetailView from django.views.generic import ListView, DetailView
from django.views.generic.base import View, TemplateResponseMixin from django.views.generic.base import View, TemplateResponseMixin
from django.core import serializers from django.core import serializers
from django.utils.translation import ugettext as _, ugettext_lazy from django.utils.translation import ugettext as _, ugettext_lazy
from django.utils.html import escape
import aircox_cms.routes as routes import aircox_cms.routes as routes
@ -29,12 +34,8 @@ class PostBaseView:
context['menus'] = { context['menus'] = {
k: v.get(self.request) k: v.get(self.request)
for k, v in { for k, v in {
'top': self.website.get_menu('top'), k: self.website.get_menu(k)
'left': self.website.get_menu('left'), for k in self.website.menu_layouts
'bottom': self.website.get_menu('bottom'),
'right': self.website.get_menu('right'),
'header': self.website.get_menu('header'),
'footer': self.website.get_menu('footer'),
}.items() if v }.items() if v
} }
@ -162,6 +163,179 @@ class PostDetailView (DetailView, PostBaseView):
return context return context
class Menu (View):
template_name = 'aircox_cms/menu.html'
name = ''
tag = 'nav'
enabled = True
classes = ''
position = '' # top, left, bottom, right, header, footer, page_top, page_bottom
sections = None
def __init__ (self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.name = self.name or ('menu_' + self.position)
def get_context_data (self, **kwargs):
return {
'name': self.name,
'tag': self.tag,
'classes': self.classes,
'position': self.position,
'sections': [
section.get(self.request, object = None)
for section in self.sections
]
}
def get (self, request, **kwargs):
self.request = request
context = self.get_context_data(**kwargs)
return render_to_string(self.template_name, context)
class BaseSection (View):
"""
Base class for sections. Sections are view that can be used in detail view
in order to have extra content about a post, or in menus.
"""
template_name = 'aircox_cms/base_section.html'
tag = 'div' # container tags
classes = '' # container classes
attrs = '' # container extra attributes
content = '' # content
def get_context_data (self, **kwargs):
return {
'tag': self.tag,
'classes': self.classes,
'attrs': self.attrs,
'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)
class Section (BaseSection):
template_name = 'aircox_cms/section.html'
require_object = False
object = None
title = ''
header = ''
bottom = ''
def get_context_data (self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'title': self.title,
'header': self.header,
'bottom': self.bottom,
})
return context
def get (self, request, **kwargs):
self.object = kwargs.get('object') or self.object
return super().get(request, **kwargs)
class Sections:
class Image (BaseSection):
url = None # relative url to the image
@property
def content (self):
return '<img src="{}">'.format(
static(self.url),
)
class PostContent (Section):
@property
def content (self):
content = escape(self.object.content)
content = re.sub(r'(^|\n\n)((\n?[^\n])+)', r'<p>\2</p>', content)
content = re.sub(r'\n', r'<br>', content)
return content
class PostImage (Section):
@property
def content (self):
return '<img src="{}" class="post_image">'.format(
self.object.image.url
)
class List (Section):
"""
Section to render list. The context item 'object_list' is used as list of
items to render.
"""
class Item:
icon = None
title = None
text = None
def __init__ (self, icon, title = None, text = None):
self.icon = icon
self.title = title
self.text = text
use_icons = True
icon_size = '32x32'
template_name = 'aircox_cms/section_list.html'
def get_object_list (self):
return []
def get_context_data (self, **kwargs):
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(),
})
return context
class UrlList (List):
classes = 'section_urls'
targets = None
def get_object_list (self, request, **kwargs):
return [
List.Item(
target.image or None,
'<a href="{}">{}</a>'.format(target.detail_url(), target.title)
)
for target in self.targets
]
class PostList (PostListView):
route = None
model = None
embed = True
def __init__ (self, *args, **kwargs):
super().__init__(*args, **kwargs)
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)
class ViewSet: class ViewSet:
""" """
A ViewSet is a class helper that groups detail and list views that can be A ViewSet is a class helper that groups detail and list views that can be
@ -173,7 +347,10 @@ class ViewSet:
list_routes = [] list_routes = []
detail_view = PostDetailView detail_view = PostDetailView
detail_sections = [] detail_sections = [
Sections.PostContent,
Sections.PostImage,
]
def __init__ (self, website = None): def __init__ (self, website = None):
self.detail_sections = [ self.detail_sections = [
@ -195,131 +372,3 @@ class ViewSet:
[ routes.DetailRoute.as_url(self.model, self.detail_view ) ] [ routes.DetailRoute.as_url(self.model, self.detail_view ) ]
class Menu (View):
template_name = 'aircox_cms/menu.html'
name = ''
enabled = True
classes = ''
position = '' # top, left, bottom, right, header, footer
sections = None
def __init__ (self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.name = self.name or ('menu_' + self.position)
def get_context_data (self, **kwargs):
return {
'name': self.name,
'classes': self.classes,
'position': self.position,
'sections': [
section.get(self.request, object = None)
for section in self.sections
]
}
def get (self, request, **kwargs):
self.request = request
context = self.get_context_data(**kwargs)
return render_to_string(self.template_name, context)
class Section (View):
"""
Base class for sections. Sections are view that can be used in detail view
in order to have extra content about a post.
"""
template_name = 'aircox_cms/section.html'
require_object = False
object = None
classes = ''
title = ''
content = ''
header = ''
bottom = ''
def get_context_data (self, **kwargs):
context = {
'title': self.title,
'header': self.header,
'content': self.content,
'bottom': self.bottom,
'classes': self.classes,
}
return context
def get (self, request, **kwargs):
self.object = kwargs.get('object') or self.object
self.request = request
context = self.get_context_data(**kwargs)
return render_to_string(self.template_name, context)
class ListSection (Section):
"""
Section to render list. The context item 'object_list' is used as list of
items to render.
"""
class Item:
icon = None
title = None
text = None
def __init__ (self, icon, title = None, text = None):
self.icon = icon
self.title = title
self.text = text
use_icons = True
icon_size = '32x32'
template_name = 'aircox_cms/section_list.html'
def get_object_list (self):
return []
def get_context_data (self, **kwargs):
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(),
})
return context
class UrlListSection (ListSection):
classes = 'section_urls'
targets = None
def get_object_list (self, request, **kwargs):
return [
ListSection.Item(
target.image or None,
'<a href="{}">{}</a>'.format(target.detail_url(), target.title)
)
for target in self.targets
]
class PostListSection (PostListView):
route = None
model = None
embed = True
def __init__ (self, *args, **kwargs):
super().__init__(*args, **kwargs)
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)
# TODO:
# - get_title: pass object / queryset

View File

@ -6,38 +6,46 @@ class Website:
description = 'An aircox website' # public description (used in meta info) description = 'An aircox website' # public description (used in meta info)
tags = 'aircox,radio,music' # public keywords (used in meta info) tags = 'aircox,radio,music' # public keywords (used in meta info)
logo = None styles = '' # relative url to stylesheet file
menus = None menus = None # list of menus
menu_layouts = ['top', 'left', # available positions
'right', 'bottom',
'header', 'footer']
router = None router = None
@property
def urls (self):
return self.router.get_urlpatterns()
def __init__ (self, **kwargs): def __init__ (self, **kwargs):
self.__dict__.update(kwargs) self.__dict__.update(kwargs)
if not self.router: if not self.router:
self.router = routes.Router() self.router = routes.Router()
def register_set (self, view_set): def register_set (self, view_set):
"""
Register a ViewSet (or subclass) to the router,
and connect it to self.
"""
view_set = view_set(website = self) view_set = view_set(website = self)
self.router.register_set(view_set) self.router.register_set(view_set)
def get_menu (self, position): def get_menu (self, position):
"""
Get an enabled menu by its position
"""
for menu in self.menus: for menu in self.menus:
if menu.enabled and menu.position == position: if menu.enabled and menu.position == position:
self.check_menu_tag(menu)
return menu return menu
def get_top_menu (self): def check_menu_tag (self, menu):
return self.get_menu('top') """
Update menu tag if it is a footer or a header
def get_left_menu (self): """
return self.get_menu('left') if menu.position in ('footer','header'):
menu.tag = menu.position
def get_bottom_menu (self): if menu.position in ('left', 'right'):
return self.get_menu('bottom') menu.tag = 'side'
def get_right_menu (self):
return self.get_menu('right')
@property
def urls (self):
return self.router.get_urlpatterns()

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@ -0,0 +1,44 @@
body {
background-color: #F2F2F2;
font-family: "Myriad Pro",Calibri,Helvetica,Arial,sans-serif;
}
h1, h2, h3 {
font-family: "Myriad Pro",Calibri,Helvetica,Arial,sans-serif
}
nav.menu {
padding: 0.5em;
}
nav.menu_top {
background-color: #212121;
color: #007EDF;
font-size: 1.1em;
}
header {
height: 9em;
}
header img {
height: 100%;
float: left;
}
header .colony {
position: fixed;
top: 0em;
right: 0;
z-index: -1;
}
.page {
width: 100%;
background-color: rgba(255, 255, 255, 0.8);
}

View File

@ -4,7 +4,7 @@ from website.models import *
from website.views import * from website.views import *
from aircox_cms.models import Article from aircox_cms.models import Article
from aircox_cms.views import ViewSet, Menu, Section from aircox_cms.views import Menu, Section, Sections, ViewSet
from aircox_cms.routes import * from aircox_cms.routes import *
from aircox_cms.website import Website from aircox_cms.website import Website
@ -17,8 +17,8 @@ class ProgramSet (ViewSet):
DateRoute, DateRoute,
] ]
detail_sections = [ detail_sections = ViewSet.detail_sections + [
ScheduleSection ScheduleSection,
] ]
class EpisodeSet (ViewSet): class EpisodeSet (ViewSet):
@ -42,17 +42,33 @@ class ArticleSet (ViewSet):
website = Website( website = Website(
name = 'RadioCampus', name = 'RadioCampus',
styles = 'website/styles.css',
menus = [ menus = [
Menu(
position = 'header',
sections = [
Sections.Image(url = 'website/logo.png'),
Sections.Image(url = 'website/colony.png', classes='colony'),
]
),
Menu( Menu(
position = 'top', position = 'top',
sections = [ sections = [
Section(content = "Radio Campus le SITE") Section(content = "Radio Campus le SITE")
] ]
) ),
Menu(
position = 'left',
sections = [
Section(content = 'loool<br>blob')
],
),
], ],
) )
website.register_set(ProgramSet) website.register_set(ProgramSet)
website.register_set(EpisodeSet) website.register_set(EpisodeSet)
website.register_set(ArticleSet) website.register_set(ArticleSet)

View File

@ -6,21 +6,21 @@ from django.core import serializers
from django.utils.translation import ugettext as _, ugettext_lazy 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 ListSection from aircox_cms.views import Sections
class PlaylistSection (ListSection): class PlayListSection (Sections.List):
title = _('Playlist') title = _('Playlist')
def get_object_list (self): def get_object_list (self):
tracks = programs.Track.objects \ tracks = programs.Track.objects \
.filter(episode = self.object) \ .filter(episode = self.object) \
.order_by('position') .order_by('position')
return [ ListSection.Item(None, track.title, track.artist) return [ Sections.List.Item(None, track.title, track.artist)
for track in tracks ] for track in tracks ]
class ScheduleSection (ListSection): class ScheduleSection (Sections.List):
title = _('Schedule') title = _('Schedule')
def get_object_list (self): def get_object_list (self):
@ -28,7 +28,7 @@ class ScheduleSection (ListSection):
.filter(program = self.object.pk) .filter(program = self.object.pk)
return [ return [
ListSection.Item(None, sched.get_frequency_display(), Sections.List.Item(None, sched.get_frequency_display(),
_('rerun') if sched.rerun else None) _('rerun') if sched.rerun else None)
for sched in scheds for sched in scheds
] ]