Skip to main content

RenderComponentPresentation Tag for Java Mediator

In order to write the first complete Page Template for the Java Mediator for SDL Tridion Templating, I needed a Java tag similar to the @@RenderComponentPresentation@@ from Dreamweaver Templating.

The logic to write such a tag is pretty simple. However, I wanted to have a versatile tag that would not only accept a Component and a Component Template, but also the possibility to specify some 'wildcards' or, more exactly, some Regular Expressions to allow the rendering of several Component Presentations based on different criterias.

The criterias I implemented are:
  • Component title: in the current Page, matches each Component Presentation Component title on this Regular Expression and if a match is found, the CP is displayed;
  • Component Template title: same as above, but matching is done on the Component Presentation's Component Template title;
  • Schema title: same as above, only matches the Schema title of the Component in the Component Presentation;
Additionally, I wanted to have the choice of sharing the Package object a Page Template would interact with, in the Component Template. Therefore, by using an 'inherit' boolean attribute on the tag, I can specify whether the Package from the Page Template would be 'inherited' into the Component Templates.

Examples

A 'Real' Page Template

The JSP TBB below iterates over each Component Presentation in the current Page in the Package and triggers its rendering:

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="t" uri="http://www.mitza.net/java/mediator/tag"%>
<html>
<head>
    <title>${Page.Title}</title>
</head>
<body>
    <c:forEach var="cp" items="${Page.ComponentPresentationsList}">
        <t:renderComponentPresentation cp="${cp}" />
    </c:forEach>
</body>
</html>

Specify Component and Component Template

The code below outputs each Component Presentation in the Page:

<c:forEach var="cp" items="${Page.ComponentPresentationsList}">
    <t:renderComponentPresentation component="${cp.Component}"
        componentTemplate="${cp.ComponentTemplate}" />
</c:forEach>

Attributes component and componentTemplate can in this case Strings representing TcmUri values, or actual TcmUri objects.

Specify Schema

Display all Component Presentations with Component based on the Schema with TcmUri tcm:1-2-8:

<t:renderComponentPresentation schema="tcm:1-2-8" />

Note the tag above should not be placed inside a for loop -- it will display all Component Presentations that match the specified Schema.

Combine Schema and Component Template

Combinations of attributes are possible. The more attributes are specified, the more restrictive the matching Component Presentations will become (i.e. the operator between several attributes is AND):

<t:renderComponentPresentation schema="tcm:1-2-8" componentTemplate="tcm:1-3-32" />

Inherit Page Template Package into Component Templates

Specifying attribute inherit="true" (default, false), the Package object used during the rendering of the Page Template will be re-used (inherited) into the Component Template rendering. This means that potentially, several Component Templates can share the same Package.

This will of course work also with other, non Java, mediators. For example, adding a C# TBB, the Package available to it could contain items inserted by previous TBBs. This way it is no longer needed to use the ContextVariables to pass values and objects from Page Template to Component Templates.

<c:forEach var="cp" items="${Page.ComponentPresentationsList}">
    <t:renderComponentPresentation cp="${cp}" inherit="true" />
</c:forEach>

Specifying with Regular Expressions

All three attributes -- component, componentTemplate and schema, allow Regular Expressions. They will be matched on their corresponding values of the Component Presentation Component title, Component Template title and Component's Schema title. The operator between the attributes is AND. The tag renders all Component Presentations matched; no need placing it inside a loop.

<t:renderComponentPresentation schema="Article" /> - displays all CPs where the Component's Schema title contains word "Article"

<t:renderComponentPresentation componentTemplate="^Summary News$" /> - displays all CPs where the Component Template's title matches exactly "Summary News"

<t:renderComponentPresentation component="^Quarterly Report.*2012" componentTemplate="tcm:1-3-32" /> - displays all CPs where the Component's title starts with "Qarterly Report" and contains the string "2012" and are using Component Template tcm:1-3-32

Code

public class RenderComponentPresentationTag extends SimpleTagSupport {

    private TcmUri component;
    private Pattern componentPattern;
    private TcmUri componentTemplate;
    private Pattern componentTemplatePattern;
    private TcmUri schema;
    private Pattern schemaPattern;
    private boolean inherit;

    private Log log = Log.getInstance();

    @Override
    public void doTag() throws JspException, IOException {
        log.debug("RenderComponentPresentationTag.ByTcmUri: component=%s, componentTemplate=%s", component,
                componentTemplate);

        if (component != null && componentTemplate != null) {
            renderComponentPresentation(component, componentTemplate);
        } else {
            FakePageContext pageContext = getJspContext();
            Engine engine = pageContext.getEngine();
            Package _package = pageContext.getPackage();

            Item pageItem = _package.GetByName("Page");
            if (pageItem == null) {
                throw new MediatorException("Cannot find item Page in the Package. Are you in Page Template context?");
            }

            Page page = (Page) engine.GetObject(pageItem);
            renderComponentPresentations(page);
        }
    }

    @Override
    protected FakePageContext getJspContext() {
        return (FakePageContext) super.getJspContext();
    }

    public void setComponent(Object component) {
        if (component instanceof TcmUri) {
            this.component = (TcmUri) component;
        } else if (component instanceof Component) {
            this.component = ((Component) component).getId();
        } else {
            String value = component.toString();
            if (TcmUri.IsValid(value)) {
                this.component = new TcmUri(value);
            } else {
                componentPattern = Pattern.compile(value);
            }
        }
    }

    public void setComponentTemplate(Object componentTemplate) {
        if (componentTemplate instanceof TcmUri) {
            this.componentTemplate = (TcmUri) componentTemplate;
        } else if (componentTemplate instanceof ComponentTemplate) {
            this.componentTemplate = ((ComponentTemplate) componentTemplate).getId();
        } else {
            String value = componentTemplate.toString();
            if (TcmUri.IsValid(value)) {
                this.componentTemplate = new TcmUri(value);
            } else {
                componentTemplatePattern = Pattern.compile(value);
            }
        }
    }

    public void setCp(ComponentPresentation cp) {
        this.component = cp.getComponent().getId();
        this.componentTemplate = cp.getComponentTemplate().getId();
    }

    public void setSchema(Object schema) {
        if (schema instanceof TcmUri) {
            this.schema = (TcmUri) schema;
        } else if (schema instanceof Schema) {
            this.schema = ((Schema) schema).getId();
        } else {
            String value = schema.toString();
            if (TcmUri.IsValid(value)) {
                this.schema = new TcmUri(value);
            } else {
                schemaPattern = Pattern.compile(value);
            }
        }
    }

    public void setInherit(boolean inherit) {
        this.inherit = inherit;
    }

    private void renderComponentPresentations(Page page) throws IOException {
        for (ComponentPresentation cp : page.getComponentPresentationsList()) {
            boolean isRender = true;

            Component cpComponent = cp.getComponent();
            ComponentTemplate cpComponentTemplate = cp.getComponentTemplate();
            Schema cpSchema = cpComponent.getSchema();

            TcmUri cpComponentUri = cpComponent.getId();
            TcmUri cpComponentTemplateUri = cpComponentTemplate.getId();

            if (component != null) {
                isRender = component.equals(cpComponentUri);
            }

            if (isRender && componentPattern != null) {
                isRender = componentPattern.matcher(cpComponent.getTitle()).find();
            }

            if (isRender && componentTemplate != null) {
                isRender = componentTemplate.equals(cpComponentTemplateUri);
            }

            if (isRender && componentTemplatePattern != null) {
                isRender = componentTemplatePattern.matcher(cpComponentTemplate.getTitle()).find();
            }

            if (isRender && schema != null) {
                isRender = schema.equals(cpSchema.getId());
            }

            if (isRender && schemaPattern != null) {
                isRender = schemaPattern.matcher(cpSchema.getTitle()).find();
            }

            if (isRender) {
                renderComponentPresentation(cpComponentUri, cpComponentTemplateUri);
            }
        }
    }

    private void renderComponentPresentation(TcmUri component, TcmUri componentTemplate) throws IOException {
        log.debug(
                "RenderComponentPresentationTag.renderComponentPresentation: component=%s, componentTemplate=%s, inherit=%b",
                component, componentTemplate, inherit);
        String cpContent;
        FakePageContext pageContext = getJspContext();
        Engine engine = pageContext.getEngine();
        JspWriter out = pageContext.getOut();

        if (inherit) {
            MediatorEngine mediatorEngine = MediatorEngine.Wrap(engine);
            cpContent = mediatorEngine.RenderComponentPresentation(component, componentTemplate);
        } else {
            cpContent = engine.RenderComponentPresentation(component, componentTemplate);
        }

        out.print(cpContent);
    }
}


Comments

Popular posts from this blog

Toolkit - Dynamic Content Queries

This post if part of a series about the  File System Toolkit  - a custom content delivery API for SDL Tridion. This post presents the Dynamic Content Query capability. The requirements for the Toolkit API are that it should be able to provide CustomMeta queries, pagination, and sorting -- all on the file system, without the use third party tools (database, search engines, indexers, etc). Therefore I had to implement a simple database engine and indexer -- which is described in more detail in post Writing My Own Database Engine . The querying logic does not make use of cache. This means the query logic is executed every time. When models are requested, the models are however retrieved using the ModelFactory and those are cached. Query Class This is the main class for dynamic content queries. It is the entry point into the execution logic of a query. The class takes as parameter a Criterion (presented below) which triggers the execution of query in all sub-criteria of a Criterio

A DD4T.net Implementation - Custom Binary Publisher

The default way to publish binaries in DD4T is implemented in class DD4T.Templates.Base.Utils.BinaryPublisher and uses method RenderedItem.AddBinary(Component) . This produces binaries that have their TCM URI as suffix in their filename. In my recent project, we had a requirement that binary file names should be clean (without the TCM URI suffix). Therefore, it was time to modify the way DD4T was publishing binaries. The method in charge with publishing binaries is called PublishItem and is defined in class BinaryPublisher . I therefore extended the BinaryPublisher and overrode method PublishItem. public class CustomBinaryPublisher : BinaryPublisher { private Template currentTemplate; private TcmUri structureGroupUri; In its simplest form, method PublishItem just takes the item and passes it to the AddBinary. In order to accomplish the requirement, we must specify a filename while publishing. This is the file name part of the binary path of Component.BinaryConten

Scaling Policies

This post is part of a bigger topic Autoscaling Publishers in AWS . In a previous post we talked about the Auto Scaling Groups , but we didn't go into details on the Scaling Policies. This is the purpose of this blog post. As defined earlier, the Scaling Policies define the rules according to which the group size is increased or decreased. These rules are based on instance metrics (e.g. CPU), CloudWatch custom metrics, or even CloudWatch alarms and their states and values. We defined a Scaling Policy with Steps, called 'increase_group_size', which is triggered first by the CloudWatch Alarm 'Publish_Alarm' defined earlier. Also depending on the size of the monitored CloudWatch custom metric 'Waiting for Publish', the Scaling Policy with Steps can add a difference number of instances to the group. The scaling policy sets the number of instances in group to 1 if there are between 1000 and 2000 items Waiting for Publish in the queue. It also sets the