diff --git a/aircox_cms/static/aircox_cms/styles.css b/aircox_cms/static/aircox_cms/styles.css
index 54d97e3..db2fe27 100644
--- a/aircox_cms/static/aircox_cms/styles.css
+++ b/aircox_cms/static/aircox_cms/styles.css
@@ -1,10 +1,41 @@
-body { padding: 0; margin: 0; }
-
-nav.menu_top {
- width: 100%
- position: absolute;
- border-bottom: 1px grey solid;
+body {
+ padding: 0;
+ margin: 0;
}
+.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%;
+ }
+
diff --git a/aircox_cms/templates/aircox_cms/base_section.html b/aircox_cms/templates/aircox_cms/base_section.html
new file mode 100644
index 0000000..4a054b6
--- /dev/null
+++ b/aircox_cms/templates/aircox_cms/base_section.html
@@ -0,0 +1,9 @@
+
+<{{ tag }} class="section {{ classes }}"
+ {% for key, value in attrs.items %}{{ key }} = "{{ value|addslashes }}"
+ {% endfor %} >
+{% block content %}
+{{ content|safe }}
+{% endblock %}
+{{ tag }}>
+
diff --git a/aircox_cms/templates/aircox_cms/base_site.html b/aircox_cms/templates/aircox_cms/base_site.html
index 3646555..5f14dc4 100644
--- a/aircox_cms/templates/aircox_cms/base_site.html
+++ b/aircox_cms/templates/aircox_cms/base_site.html
@@ -8,22 +8,23 @@
-
+
+ {% if website.styles %}
+
+ {% endif %}
{% if menus.left %}
{{ menus.left|safe }}
@@ -50,11 +51,13 @@
{% endif %}
+ {% if menus.page_bottom %}
+ {{ menus.page_bottom|safe }}
+ {% endif %}
+
{% block footer %}
{% if menus.footer %}
-
- {% if title %}
-
- {% block section_title %}
- {{ title }}
- {% endblock %}
-
- {% endif %}
+{% extends "aircox_cms/base_section.html" %}
- {% if header %}
-
- {% endif %}
+{% block content %}
+{% if title %}
+
+ {% block section_title %}
+ {{ title }}
+ {% endblock %}
+
+{% endif %}
-
- {% block section_content %}
- {{ content|safe }}
- {% endblock %}
-
+{% if header %}
+
+{% endif %}
- {% if bottom %}
-
- {% block section_bottom %}
- {{ bottom }}
- {% endblock %}
-
- {% endif %}
+
+ {% block section_content %}
+ {{ content|safe }}
+ {% endblock %}
+{% if bottom %}
+
+ {% block section_bottom %}
+ {{ bottom }}
+ {% endblock %}
+
+{% endif %}
+{% endblock %}
diff --git a/aircox_cms/views.py b/aircox_cms/views.py
index ba95209..30f681a 100644
--- a/aircox_cms/views.py
+++ b/aircox_cms/views.py
@@ -1,9 +1,14 @@
+import re
+
+
+from django.templatetags.static import static
from django.shortcuts import render
from django.template.loader import render_to_string
from django.views.generic import ListView, DetailView
from django.views.generic.base import View, TemplateResponseMixin
from django.core import serializers
from django.utils.translation import ugettext as _, ugettext_lazy
+from django.utils.html import escape
import aircox_cms.routes as routes
@@ -29,12 +34,8 @@ class PostBaseView:
context['menus'] = {
k: v.get(self.request)
for k, v in {
- 'top': self.website.get_menu('top'),
- 'left': self.website.get_menu('left'),
- 'bottom': self.website.get_menu('bottom'),
- 'right': self.website.get_menu('right'),
- 'header': self.website.get_menu('header'),
- 'footer': self.website.get_menu('footer'),
+ k: self.website.get_menu(k)
+ for k in self.website.menu_layouts
}.items() if v
}
@@ -162,6 +163,179 @@ class PostDetailView (DetailView, PostBaseView):
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 '

'.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'
\2
', content)
+ content = re.sub(r'\n', r'
', content)
+ return content
+
+
+ class PostImage (Section):
+ @property
+ def content (self):
+ return '

'.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,
+ '
{}'.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:
"""
A ViewSet is a class helper that groups detail and list views that can be
@@ -173,7 +347,10 @@ class ViewSet:
list_routes = []
detail_view = PostDetailView
- detail_sections = []
+ detail_sections = [
+ Sections.PostContent,
+ Sections.PostImage,
+ ]
def __init__ (self, website = None):
self.detail_sections = [
@@ -195,131 +372,3 @@ class ViewSet:
[ 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,
- '
{}'.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
-
-
diff --git a/aircox_cms/website.py b/aircox_cms/website.py
index fe3f877..45a4a6c 100644
--- a/aircox_cms/website.py
+++ b/aircox_cms/website.py
@@ -6,38 +6,46 @@ class Website:
description = 'An aircox website' # public description (used in meta info)
tags = 'aircox,radio,music' # public keywords (used in meta info)
- logo = None
- menus = None
+ styles = '' # relative url to stylesheet file
+ menus = None # list of menus
+ menu_layouts = ['top', 'left', # available positions
+ 'right', 'bottom',
+ 'header', 'footer']
router = None
+ @property
+ def urls (self):
+ return self.router.get_urlpatterns()
+
def __init__ (self, **kwargs):
self.__dict__.update(kwargs)
if not self.router:
self.router = routes.Router()
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)
self.router.register_set(view_set)
def get_menu (self, position):
+ """
+ Get an enabled menu by its position
+ """
for menu in self.menus:
if menu.enabled and menu.position == position:
+ self.check_menu_tag(menu)
return menu
- def get_top_menu (self):
- return self.get_menu('top')
-
- def get_left_menu (self):
- return self.get_menu('left')
-
- def get_bottom_menu (self):
- return self.get_menu('bottom')
-
- def get_right_menu (self):
- return self.get_menu('right')
-
- @property
- def urls (self):
- return self.router.get_urlpatterns()
+ def check_menu_tag (self, menu):
+ """
+ Update menu tag if it is a footer or a header
+ """
+ if menu.position in ('footer','header'):
+ menu.tag = menu.position
+ if menu.position in ('left', 'right'):
+ menu.tag = 'side'
diff --git a/website/static/website/colony.png b/website/static/website/colony.png
new file mode 100644
index 0000000..80938a2
Binary files /dev/null and b/website/static/website/colony.png differ
diff --git a/website/static/website/logo.png b/website/static/website/logo.png
new file mode 100644
index 0000000..89fa4c2
Binary files /dev/null and b/website/static/website/logo.png differ
diff --git a/website/static/website/styles.css b/website/static/website/styles.css
new file mode 100644
index 0000000..7117ce0
--- /dev/null
+++ b/website/static/website/styles.css
@@ -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);
+}
+
diff --git a/website/urls.py b/website/urls.py
index 7743b78..8026014 100644
--- a/website/urls.py
+++ b/website/urls.py
@@ -4,7 +4,7 @@ from website.models import *
from website.views import *
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.website import Website
@@ -17,8 +17,8 @@ class ProgramSet (ViewSet):
DateRoute,
]
- detail_sections = [
- ScheduleSection
+ detail_sections = ViewSet.detail_sections + [
+ ScheduleSection,
]
class EpisodeSet (ViewSet):
@@ -42,17 +42,33 @@ class ArticleSet (ViewSet):
website = Website(
name = 'RadioCampus',
+ styles = 'website/styles.css',
+
menus = [
+ Menu(
+ position = 'header',
+ sections = [
+ Sections.Image(url = 'website/logo.png'),
+ Sections.Image(url = 'website/colony.png', classes='colony'),
+ ]
+ ),
+
Menu(
position = 'top',
sections = [
Section(content = "Radio Campus le SITE")
]
- )
+ ),
+
+ Menu(
+ position = 'left',
+ sections = [
+ Section(content = 'loool
blob')
+ ],
+ ),
],
)
-
website.register_set(ProgramSet)
website.register_set(EpisodeSet)
website.register_set(ArticleSet)
diff --git a/website/views.py b/website/views.py
index dd7c3b3..9aa4676 100644
--- a/website/views.py
+++ b/website/views.py
@@ -6,21 +6,21 @@ from django.core import serializers
from django.utils.translation import ugettext as _, ugettext_lazy
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')
def get_object_list (self):
tracks = programs.Track.objects \
.filter(episode = self.object) \
.order_by('position')
- return [ ListSection.Item(None, track.title, track.artist)
+ return [ Sections.List.Item(None, track.title, track.artist)
for track in tracks ]
-class ScheduleSection (ListSection):
+class ScheduleSection (Sections.List):
title = _('Schedule')
def get_object_list (self):
@@ -28,7 +28,7 @@ class ScheduleSection (ListSection):
.filter(program = self.object.pk)
return [
- ListSection.Item(None, sched.get_frequency_display(),
+ Sections.List.Item(None, sched.get_frequency_display(),
_('rerun') if sched.rerun else None)
for sched in scheds
]