forked from rc/aircox
		
	lolilol
This commit is contained in:
		
							
								
								
									
										141
									
								
								cms/models.py
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								cms/models.py
									
									
									
									
									
								
							@ -7,35 +7,48 @@ 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 post_save
 | 
			
		||||
from django.db.models.signals import post_init, post_save, post_delete
 | 
			
		||||
from django.dispatch import receiver
 | 
			
		||||
 | 
			
		||||
from taggit.managers import TaggableManager
 | 
			
		||||
 | 
			
		||||
# Using a separate thread helps for routing, by avoiding to specify an
 | 
			
		||||
# additional argument to get the second model that implies to find it by
 | 
			
		||||
# the name that can be non user-friendly, like /thread/relatedpost/id
 | 
			
		||||
class Thread (models.Model):
 | 
			
		||||
    """
 | 
			
		||||
    Object assigned to any Post and children that can be used to have parent and
 | 
			
		||||
    children relationship between posts of different kind.
 | 
			
		||||
 | 
			
		||||
    We use this system instead of having directly a GenericForeignKey into the
 | 
			
		||||
    Post because it avoids having to define the relationship with two models for
 | 
			
		||||
    routing (one for the parent and one for the children).
 | 
			
		||||
    """
 | 
			
		||||
    post_type = models.ForeignKey(ContentType)
 | 
			
		||||
    post_id = models.PositiveIntegerField()
 | 
			
		||||
    post = GenericForeignKey('post_type', 'post_id')
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get (cl, model, **kwargs):
 | 
			
		||||
        post_type = ContentType.objects.get_for_model(model)
 | 
			
		||||
        return cl.objects.get(post_type__pk = post_type.id,
 | 
			
		||||
                              **kwargs)
 | 
			
		||||
    def __get_query_set (cl, function, model, post, kwargs):
 | 
			
		||||
        if post:
 | 
			
		||||
            model = type(post)
 | 
			
		||||
            kwargs['post_id'] = post.id
 | 
			
		||||
 | 
			
		||||
        kwargs['post_type'] = ContentType.objects.get_for_model(model)
 | 
			
		||||
        return getattr(cl.objects, function)(**kwargs)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def filter (cl, model, **kwargs):
 | 
			
		||||
        post_type = ContentType.objects.get_for_model(model)
 | 
			
		||||
        return cl.objects.filter(post_type__pk = post_type.id,
 | 
			
		||||
                              **kwargs)
 | 
			
		||||
    def get (cl, model = None, post = None, **kwargs):
 | 
			
		||||
        return cl.__get_query_set('get', model, post, kwargs)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def exclude (cl, model, **kwargs):
 | 
			
		||||
        post_type = ContentType.objects.get_for_model(model)
 | 
			
		||||
        return cl.objects.exclude(post_type__pk = post_type.id,
 | 
			
		||||
                              **kwargs)
 | 
			
		||||
    def filter (cl, model = None, post = None, **kwargs):
 | 
			
		||||
        return self.__get_query_set('filter', model, post, kwargs)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def exclude (cl, model = None, post = None, **kwargs):
 | 
			
		||||
        return self.__get_query_set('exclude', model, post, kwargs)
 | 
			
		||||
 | 
			
		||||
    def save (self, *args, **kwargs):
 | 
			
		||||
        self.post = self.__initial_post
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def __str__ (self):
 | 
			
		||||
        return self.post_type.name + ': ' + str(self.post)
 | 
			
		||||
@ -57,16 +70,26 @@ class Post (models.Model):
 | 
			
		||||
        _('date'),
 | 
			
		||||
        default = timezone.datetime.now
 | 
			
		||||
    )
 | 
			
		||||
    public = models.BooleanField(
 | 
			
		||||
    published = models.BooleanField(
 | 
			
		||||
        verbose_name = _('public'),
 | 
			
		||||
        default = True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    title = models.CharField (
 | 
			
		||||
        _('title'),
 | 
			
		||||
        max_length = 128,
 | 
			
		||||
    )
 | 
			
		||||
    content = models.TextField (
 | 
			
		||||
        _('description'),
 | 
			
		||||
        blank = True, null = True
 | 
			
		||||
    )
 | 
			
		||||
    image = models.ImageField(
 | 
			
		||||
        blank = True, null = True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    title = ''
 | 
			
		||||
    content = ''
 | 
			
		||||
    tags = TaggableManager(
 | 
			
		||||
        _('tags'),
 | 
			
		||||
        blank = True,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def detail_url (self):
 | 
			
		||||
        return reverse(self._meta.verbose_name_plural.lower() + '_detail',
 | 
			
		||||
@ -77,28 +100,7 @@ class Post (models.Model):
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@receiver(post_save)
 | 
			
		||||
def on_new_post (sender, instance, created, *args, **kwargs):
 | 
			
		||||
    """
 | 
			
		||||
    Signal handler to create a thread that is attached to the newly post
 | 
			
		||||
    """
 | 
			
		||||
    if not issubclass(sender, Post) or not created:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    thread = Thread(post = instance)
 | 
			
		||||
    thread.save()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Article (Post):
 | 
			
		||||
    title = models.CharField(
 | 
			
		||||
        _('title'),
 | 
			
		||||
        max_length = 128,
 | 
			
		||||
        blank = False, null = False
 | 
			
		||||
    )
 | 
			
		||||
    content = models.TextField(
 | 
			
		||||
        _('content'),
 | 
			
		||||
        blank = False, null = False
 | 
			
		||||
    )
 | 
			
		||||
    static_page = models.BooleanField(
 | 
			
		||||
        _('static page'),
 | 
			
		||||
        default = False,
 | 
			
		||||
@ -125,18 +127,6 @@ class RelatedPostBase (models.base.ModelBase):
 | 
			
		||||
        if related_model:
 | 
			
		||||
            attrs['related'] = models.ForeignKey(related_model)
 | 
			
		||||
 | 
			
		||||
        mapping = rel.get('mapping')
 | 
			
		||||
        if mapping:
 | 
			
		||||
            def get_prop (name, related_name):
 | 
			
		||||
                return property(related_name) if callable(related_name) \
 | 
			
		||||
                        else property(lambda self:
 | 
			
		||||
                                        getattr(self.related, related_name))
 | 
			
		||||
 | 
			
		||||
            attrs.update({
 | 
			
		||||
                name: get_prop(name, related_name)
 | 
			
		||||
                    for name, related_name in mapping.items()
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        if not '__str__' in attrs:
 | 
			
		||||
            attrs['__str__'] = lambda self: str(self.related)
 | 
			
		||||
 | 
			
		||||
@ -144,11 +134,54 @@ class RelatedPostBase (models.base.ModelBase):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RelatedPost (Post, metaclass = RelatedPostBase):
 | 
			
		||||
    related = None
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
    class Relation:
 | 
			
		||||
        related_model = None
 | 
			
		||||
        mapping = None
 | 
			
		||||
        mapping = None          # dict of related mapping values
 | 
			
		||||
        bind_mapping = False    # update fields of related data on save
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def get_attribute (self, attr):
 | 
			
		||||
        attr = self.Relation.mappings.get(attr)
 | 
			
		||||
        return self.related.__dict__[attr] if attr else None
 | 
			
		||||
 | 
			
		||||
    def save (self, *args, **kwargs):
 | 
			
		||||
        if not self.title and self.related:
 | 
			
		||||
            self.title = self.get_attribute('title')
 | 
			
		||||
 | 
			
		||||
        if self.Relation.bind_mapping:
 | 
			
		||||
            self.related.__dict__.update({
 | 
			
		||||
                rel_attr: self.__dict__[attr]
 | 
			
		||||
                for attr, rel_attr in self.Relation.mapping
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            self.related.save()
 | 
			
		||||
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@receiver(post_init)
 | 
			
		||||
def on_thread_init (sender, instance, **kwargs):
 | 
			
		||||
    if not issubclass(Thread, sender):
 | 
			
		||||
        return
 | 
			
		||||
    instance.__initial_post = instance.post
 | 
			
		||||
 | 
			
		||||
@receiver(post_save)
 | 
			
		||||
def on_post_save (sender, instance, created, *args, **kwargs):
 | 
			
		||||
    if not issubclass(sender, Post) or not created:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    thread = Thread(post = instance)
 | 
			
		||||
    thread.save()
 | 
			
		||||
 | 
			
		||||
@receiver(post_delete)
 | 
			
		||||
def on_post_delete (sender, instance, using, *args, **kwargs):
 | 
			
		||||
    try:
 | 
			
		||||
        Thread.get(sender, post = instance).delete()
 | 
			
		||||
    except:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										82
									
								
								cms/views.py
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								cms/views.py
									
									
									
									
									
								
							@ -35,10 +35,12 @@ class PostListView (ListView):
 | 
			
		||||
    template_name = 'cms/list.html'
 | 
			
		||||
    allow_empty = True
 | 
			
		||||
 | 
			
		||||
    model = None
 | 
			
		||||
    query = None
 | 
			
		||||
    classes = ''
 | 
			
		||||
    website = None
 | 
			
		||||
    title = ''
 | 
			
		||||
    classes = ''
 | 
			
		||||
 | 
			
		||||
    route = None
 | 
			
		||||
    query = None
 | 
			
		||||
    embed = False
 | 
			
		||||
    fields = [ 'date', 'time', 'image', 'title', 'content' ]
 | 
			
		||||
 | 
			
		||||
@ -48,12 +50,11 @@ class PostListView (ListView):
 | 
			
		||||
            self.query = Query(self.query)
 | 
			
		||||
 | 
			
		||||
    def dispatch (self, request, *args, **kwargs):
 | 
			
		||||
        self.route = self.kwargs.get('route')
 | 
			
		||||
        self.route = self.kwargs.get('route') or self.route
 | 
			
		||||
        return super().dispatch(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_queryset (self):
 | 
			
		||||
        qs = self.route.get_queryset(self.model, self.request, **self.kwargs)
 | 
			
		||||
        qs = qs.filter(public = True)
 | 
			
		||||
 | 
			
		||||
        query = self.query or PostListView.Query(self.request.GET)
 | 
			
		||||
        if query.exclude:
 | 
			
		||||
@ -92,6 +93,9 @@ class PostDetailView (DetailView):
 | 
			
		||||
    Detail view for posts and children
 | 
			
		||||
    """
 | 
			
		||||
    template_name = 'cms/detail.html'
 | 
			
		||||
    website = None
 | 
			
		||||
 | 
			
		||||
    embed = False
 | 
			
		||||
    sections = []
 | 
			
		||||
 | 
			
		||||
    def __init__ (self, sections = None, *args, **kwargs):
 | 
			
		||||
@ -100,13 +104,13 @@ class PostDetailView (DetailView):
 | 
			
		||||
 | 
			
		||||
    def get_queryset (self, **kwargs):
 | 
			
		||||
        if self.model:
 | 
			
		||||
            return super().get_queryset(**kwargs).filter(public = True)
 | 
			
		||||
            return super().get_queryset(**kwargs).filter(published = True)
 | 
			
		||||
        return []
 | 
			
		||||
 | 
			
		||||
    def get_object (self, **kwargs):
 | 
			
		||||
        if self.model:
 | 
			
		||||
            object = super().get_object(**kwargs)
 | 
			
		||||
            if object.public:
 | 
			
		||||
            if object.published:
 | 
			
		||||
                return object
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
@ -134,17 +138,19 @@ class ViewSet:
 | 
			
		||||
    detail_view = PostDetailView
 | 
			
		||||
    detail_sections = []
 | 
			
		||||
 | 
			
		||||
    def __init__ (self):
 | 
			
		||||
    def __init__ (self, website = None):
 | 
			
		||||
        self.detail_sections = [
 | 
			
		||||
            section()
 | 
			
		||||
                for section in self.detail_sections
 | 
			
		||||
        ]
 | 
			
		||||
        self.detail_view = self.detail_view.as_view(
 | 
			
		||||
            model = self.model,
 | 
			
		||||
            sections = self.detail_sections
 | 
			
		||||
            sections = self.detail_sections,
 | 
			
		||||
            website = website,
 | 
			
		||||
        )
 | 
			
		||||
        self.list_view = self.list_view.as_view(
 | 
			
		||||
            model = self.model
 | 
			
		||||
            model = self.model,
 | 
			
		||||
            website = website,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.urls = [ route.as_url(self.model, self.list_view)
 | 
			
		||||
@ -152,6 +158,31 @@ class ViewSet:
 | 
			
		||||
                      [ routes.DetailRoute.as_url(self.model, self.detail_view ) ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Menu (DetailView):
 | 
			
		||||
    template_name = 'cms/menu.html'
 | 
			
		||||
 | 
			
		||||
    name = ''
 | 
			
		||||
    enabled = True
 | 
			
		||||
    classes = ''
 | 
			
		||||
    position = ''   # top, left, bottom, right
 | 
			
		||||
    sections = None
 | 
			
		||||
 | 
			
		||||
    def get_context_data (self, **kwargs):
 | 
			
		||||
        context = super().get_context_data(**kwargs)
 | 
			
		||||
        context.update({
 | 
			
		||||
            'name': self.name,
 | 
			
		||||
            'classes': self.classes,
 | 
			
		||||
            'position': self.position,
 | 
			
		||||
            'sections': [
 | 
			
		||||
                section.get(self.request, object = self.object)
 | 
			
		||||
                    for section in self.sections
 | 
			
		||||
            ]
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    def get (self, **kwargs):
 | 
			
		||||
        context = self.get_context_data(**kwargs)
 | 
			
		||||
        return render_to_string(self.template_name, context)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Section (DetailView):
 | 
			
		||||
    """
 | 
			
		||||
@ -159,6 +190,7 @@ class Section (DetailView):
 | 
			
		||||
    in order to have extra content about a post.
 | 
			
		||||
    """
 | 
			
		||||
    template_name = 'cms/section.html'
 | 
			
		||||
    require_object = False
 | 
			
		||||
    classes = ''
 | 
			
		||||
    title = ''
 | 
			
		||||
    content = ''
 | 
			
		||||
@ -177,11 +209,12 @@ class Section (DetailView):
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
    def get (self, request, **kwargs):
 | 
			
		||||
        self.object = kwargs.get('object')
 | 
			
		||||
        self.object = kwargs.get('object') or self.object
 | 
			
		||||
        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
 | 
			
		||||
@ -197,6 +230,8 @@ class ListSection (Section):
 | 
			
		||||
            self.title = title
 | 
			
		||||
            self.text = text
 | 
			
		||||
 | 
			
		||||
    use_icons = True
 | 
			
		||||
    icon_size = '32x32'
 | 
			
		||||
    template_name = 'cms/section_list.html'
 | 
			
		||||
 | 
			
		||||
    def get_object_list (self):
 | 
			
		||||
@ -204,11 +239,28 @@ class ListSection (Section):
 | 
			
		||||
 | 
			
		||||
    def get_context_data (self, **kwargs):
 | 
			
		||||
        context = super().get_context_data(**kwargs)
 | 
			
		||||
        context['classes'] += ' section_list'
 | 
			
		||||
        context['object_list'] = self.get_object_list()
 | 
			
		||||
        context.update({
 | 
			
		||||
            'classes': context.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
 | 
			
		||||
@ -222,8 +274,8 @@ class PostListSection (PostListView):
 | 
			
		||||
 | 
			
		||||
    def dispatch (self, request, *args, **kwargs):
 | 
			
		||||
        kwargs = self.get_kwargs(kwargs)
 | 
			
		||||
        # TODO: to_string
 | 
			
		||||
        return super().dispatch(request, *args, **kwargs)
 | 
			
		||||
        response = super().dispatch(request, *args, **kwargs)
 | 
			
		||||
        return str(response.content)
 | 
			
		||||
 | 
			
		||||
# TODO:
 | 
			
		||||
# - get_title: pass object / queryset
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								cms/website.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								cms/website.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
import cms.routes as routes
 | 
			
		||||
 | 
			
		||||
class Website:
 | 
			
		||||
    name = ''
 | 
			
		||||
    logo = None
 | 
			
		||||
    menus = None
 | 
			
		||||
    router = None
 | 
			
		||||
 | 
			
		||||
    def __init__ (self, **kwargs):
 | 
			
		||||
        self.__dict__.update(kwargs)
 | 
			
		||||
        if not self.router:
 | 
			
		||||
            self.router = routes.Router()
 | 
			
		||||
        if not self.sets:
 | 
			
		||||
            self.sets = []
 | 
			
		||||
 | 
			
		||||
    def register_set (self, view_set):
 | 
			
		||||
        view_set = view_set(website = self)
 | 
			
		||||
        self.router.register_set(view_set)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def urls (self):
 | 
			
		||||
        return self.router.get_urlpatterns()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user