.. _topics-blocks:

======
Blocks
======

This section describes how blocks are used in Merengue. Merengue "blocks" are components
which can be positioned in various locations and render a fragment with information
(usually via HTML). Blocks are defined inside plugins and registered when the plugin
is activated.

The position or location of every block can be defined in the admin site. You can also
use the Merengue `block reordering interface`_ to change a block's location.

.. _`block reordering interface`: http://www.merengueproject.org/features/visual-blocks-management/

Types of Blocks
---------------

* **Basic blocks**: blocks of information which are rendered on 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.

A Plugin With Blocks
--------------------

You should be familiar with the conventional plugin directory structure (if not,
see :ref:`plugin development reference<topics-plugins-development>`). Here we want
to notice the files which we are going to use in this example:

.. code-block:: html

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


An Example Plugin Block
-----------------------

What follows is a ``blocks.py`` file code fragment:

.. code-block:: python

    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 position  where the block will be rendered by default.
* ``render`` is a similar function to the ``render_to_response`` Django function.
  This function is responsible for displaying the block.
* ``news_list`` stores the number of news items retrieved 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 used to render the template.

Block Rendering
---------------

In a Merengue block, the ``render`` method is a callback function which is
called when Merengue renders blocks in the location defined for that particular block.

In the block rendering code you can use several templatetags like ``render_blocks``,
``render_content_blocks``, ``render_section_blocks`` and ``render_all_blocks``.

The block rendering is done using base templates. For example, in
`base/inc.leftsidebar.html`_ template, the blocks placed in the ``leftsidebar`` are
rendered using the ``render_all_blocks`` templatetag as follows:

.. code-block:: html+django

  {% load block_tags %}

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

In the example above, the ``render_all_blocks`` templatetags initiates a call to
the ``render`` method, passing ``leftsidebar`` as the ``place`` parameter. The ``render_all_blocks``
method is, in fact, a shortcut for using this HTML fragment:

.. code-block:: html+django

    {% 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. The
``render_content_blocks`` call will render all content blocks and the
``render_section_blocks`` will render all section blocks.

Every ``render_*_blocks`` templatetag will call the ``render`` method of
every registered block. Also, the request and context will be passed via parameters, so
that that information can be used in the ``render`` method, if you want.

The next example is self-explanatory:

.. code-block:: python

    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

.. _`base/inc.leftsidebar.html`: http://dev.merengueproject.org/browser/trunk/merengueproj/merengue/base/templates/base/inc.leftsidebar.html

Implementing the Block Template
-------------------------------

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

.. code-block:: html+django

    {% 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 titles of the last published news items. The number of
news items considered *last news* is specified in the 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
editing of the block order, location, and active attributes. For example:

.. code-block:: python

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

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

The block defined above 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 simpler way of preventing the ability to edit the block location (i.e. where
it will be rendered) is to use the ``fixed_place`` attribute. This attribute, when
set to ``True`` in the block definition, will prevent the editing of the location,
both from the admin interface as well as the from visual drag and drop GUI 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.

.. code-block:: python

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

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

Showing/Hiding Blocks
--------------------------

Since 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 determine whether, or not, to render the block. If both
are empty, the block will always be rendered at its location. Otherwise, the block 
will be shown or hidden if the current url is found within the specified values.

``hidden in urls`` only takes 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 <http://docs.python.org/library/re.html#regular-expression-syntax/>`_
and no quotes.

For example:

``shown in urls``

.. code-block:: python

  news/

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

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

For example:

``hidden in urls``

.. code-block:: python

  /$

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``

.. code-block:: python

  ^/$


Configuring a Block
-------------------

Some blocks can be configured in the public view. For example, you may use the
``limit`` parameter to configure a block which lists the latest content added to
the site:

.. code-block:: python

    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``  parameter is used in the render method to limit the
size of the queryset. If you are logged as the admin user in the public interface,
you will see a configuration icon if you hover the mouse arrow over the block:

.. image:: _images/latest_added_block.png

If you click the configuration icon, Merengue will show a form which will allow
you to configure the block:

.. image:: _images/latest_added_block_config.png

Registering a Block
-------------------

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

.. code-block:: python

    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 specific blocks that we want to register.


Multimedia Assets in Blocks
---------------------------

If the block you are implementing needs to use some Javascript or CSS files in the
block template, use the following example as a way was to handle this situation:

.. code-block:: html+django

    {% 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 recommended location for the CSS files
is in the ``<head>...</head>`` section, and Javascript files should be placed just
before the ``</body>`` tag, for performance reasons.
* You cannot do features like compression or merging, because Merengue does know
which blocks use Javascript or CSS (because there is no registration).

Merengue comes with the following template features to add CSS or Javascript files like this:

.. code-block:: html+django

    {% 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 of the media definitions declared in the ``{% addmedia "css" %}`` block will be
included in the ``<head>`` section and the ``{% addmedia "js" %}`` stuff will be
inserted before the ``</body>`` tag.

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


Block Caching
-------------

The HTML rendered in all blocks can be cached in the management interface.
Check out the :ref:`block caching section <topics-userguide-block_caching>` in
the User Guide for more information.

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

.. code-block:: python

    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 users, with a 
timeout of one hour. This cache will not vary by 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:

.. code-block:: python

    class LatestAddedBlock(Block):
        cache_allowed = False
