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.
Diagram
Questions
Why do we have
PageSection
andTemplateSection
models in theblock
app?The
TemplateSection
is a list of theSection
types that aPage
is allowed to have.The
PageSection
is a list of the actual sections that aPage
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…
Design Mode link
The standard anchor tag for the design menu should be similar to this:
<a href="{% if design %}{{ view_url }}{% else %}{{ page.get_design_url }}?view={{ path }}{% endif %}"
title="{% if design %}View{% else %}Design{% endif %}">
Note
The view_url
is generated from the view
parameters. The view
passes the current URL to the PageDesignMixin
so that it knows
where to return to when the user clicks View to leave Design
mode.
For the code, see PageDesignMixin`` in block/block/views.py
.
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 theslug
(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:
Use the
block/base.html
templateSet 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:
Browse to
/admin/
Select Block Settings
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()