eZ Flow enables content editors to create rich layouts in eZ Publish and is mostly used on portal pages. Read on to see how we can use the same tool for managing content elements in pagelayout template.
The problem
I suppose everyone working in eZ Publish stumbled upon the page (http://doc.ez.no/eZ-Publish/Technical-manual/4.x/Templates/Template-basics) explaining how the output of an eZ Publish module injects into the pagelayout templates at one time or another. The picture in the eZ v4 documentation pages sums it up very nicely:
Module result output is either defined by a node template or a system template, depending on the module being called. Pagelayout template (and the templates included in it) encloses this dynamic content with standard elements of a page, adding blocks such as header, footer, navigation elements such as menus and breadcrumbs and so on.
Although easy to understand, this is sometimes an oversimplified solution, as we often need to change the pagelayout elements depending on the module result details. There is a number of approaches to this problem, ranging from intensive template logic in pagelayout templates (not a scalable solution and not recommended) to using persistent variables to transfering chunks of HTML from node templates tot the pagelayout template.
One of the use cases we have came across is the need to insert blocks of related content (or feature blocks such as search or login boxes) to a region on a page (for example right column), but specifically to a given eZ Publish content subtree. An example could be an image gallery or an advertisement block specifically related to a content category, search box that should appear only on some content subtrees, etc. In this article we propose a scalable approach for solving this situation.
The solution
So to sum up the requirements, we want:
- to insert eZ content to regions of the page that are handled by the pagelayout template, and do this with eZ administration interface
- to mix content based blocks with feature related ones such as login or search boxes
- to control these regions per content subtree
As it happens, there is an eZ Publish feature which fits nicely into this scheme already - eZ Flow
eZ Flow enables creation of blocks which could either be content based (such as content grids, campaign blocks, etc) or which implement a specific feature (tag cloud, login or search box etc) that are stacked into different zones. Originally these blocks and zones are intended to be used on front pages of content heavy sites such as newspaper or TV station portals, but we can easily use them in pagelayout templates as well.
First step is to define different sections on the page, in which we want to insert the content. Let’s call these sections regions and define four of them (of course, you can create as much as you need) which are named appropriately:
These region names could be somewhat misleading in a responsive web solution, in which left/right does not always relate to the exact positioning on a given resolution.
As a second step, let’s introduce a helper eZ Publish class (ng_layout) with 3 attributes: name (Text line), apply_to (Object relations), and layout (eZ Flow Layout). Corresponding with page regions we defined in the first step, we specify an eZ Flow layout (in zone.ini) with 4 zones (top zone, left zone, right zone and bottom zone) that can be used in ng_layout objects.
This class is now used for injecting different eZ Flow blocks into sections of pagelayout templates. By using apply_to relations attribute we can connect specific ng_layout objects to whatever content subtree.
Some simple code in pagelayout template enables us to traverse the node hierarchy and pinpoint and insert the appropriate eZ Flow blocks into page regions, specific for a given content node/subtree:
{* place this code into top/left/right/bottom region *}
{def $layouts = array()}
{* traversing from current to root node *}
{foreach $current_node.path|append($current_node)|reverse as $zone_node}
{set $layouts = fetch('content', 'reverse_related_objects',hash(
'object_id',$zone_node.object.id,
'attribute_identifier', 'ng_layout/apply_to') )}
{* can not have more than one layout connected to a node *}
{if $layouts|count|gt(0)}
{set $layout = $layouts[0]}
{break}
{/if}
{if or($zone_node.node_id|eq(ezini('NodeSettings','RootNode','content.ini')))}
{break}
{/if}
{/foreach}
{* $zone_index needs to be set in regards with which region we checking. For example for TopRegion $zone_index could be 0, if it is the first zone defined for ng_layout layout attribute *}
{def $layout_region_zone = $layout.data_map.page.content.zones[$zone_index]}
{if and( is_set( $layout_region_zone.blocks ), $layout_region_zone.blocks|count() )}
<div class="region region-{$zone_index}">
{foreach $layout_region_zone.blocks as $block}
{block_view_gui block=$block}
{/foreach}
</div>
{/if}
With this system in place, we do not have to rely anymore on the node or pagelayout templates to implement the complex logic for defining the content in different regions of the page. We can now leave it up to editors who can use the tool they already know (eZ Flow), and even reuse the blocks already developed for eZ Flow frontpages (such as content grids or campaign blocks). Also, we have a cleaner separation of content and layout objects, which makes CMS installation more robust and easier to maintain and evolve.
Future improvements
Although this technique works nicely in wide range of situations, there is a number of improvements that would make it more useful.
- Creating an separate administration tab that would be used for configuration. This interface would also be used for quickly determining which layout object applies to which content. Currently, this information can be deduced from relations/reverse relation tab, but this could surely be done more elegantly.
- Enable the apply_to logic specific per siteaccess. This would enable us to apply different blocks for the same content, when accessed through different siteaccesses.
- Insert all the pagelayout elements into regions (like header/footer/main menu...). Even $module.result could be placed in the outputted HTML code through this technique, enabling editors to create puzzle-style layouts.
- This is an eZ v4 solution, so porting it to v5 would be nice
- a lot more :-)
Hope you find this tip useful, feel free to comment below!