CMS System

Cubane’s content management system is provided by the cubane.cms.views.CMS class. You need to provide your own class that derives from cubane.cms.views.CMS. Usually you declare your class in your main views.py file for example:

from cubane.cms.views import CMS

class MyCMS(CMS):
    pass

By introducing your own class, you can alter the behaviour of the CMS system by overriding various methods. The CMS class is declared via the Django setting variable CMS for your application in the following way:

CMS = 'myapp.views.MyCMS'

The given string value is referring to the actual class that is used by Cubane for representing the CMS system. The path must include the full path to the class including modules and sub-modules.

The following sections will provide details on how to extend and override certain methods of the CMS class in order to extend and change the behaviour of the CMS system.

Template Context Pipeline

The CMS system will derive a default template context by deriving a number of variables that are provided when rendering any CMS content.

Then, such template context is further processed by a number of steps until finally the actual page is being rendered.

The CMS class can be extended to intercept and process the template content at various stages in the pipeline, for example to include page-specific or general data that must be available for the template.

The following example demonstrates how the template variable featured is made available to every page that is rendered. This could be used for example to render a list of featured posts on every page.

from cubane.cms.views import CMS
from myapp.models import NewsArticle

class MyCMS(CMS):
    def on_template_context(self, request, context, template_context):
        featured = NewsArticle.objects.order_by('-posted_on')[:3]
        template_context.update({
            'featured': featured
        })

        return template_context

The method cubane.views.CMS.on_template_context() is being called by the CMS system for every page that is rendered. So, any additional database roundtrips or processing work undertaken here occurs for every single page.

The given template context has already been compiled and is ready to be used by the page template. Therefore, you can use it within your version of cubane.views.CMS.on_template_context() in order to obtain critical information about what is being rendered as the following example demonstrates:

from cubane.cms.views import CMS
from myapp.models import NewsArticle

class MyCMS(CMS):
    def on_template_context(self, request, context, template_context):
        current_page = template_context.get('current_page')
        if current_page and current_page.is_homepage:
            featured = NewsArticle.objects.order_by('-posted_on')[:3]
            template_context.update({
                'featured': featured
            })

        return template_context

Here, we are only fetching the latest news articles if the current page is the homepage. Cubane’s CMS system provides a number of similar methods to make those common cases easy for us. For example, we could have written the previous example in the following way:

from cubane.cms.views import CMS
from myapp.models import NewsArticle

class MyCMS(CMS):
    def on_homepage(self, request, context, template_context):
        featured = NewsArticle.objects.order_by('-posted_on')[:3]
        template_context.update({
            'featured': featured
        })

        return template_context

The method cubane.views.CMS.on_homepage() is only being called whenever the homepage is rendered. Otherwise the method works in the exact same way as cubane.views.CMS.on_template_context().

The following pipeline steps can be overridden:

Method Description
cubane.views.CMS.on_template_context() Called for every request.
cubane.views.CMS.on_homepage() Called only for requests concerning the homepage.
cubane.views.CMS.on_contact_page() Called only for requests concerning the contact page.
cubane.views.CMS.on_404_page() Called only for requests that will trigger a 404 status code.

Template Context

The CMS system derives a default template context for every request that is being processed. Such template context contains information about the current page, the navigation and website-wide settings.

The following template variables are available when rendering CMS content:

active_nav

Contains information about the top level navigation item that is currently the active item. The active navigation item is representing the current page or any parent page therefore.

current_page

Represents the page model instance of the current page that is rendered.

homepage

The page model instance of the page that represents the homepage.

images

A list of all image model instances that are referenced on the current page via one or more content slots.

is_404_page

True if the current page is rendered as a 404 not found page. Please refer to section Default Page Roles for more information about page roles.

is_contact_page

True if the current page is the contact page. Please refer to section Default Page Roles for more information about page roles.

is_enquiry_template

True if the current page is the enquiry page. Please refer to section Default Page Roles for more information about page roles.

is_homepage

True if the current page is the homepage. Please refer to section Default Page Roles for more information about page roles.

nav

Represents the navigation structure of the page. Please refer to section Navigation for more information.

pages

Represents a list of navigation items for every page that has a unique navigation identifier assigned to it. Please refer to section Navigation by Identifier for more information on navigation identifiers.

page_links

Represents a list of CMS content to which the current page is linking to via its content slots. Please refer to section Links for more information about page links.

page

Refers to the model instance of the current page or – if the current page is represented by a post – the parent page of the current post.

preview

True if the current page is currently rendered for the purpose of being previewed within the backend system. Please refer to section Preview for more information.

settings

The model instance of the CMS settings as specified via CMS_SETTINGS_MODEL. Please refer to section CMS Settings for more information about CMS settings.

slots

Represents a list of slot names of all slots of the current page that are not empty.

If a page hierarchy is used (PAGE_HIERARCHY), then the following template variable is made available in addition to the default context:

hierarchical_pages

Represents a list of all page model instances that are direct child pages of the current page. Please refer to section Page Hierarchy for more information about page hierarchies.

If the page has posts assigned to it then the following information is made available in addition to the default context:

paged_posts

List of posts that are assigned to the current page – organised as a list of pages.

paginator

The paginator object that is responsible to paginate the list of all posts that are assigned to the current page.

posts

List of all posts that are assigned to the current page.

post_slug

The slug representing the type of posts that are assigned to the current page.

verbose_name_plural

The plural verbose name of the model that is representing posts that are assigned to the current page.

verbose_name

The singular verbose name of the model that is representing posts that are assigned to the current page.

The template context is first derived for any request that is being processed. Then the template context might be intercepted and further processed by various parts of the CMS system. Please refer to section Template Context Pipeline for more information about pipeline steps.

Hierarchical Pages Queryset

The list of hierarchical pages is determined by the method cubane.cms.views.CMS.get_hierarchical_pages(). You can override the method in order to customise the list of hierarchical pages that are returned:

from cubane.cms.views import CMS

class MyCMS(CMS):
    def get_hierarchical_pages(self, request, current_page, pages):
        pages = pages.order_by('title')
        return pages

The example changes the order in which child pages are represented. While you would normally find child pages to be ordered in their distinct sequential order, here the order has been changed to be alphabetically by the page title.

Posts Queryset

The list of posts that are assigned to the current page is determined by the method cubane.cms.views.CMS.get_posts(). You can override the method in order to customise the list of posts that are returned:

from django.http import HttpResponse
from cubane.cms.views import CMS

class MyCMS(CMS):
    def get_posts(self, request, model, posts):
        posts = posts.order_by('title)
        return posts

Again, we are only changing the order of posts here, but we could customise the initial query or filter by further arguments.

Preview

Any CMS-enabled page can be rendered for preview mode in the backend system. In fact, the preview is used to select particular page slots in order to edit their content.

When rendering page slots via the slot template tag, then it it particularly important to always render to slot if the page is presented in preview mode: For example, a slot may only be rendered under a certain condition, but in preview mode you would want to always render a slot; otherwise users will not be able to select the slot to begin with.

{% load cms_tags %}
<!DOCTYPE html>
<html>
    <body>
        {% if 'content' in slots or preview %}
            <div class="content-slot">{% slot 'content' %}</div>
        {% endif %}
    </body>
</html>

The example demonstrates a condition under which the slot is rendered: The slot is only rendered if the slot has actual content assigned to it. However we also render the slot if we are in preview mode; otherwise no-one would be able to assign content to the slot to start with.

Request/Response Hooks

On every request, the CMS system calls the methods cubane.cms.views.CMS.on_request() and cubane.cms.views.CMS.on_response(). cubane.cms.views.CMS.on_request() is called first before any other pipeline function is called. Just before the response has been compiled, the method cubane.cms.views.CMS.on_response() is called.

Both methods can alter the response if a response object is returned by either of them. If cubane.cms.views.CMS.on_request() returns a response object, then any further processing is aborted and the returned response object is delivered back to the client.

from django.http import HttpResponse
from cubane.cms.views import CMS

class MyCMS(CMS):
    def on_request(self, request, context):
        return HttpResponse('Intercepting each request replacing it with this response.')

Identifier Hooks

For CMS pages that have a unique identifier assigned, a method can be defined as part of the cubane.cms.views.CMS class that matches the identifier. For example, if a page is rendered that had an identifier of map then the CMS system would automatically attempt to call the method on_page_identifier_map.

from django.conf import settings
from django.http import HttpResponse
from cubane.cms.views import CMS

class MyCMS(CMS):
    def on_page_identifier_map(self, request, context, template_context):
        template_context.update({
            'map_api_key': settings.MAP_API_KEY
        })
        return template_context

The example above demonstrates how the settings variable MAP_API_KEY is provided to the template for the page with the identifier map.

See also

A unique page identifier is also useful when using the navigation system. Please refer to section Navigation by Identifier for more information about identifiers being used in combination with the navigation system.

Django View Handlers

The CMS system can be integrated with ordinary Django view handlers by allowing the system to determine a template context including navigation and other aspects to be derived without any real CMS page being present.

For example, you could implement a member area where the login page does not really exist as part of the CMS system. However, you can still generate all other CMS-related information that is required to render the page, such as the navigation:

from cubane.decorators import template
from cubane.cms.decorators import cubane_cms_context

@template('myapp/login.html')
@cubane_cms_context()
def members_login(request):
    ...

    return {
        'form': form
    }

The template decorator can be used to render a template file (in this case myapp/login.html based on the template context that is returned by the view handler function in the form of a dictionary.

The cubane_cms_context decorator generates a regular template context as you would expect from the CMS system (see section Template Context) but then also updates the template context based on the dictionary that is returned from the django view handler function.