forked from rc/aircox
		
	add 'parts' system + script; work on player; create list_item.html template; update on_air
This commit is contained in:
		
							
								
								
									
										96
									
								
								cms/decorators.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								cms/decorators.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
			
		||||
from django.template.loader import render_to_string
 | 
			
		||||
from django.conf.urls import url
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
from django.utils.text import slugify
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def __part_normalize(value, default):
 | 
			
		||||
    value = value if value else default
 | 
			
		||||
    return slugify(value.lower())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parts(cls, name = None, pattern = None):
 | 
			
		||||
    """
 | 
			
		||||
    the decorated class is a parts class, and contains part
 | 
			
		||||
    functions. Look `part` decorator doc for more info.
 | 
			
		||||
    """
 | 
			
		||||
    name = __part_normalize(name, cls.__name__)
 | 
			
		||||
    pattern = __part_normalize(pattern, cls.__name__)
 | 
			
		||||
 | 
			
		||||
    cls._parts = []
 | 
			
		||||
    for part in cls.__dict__.values():
 | 
			
		||||
        if not hasattr(part, 'is_part'):
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        part.name = name + '_' + part.name
 | 
			
		||||
        part.pattern = pattern + '/' + part.pattern
 | 
			
		||||
        part = url(part.pattern, name = part.name,
 | 
			
		||||
                   view = part, kwargs = {'cl': cls})
 | 
			
		||||
        cls._parts.append(part)
 | 
			
		||||
    return cls
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def part(view, name = None, pattern = None):
 | 
			
		||||
    """
 | 
			
		||||
    A part function is a view that is used to retrieve data dynamically,
 | 
			
		||||
    e.g. from Javascript with XMLHttpRequest. A part function is a classmethod
 | 
			
		||||
    that returns a string and has the following signature:
 | 
			
		||||
 | 
			
		||||
        `part(cl, request, parent, *args, **kwargs)`
 | 
			
		||||
 | 
			
		||||
    When a section with parts is added to the website, the parts' urls
 | 
			
		||||
    are added to the website's one and make them available.
 | 
			
		||||
 | 
			
		||||
    A part function can have the following parameters:
 | 
			
		||||
    * name: part.name or part.__name__
 | 
			
		||||
    * pattern: part.pattern or part.__name__
 | 
			
		||||
 | 
			
		||||
    An extra method `url` is added to the part function to return the adequate
 | 
			
		||||
    url.
 | 
			
		||||
 | 
			
		||||
    Theses are combined with the containing parts class params such as:
 | 
			
		||||
    * name: parts.name + '_' + part.name
 | 
			
		||||
    * pattern: parts.pattern + '/' + part.pattern
 | 
			
		||||
 | 
			
		||||
    The parts class will have an attribute '_parts' as list of generated
 | 
			
		||||
    urls.
 | 
			
		||||
    """
 | 
			
		||||
    if hasattr(view, 'is_part'):
 | 
			
		||||
        return view
 | 
			
		||||
 | 
			
		||||
    def view_(request, as_str = False, cl = None, *args, **kwargs):
 | 
			
		||||
        v = view(cl, request, *args, **kwargs)
 | 
			
		||||
        if as_str:
 | 
			
		||||
            return v
 | 
			
		||||
        return HttpResponse(v)
 | 
			
		||||
 | 
			
		||||
    def url(*args, **kwargs):
 | 
			
		||||
        return reverse(view_.name, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    view_.name = __part_normalize(name, view.__name__)
 | 
			
		||||
    view_.pattern = __part_normalize(pattern, view.__name__)
 | 
			
		||||
    view_.is_part = True
 | 
			
		||||
    view_.url = url
 | 
			
		||||
    return view_
 | 
			
		||||
 | 
			
		||||
def template(template_name):
 | 
			
		||||
    """
 | 
			
		||||
    the decorated function returns a context that is used to
 | 
			
		||||
    render a template value.
 | 
			
		||||
 | 
			
		||||
    * template_name: name of the template to use
 | 
			
		||||
    * hide_empty: an empty context returns an empty string
 | 
			
		||||
    """
 | 
			
		||||
    def wrapper(func):
 | 
			
		||||
        def view_(cl, request, *args, **kwargs):
 | 
			
		||||
            context = func(cl, request, *args, **kwargs)
 | 
			
		||||
            if not context and hide_empty:
 | 
			
		||||
                return ''
 | 
			
		||||
            context['embed'] = True
 | 
			
		||||
            return render_to_string(template_name, context, request=request)
 | 
			
		||||
        view_.__name__ = func.__name__
 | 
			
		||||
        return view_
 | 
			
		||||
    return wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -212,7 +212,7 @@ class Post (models.Model, Routable):
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RelatedPostBase (models.base.ModelBase):
 | 
			
		||||
class RelatedMeta (models.base.ModelBase):
 | 
			
		||||
    """
 | 
			
		||||
    Metaclass for RelatedPost children.
 | 
			
		||||
    """
 | 
			
		||||
@ -310,7 +310,7 @@ class RelatedPostBase (models.base.ModelBase):
 | 
			
		||||
        return model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RelatedPost (Post, metaclass = RelatedPostBase):
 | 
			
		||||
class RelatedPost (Post, metaclass = RelatedMeta):
 | 
			
		||||
    """
 | 
			
		||||
    Post linked to an object of other model. This object is accessible through
 | 
			
		||||
    the field "related".
 | 
			
		||||
@ -346,29 +346,6 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
 | 
			
		||||
        """
 | 
			
		||||
        Relation descriptor used to generate and manage the related object.
 | 
			
		||||
 | 
			
		||||
        * model: model of the related object
 | 
			
		||||
        * bindings: values that are bound between the post and the related
 | 
			
		||||
            object. When the post is saved, these fields are updated on it.
 | 
			
		||||
            It is a dict of { post_attr: rel_attr }
 | 
			
		||||
 | 
			
		||||
            If there is a post_attr "thread", the corresponding rel_attr is used
 | 
			
		||||
            to update the post thread to the correct Post model (in order to
 | 
			
		||||
            establish a parent-child relation between two models)
 | 
			
		||||
 | 
			
		||||
            When a callable is set as bound value, it will be called to retrieve
 | 
			
		||||
            the value, as: callable_func(post, related)
 | 
			
		||||
 | 
			
		||||
            Note: bound values can be any value, not only Django field.
 | 
			
		||||
        * post_to_rel: auto update related object when post is updated
 | 
			
		||||
        * rel_to_post: auto update the post when related object is updated
 | 
			
		||||
        * thread_model: generated by the metaclass, points to the RelatedPost
 | 
			
		||||
          model generated for the bindings.thread object.
 | 
			
		||||
        * field_args: dict of arguments to pass to the ForeignKey constructor,
 | 
			
		||||
          such as: ForeignKey(related_model, **field_args)
 | 
			
		||||
        * auto_create: automatically create a RelatedPost for each new item of
 | 
			
		||||
          the related object and init it with bounded values. Use 'post_save'
 | 
			
		||||
          signal. If auto_create is callable, use `auto_create(related_object)`.
 | 
			
		||||
 | 
			
		||||
        Be careful with post_to_rel!
 | 
			
		||||
        * There is no check of permissions when related object is synchronised
 | 
			
		||||
            from the post, so be careful when enabling post_to_rel.
 | 
			
		||||
@ -376,12 +353,48 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
 | 
			
		||||
            (sub-)class thread_model, the related parent is set to None
 | 
			
		||||
        """
 | 
			
		||||
        model = None
 | 
			
		||||
        bindings = None          # values to map { post_attr: rel_attr }
 | 
			
		||||
        """
 | 
			
		||||
        model of the related object
 | 
			
		||||
        """
 | 
			
		||||
        bindings = None
 | 
			
		||||
        """
 | 
			
		||||
        dict of `post_attr: rel_attr` that represent bindings of values
 | 
			
		||||
        between the post and the related object. Field are updated according
 | 
			
		||||
        to `post_to_rel` and `rel_to_post`.
 | 
			
		||||
 | 
			
		||||
        If there is a post_attr "thread", the corresponding rel_attr is used
 | 
			
		||||
        to update the post thread to the correct Post model (in order to
 | 
			
		||||
        establish a parent-child relation between two models)
 | 
			
		||||
 | 
			
		||||
        When a callable is set as `rel_attr`, it will be called to retrieve
 | 
			
		||||
        the value, as `rel_attr(post, related)`
 | 
			
		||||
 | 
			
		||||
        note: bound values can be any value, not only Django field.
 | 
			
		||||
        """
 | 
			
		||||
        post_to_rel = False
 | 
			
		||||
        """
 | 
			
		||||
        update related object when the post is saved, using bindings
 | 
			
		||||
        """
 | 
			
		||||
        rel_to_post = False
 | 
			
		||||
        """
 | 
			
		||||
        update the post when related object is updated, using bindings
 | 
			
		||||
        """
 | 
			
		||||
        thread_model = None
 | 
			
		||||
        """
 | 
			
		||||
        generated by the metaclass, points to the RelatedPost model
 | 
			
		||||
        generated for the bindings.thread object.
 | 
			
		||||
        """
 | 
			
		||||
        field_args = None
 | 
			
		||||
        """
 | 
			
		||||
        dict of arguments to pass to the ForeignKey constructor, such as
 | 
			
		||||
        `ForeignKey(related_model, **field_args)`
 | 
			
		||||
        """
 | 
			
		||||
        auto_create = False
 | 
			
		||||
        """
 | 
			
		||||
        automatically create a RelatedPost for each new item of the related
 | 
			
		||||
        object and init it with bounded values. Use 'post_save' signal. If
 | 
			
		||||
        auto_create is callable, use `auto_create(related_object)`.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
    def get_rel_attr(self, attr):
 | 
			
		||||
        attr = self._relation.bindings.get(attr)
 | 
			
		||||
 | 
			
		||||
@ -3,9 +3,10 @@ Define different Section css_class that can be used by views.Sections;
 | 
			
		||||
"""
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
from django.templatetags.static import static
 | 
			
		||||
from django.template.loader import render_to_string
 | 
			
		||||
from django.views.generic.base import View
 | 
			
		||||
from django.templatetags.static import static
 | 
			
		||||
from django.http import HttpResponse
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.utils.html import escape
 | 
			
		||||
from django.utils.translation import ugettext as _, ugettext_lazy
 | 
			
		||||
@ -75,9 +76,6 @@ class Section(Viewable, View):
 | 
			
		||||
    * title: title of the section
 | 
			
		||||
    * header: header of the section
 | 
			
		||||
    * footer: footer of the section
 | 
			
		||||
 | 
			
		||||
    * force_object: (can be persistent) related object
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    template_name = 'aircox/cms/website.html'
 | 
			
		||||
 | 
			
		||||
@ -88,7 +86,6 @@ class Section(Viewable, View):
 | 
			
		||||
    title = ''
 | 
			
		||||
    header = ''
 | 
			
		||||
    footer = ''
 | 
			
		||||
    force_object = None
 | 
			
		||||
 | 
			
		||||
    request = None
 | 
			
		||||
    object = None
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										163
									
								
								cms/static/aircox/cms/scripts.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								cms/static/aircox/cms/scripts.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,163 @@
 | 
			
		||||
 | 
			
		||||
function Part(url = '', params = '') {
 | 
			
		||||
  return new Part_(url, params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Small utility used to make XMLHttpRequests, and map results to other
 | 
			
		||||
// objects
 | 
			
		||||
function Part_(url = '', params = '') {
 | 
			
		||||
  this.url = url;
 | 
			
		||||
  this.params = params;
 | 
			
		||||
  this.selectors = [];
 | 
			
		||||
  this.actions = [];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Part_.prototype = {
 | 
			
		||||
  // XMLHttpRequest object used to retrieve data
 | 
			
		||||
  xhr: null,
 | 
			
		||||
 | 
			
		||||
  // delayed actions that have been registered
 | 
			
		||||
  actions: null,
 | 
			
		||||
 | 
			
		||||
  // registered selectors
 | 
			
		||||
  selectors: null,
 | 
			
		||||
 | 
			
		||||
  /// parse request result and save in this.stanza
 | 
			
		||||
  __parse_dom: function() {
 | 
			
		||||
    var doc = document.implementation.createHTMLDocument('xhr').documentElement;
 | 
			
		||||
    doc.innerHTML = this.xhr.responseText;
 | 
			
		||||
    this.stanza = doc;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // make an xhr request, and call callback(err, xhr) if given
 | 
			
		||||
  get: function() {
 | 
			
		||||
    var self = this;
 | 
			
		||||
 | 
			
		||||
    var xhr = new XMLHttpRequest();
 | 
			
		||||
    xhr.onreadystatechange = function() {
 | 
			
		||||
      if(xhr.readyState != 4)
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
      // TODO: error handling
 | 
			
		||||
      var err = self.xhr.status != 200 && self.xhr.status;
 | 
			
		||||
      if(err)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
      for(var i = 0; i < self.actions.length; i++)
 | 
			
		||||
        self.actions[i].apply(self);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(this.params)
 | 
			
		||||
      xhr.open('GET', this.url + '?' + this.params, true);
 | 
			
		||||
    else
 | 
			
		||||
      xhr.open('GET', this.url, true);
 | 
			
		||||
    this.xhr = xhr;
 | 
			
		||||
    return this;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // send request
 | 
			
		||||
  send: function() {
 | 
			
		||||
    this.xhr.send();
 | 
			
		||||
    return this;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // set selectors. if callback is set, call this callback
 | 
			
		||||
  // once data are retrieved with an object of
 | 
			
		||||
  // `selector_name: select_result`
 | 
			
		||||
  select: function(selectors, callback = undefined) {
 | 
			
		||||
    for(var i in selectors) {
 | 
			
		||||
      selector = selectors[i];
 | 
			
		||||
      if(!selector.sort)
 | 
			
		||||
        selector = [selector]
 | 
			
		||||
 | 
			
		||||
      selector = new Part_.Selector(i, selector[0], selector[1], selector[2])
 | 
			
		||||
      this.selectors.push(selector)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(callback) {
 | 
			
		||||
      var self = this;
 | 
			
		||||
      this.actions.push(function() {
 | 
			
		||||
        var r = {}
 | 
			
		||||
        for(var i in self.selectors)
 | 
			
		||||
          r[i] = self.selectors[i].select(self.stanza);
 | 
			
		||||
        callback(r);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    return this;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // map data using this.selectors on xhr result *and* dest
 | 
			
		||||
  map: function(dest) {
 | 
			
		||||
    var self = this;
 | 
			
		||||
    this.actions.push(function() {
 | 
			
		||||
      if(!self.stanza)
 | 
			
		||||
        self.__parse_dom();
 | 
			
		||||
 | 
			
		||||
      for(var i = 0; i < self.selectors.length; i++) {
 | 
			
		||||
        selector = self.selectors[i]
 | 
			
		||||
        selector.map(self.stanza, dest);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    return this;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // add an action to the list of actions
 | 
			
		||||
  on: function(callback) {
 | 
			
		||||
    this.actions.push(callback)
 | 
			
		||||
    return this;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Part_.Selector = function(name, selector, attribute = null, all = false) {
 | 
			
		||||
  this.name = name;
 | 
			
		||||
  this.selector = selector;
 | 
			
		||||
  this.attribute = attribute;
 | 
			
		||||
  this.all = all;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Part_.Selector.prototype = {
 | 
			
		||||
  select: function(obj, use_attr = true) {
 | 
			
		||||
    if(!this.all) {
 | 
			
		||||
      obj = obj.querySelectorAll(this.selector)
 | 
			
		||||
      if(obj)
 | 
			
		||||
        obj = obj[0];
 | 
			
		||||
      return (this.attribute && use_attr && obj) ? obj[this.attribute] : obj;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    obj = obj.querySelectorAll(this.selector);
 | 
			
		||||
    if(!obj)
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    r = []
 | 
			
		||||
    for(var i = 0; i < obj.length; i++) {
 | 
			
		||||
      r.push(this.attribute && use_attr ? obj[i][this.attribute] : obj[i])
 | 
			
		||||
    }
 | 
			
		||||
    return r;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  map: function(src, dst) {
 | 
			
		||||
    src_qs = this.select(src, false);
 | 
			
		||||
    dst_qs = this.select(dst, false);
 | 
			
		||||
    if(!src_qs || !dst_qs)
 | 
			
		||||
      return
 | 
			
		||||
 | 
			
		||||
    if(!this.all) {
 | 
			
		||||
      src_qs = [ src_qs ];
 | 
			
		||||
      dst_qs = [ dst_qs ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var size = Math.min(src_qs.length, dst_qs.length);
 | 
			
		||||
    for(var i = 0; i < size; i++) {
 | 
			
		||||
      var src = src_qs[i];
 | 
			
		||||
      var dst = dst_qs[i];
 | 
			
		||||
 | 
			
		||||
      if(this.attribute)
 | 
			
		||||
        dst[this.attribute] = src[this.attribute];
 | 
			
		||||
      else
 | 
			
		||||
        dst.parentNode.replaceChild(src, dst);
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,65 +8,7 @@
 | 
			
		||||
{% block content %}
 | 
			
		||||
<ul class="content">
 | 
			
		||||
{% for item in object_list %}
 | 
			
		||||
    <li {% if item.css_class %}class="{{ item.css_class }}"{% endif %}
 | 
			
		||||
        {% for k, v in item.attrs.items %}
 | 
			
		||||
        {{ k }} = "{{ v|addslashes }}"
 | 
			
		||||
        {% endfor %} >
 | 
			
		||||
    {% if item.url %}
 | 
			
		||||
        <a href="{{ item.url }}">
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    {% if 'image' in list.fields and item.image %}
 | 
			
		||||
    <img src="{% thumbnail item.image list.image_size crop %}">
 | 
			
		||||
    {% endif %}
 | 
			
		||||
 | 
			
		||||
    <div class="content">
 | 
			
		||||
        {% if 'title' in list.fields and item.title %}
 | 
			
		||||
        <h2 class="title">{{ item.title }}</h2>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
        {% if 'content' in list.fields and item.content %}
 | 
			
		||||
        <div class="text">
 | 
			
		||||
            {% if list.truncate %}
 | 
			
		||||
            {{ item.content|striptags|truncatewords:list.truncate }}
 | 
			
		||||
            {% else %}
 | 
			
		||||
            {{ item.content|striptags }}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="meta">
 | 
			
		||||
        {% if item.date and 'date' in list.fields or 'time' in list.fields %}
 | 
			
		||||
        <time datetime="{{ item.date }}">
 | 
			
		||||
            {% if 'date' in list.fields %}
 | 
			
		||||
            <span class="date">
 | 
			
		||||
                {{ item.date|date:'D. d F' }}
 | 
			
		||||
            </span>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if 'time' in list.fields %}
 | 
			
		||||
            <span class="time">
 | 
			
		||||
                {{ item.date|date:'H:i' }}
 | 
			
		||||
            </span>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </time>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        {% if item.author and 'author' in list.fields %}
 | 
			
		||||
        <span class="author">
 | 
			
		||||
            {{ item.author }}
 | 
			
		||||
        </span>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
        {% if item.info and 'info' in list.fields %}
 | 
			
		||||
        <span class="info">
 | 
			
		||||
            {{ item.info }}
 | 
			
		||||
        </span>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    {% if item.url %}
 | 
			
		||||
    </a>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
{% include "aircox/cms/list_item.html" %}
 | 
			
		||||
{% empty %}
 | 
			
		||||
<div class="message empty">
 | 
			
		||||
{{ list.message_empty }}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										65
									
								
								cms/templates/aircox/cms/list_item.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								cms/templates/aircox/cms/list_item.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
			
		||||
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% load thumbnail %}
 | 
			
		||||
 | 
			
		||||
<li {% if item.css_class %}class="{{ item.css_class }}"{% endif %}
 | 
			
		||||
      {% for k, v in item.attrs.items %}
 | 
			
		||||
      {{ k }} = "{{ v|addslashes }}"
 | 
			
		||||
      {% endfor %} >
 | 
			
		||||
  {% if item.url %}
 | 
			
		||||
      <a class="url" href="{{ item.url }}">
 | 
			
		||||
  {% endif %}
 | 
			
		||||
  {% if 'image' in list.fields and item.image %}
 | 
			
		||||
  <img class="image" src="{% thumbnail item.image list.image_size crop %}">
 | 
			
		||||
  {% endif %}
 | 
			
		||||
 | 
			
		||||
  <div class="body">
 | 
			
		||||
      {% if 'title' in list.fields and item.title %}
 | 
			
		||||
      <h2 class="title">{{ item.title }}</h2>
 | 
			
		||||
      {% endif %}
 | 
			
		||||
 | 
			
		||||
      {% if 'content' in list.fields and item.content %}
 | 
			
		||||
      <div class="content">
 | 
			
		||||
          {% if list.truncate %}
 | 
			
		||||
          {{ item.content|striptags|truncatewords:list.truncate }}
 | 
			
		||||
          {% else %}
 | 
			
		||||
          {{ item.content|striptags }}
 | 
			
		||||
          {% endif %}
 | 
			
		||||
      </div>
 | 
			
		||||
      {% endif %}
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="meta">
 | 
			
		||||
      {% if item.date and 'date' in list.fields or 'time' in list.fields %}
 | 
			
		||||
      <time datetime="{{ item.date }}">
 | 
			
		||||
          {% if 'date' in list.fields %}
 | 
			
		||||
          <span class="date">
 | 
			
		||||
              {{ item.date|date:'D. d F' }}
 | 
			
		||||
          </span>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
          {% if 'time' in list.fields %}
 | 
			
		||||
          <span class="time">
 | 
			
		||||
              {{ item.date|date:'H:i' }}
 | 
			
		||||
          </span>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
      </time>
 | 
			
		||||
      {% endif %}
 | 
			
		||||
      {% if item.author and 'author' in list.fields %}
 | 
			
		||||
      <span class="author">
 | 
			
		||||
          {{ item.author }}
 | 
			
		||||
      </span>
 | 
			
		||||
      {% endif %}
 | 
			
		||||
 | 
			
		||||
      {% if item.info and 'info' in list.fields %}
 | 
			
		||||
      <span class="info">
 | 
			
		||||
          {{ item.info }}
 | 
			
		||||
      </span>
 | 
			
		||||
      {% endif %}
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  {% if item.url %}
 | 
			
		||||
  </a>
 | 
			
		||||
  {% endif %}
 | 
			
		||||
</li>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
    <head>
 | 
			
		||||
        {# FIXME: page tags #}
 | 
			
		||||
        {# FIXME: extra head block #}
 | 
			
		||||
        <meta charset="utf-8">
 | 
			
		||||
        <meta name="application-name" content="aircox-cms">
 | 
			
		||||
        <meta name="description" content="{{ website.description }}">
 | 
			
		||||
@ -13,6 +13,7 @@
 | 
			
		||||
        {% if website.styles %}
 | 
			
		||||
        <link rel="stylesheet" href="{% static website.styles %}" type="text/css">
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        <script src="{% static "aircox/cms/scripts.js" %}"></script>
 | 
			
		||||
        <title>{% if title %}{{ title }} - {% endif %}{{ website.name }}</title>
 | 
			
		||||
    </head>
 | 
			
		||||
    <body>
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
from django.utils.text import slugify
 | 
			
		||||
from django.conf.urls import url
 | 
			
		||||
from django.conf.urls import include, url
 | 
			
		||||
 | 
			
		||||
import aircox.cms.routes as routes
 | 
			
		||||
import aircox.cms.routes as routes_
 | 
			
		||||
@ -35,6 +35,8 @@ class Website:
 | 
			
		||||
    ## components
 | 
			
		||||
    urls = []
 | 
			
		||||
    """list of urls generated thourgh registrations"""
 | 
			
		||||
    parts = []
 | 
			
		||||
    """list of registered parts (done through sections registration)"""
 | 
			
		||||
    registry = {}
 | 
			
		||||
    """dict of registered models by their name"""
 | 
			
		||||
 | 
			
		||||
@ -43,7 +45,8 @@ class Website:
 | 
			
		||||
        * menus: a list of menus to add to the website
 | 
			
		||||
        """
 | 
			
		||||
        self.registry = {}
 | 
			
		||||
        self.urls = []
 | 
			
		||||
        self.parts = []
 | 
			
		||||
        self.urls = [ url(r'^parts/', include(self.parts)) ]
 | 
			
		||||
        self.menus = {}
 | 
			
		||||
        self.__dict__.update(kwargs)
 | 
			
		||||
 | 
			
		||||
@ -93,9 +96,23 @@ class Website:
 | 
			
		||||
        model._website = self
 | 
			
		||||
        return name
 | 
			
		||||
 | 
			
		||||
    def register_parts(self, sections):
 | 
			
		||||
        """
 | 
			
		||||
        Register parts that are used in the given sections.
 | 
			
		||||
        """
 | 
			
		||||
        if not hasattr(sections, '__iter__'):
 | 
			
		||||
            sections = [sections]
 | 
			
		||||
 | 
			
		||||
        for section in sections:
 | 
			
		||||
            if not hasattr(section, '_parts'):
 | 
			
		||||
                continue
 | 
			
		||||
            self.parts += [
 | 
			
		||||
                url for url in section._parts
 | 
			
		||||
                if url not in self.urls
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
    def register(self, name, routes = [], view = views.PageView,
 | 
			
		||||
                 model = None, **view_kwargs):
 | 
			
		||||
                 model = None, sections = None, **view_kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Register a view using given name and routes. If model is given,
 | 
			
		||||
        register the views for it.
 | 
			
		||||
@ -110,6 +127,11 @@ class Website:
 | 
			
		||||
 | 
			
		||||
        if not view_kwargs.get('menus'):
 | 
			
		||||
            view_kwargs['menus'] = self.menus
 | 
			
		||||
 | 
			
		||||
        if sections:
 | 
			
		||||
            self.register_parts(sections)
 | 
			
		||||
            view_kwargs['sections'] = sections
 | 
			
		||||
 | 
			
		||||
        view = view.as_view(
 | 
			
		||||
            website = self,
 | 
			
		||||
            **view_kwargs
 | 
			
		||||
@ -150,6 +172,7 @@ class Website:
 | 
			
		||||
        elif menu.position in ('left', 'right'):
 | 
			
		||||
            menu.tag = 'side'
 | 
			
		||||
        self.menus[menu.position] = menu
 | 
			
		||||
        self.register_parts(menu.sections)
 | 
			
		||||
 | 
			
		||||
    def get_menu(self, position):
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user