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