block

Tip

The block app is part of our super simple CMS site which can be cloned from here: https://gitlab.com/kb/hatherleighcommunitymarket_org

Note

Some of this documentation is awaiting review. It has been moved from the old cms app.

https://gitlab.com/kb/block

Diagram

misc/block.pdf

Questions

  1. Why do we have PageSection and TemplateSection models in the block app?

    The TemplateSection is a list of the Section types that a Page is allowed to have.

    The PageSection is a list of the actual sections that a Page has!

Ordering

The block and compose apps supports ordering of blocks on the master branch.

Older projects which have not been converted to use ordering, must use the 962-legacy-block-order branch.

Project

requirements/base.txt:

django-taggit==

Tip

See Requirements for the current version.

settings/base.py:

TEMPLATES = [
    {
        'OPTIONS': {
            'context_processors': [
                # ...
                'django.template.context_processors.request',

THIRD_PARTY_APPS = (
    'taggit',

CMS

To use the block system as a CMS:

url(regex=r'^block/',
    view=include('block.urls.block')
    ),
url(regex=r'^compose/',
    view=include('compose.urls.compose')
    ),
url(regex=r'^wizard/',
    view=include('block.urls.wizard')
    ),
# this url include should come last
url(regex=r'^',
    view=include('block.urls.cms')
    ),

Note

The block.urls.cms URLs add a header and footer to the page. The block.urls.wizard URLs add the image and link wizard.

Custom Pages

If you want to add a form or extra context to a page, then use a CUSTOM page. Our standard is to use Page.CUSTOM for the page parameter.

The URL of a custom page must be different to the standard URL for the page.

Here is an example management command to create a custom page:

# -*- encoding: utf-8 -*-
from django.core.management.base import BaseCommand

from block.models import Page, Section, Template, TemplateSection
from web.service import SECTION_MAIN

class Command(BaseCommand):

    def init_page(self, page_name, slug_menu, template):
        self.stdout.write("      - {}".format(page_name))
        page = Page.objects.init_page(
            Page.CUSTOM,
            slug_menu,
            page_name,
            0,
            template,
            is_custom=True,
        )
        # update page sections
        page.refresh_sections_from_template()

    def init_template(self, page_name, template_name):
        # page section
        section = Section.objects.get(slug=SECTION_MAIN)
        # template
        template = Template.objects.init_template(page_name, template_name)
        TemplateSection.objects.init_template_section(template, section)
        return template

    def handle(self, *args, **options):
        self.stdout.write('Pages')
        data = [
            ('Question', 'question', 'web/question.html'),
            ('Student', 'student', 'web/student.html'),
        ]
        for page_name, slug_menu, template_name in data:
            template = self.init_template(page_name, template_name)
            self.init_page(page_name, slug_menu, template)
        self.stdout.write('Complete')

See enquiry for an older example…

Field List

The field list works by allowing the user to enter a list of space separated field names in the Section create / update form.

  • If the field is blank all fields in the form are displayed

  • The exception to this is for the Article block for which the default is all the fields except the slug (Article ID) on the form.

Tip

The tests in test_article.py give a good idea of how it works.

Google Analytics

To add Google Analytics to a CMS site:

  1. Use the block/base.html template

  2. Set the google_site_tag in the block settings: /admin/block/blocksettings/

The base.html template for the block and our Simple Site (CMS) will include the Google Analytics template by default.

Move Blocks Up and Down

The feature to move blocks up and down is enabled in the admin interface.

To switch it on:

  1. Browse to /admin/

  2. Select Block Settings

  3. Tick the Can move blocks up and down checkbox.

Pagination

To use pagination in a block view, we must change the page_kwarg.

As a standard we will use page-no e.g:

page_kwarg = 'page-no'
paginate_by = 15

The cause of this issue is our use of the page variable in the block app to identify the page. The default value of page_kwarg is page. Django looks for page_kwarg in views/generic/list.py:

page = self.kwargs.get(page_kwarg) or self.request.GET.get(page_kwarg) or 1

Template

To use the simple CMS menu and styling in your project we can use block/base.html in place of base/base.html:

<!-- project/templates/project/base.html -->
{% extends 'block/base.html' %}

<!-- web/templates/web/page.html -->
{% extends 'project/base.html' %}

To extend the block menu in your project:

<!-- this block will appear below the dashboard and above the pages -->
{% block menu_dash %}

<!-- this block will below the pages and above logout-->
{% block menu_extra %}

Testing

To test a view with a custom page with this URL:

url(regex=r'^contact/$',
    view=EnquiryCreateView.as_view(),
    kwargs=dict(page=Page.CUSTOM, menu='contact'),
    name='web.contact'
    ),

You need to create the Page with a factory e.g:

from block.models import Page
from block.tests.factories import (
    PageFactory,
    TemplateFactory,
)

@pytest.mark.django_db
def test_contact(client):
    PageFactory(
        is_custom=True,
        slug=Page.CUSTOM,
        slug_menu='contact',
        template=TemplateFactory(template_name='web/page_panelled.html'),
    )
    url = reverse('web.contact')
    r = client.get(url)
    assert 200 == r.status_code

URL

To use the Django url tag to link to a page:

<a href="{% url 'project.page' 'custom' 'contact' %}">
  You can find our contact details by clicking here...
</a>

And to reverse:

url = reverse('project.page', kwargs=dict(page='thank-you'))

Tip

The URL name (project.page) is in the block.urls.cms module.

Wizard

We have a link and an image wizard. The following field types are available for use in a ContentModel:

link = models.ForeignKey(
    Link,
    related_name='article_link',
    blank=True, null=True
)
references = models.ManyToManyField(Link)
picture = models.ForeignKey(
    Image,
    related_name='article_picture',
    blank=True, null=True
)
carousel = models.ManyToManyField(Image)

The field names are returned as a list to the block app in a wizard_fields method e.g:

@property
def wizard_fields(self):
    return [
        Wizard('picture', Wizard.IMAGE, Wizard.SINGLE),
        Wizard('link', Wizard.LINK, Wizard.SINGLE),
        Wizard('carousel', Wizard.IMAGE, Wizard.MULTI),
        Wizard('references', Wizard.LINK, Wizard.MULTI),
    ]

If you want the user to be able to link a single image (or link), then specify Wizard.SINGLE. For multi-links or images, use Wizard.MULTI.

The urls for these fields are rendered in the block/block/templates/block/_moderate.html template.

To create the Urls for all non custom pages use the following code:

from block.models import Url
Url.objects.init_pages()