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.
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
|
...
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})
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
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).
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.
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):
# ...
From Merengue v0.6, block rendering can be controlled using two additional parameters in the block configuration:
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
^/$
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:
If you click in configuration icon Merengue will show a form to config 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]
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:
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.
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
Jul 01, 2011