Blocks

This section is about blocks in Merengue. Merengue blocks are components which can be located in several places and render a fragment with information (usually HTML). Blocks are defined inside plugins and registered when plugin is activated.

The place of every block can be defined in admin site and you can even using the Merengue block reordering interface to change blocks location.

Block kinds

  • Basic blocks: plain blocks which are thought to be render in every Merengue page. Some examples:
    • The calendar block.
    • The portal menu block.
  • Content blocks: blocks which will be rendered in every content view. Examples are:
    • The feedback block, for content comments.
    • The Open Document export block.
  • Section blocks: blocks to be render inside a section, like the section menu block.

Plugin tree with blocks

We know the conventional plugin tree (see plugin development reference). Here we emphasize the files on which we are going to work in this example:

/plugins/
    |-- news/
    |   |-- ...
    |   |-- config.py
    |   |-- blocks.py
    |   `-- templates/
    |       `-- news/
    |           |-- block_latest.html
    |
    ...

Example plugin block

This is a blocks.py file code fragment:

from merengue.block.blocks import Block
from plugins.news.views import get_news

class LatestNewsBlock(Block):
    name = 'latestnews'
    default_place = 'leftsidebar'

    def render(self, request, place, context):
        news_list = get_news(request, 5)
        return self.render_block(request, template_name='news/block_latest.html',
                                block_title='Latest news',
                                context={'news_list': news_list})
  • name is the block name.
  • default_place indicates the place where the block will be showed by default.
  • render is a similar function to the render_to_response Django function. This function is the responsible for displaying the block.
  • news_list stores the number of news gotten by get_news.
  • template_name is the full name of the block template.
  • block_title is the block title.
  • context is a dictionary of values to render the template with.

About block rendering

In a Merengue block, render method is a callback function which will be called when Merengue is rendering blocks in location defined for that block.

For rendering block you can use several templatetags like render_blocks, render_content_blocks, render_section_blocks and render_all_blocks.

The block rendering is done in some base templates. For example, in base/inc.leftsidebar.html template, the blocks placed at leftsidebar are rendered, using this render_all_blocks templatetags as follows:

{% load block_tags %}

{% block leftsidebarblocks %}
  {% render_all_blocks "leftsidebar" %}
  {# more stuff #}
{% endblock %}

This render_all_blocks templatetags will cause calling to render method with leftsidebar passed in place parameter. The render_all_blocks method is in fact a shortcut of this HTML fragment:

{% render_blocks "leftsidebar" %}
{% if content %}
    {% render_content_blocks "leftsidebar" for content %}
{% endif %}
{% if section %}
    {% render_section_blocks "leftsidebar" for section %}
{% endif %}

So, the first render_blocks call will render the plain blocks, render_content_blocks call will render all content block and the render_section_blocks will render all section blocks.

Every render_*_blocks templatetag will call to the render method of every registered block. Also, request and context will be passed by param, to be used in render method, if you want. Next example is self-explanatory:

from merengue.block.blocks import Block

class WhereIAmBlock(Block):
    name = 'whereiam'
    default_place = 'leftsidebar'

    def render(self, request, place, context):
        if place in ['leftsidebar', 'rightsidebar']:
            return 'I am in a side column'
        else:
            return 'I am in %s place' % place

Implementing the block template

Let's back to LatestNewsBlock block. To finish block rendering we must create a template inside block_latest.html:

{% extends "block.html" %}

{% block blockbody %}
<ul>
{% for n in news_list %}
    <li><a href="{{ n.get_absolute_url }}" title="{{ n }}">{{ n }}</a></li>
{% endfor %}
</ul>
{% endblock %}

This template shows the title of the last published news. The number of news that is considered last news is specified in render function inside the blocks.py (five in this case).

Setting a block as fixed

There are two ways you can prevent staff users from changing the way a block is displayed.

The attribute is_fixed = True, when set on the block definition, will prevent edition on the block order, place and active attributes. For example:

class AlwaysVisibleAtRight(Block):
    name = 'alwaysright'
    default_place = 'rightsidebar'
    is_fixed = True

    def render(self, request, place, context):
        # ...

The defined block will always be shown when the rightsidebar blocks are rendered, and no admin user will be able to change its order, move it to another place, or disable it.

A more relaxed way to avoid editing the place where the block will be rendered is to use the fixed_place attribute. When its set to True in the block definition, it will prevent the edition on the place, both from the admin and the visual drag and drop in the public view.

Both is_fixed and fixed_place default to False when not set.

Setting a block as non addable

A non addable block is a block that the manager user cannot add using the user interface.

class NonAddableBlock(Block):
    name = 'nonaddableblock'
    default_place = 'rightsidebar'
    is_addable = False

    def render(self, request, place, context):
        # ...

Showing and hiding blocks

From Merengue v0.6, block rendering can be controlled using two additional parameters in the block configuration:

  • Shown in urls
  • Hidden in urls

These parameters are used to decide wether to render or not the block. If both are empty, the block will always be rendered in its place. Otherwise, the block will be shown or hidden if the current url is found within the specified values.

hiden in urls does only take effect if shown in urls is not set.

Both parameters accept a list of regular expressions, written one per line with the standard python regular expression syntax and no quotes.

For example:

shown in urls

news/

would show the block only in urls with news/ in it. This means the block won't be displayed in any other place (such as the homepage).

The regular expressions are matched towards the site relative url, so leave out the http and domain part. Nonetheless, make sure you provide complete expressions in order to avoid undesired matches.

For example:

hiden in urls

/$

will hide the block in all urls ending with a / character. If you want to hide the block in your home page, you should write:

hiden in urls

^/$

Configuring a block

Some blocks can be configured in public view. In example, you may configure a block which list latest contents added with a limit parameter to configure it:

class LatestAddedBlock(Block):
    name = 'latestadded'
    verbose_name = _('Latest added contents')
    default_place = 'leftsidebar'

    config_params = [
        params.PositiveInteger(
            name='limit',
            label=_('number of contents to show'),
            default=5,
        ),
    ]

    def render(self, request, place, context, *args, **kwargs):
        limit = self.get_config()['limit'].get_value()
        content_list = BaseContent.objects.all().order_by('-creation_date')[:limit]
        return self.render_block(request, template_name='fooplugin/block_latest.html',
                                 block_title=ugettext('Latest added contents'),
                                 context={'content_list': content_list})

The limit configuration parameter is used in render method to limit the queryset. If you are logged as admin user in public interface, you will see a configuration icon if you enter the mouse over the block:

../_images/latest_added_block.png

If you click in configuration icon Merengue will show a form to config the block:

../_images/latest_added_block_config.png

How to register the block

Our LatestNewsBlock class must be referenced in the plugin configuration inside config.py file:

from merengue.pluggable import Plugin
from plugins.news.blocks import LatestNewsBlock
#some stuff

class PluginConfig(Plugin):
    #some stuff

    def get_blocks(self):
        return [LatestNewsBlock]
  • get_blocks is a method that returns the specified blocks that we want to register.

Multimedia assets in blocks

If block you are implemented need to use some Javascript or CSS files in the block template. The direct way was to do something like this:

{% extends "block.html" %}

{% block blockbody %}
<link href="{{ MEDIA_URL }}fooplugin/block.css" type="text/css" />
<link href="{{ MEDIA_URL }}fooplugin/block_print.css" type="text/css" media="print" />
<script type="text/javascript" src="{{ MEDIA_URL }}fooplugin/block.js"></script>

# stuff that uses these assets
{% endblock %}

The previous solution has two inconveniences:

  • Mix media assets with HTML content. The CSS files are recommended to put into <head>...</head> section, and Javascript files should be just before </body> tag, for performance reasons.
  • You cannot do features like compression or merging, because Merengue does know the blocks which uses Javascript or CSS (because there is no registration).

Merengue came with multimedia features to add CSS or Javascript files like this:

{% extends "block.html" %}

{% block blockbody %}

{% addmedia "css" %}
  <link href="{{ MEDIA_URL }}fooplugin/block.css" type="text/css"/>
  <link href="{{ MEDIA_URL }}fooplugin/block_print.css" type="text/css" media="print" />
{% endaddmedia %}

{% addmedia "js" %}
  <script type="text/javascript" src="{{ MEDIA_URL }}fooplugin/block.js"></script>
{% endaddmedia %}

# stuff that uses these assets
{% endblock %}

All the media definitions declared into {% addmedia "css" %} block will be included in <head> section and the {% addmedia "js" %} stuff will before </body> tag.

Also, those media assets will be merged and compressed if DEBUG is False or COMPRESS setting is set to True.

Blocks Caching

The HTML rendered in all the blocks can be cached in the management interface. Look at the block caching section in the user guide for more information.

You can define default block parameters for your blocks by including a default_caching_params parameter in block definition:

class LatestAddedBlock(Block):
    #some stuff
    default_caching_params = {
        'enabled': True,
        'only_anonymous': True,
        'timeout': 3600,
        'vary_on_url': False,
        'vary_on_language': True,
        'vary_on_user': False,
    }

The previous block will be cached by default only for anonymous people, with a timeout of one hour and this cache will not vary on URL and user, but will vary on the current language.

Of course, these default cache settings can be overriden by managers in the administrative interface.

You may disallow to cache a block by setting a cache_allowed attribute as follows:

class LatestAddedBlock(Block):
    cache_allowed = False