.. _contenttypes:
==================================================
Content types - what your page content is built of
==================================================
You will learn how to add your own content types and how you can
render them in a template.
What is a content type anyway?
==============================
In FeinCMS, a content type is something to attach as content to a base model,
for example a CMS Page (the base model) may have several rich text components
associated to it (those would be RichTextContent content types).
Every content type knows, amongst other things, how to render itself.
Think of content types as "snippets" of information to appear on a page.
Rendering contents in your templates
====================================
Simple:
::
{% block content %}
{% for content in feincms_page.content.main %}
{{ content.render }}
{% endfor %}
{% endblock %}
{% block sidebar %}
{% for content in feincms_page.content.sidebar %}
{{ content.render }}
{% endfor %}
{% endblock %}
Implementing your own content types
===================================
The minimal content type is an abstract Django model with a :func:`render`
method, nothing else::
class TextileContent(models.Model):
content = models.TextField()
class Meta:
abstract = True
def render(self, **kwargs):
return textile(self.content)
All content types' :func:`render` methods must accept ``**kwargs``. This
allows easily extending the interface with additional parameters. But more
on this later.
FeinCMS offers a method on :class:`feincms.models.Base` called
:func:`create_content_type` which will create concrete content types from
your abstract content types. Since content types can be used for different
CMS base models such as pages and blog entries (implementing a rich text
or an image content once and using it for both models makes lots of sense)
your implementation needs to be abstract. :func:`create_content_type` adds
a few utility methods and a few model fields to build the concrete type,
a foreign key to the base model (f.e. the :class:`Page`) and
several properties indicating where the content block will be positioned
in the rendered result.
.. note::
The examples on this page assume that you use the
:class:`~feincms.module.page.models.Page` CMS base model. The principles
outlined apply for all other CMS base types.
The complete code required to implement and include a custom textile content
type is shown here::
from feincms.module.page.models import Page
from django.contrib.markup.templatetags.markup import textile
from django.db import models
class TextilePageContent(models.Model):
content = models.TextField()
class Meta:
abstract = True
def render(self, **kwargs):
return textile(self.content)
Page.create_content_type(TextilePageContent)
There are three field names you should not use because they are added
by ``create_content_type``: These are ``parent``, ``region`` and ``ordering``.
These fields are used to specify the place where the content will be
placed in the output.
Customizing the render method for different regions
===================================================
The default ``render`` method uses the region key to find a render method
in your concrete content type and calls it. This allows you to customize
the output depending on the region; you might want to show the same
content differently in a sidebar and in the main region for example.
If no matching method has been found a ``NotImplementedError`` is raised.
This ``render`` method tries to be a sane default, nothing more. You can
simply override it and put your own code there if you do not any
differentiation, or if you want to do it differently.
All ``render`` methods should accept ``**kwargs``. Some render methods might
need the request, for example to determine the correct Google Maps API
key depending on the current domain. The two template tags ``feincms_render_region``
and ``feincms_render_content`` pass the current rendering context as a
keyword argument too.
The example above could be rewritten like this::
{% load feincms_tags %}
This does exactly the same, but you do not have to loop over the page content
blocks yourself. You need to add the request context processor to your list
of context processors for this example to work.
.. _contenttypes-extramedia:
Extra media for content types
=============================
Some content types require extra CSS or javascript to work correctly. The
content types have a way of individually specifying which CSS and JS files
they need. The mechanism in use is almost the same as the one used in
`form and form widget media`_.
.. _`form and form widget media`: http://docs.djangoproject.com/en/dev/topics/forms/media/
Include the following code in the `` section of your template to include
all JS and CSS media file definitions::
{{ feincms_page.content.media }}
The individual content types should use a ``media`` property do define the
media files they need::
from django import forms
from django.db import models
from django.template.loader import render_to_string
class MediaUsingContentType(models.Model):
album = models.ForeignKey('gallery.Album')
class Meta:
abstract = True
@property
def media(self):
return forms.Media(
css={'all': ('gallery/gallery.css',),},
js=('gallery/gallery.js',),
)
def render(self, **kwargs):
return render_to_string('content/gallery/album.html', {
'content': self,
})
Please note that you can't define a ``Media`` inner class (yet). You have to
provide the ``media`` property yourself. As with form and widget media definitions,
either ``STATIC_URL`` or ``MEDIA_URL`` (in this order) will be prepended to
the media file path if it is not an absolute path already.
Alternatively, you can use the ``media_property`` function from django.forms
to implement the functionality, which then also supports inheritance
of media files::
from django.forms.widgets import media_property
class MediaUsingContentType(models.Model):
class Media:
js = ('whizbang.js',)
MediaUsingContentType.media = media_property(MediaUsingContentType)
.. _contenttypes-processfinalize:
Influencing request processing through a content type
=====================================================
Since FeinCMS 1.3, content types are not only able to render themselves, they
can offer two more entry points which are called before and after the response
is rendered. These two entry points are called :func:`process` and :func:`finalize`.
:func:`process` is called before rendering the template starts. The method always
gets the current request as first argument, but should accept ``**kwargs`` for
later extensions of the interface. This method can short-circuit
the request-response-cycle simply by returning any response object. If the return
value is a ``HttpResponse``, the standard FeinCMS view function does not do any
further processing and returns the response right away.
As a special case, if a :func:`process` method returns ``True`` (for successful
processing), ``Http404`` exceptions raised by any other content type on the
current page are ignored. This is especially helpful if you have several
``ApplicationContent`` content types on a single page.
:func:`finalize` is called after the response has been rendered. It receives
the current request and response objects. This function is normally used to
set response headers inside a content type or do some other post-processing.
If this function has any return value, the FeinCMS view will return this value
instead of the rendered response.
Here's an example form-handling content which uses all of these facilities::
class FormContent(models.Model):
class Meta:
abstract = True
def process(self, request, **kwargs):
if request.method == 'POST':
form = FormClass(request.POST)
if form.is_valid():
# Do something with form.cleaned_data ...
return HttpResponseRedirect('?thanks=1')
else:
form = FormClass()
self.rendered_output = render_to_string('content/form.html', {
'form': form,
'thanks': request.GET.get('thanks'),
})
def render(self, **kwargs):
return getattr(self, 'rendered_output', u'')
def finalize(self, request, response):
# Always disable caches if this content type is used somewhere
response['Cache-Control'] = 'no-cache, must-revalidate'
.. note::
Please note that the ``render`` method should not raise an exception if
``process`` has not been called beforehand.
.. warning::
The FeinCMS page module views
guarantee that ``process`` is called beforehand, other modules may not do
so. ``feincms.module.blog`` for instance does not.
Bundled content types
=====================
Application content
-------------------
.. module:: feincms.content.application.models
.. class:: ApplicationContent()
Used to let the administrator freely integrate 3rd party applications into
the CMS. Described in :ref:`integration-applicationcontent`.
Comments content
----------------
.. module:: feincms.content.comments.models
.. class:: CommentsContent()
Comment list and form using ``django.contrib.comments``.
Contact form content
--------------------
.. module:: feincms.content.contactform.models
.. class:: ContactFormContent()
Simple contact form. Also serves as an example how forms might be used inside
content types.
Inline files
------------
.. module:: feincms.content.file.models
.. class:: FileContent()
Simple content types holding just a file.
You should probably use the MediaFileContent though.
Inline images
-------------
.. module:: feincms.content.image.models
.. class:: ImageContent()
Simple content types holding just an image with a
position. You should probably use the MediaFileContent though.
Additional arguments for :func:`~feincms.models.Base.create_content_type`:
* ``POSITION_CHOICES``
* ``FORMAT_CHOICES``
Media library integration
-------------------------
.. module:: feincms.content.medialibrary.v2
.. class:: MediaFileContent()
Mini-framework for arbitrary file types with customizable rendering
methods per-filetype. Add 'feincms.module.medialibrary' to INSTALLED_APPS.
Additional arguments for :func:`~feincms.models.Base.create_content_type`:
* ``TYPE_CHOICES``: (mandatory)
A list of tuples for the type choice radio input fields.
This field allows the website administrator to select a suitable presentation
for a particular media file. For example, images could be shown as thumbnail
with a lightbox or offered as downloads. The types should be specified as
follows for this use case::
..., TYPE_CHOICES=(('lightbox', _('lightbox')), ('download', _('as download'))),
The ``MediaFileContent`` tries loading the following templates in order for
a particular image media file with type ``download``:
* ``content/mediafile/image_download.html``
* ``content/mediafile/image.html``
* ``content/mediafile/download.html``
* ``content/mediafile/default.html``
The media file type is stored directly on
:class:`~feincms.module.medialibrary.models.MediaFile`.
The file type can also be used to select templates which can be used
to further customize the presentation of mediafiles, f.e.
``content/mediafile/swf.html`` to automatically generate the necessary
``