block
*****
.. highlight:: python
.. 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
=======
:download:`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``:
.. code-block:: text
django-taggit==
.. tip:: See :doc:`dev-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 :doc:`app-enquiry` for an older example...
Design Mode link
----------------
The standard anchor tag for the design menu should be similar to this:
.. code-block:: html
.. 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 the ``slug`` (*Article ID*) on the form.
.. tip:: The tests in `test_article.py`_ give a good idea of how it works.
.. _block_google_analytics:
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 :doc:`project-simple-site`
will include the :ref:`base_google_analytics` template by default.
Menus
=====
.. _block_menu_cms_pages:
CMS Pages
---------
A list of pages (excluding ``custom`` and hidden) are passed to the template in
the context as ``menu_list``:
- A menu is created from this list of pages using ``block/_menu_list.html``
.. _block_menu_navigation_menu:
Navigation Menu
---------------
To use a different styling create a Navigation Menu
(*Settings*, *Navigation Menu*) and include the following in your base template:
.. code-block:: html
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``:
.. code-block:: html
{% extends 'block/base.html' %}
{% extends 'project/base.html' %}
To extend the block menu in your project:
.. code-block:: html
{% block menu_dash %}
{% 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::
You can find our contact details by clicking here...
And to ``reverse``:
.. code-block:: python
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()
.. _`test_article.py`: https://gitlab.com/kb/compose/blob/master/compose/tests/test_article.py