diff --git a/cms/sections.py b/cms/sections.py
new file mode 100644
index 0000000..a102406
--- /dev/null
+++ b/cms/sections.py
@@ -0,0 +1,240 @@
+"""
+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.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
+
+
+class Section(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.
+
+ ! Important Note: values given for rendering are considered as safe
+ HTML in templates.
+
+ Attributes:
+ * template_name: template to use for rendering
+ * tag: container's tags
+ * css_class: css classes of the container
+ * attr: HTML attributes of the container
+ * hide_empty: if true, section is not rendered when content is empty
+
+ * title: title of the section
+ * header: header of the section
+ * footer: footer of the section
+
+ * object_required: if true and not object has been given, gets angry
+ * object: (can be persistent) related object
+
+ """
+ template_name = 'aircox/cms/content_object.html'
+
+ tag = 'div'
+ css_class = ''
+ attrs = ''
+ hide_empty = False
+ title = ''
+ header = ''
+ footer = ''
+ object = None
+
+ def __init__ (self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.css_class += ' section'
+
+ def get_content(self):
+ return ''
+
+ def get_context_data(self):
+ return {
+ 'view': self,
+ 'tag': self.tag,
+ 'css_class': self.css_class,
+ 'attrs': self.attrs,
+ 'title': self.title,
+ 'header': self.header,
+ 'footer': self.footer,
+ 'content': self.get_content(),
+ 'object': self.object,
+ }
+
+ def get(self, request, object=None, **kwargs):
+ if not self.object:
+ self.object = object
+
+ self.request = request
+ self.kwargs = kwargs
+
+ context = self.get_context_data()
+ # if not context['content'] and self.hide_empty:
+ # return ''
+ return render_to_string(self.template_name, context, request=request)
+
+
+class Image(Section):
+ """
+ Render an image using the relative url or relative to self.object.
+
+ Attributes:
+ * url: relative image url
+ * rel_attr: name of the attribute of self.object to use
+ """
+ url = None
+ rel_attr = 'image'
+
+ def get_content(self, **kwargs):
+ if self.url is None:
+ image = getattr(self.object, self.rel_attr)
+ return ''.format(image.url) if image else ''
+ return '
'.format(static(self.url))
+
+
+class Content(Section):
+ """
+ Render content using the self.content or relative to self.object.
+
+ Attributes:
+ * content: raw HTML code to render
+ * rel_attr: name of the attribute of self.object to use
+ """
+ content = None
+ rel_attr = 'content'
+
+ def get_content(self):
+ if self.content is None:
+ # FIXME: markdown?
+ content = getattr(self.object, self.rel_attr)
+ content = escape(content)
+ content = re.sub(r'(^|\n\n)((\n?[^\n])+)', r'
\2
', content) + content = re.sub(r'\n', r'