"""
Define different Section css_class that can be used by views.Sections;
"""
import re
import datetime # used in calendar
from random import shuffle
from django.utils import timezone as tz
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
from honeypot.decorators import check_honeypot
from aircox.cms.forms import CommentForm
from aircox.cms.exposures import expose
from aircox.cms.actions import Actions
class Viewable:
"""
Describe a view that is still usable as a class after as_view() has
been called.
"""
@classmethod
def as_view (cl, *args, **kwargs):
"""
Create a view containing the current viewable, using a subclass
of aircox.cms.views.BaseView.
All the arguments are passed to the view directly.
"""
from aircox.cms.views import PageView
kwargs['sections'] = cl
return PageView.as_view(*args, **kwargs)
@classmethod
def extends (cl, **kwargs):
"""
Return a sub class where the given attribute have been updated
using kwargs.
"""
class Sub(cl):
pass
Sub.__name__ = cl.__name__
for k, v in kwargs.items():
setattr(Sub, k, v)
if hasattr(cl, '_exposure'):
return expose(Sub)
return Sub
class Sections(Viewable, list):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def prepare(self, *args, **kwargs):
"""
prepare all children sections
"""
for i, section in enumerate(self):
if callable(section) or type(section) == type:
self[i] = section()
self[i].prepare(*args, **kwargs)
def render(self, *args, **kwargs):
if args:
self.prepare(*args, **kwargs)
return ''.join([
section.render()
for section in self
])
def filter(self, predicate):
return [ section for section in self if predicate(section) ]
class Section(Viewable, View):
"""
On the contrary to Django's views, we create an instance of the view
only once, when the server is run.
Attributes are not changed once they have been set, and are related
to Section configuration/rendering. However, some data are considered
as temporary, and are reset at each rendering, using given arguments.
When get_context_data returns None, returns an empty string
! Important Note: values given for rendering are considered as safe
HTML in templates.
"""
template_name = 'aircox/cms/website.html'
"""
Template used for rendering
"""
tag = 'div'
"""
HTML tag used for the container
"""
name = ''
"""
Name/ID of the container
"""
css_class = ''
"""
CSS classes for the container
"""
attrs = None
"""
HTML Attributes of the container
"""
title = ''
"""
Safe HTML code for the title
"""
header = ''
"""
Safe HTML code for the header
"""
footer = ''
"""
Safe HTML code for the footer
"""
message_empty = None
"""
If message_empty is not None, print its value as
content of the section instead of hiding it. This works also when
its value is an empty string (prints an empty string).
"""
view = None
request = None
object = None
kwargs = None
def add_css_class(self, css_class):
if self.css_class:
if css_class not in self.css_class.split(' '):
self.css_class += ' ' + css_class
else:
self.css_class = css_class
def __init__ (self, **kwargs):
super().__init__(**kwargs)
self.add_css_class('section')
if type(self) != Section:
self.add_css_class('section_' + type(self).__name__.lower())
if not self.attrs:
self.attrs = {}
if self.name:
self.attrs['name'] = self.name
self.attrs['id'] = self.name
def is_empty(self):
"""
Return True if the section content will be empty. This allows to
hide the section.
This must be implemented by the subclasses.
"""
return False
def get_context_data(self):
return {
'view': self,
'exp': (hasattr(self, '_exposure') and self._exposure) or None,
'tag': self.tag,
'css_class': self.css_class,
'attrs': self.attrs,
'title': self.title,
'header': self.header,
'footer': self.footer,
'content': '',
'object': self.object,
'embed': True,
}
def prepare(self, view = None, **kwargs):
"""
initialize the object with valuable informations.
"""
self.kwargs = kwargs
if view:
self.view = view
self.request = view.request
if hasattr(view, 'object'):
self.object = view.object
def render(self, *args, **kwargs):
"""
Render the section as a string. Use *args and **kwargs to prepare
the section, then get_context_data and render.
"""
if args or kwargs:
self.prepare(*args, **kwargs)
context = self.get_context_data()
is_empty = self.is_empty()
if not context or (is_empty and not self.message_empty):
return ''
if is_empty and self.message_empty:
context['content'] = self.message_empty
context['embed'] = True
return render_to_string(
self.template_name, context, request=self.request
)
class Image(Section):
"""
Render an image using the relative url or relative to self.object.
Attributes:
* url: relative image url
* img_attr: name of the attribute of self.object to use
"""
url = None
img_attr = 'image'
def get_image(self):
if self.url:
return static(self.url)
if hasattr(self.object, self.img_attr):
image = getattr(self.object, self.img_attr)
return (image and image.url) or None
def is_empty(self):
return not self.get_image()
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
url = self.get_image()
if url:
context['content'] += ''.format(url)
return context
class Content(Image):
"""
Render content using the self.content or relative to self.object.
Since it is a child of Image, can also render an image.
Attributes:
* content: raw HTML code to render
* content_attr: name of the attribute of self.object to use
"""
# FIXME: markup language -- coordinate with object's one (post/comment)?
content = None
content_attr = 'content'
def get_content(self):
if self.content:
return self.content
if hasattr(self.object, self.content_attr):
return getattr(self.object, self.content_attr) or None
def is_empty(self):
return super().is_empty() and not self.get_content()
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
content = self.get_content()
if content:
if not self.content:
content = escape(content)
content = re.sub(r'(^|\n\n)((\n?[^\n])+)', r'
\2
', content) content = re.sub(r'\n', r'