Rendering content

November 14, 2023

The rendering process is the chain of operations that Jahia executes to extract nodes from the repository and transform the content (usually an HTML page) before it is delivered to the user who requested it. The rendering process provides flexibility for developers who can add their own operations to the process.

Component rendering and views

Every component can be rendered by a view. A view is a script, usually stored as a JSP file.

Using the module tag

The module tag allows you to render other components. The path or node needs to be passed.

Templates

A node must be associated with a template to display in an HTML page. A template defines which elements to display, where to display them, and compiles different component views in a single HTML page.

You create templates in the Studio. You create page templates for pages and content templates for all other types of content. Content templates are associated with a node type. You can use permissions to limit templates to certain users. For example, you can hide templates from regular users that show private areas or that allow them to edit the content.

Selecting a template

A node can be associated with a template. This is the case for all page nodes. A template must be specified when a page is created. The template is set in the j:templateNode property, defined by the jmix:hasTemplateNode mixin type.

A user can also force the choice of a template by specifiying a template name in a URL before the .html extension. This example forces the use of the content-template to display the news_36 node.

http://localhost:8080/cms/render/default/en/sites/ACME/home/news/maincontent/news_36.content-template.html

Default templates and priority

Default templates apply if no specific template is requested by a user and if no template is associated with a node. As multiple templates can be used for one single node and only one can be selected, you can define a template priority. The template with the highest priority will apply.

If no template is associated with a node or specified in the URL and no default template can be found, then the node cannot be displayed and the system will throw a 404 error.

Cascading templates

A template must either have an associated view or be contained inside another template. If a view is defined, the view is called to render the template. Otherwise, the parent template is called instead. This allows you to cascade templates, ranging from specific to general. The latter could, for example, contain standard headers and footers. The top-level template, that defines common areas, is usually called the base template.

cascade.png

The template sequence for rendering is then defined in this order:

  • template view
  • base template
  • home template
  • home page

Calling content from a template

Building a page is a matter of aggregating the template and the requested node. This node is called the main resource, which you can find in the URL and contains the information to display. Jahia looks for the template node when building a page. The template node defines the layout and the information to display from the main resource.

Other components are available in the Studio for you to use content from the main resource.

Areas

Areas are defined in JSP views with template:area tags or in templates with the area component. An area displays content from the next node in the template sequence. An area is considered enabled under one node if a list with the same name as the area is present under that node. For example, a maincontent area defined in the template view is enabled in the main resource if the maincontent list is created under the main resource.

Areas are represented as blue blocks in the Studio. For example, let's use the same templates sequence as before: template view, base template, home template and home page. In the following diagram:

  • The template view defines two area tags: header and pagecontent.
  • Both are enabled in the base template as lists. The header list contains a head content content item and the pagecontent list contains two area nodes: areaA and pagecontent.
  • The home template enables areaA containing one content1 content item.
  • The home page enables the pagecontent area, containing the content2 content item.

areas.png

Absolute areas

Absolute areas are similar to areas but do not use the templates sequence to resolve the list to display. Instead, they use an ancestor of the main resource. You can specify ancestor levels, 0 for the home page level, 1 for the first sub page level, etc. If no level is specified, the content list is taken from the home page of the site. Absolute areas are represented as red blocks in the Studio.

Main resource display

The main resource display component calls a specific view on the main resource node directly and is used in content templates only. The component is represented in the Studio as a grey area. Use the component to apply different views in one template. The following example shows multiple main resource display components. Here in the History tab, the history and the compare views display one on top of the other.

wiki-content.png

Example of rendering sequence

This section shows an example of a rendering sequence. In the example:

  1. The user requests a page.
  2. Jahia first tries to resolve the template and finds a home template associated with the page.
  3. The home template is contained inside the base template. Jahia first renders the base template.
  4. The base template is associated with a view, the template.myset.jsp file, and the JSP is called to start building the page.
  5. An area header is found. Jahia looks sequentially in base, home, and finally in the page to find a list named header. This list is found in the base template and contains a navigation menu. All nodes here are rendered using their own views.
  6. Another area pagecontent is found. This one does not exist in base, but is present in home. Jahia starts rendering the home/pagecontent.
  7. The first node found is a jnt:row node. The node is rendered using its own view, row.jsp.
  8. The JSP contains a first area, row-col1. The system finds the area in home, which contains itself an area node named areaA.
  9. The areaA template is found in the page. The content of page/areaA renders.
  10. The rendering of row-col1 is over, another area is defined in the row.jsp file: row-col2. row-col2 is found in home, which contains an area
  11. Again, the areaB is found in the page. The content of page/areaB renders.

The view row.jsp is fully rendered. As home/pagecontent does not contain any other nodes, the home/pagecontent area rendering is finished. The view template.myset.jsp finishes its execution. The page is fully rendered.

area-display.png

Rendering filters

Rendering filters are classes that can transform the output of a module. They work like standard servlet filters, except that they are executed independently for every module inclusion. Filters can be executed on all modules or on specific module and nodes. Find an overview of all currently registered filters in the Jahia Tools area JSP when the Jahia is running: http://localhost:8080/tools/renderFilters.jsp.

Filter configuration

Jahia supports filter configuration using OSGi services in modules.

Module filters

A module can define its own filters to execute. To define module filters, you simply need to register OSGi services. The filters will be added to the render chain and executed for every request for all modules.

The priority defines where the filter is inserted in the render chain. If the priority is greater than 16, it  executes the first time the resource is generated, and then the result is cached. If the priority is less than 16, it is evaluated each time the resource is served. Be careful, this is very consuming in terms of performance.

The following example is an extract of OSGi filter module sample

@Component(service = RenderFilter.class)
public class SimpleFilter extends AbstractFilter {
    private String headScript;
    @Activate
    public void activate() {
        setPriority(3);
        setApplyOnEditMode(true);
        setSkipOnAjaxRequest(true);
        setApplyOnConfigurations("page");
        setApplyOnTemplateTypes("html,html-*");
    }
    @Override
    public String prepare(RenderContext renderContext, Resource resource, RenderChain chain) throws Exception {
        headScript = "\n<script>alert('Hello Jahia!')</script>\n<";
        return super.prepare(renderContext, resource, chain);
    }
    @Override
    public String execute(String previousOut, RenderContext renderContext, Resource resource, RenderChain chain) throws Exception {
        String output = super.execute(previousOut, renderContext, resource, chain);
        // add your custom process of the output here.
        return output;
    }
}

Conditional execution of filters

Any filter that extends org.jahia.services.render.filter.AbstractFilter can accept several types of conditions that control filter execution. If all conditions are matched, the filter is executed. Otherwise, it is bypassed and execution continues with the next filter in the chain. For example, you could have a filter that only executes for nodes of type jnt:wikiContent. More conditions can be provided using:

  • setApplyOnMainResource
    the filter is applied only on the main resource
  • setApplyOnModules
    comma-separated list of module names that the filter is executed for (all others are skipped)
  • setApplyOnNodeTypes
    comma-separated list of node type names that the filter is executed for (all others are skipped)
  • setApplyOnTemplates
    comma-separated list of template names that the filter is executed for (all others are skipped)
  • setApplyOnTemplateTypes
    comma-separated list of template type names that the filter is executed for (all others are skipped)
  • setApplyOnConfigurations
    comma-separated list of configurations that the filter is for (all others are skipped)
  • skipOnModules
    comma-separated list of module names that the filter is not executed for
  • skipOnNodeTypes
    comma-separated list of node type names that the filter is not executed for
  • skipOnTemplates
    comma-separated list of template names that the filter is not executed for
  • skipOnTemplateTypes
    comma-separated list of template type names that the filter is not executed for
  • skipOnConfigurations
    comma-separated list of configurations that the filter is not executed for

For advanced filtering scenarios, such as regular expressions for matching and NOT conditions, use the filter's conditions property and provide a list of condition beans (instances of classes that implement org.jahia.services.render.filter.AbstractFilter.ExecutionCondition). See the documentation on org.jahia.services.render.filter.AbstractFilter for more details.

Implementing filters

A filter is single class that implements the org.jahia.services.render.filter.RenderFilter interface. The interface primarily defines one method to implement:

String execute(String previousOut, RenderContext renderContext, Resource resource, RenderChain chain) throws Exception;

This method returns the final output after filtering. The chain attribute enables you to get the result generated by the remaining chain. This result can be used as an input for the filter. If you want your filter to transform the output of the chain, the filter must get the output from the previousOut method argument.

A basic filter that replaces all occurrences of letter a with b would look like:

public String execute(RenderContext renderContext, Resource resource, RenderChain chain)
        throws RenderFilterException {
    return previousOut.replace("a","b");
}

An abstract org.jahia.services.render.filter.AbstractFilter class should be used as the base class for a new filter, which allows you to specify execution conditions for a filter. If your filter extends the AbstractFilter, then you can implement the following methods:

String prepare(RenderContext renderContext, Resource resource, RenderChain chain) throws Exception;

When a resource is called by the end user, you enter the prepare method. This method allows you to put information in the scope of the request before generating the HTML output.

String execute(String previousOut, RenderContext renderContext, Resource resource, RenderChain chain)
            throws Exception;

After the resource rendering (HTML output generation), you enter in the execute method. It allows you to modify the generated HTML fragment before to returning it to the end user.

void finalize(RenderContext renderContext, Resource resource, RenderChain renderChain);

Finally, when the fragment is finalized, you enter in the finalize method, which allows you to reset specific context or reinitialize variables. It is usually used by the system. At this step, you can no longer interact with the generated HTML fragment.

Script execution info

You can add a scriptinfo request parameter to obtain information about files used in modules that render content. This will surround all modules within a page with a border and display an info bullet for more information (like script used to display and time to render).

To activate the script, add ?moduleinfo=true in the url parameters in Preview mode.

Lists

The list component is composed of different elements that you can use to customize rendering while still retaining all the flexibility of the default component. The component covers many different uses cases for rendering lists.

To identify lists, apply a mixin to the node.

[jmix:list] mixin

Sample lists defined in Jahia include:

jnt:contentList, jnt:query, jnt:folder

By default, the rendering of this mixin displays the list. This rendering is structured like this:

<template:include view="hidden.header"/>
<c:forEach items="${moduleMap.currentList}" var="subchild" begin="${moduleMap.begin}" end="${moduleMap.end}">
<template:module node="${node}"/>
</c:forEach>
<template:include view="hidden.footer"/>

The hidden.header and hidden.footer views allow you to define parameters for the list to be displayed. The hidden prefix allows you to exclude these views from the list of possible views offered in the edit interface. These parameters are stored in a map accessible by the moduleMap module.

The default parameters are:

  • currentList
    List of nodes to display
  • begin
    Beginning of a list (integer)
  • end
    End of a list (integer)
  • liveOnly
    Makes an AJAX call of the list in live state and cannot be edited in default state
  • subNodesView
    This view is used to display sub Nodes
  • editable
    Allows editing of content (boolean, true by default)
  • emptyListMessage
    Message to display if list is empty

You define a variable as follows:

<c:set target="${moduleMap}" property="subNodesView" value="${subNodesView}"/>

hidden.header calls a "loader" whose role is to get all nodes to display and store them in ${moduleMap}.

Jahia stores different types of content,

  • Simple lists
    currentList
  • SQL2 requests
    listQuerySql
  • QOM requests
    listQuery

These requests or lists are evaluated in the hidden.header view. The hidden.header view allows for managing list sorting and filters.

Summary for a simple list (list.jsp):

<template:include view="hidden.header"/>
list.hidden.header.jsp
<template:include view="hidden.load"/>
list.hidden.load.jsp
<c:set target="${moduleMap}" property="currentList" value="${jcr:getChildrenOfType(currentNode, jcr:getConstraints(currentNode))}" />
<c:forEach items="${moduleMap.currentList}" var="subchild" begin="${moduleMap.begin}" end="${moduleMap.end}">
    <template:module node="${node}"/>
</c:forEach>

Usage of these views:

Generally speaking, only "loader" is necessary to define a specific list type, it allows you to define the elements to be displayed. If there is a request, you define listQuery or listQuerySql. If it is sub node, currentList should be used. 

For example, to view the list of recent comments as component, first define a type that extends jmix:list.
[jnt:latestComment] > jnt:content, jmix:queryContent, jmix:list, mix:title, jmix:renderableList, jmix:studioOnly, jmix:bindedComponent
- j:subNodesView (string, choicelist[templates=jnt:post,resourceBundle,image]) nofulltext  itemtype = layout

Rendering is performed through the jnt_latestComment/html/latestComment.hidden.load.jsp file. It will setup the listQuery variable in the moduleMap as shown here:

<query:definition var="listQuery"
                  statement="select * from [jnt:post] as comments  where isdescendantnode(comments, ['${renderContext.mainResource.node.path}']) order by comments.[jcr:lastModified] desc"
                  limit="20"/>
<c:set target="${moduleMap}" property="editable" value="false"/>
<c:set target="${moduleMap}" property="listQuery" value="${listQuery}"/>

List filtering

You can filter lists when they are rendered based on the value of a property. Only elements which correspond to the filter criteria will display. You do so by adding a filter parameter to the URL of the page containing the list to filter. The actual value of the filter parameter specifies how the list is filtered using a JSON format.
A filter is defined by the combination of the following name and value pairs:
  • uuid
    The Universally Unique IDentifier (UUID) identifying which list the filter should operate on.
  • op
    A predicate that each element of the list with the name property must satisfy to be included in the filtered result. The only supported operation at this time is the equality, specified by the eq value. You can also use the negation operator ! to only include elements not satisfying the predicate in the filtered result. To use the negation operator, simply preprend ! in front of the operation name.
  • value
    The value of the property used to filter the list.
  • name
    The name of the property present on the list elements and on which the filtering will occur.
  • type
    The expected JCR type of the specified value. Possible values are defined by possible return values of javax.jcr.PropertyType.TYPENAME_* constants. If omitted, the specified property value will be interpreted as a String (javax.jcr.PropertyType.TYPENAME_STRING).
Optionally, you can also pass a format filter attribute used to further refine the evaluation of the filtering value. This is useful to indicate which format is used for Date property values.

Date filtering

This example specifies that the list identified by the 226a954111-3279-475b-9129-e3110736a565 UUID on the sites/ACME/home/page8.html page should be filtered to only keep elements where the jcr:created property is equal to 2010-04-30 (op attribute is equals to "eq"). 2010-04-30 is of Date type with the yyyy-MM-dd format.

sites/ACME/home/page8.html?filter={name:"jcr:created",value:"2010-04-30",op:"eq",uuid:"226a954111-3279-475b-9129-e3110736a565",format:"yyyy-MM-dd",type:"Date"}

Creating a URL to filter a bound component in a JSP

This example creates a URL targeting the list associated with the component bound to the view associated with the JSP. The filter only displays elements that are not tagged and identified by the $tag.uuid UUID. An element's tags are children nodes of the j:tags property for this element and each is a WeakReference to the "real" tag node. Therefore, j:tags is a list of UUIDs, each identifying a tag. Note that $tag.type is WeakReference so that the filter knows to interpret the value attribute as a UUID pointing to a tag.

<c:url var="targetURL" value="${url.mainResource}" context="/">
  <c:param name="filter" value='{name="j:tags",value:"${tag.uuid}",op:"!eq",uuid:"${boundComponent.UUID}",type:"${tag.type}"}'/>
</c:url>

Macros

Macros allow you to add and process dynamic information in your text.

Defining a macro

All macros are found under the macros directory in your modules. They respect a simple rule: the filename of the macro defines its name. This example defines a macro name username.

<module>WEB-INF/macros/username.groovy

Using a macro

It is as simple as putting in your text a call like this ##macro_name##. This will render something like, "Dear John Doe," based on the user rendering the page. You have to check the perUser option for the cache in the options panel of your content to ensure that each user see his own name.

Dear ##username##,

Writing a macro

To create a macro, you can use any scripting language JSR-223 compliant deploy on your platform (by default Groovy, Velocity and Freemarker).

username.groovy

if (currentUser.username.trim().equals("guest")) {
    print PrincipalViewHelper.getUserDisplayName(currentUser.username.trim());
} else {
    String property1 = currentUser.getProperty("j:firstName")
    if (property1 != null)
        print(property1.capitalize() + " ");
    String property2 = currentUser.getProperty("j:lastName")
    if (property2 != null)
        print(property2.capitalize())
    if (property1 == null && property2 == null)
        print(currentUser.getUsername().capitalize())
}

Default bindings available in your macros

Name Class Description
currentNode org.jahia.services.content.JCRNodeWrapper The node you are currently rendering
currentUser org.jahia.services.usermanager.JahiaUser The user currently connected
currentAliasUser org.jahia.services.usermanager.JahiaUser The user currently rendered if not the one connected
renderContext org.jahia.services.render.RenderContext The current context for rendering
currentResource org.jahia.services.render.Resource The resource associated with the current node
url org.jahia.services.render.URLGenerator An URL generator allowing to create your own URL

Default macros

  • ##username##
    Displays the current user name
  • ##authorname##
    Displays the author name
  • ##creationdate##
    Displays the content creation date
  • ##keywords##
    Displays the keywords for the current node
  • ##devmode##
    Displays information about the current content: the content id and the content path. For example, ##devmode(full)## renders something like, "Content path : /home/services Content id : services"
  • ##linktohomepage##
    Displays a link to home page. For example, ##linktohomepage## renders something like "http://www.site.com/index.html". You can also make a link like this <a href="##linktohomepage##" title="Home Page">Home Page</a> to render a clean Home Page link .
  • ##userprofiledata##
    Displays information about the current profile. The first parameter should be a node name like "contents". The second parameter should be a property name like "jcr:created". You can also use only one parameter in your text call like this ##userprofiledata(parameter)##. In this case, the parameter should be a property of the current user like "j:firstName". For example, ##userprofiledata(contents,jcd:created)## renders something like, "2013-11-05T18:26:10.357+01:00".   ##userprofiledata(j:firstName)## renders something like, "Damien".