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

Running sp_updatestats on AWS RDS database

Part of the maintenance tasks that I perform on a MSSQL Content Manager database is to run stored procedure sp_updatestats . exec sp_updatestats However, that is not supported on an AWS RDS instance. The error message below indicates that only the sa  account can perform this: Msg 15247 , Level 16 , State 1 , Procedure sp_updatestats, Line 15 [Batch Start Line 0 ] User does not have permission to perform this action. Instead there are several posts that suggest using UPDATE STATISTICS instead: https://dba.stackexchange.com/questions/145982/sp-updatestats-vs-update-statistics I stumbled upon the following post from 2008 (!!!), https://social.msdn.microsoft.com/Forums/sqlserver/en-US/186e3db0-fe37-4c31-b017-8e7c24d19697/spupdatestats-fails-to-run-with-permission-error-under-dbopriveleged-user , which describes a way to wrap the call to sp_updatestats and execute it under a different user: create procedure dbo.sp_updstats with execute as 'dbo' as

Content Delivery Monitoring in AWS with CloudWatch

This post describes a way of monitoring a Tridion 9 combined Deployer by sending the health checks into a custom metric in CloudWatch in AWS. The same approach can also be used for other Content Delivery services. Once the metric is available in CloudWatch, we can create alarms in case the service errors out or becomes unresponsive. The overall architecture is as follows: Content Delivery service sends heartbeat (or exposes HTTP endpoint) for monitoring Monitoring Agent checks heartbeat (or HTTP health check) regularly and stores health state AWS lambda function: runs regularly reads the health state from Monitoring Agent pushes custom metrics into CloudWatch I am running the Deployer ( installation docs ) and Monitoring Agent ( installation docs ) on a t2.medium EC2 instance running CentOS on which I also installed the Systems Manager Agent (SSM Agent) ( installation docs ). In my case I have a combined Deployer that I want to monitor. This consists of an Endpoint and a

Debugging a Tridion 2011 Event System

OK, so you wrote your Tridion Event System. Now it's time to debug it. I know this is a hypothetical situtation -- your code never needs any kind of debugging ;) but indulge me... Recently, Alvin Reyes ( @nivlong ) blogged about being difficult to know how exactly to debug a Tridion Event System. More exactly, the question was " What process do I attach to for debugging even system code? ". Unfortunately, there is no simple or generic answer for it. Different events are fired by different Tridion CM modules. These modules run as different programs (or services) or run inside other programs (e.g. IIS). This means that you will need to monitor (or debug) different processes, based on which events your code handles. So the usual suspects are: dllhost.exe (or dllhost3g.exe ) - running as the MTSUser is the SDL Tridion Content Manager COM+ application and it fires events on generic TOM objects (e.g. events based on Tridion.ContentManager.Extensibility.Events.CrudEven