integrate qcombine with routes; default routes in website; search in tags too; qcombine fixes (search, model); website's Publications model
This commit is contained in:
		@ -5,7 +5,6 @@ from django.contrib.contenttypes.models import ContentType
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from django.utils.text import slugify
 | 
			
		||||
from django.utils.translation import ugettext as _, ugettext_lazy
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
 | 
			
		||||
from django.db.models.signals import Signal, post_save, pre_save
 | 
			
		||||
from django.dispatch import receiver
 | 
			
		||||
@ -38,11 +37,12 @@ class Routable:
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def route_url(cl, route, **kwargs):
 | 
			
		||||
        name = cl._website.name_of_model(cl)
 | 
			
		||||
        name = route.get_view_name(name)
 | 
			
		||||
        r = reverse(name, kwargs = kwargs)
 | 
			
		||||
        return r
 | 
			
		||||
    def reverse(cl, route, use_default = True, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Reverse a url using a given route for the model - simple wrapper
 | 
			
		||||
        around cl._website.reverse
 | 
			
		||||
        """
 | 
			
		||||
        return cl._website.reverse(cl, route, use_default, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Comment(models.Model, Routable):
 | 
			
		||||
@ -154,7 +154,10 @@ class Post (models.Model, Routable):
 | 
			
		||||
        blank = True,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    search_fields = [ 'title', 'content' ]
 | 
			
		||||
    search_fields = [ 'title', 'content', 'tags__name' ]
 | 
			
		||||
    """
 | 
			
		||||
    Fields on which routes.SearchRoute must run the search
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def get_comments(self):
 | 
			
		||||
        """
 | 
			
		||||
@ -171,12 +174,33 @@ class Post (models.Model, Routable):
 | 
			
		||||
        """
 | 
			
		||||
        Return an url to the post detail view.
 | 
			
		||||
        """
 | 
			
		||||
        return self.route_url(
 | 
			
		||||
        return self.reverse(
 | 
			
		||||
            routes.DetailRoute,
 | 
			
		||||
            pk = self.pk, slug = slugify(self.title)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def fill_empty(self):
 | 
			
		||||
        """
 | 
			
		||||
        Fill empty values using parent thread. Can be used before saving or
 | 
			
		||||
        at loading
 | 
			
		||||
        """
 | 
			
		||||
        if not self.thread:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if not self.title:
 | 
			
		||||
            self.title = _('{name} // {date}').format(
 | 
			
		||||
                name = self.thread.title,
 | 
			
		||||
                date = self.date
 | 
			
		||||
            )
 | 
			
		||||
        if not self.content:
 | 
			
		||||
            self.content = self.thread.content
 | 
			
		||||
        if not self.image:
 | 
			
		||||
            self.image = self.thread.image
 | 
			
		||||
        if not self.tags and self.pk:
 | 
			
		||||
            self.tags = self.thread.tags
 | 
			
		||||
 | 
			
		||||
    def get_object_list(self, request, object, **kwargs):
 | 
			
		||||
        # FIXME: wtf
 | 
			
		||||
        type = ContentType.objects.get_for_model(object)
 | 
			
		||||
        qs = Comment.objects.filter(
 | 
			
		||||
            thread_id = object.pk,
 | 
			
		||||
@ -185,6 +209,9 @@ class Post (models.Model, Routable):
 | 
			
		||||
        return qs
 | 
			
		||||
 | 
			
		||||
    def make_safe(self):
 | 
			
		||||
        """
 | 
			
		||||
        Ensure that data of the publication are safe from code injection.
 | 
			
		||||
        """
 | 
			
		||||
        self.title = bleach.clean(
 | 
			
		||||
            self.title,
 | 
			
		||||
            tags=settings.AIRCOX_CMS_BLEACH_TITLE_TAGS,
 | 
			
		||||
@ -293,6 +320,7 @@ class RelatedMeta (models.base.ModelBase):
 | 
			
		||||
            else:
 | 
			
		||||
                return
 | 
			
		||||
            post.rel_to_post()
 | 
			
		||||
            post.fill_empty()
 | 
			
		||||
            post.save(avoid_sync = True)
 | 
			
		||||
        post_save.connect(handler_rel, model._relation.model, False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,12 @@ import operator
 | 
			
		||||
import itertools
 | 
			
		||||
import heapq
 | 
			
		||||
 | 
			
		||||
from django.utils.translation import ugettext as _, ugettext_lazy
 | 
			
		||||
from django.db.models.query import QuerySet
 | 
			
		||||
 | 
			
		||||
from aircox.cms.models import Routable
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QCombine:
 | 
			
		||||
    """
 | 
			
		||||
    This class helps to combine querysets of different models and lists of
 | 
			
		||||
@ -29,9 +33,9 @@ class QCombine:
 | 
			
		||||
        Map results of qs_func for QuerySet instance and of non_qs for
 | 
			
		||||
        the others (if given), because QuerySet always clones itself.
 | 
			
		||||
        """
 | 
			
		||||
        for i, qs in self.lists:
 | 
			
		||||
            if issubclass(type(qs, QuerySet):
 | 
			
		||||
                self.lists[i] = func(qs)
 | 
			
		||||
        for i, qs in enumerate(self.lists):
 | 
			
		||||
            if issubclass(type(qs), QuerySet):
 | 
			
		||||
                self.lists[i] = qs_func(qs)
 | 
			
		||||
            elif non_qs:
 | 
			
		||||
                self.lists[i] = non_qs(qs)
 | 
			
		||||
 | 
			
		||||
@ -47,7 +51,7 @@ class QCombine:
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
    def distinct(self, **kwargs):
 | 
			
		||||
        self.map(qs.distinct())
 | 
			
		||||
        self.map(lambda qs: qs.distinct())
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
    def get(self, **kwargs):
 | 
			
		||||
@ -68,13 +72,12 @@ class QCombine:
 | 
			
		||||
 | 
			
		||||
        self.order_reverse = reverse
 | 
			
		||||
        self.order_fields = fields
 | 
			
		||||
 | 
			
		||||
        self.map(
 | 
			
		||||
            lambda qs: qs.order_by(*fields),
 | 
			
		||||
            lambda qs: sorted(
 | 
			
		||||
                qs,
 | 
			
		||||
                qs.sort(
 | 
			
		||||
                    key = operator.attrgetter(fields),
 | 
			
		||||
                    key = operator.attrgetter(*fields),
 | 
			
		||||
                    reverse = reverse
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
@ -112,7 +115,18 @@ class QCombine:
 | 
			
		||||
        return list(it)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QCombined:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Manager(type):
 | 
			
		||||
    models = []
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def objects(self):
 | 
			
		||||
        qs = QCombine(*[model.objects.all() for model in self.models])
 | 
			
		||||
        return qs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FakeModel(Routable,metaclass=Manager):
 | 
			
		||||
    """
 | 
			
		||||
    This class is used to register a route for multiple models to a website.
 | 
			
		||||
    A QCombine is created with qs for all given models when objects
 | 
			
		||||
@ -120,21 +134,12 @@ class QCombined:
 | 
			
		||||
 | 
			
		||||
    Note: there no other use-case.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(*models):
 | 
			
		||||
        self.models = models
 | 
			
		||||
        self._meta = self.Meta()
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _('publication')
 | 
			
		||||
        verbose_name_plural = _('publications')
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def objects(self):
 | 
			
		||||
        """
 | 
			
		||||
        The QCombine that is returned actually holds the models' managers,
 | 
			
		||||
        in order to simulate the same behaviour than a regular model.
 | 
			
		||||
        """
 | 
			
		||||
        qs = QCombine([model.objects for model in self.models])
 | 
			
		||||
        return qs
 | 
			
		||||
    _meta = Meta()
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        self.__dict__.update(kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,9 @@ from django.contrib.contenttypes.models import ContentType
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from django.utils.translation import ugettext as _, ugettext_lazy
 | 
			
		||||
 | 
			
		||||
import aircox.cms.qcombine as qcombine
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Route:
 | 
			
		||||
    """
 | 
			
		||||
    Base class for routing. Given a model, we generate url specific for each
 | 
			
		||||
@ -41,7 +44,7 @@ class Route:
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_view_name(cl, name):
 | 
			
		||||
    def make_view_name(cl, name):
 | 
			
		||||
        return name + '.' + cl.name
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
@ -65,7 +68,7 @@ class Route:
 | 
			
		||||
            kwargs.update(view_kwargs)
 | 
			
		||||
 | 
			
		||||
        return url(pattern, view, kwargs = kwargs,
 | 
			
		||||
                   name = cl.get_view_name(name))
 | 
			
		||||
                   name = cl.make_view_name(name))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DetailRoute(Route):
 | 
			
		||||
@ -174,21 +177,28 @@ class SearchRoute(Route):
 | 
			
		||||
    name = 'search'
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_queryset(cl, model, request, **kwargs):
 | 
			
		||||
        q = request.GET.get('q') or ''
 | 
			
		||||
    def __search(cl, model, q):
 | 
			
		||||
        qs = None
 | 
			
		||||
 | 
			
		||||
        ## TODO: by tag
 | 
			
		||||
        for search_field in model.search_fields or []:
 | 
			
		||||
            r = models.Q(**{ search_field + '__icontains': q })
 | 
			
		||||
            if qs: qs = qs | r
 | 
			
		||||
            else: qs = r
 | 
			
		||||
 | 
			
		||||
        return model.objects.filter(qs).distinct()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_queryset(cl, model, request, **kwargs):
 | 
			
		||||
        q = request.GET.get('q') or ''
 | 
			
		||||
        if issubclass(model, qcombine.FakeModel):
 | 
			
		||||
            models = model.models
 | 
			
		||||
            return qcombine.QCombine(
 | 
			
		||||
                *(cl.__search(model, q) for model in models)
 | 
			
		||||
            )
 | 
			
		||||
        return cl.__search(model, q)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_title(cl, model, request, **kwargs):
 | 
			
		||||
        return _('Search "%(search)s" in %(model)s') % {
 | 
			
		||||
        return _('Search <i>%(search)s</i> in %(model)s') % {
 | 
			
		||||
            'model': model._meta.verbose_name_plural,
 | 
			
		||||
            'search': request.GET.get('q') or '',
 | 
			
		||||
        }
 | 
			
		||||
@ -207,14 +217,14 @@ class TagsRoute(Route):
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_queryset(cl, model, request, tags, **kwargs):
 | 
			
		||||
        tags = tags.split('+')
 | 
			
		||||
        return model.objects.filter(tags__name__in=tags)
 | 
			
		||||
        return model.objects.filter(tags__slug__in=tags).distinct()
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_title(cl, model, request, tags, **kwargs):
 | 
			
		||||
        return _('Tagged %(model)s with %(tags)s') % {
 | 
			
		||||
        # FIXME: get tag name instead of tag slug
 | 
			
		||||
        return _('%(model)s tagged with %(tags)s') % {
 | 
			
		||||
            'model': model._meta.verbose_name_plural,
 | 
			
		||||
            'tags': tags.replace('+', ', ')
 | 
			
		||||
            'tags': model.tags_to_html(model, tags = tags.split('+'))
 | 
			
		||||
                    if '+' in tags else tags
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@
 | 
			
		||||
Define different Section css_class that can be used by views.Sections;
 | 
			
		||||
"""
 | 
			
		||||
import re
 | 
			
		||||
from random import shuffle
 | 
			
		||||
 | 
			
		||||
from django.template.loader import render_to_string
 | 
			
		||||
from django.views.generic.base import View
 | 
			
		||||
@ -329,13 +330,22 @@ class Similar(List):
 | 
			
		||||
    List of models allowed in the resulting list. If not set, all models
 | 
			
		||||
    are available.
 | 
			
		||||
    """
 | 
			
		||||
    shuffle = 20
 | 
			
		||||
    """
 | 
			
		||||
    Shuffle results in the self.shuffle most recents articles. If 0 or
 | 
			
		||||
    None, do not shuffle.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    # FIXME: limit in a date range
 | 
			
		||||
    def get_object_list(self):
 | 
			
		||||
        if not self.object:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        qs = self.object.tags.similar_objects()
 | 
			
		||||
        qs.sort(key = lambda post: post.date, reverse=True)
 | 
			
		||||
        if self.shuffle:
 | 
			
		||||
            qs = qs[:self.shuffle]
 | 
			
		||||
            shuffle(qs)
 | 
			
		||||
        return qs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -371,11 +381,9 @@ class Comments(List):
 | 
			
		||||
        import aircox.cms.models as models
 | 
			
		||||
        import aircox.cms.routes as routes
 | 
			
		||||
        if self.object:
 | 
			
		||||
            return models.Comment.route_url(routes.ThreadRoute, {
 | 
			
		||||
            return models.Comment.reverse(routes.ThreadRoute, {
 | 
			
		||||
                'pk': self.object.id,
 | 
			
		||||
                'thread_model': self.object._website.name_of_model(
 | 
			
		||||
                    self.object.__class__
 | 
			
		||||
                ),
 | 
			
		||||
                'thread_model': self.object._registration.name
 | 
			
		||||
            })
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,27 +1,16 @@
 | 
			
		||||
from django import template
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
 | 
			
		||||
import aircox.cms.routes as routes
 | 
			
		||||
 | 
			
		||||
import aircox.cms.utils as utils
 | 
			
		||||
 | 
			
		||||
register = template.Library()
 | 
			
		||||
 | 
			
		||||
@register.filter(name='post_tags')
 | 
			
		||||
def post_tags(post, sep = '-'):
 | 
			
		||||
def post_tags(post, sep = ' - '):
 | 
			
		||||
    """
 | 
			
		||||
    print the list of all the tags of the given post, with url if available
 | 
			
		||||
    return the result of post.tags_url
 | 
			
		||||
    """
 | 
			
		||||
    tags = post.tags.all()
 | 
			
		||||
    r = []
 | 
			
		||||
    for tag in tags:
 | 
			
		||||
        try:
 | 
			
		||||
            r.append('<a href="{url}">{name}</a>'.format(
 | 
			
		||||
                url = post.route_url(routes.TagsRoute, tags = tag),
 | 
			
		||||
                name = tag,
 | 
			
		||||
            ))
 | 
			
		||||
        except:
 | 
			
		||||
            r.push(tag)
 | 
			
		||||
    return sep.join(r)
 | 
			
		||||
    return utils.tags_to_html(type(post), post.tags.all(), sep)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register.filter(name='threads')
 | 
			
		||||
@ -37,7 +26,7 @@ def threads(post, sep = '/'):
 | 
			
		||||
 | 
			
		||||
    return sep.join([
 | 
			
		||||
        '<a href="{}">{}</a>'.format(post.url(), post.title)
 | 
			
		||||
        for post in posts if post.published
 | 
			
		||||
        for post in posts[:-1] if post.published
 | 
			
		||||
    ])
 | 
			
		||||
 | 
			
		||||
@register.filter(name='around')
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										147
									
								
								cms/website.py
									
									
									
									
									
								
							
							
						
						
									
										147
									
								
								cms/website.py
									
									
									
									
									
								
							@ -1,4 +1,7 @@
 | 
			
		||||
from collections import namedtuple
 | 
			
		||||
 | 
			
		||||
from django.utils.text import slugify
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
from django.conf.urls import include, url
 | 
			
		||||
 | 
			
		||||
import aircox.cms.routes as routes
 | 
			
		||||
@ -33,6 +36,10 @@ class Website:
 | 
			
		||||
    """register list routes for the Comment model"""
 | 
			
		||||
 | 
			
		||||
    ## components
 | 
			
		||||
    Registration = namedtuple('Registration',
 | 
			
		||||
        'name model routes as_default'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    urls = []
 | 
			
		||||
    """list of urls generated thourgh registrations"""
 | 
			
		||||
    exposures = []
 | 
			
		||||
@ -57,44 +64,26 @@ class Website:
 | 
			
		||||
        if self.comments_routes:
 | 
			
		||||
            self.register_comments()
 | 
			
		||||
 | 
			
		||||
    def name_of_model(self, model):
 | 
			
		||||
    def register_model(self, name, model, as_default):
 | 
			
		||||
        """
 | 
			
		||||
        Return the registered name for a given model if found.
 | 
			
		||||
        """
 | 
			
		||||
        for name, _model in self.registry.items():
 | 
			
		||||
            if model is _model:
 | 
			
		||||
                return name
 | 
			
		||||
        Register a model and update model's fields with few data:
 | 
			
		||||
        - _website: back ref to self
 | 
			
		||||
        - _registration: ref to the registration object
 | 
			
		||||
 | 
			
		||||
    def register_comments(self):
 | 
			
		||||
        """
 | 
			
		||||
        Register routes for comments, for the moment, only
 | 
			
		||||
        ThreadRoute
 | 
			
		||||
        """
 | 
			
		||||
        self.register(
 | 
			
		||||
            'comment',
 | 
			
		||||
            view = views.PostListView,
 | 
			
		||||
            routes = [routes.ThreadRoute],
 | 
			
		||||
            model = models.Comment,
 | 
			
		||||
            css_class = 'comments',
 | 
			
		||||
            list = sections.Comments(
 | 
			
		||||
                truncate = 30,
 | 
			
		||||
                fields = ['content','author','date','time'],
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def __register_model(self, name, model):
 | 
			
		||||
        """
 | 
			
		||||
        Register a model and return the name under which it is registered.
 | 
			
		||||
        Raise a ValueError if another model is yet associated under this name.
 | 
			
		||||
        """
 | 
			
		||||
        if name in self.registry:
 | 
			
		||||
            if self.registry[name] is model:
 | 
			
		||||
                return name
 | 
			
		||||
            reg = self.registry[name]
 | 
			
		||||
            if reg.model is model:
 | 
			
		||||
                return reg
 | 
			
		||||
            raise ValueError('A model has yet been registered under "{}"'
 | 
			
		||||
                             .format(name))
 | 
			
		||||
        self.registry[name] = model
 | 
			
		||||
 | 
			
		||||
        reg = self.Registration(name, model, [], as_default)
 | 
			
		||||
        self.registry[name] = reg
 | 
			
		||||
        model._registration = reg
 | 
			
		||||
        model._website = self
 | 
			
		||||
        return name
 | 
			
		||||
        return reg
 | 
			
		||||
 | 
			
		||||
    def register_exposures(self, sections):
 | 
			
		||||
        """
 | 
			
		||||
@ -112,7 +101,8 @@ class Website:
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
    def register(self, name, routes = [], view = views.PageView,
 | 
			
		||||
                 model = None, sections = None, **view_kwargs):
 | 
			
		||||
                 model = None, sections = None,
 | 
			
		||||
                 as_default = False, **view_kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Register a view using given name and routes. If model is given,
 | 
			
		||||
        register the views for it.
 | 
			
		||||
@ -120,11 +110,21 @@ class Website:
 | 
			
		||||
        * name is used to register the routes as urls and the model if given
 | 
			
		||||
        * routes: can be a path or a route used to generate urls for the view.
 | 
			
		||||
            Can be a one item or a list of items.
 | 
			
		||||
        * view: route that is registered for the given routes
 | 
			
		||||
        * model: model being registrated. If given, register it in the website
 | 
			
		||||
            under the given name, and make it available to the view.
 | 
			
		||||
        * as_default: make the view available as a default view.
 | 
			
		||||
        """
 | 
			
		||||
        if type(routes) not in (tuple, list):
 | 
			
		||||
            routes = [ routes ]
 | 
			
		||||
 | 
			
		||||
        # model registration
 | 
			
		||||
        if model:
 | 
			
		||||
            name = self.__register_model(name, model)
 | 
			
		||||
            reg = self.register_model(name, model, as_default)
 | 
			
		||||
            reg.routes.extend(routes)
 | 
			
		||||
            view_kwargs['model'] = model
 | 
			
		||||
 | 
			
		||||
        # init view
 | 
			
		||||
        if not view_kwargs.get('menus'):
 | 
			
		||||
            view_kwargs['menus'] = self.menus
 | 
			
		||||
 | 
			
		||||
@ -137,9 +137,7 @@ class Website:
 | 
			
		||||
            **view_kwargs
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if type(routes) not in (tuple, list):
 | 
			
		||||
            routes = [ routes ]
 | 
			
		||||
 | 
			
		||||
        # url gen
 | 
			
		||||
        self.urls += [
 | 
			
		||||
            route.as_url(name, view)
 | 
			
		||||
                if type(route) == type and issubclass(route, routes_.Route)
 | 
			
		||||
@ -148,24 +146,51 @@ class Website:
 | 
			
		||||
            for route in routes
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def register_post(self, name, model, sections = None, routes = None,
 | 
			
		||||
                      list_view = views.PostListView,
 | 
			
		||||
                      detail_view = views.PostDetailView,
 | 
			
		||||
                      list_kwargs = {}, detail_kwargs = {}):
 | 
			
		||||
    def register_dl(self, name, model, sections = None, routes = None,
 | 
			
		||||
                    list_view = views.PostListView,
 | 
			
		||||
                    detail_view = views.PostDetailView,
 | 
			
		||||
                    list_kwargs = {}, detail_kwargs = {},
 | 
			
		||||
                    as_default = False):
 | 
			
		||||
        """
 | 
			
		||||
        Register a detail and list view for a given model, using
 | 
			
		||||
        routes. Just a wrapper around register.
 | 
			
		||||
        routes.
 | 
			
		||||
 | 
			
		||||
        Just a wrapper around `register`.
 | 
			
		||||
        """
 | 
			
		||||
        if sections:
 | 
			
		||||
            self.register(name, [ routes_.DetailRoute ], view = detail_view,
 | 
			
		||||
                          model = model, sections = sections, **detail_kwargs)
 | 
			
		||||
                          model = model, sections = sections,
 | 
			
		||||
                          as_default = as_default,
 | 
			
		||||
                          **detail_kwargs)
 | 
			
		||||
        if routes:
 | 
			
		||||
            self.register(name, routes, view = list_view,
 | 
			
		||||
                          model = model, **list_kwargs)
 | 
			
		||||
                          model = model, as_default = as_default,
 | 
			
		||||
                          **list_kwargs)
 | 
			
		||||
 | 
			
		||||
    def register_comments(self):
 | 
			
		||||
        """
 | 
			
		||||
        Register routes for comments, for the moment, only
 | 
			
		||||
        ThreadRoute.
 | 
			
		||||
 | 
			
		||||
        Just a wrapper around `register`.
 | 
			
		||||
        """
 | 
			
		||||
        self.register(
 | 
			
		||||
            'comment',
 | 
			
		||||
            view = views.PostListView,
 | 
			
		||||
            routes = [routes.ThreadRoute],
 | 
			
		||||
            model = models.Comment,
 | 
			
		||||
            css_class = 'comments',
 | 
			
		||||
            list = sections.Comments(
 | 
			
		||||
                truncate = 30,
 | 
			
		||||
                fields = ['content','author','date','time'],
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def set_menu(self, menu):
 | 
			
		||||
        """
 | 
			
		||||
        Set a menu, and remove any previous menu at the same position
 | 
			
		||||
        Set a menu, and remove any previous menu at the same position.
 | 
			
		||||
        Also update the menu's tag depending on its position, in order
 | 
			
		||||
        to have a semantic HTML5 on the web 2.0 (lol).
 | 
			
		||||
        """
 | 
			
		||||
        if menu.position in ('footer','header'):
 | 
			
		||||
            menu.tag = menu.position
 | 
			
		||||
@ -174,10 +199,40 @@ class Website:
 | 
			
		||||
        self.menus[menu.position] = menu
 | 
			
		||||
        self.register_exposures(menu.sections)
 | 
			
		||||
 | 
			
		||||
    def get_menu(self, position):
 | 
			
		||||
    def find_default(self, route):
 | 
			
		||||
        """
 | 
			
		||||
        Get an enabled menu by its position
 | 
			
		||||
        Return a registration that can be used as default for the
 | 
			
		||||
        given route.
 | 
			
		||||
        """
 | 
			
		||||
        return self.menus.get(position)
 | 
			
		||||
        for r in self.registry.values():
 | 
			
		||||
            if r.as_default and route in r.routes:
 | 
			
		||||
                return r
 | 
			
		||||
 | 
			
		||||
    def reverse(self, model, route, use_default = True, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Reverse a url using the given model and route. If the reverse does
 | 
			
		||||
        not function and use_default is True, use a model that have been
 | 
			
		||||
        registered as a default view and that have the given road.
 | 
			
		||||
 | 
			
		||||
        If no model is given reverse with default.
 | 
			
		||||
        """
 | 
			
		||||
        if model and route in model._registration.routes:
 | 
			
		||||
            try:
 | 
			
		||||
                name = route.make_view_name(model._registration.name)
 | 
			
		||||
                return reverse(name, kwargs = kwargs)
 | 
			
		||||
            except:
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
        if model and not use_default:
 | 
			
		||||
            return ''
 | 
			
		||||
 | 
			
		||||
        for r in self.registry.values():
 | 
			
		||||
            if r.as_default and route in r.routes:
 | 
			
		||||
                try:
 | 
			
		||||
                    name = route.make_view_name(r.name)
 | 
			
		||||
                    return reverse(name, kwargs = kwargs)
 | 
			
		||||
                except:
 | 
			
		||||
                    pass
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								notes.md
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								notes.md
									
									
									
									
									
								
							@ -21,11 +21,12 @@
 | 
			
		||||
    - empty content -> empty string
 | 
			
		||||
    - update documentation:
 | 
			
		||||
        - cms.views
 | 
			
		||||
        - cms.parts
 | 
			
		||||
        - cms.exposure
 | 
			
		||||
        - cms.script
 | 
			
		||||
        - cms.qcombine
 | 
			
		||||
    - routes
 | 
			
		||||
        - integrate QCombine
 | 
			
		||||
        - tag name instead of tag slug for the title
 | 
			
		||||
        - optional url args
 | 
			
		||||
    - admin cms
 | 
			
		||||
    - content management -> do we use a markup language?
 | 
			
		||||
    - sections:
 | 
			
		||||
 | 
			
		||||
@ -7,11 +7,12 @@ logger = logging.getLogger('aircox')
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.utils.translation import ugettext as _, ugettext_lazy
 | 
			
		||||
 | 
			
		||||
from aircox.cms.models import Post, RelatedPost
 | 
			
		||||
import aircox.programs.models as programs
 | 
			
		||||
import aircox.cms.models as cms
 | 
			
		||||
import aircox.cms.qcombine as qcombine
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Article (Post):
 | 
			
		||||
class Article (cms.Post):
 | 
			
		||||
    """
 | 
			
		||||
    Represent an article or a static page on the website.
 | 
			
		||||
    """
 | 
			
		||||
@ -29,7 +30,7 @@ class Article (Post):
 | 
			
		||||
        verbose_name_plural = _('Articles')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Program (RelatedPost):
 | 
			
		||||
class Program (cms.RelatedPost):
 | 
			
		||||
    website = models.URLField(
 | 
			
		||||
        _('website'),
 | 
			
		||||
        blank=True, null=True
 | 
			
		||||
@ -49,7 +50,7 @@ class Program (RelatedPost):
 | 
			
		||||
        auto_create = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Diffusion (RelatedPost):
 | 
			
		||||
class Diffusion (cms.RelatedPost):
 | 
			
		||||
    class Relation:
 | 
			
		||||
        model = programs.Diffusion
 | 
			
		||||
        bindings = {
 | 
			
		||||
@ -68,18 +69,7 @@ class Diffusion (RelatedPost):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        if self.thread:
 | 
			
		||||
            if not self.title:
 | 
			
		||||
                self.title = _('{name} // {first_diff}').format(
 | 
			
		||||
                    name = self.related.program.name,
 | 
			
		||||
                    first_diff = self.related.start.strftime('%A %d %B')
 | 
			
		||||
                )
 | 
			
		||||
            if not self.content:
 | 
			
		||||
                self.content = self.thread.content
 | 
			
		||||
            if not self.image:
 | 
			
		||||
                self.image = self.thread.image
 | 
			
		||||
            if not self.tags and self.pk:
 | 
			
		||||
                self.tags = self.thread.tags
 | 
			
		||||
        self.fill_empty()
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def info(self):
 | 
			
		||||
@ -90,7 +80,7 @@ class Diffusion (RelatedPost):
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Sound (RelatedPost):
 | 
			
		||||
class Sound (cms.RelatedPost):
 | 
			
		||||
    """
 | 
			
		||||
    Publication concerning sound. In order to manage access of sound
 | 
			
		||||
    files in the filesystem, we use permissions -- it is up to the
 | 
			
		||||
@ -138,3 +128,12 @@ class Sound (RelatedPost):
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Publications (qcombine.FakeModel):
 | 
			
		||||
    """
 | 
			
		||||
    Combine views
 | 
			
		||||
    """
 | 
			
		||||
    models = [ Article, Program, Diffusion, Sound ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -110,7 +110,9 @@ class Diffusions(sections.List):
 | 
			
		||||
                continue
 | 
			
		||||
            name = post.related.program.name
 | 
			
		||||
            if name not in post.title:
 | 
			
		||||
                post.title = '{}: {}'.format(name, post.title)
 | 
			
		||||
                post.title = ': ' + post.title if post.title else \
 | 
			
		||||
                            ' // ' + post.related.start.strftime('%A %d %B')
 | 
			
		||||
                post.title = name + post.title
 | 
			
		||||
        return object_list
 | 
			
		||||
 | 
			
		||||
    def get_object_list(self):
 | 
			
		||||
 | 
			
		||||
@ -524,8 +524,10 @@ player = {
 | 
			
		||||
    /// Select the next track in the current playlist, eventually play it
 | 
			
		||||
    next: function(play = true) {
 | 
			
		||||
        var playlist = this.playlist;
 | 
			
		||||
        if(playlist == this.live)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        var index = this.playlist.items.indexOf(this.item);
 | 
			
		||||
        console.log(index, this.item, this.playlist.items)
 | 
			
		||||
        if(index == -1)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user