Working with Content in Magnolia CMS

Working with Content in Magnolia CMS

This is an update of the older post Getting the path of the current content in Magnolia CMS.

With the release of Magnolia 4.5 the Content API has mostly been replaced by the JCR Node API. Therefore it is recommended to work with the Node API in your Java code. You can find a summary of the differences between those two APIs here.

Switching between APIs

Some methods in the Magnolia code still return or use the deprecated info.magnolia.cms.core.Content object instead of javax.jcr.Node. To convert between those two types you can use the following methods.

// content gets automatically injected into your models by Guice and is of type Node

// Node is best used in your Java code
Node node1 = content;

// Content is deprecated and should only be used if there's no other way
Content deprecatedContent = ContentUtil.asContent(node);

// ContentMap is best used in Freemarker templates
ContentMap contentMap = new ContentMap(node);

// To get a Node from either ContentMap or Content use the getJCRNode() method
Node node2 = deprecatedContent.getJCRNode();
Node node3 = contentMap.getJCRNode();

In Freemarker you can use:

${cmsfn.asJCRNode(aContentMap)}
${cmsfn.asContentMap(aJCRNode)}

Get the current content node

Calling MgnlContext.getAggregationState().getCurrentContent() in a component model gets you the component content node of the type mgnl:component as a Content object. For example website:/demo-project/en/home/content/0[mgnl:component]. It makes little sense calling this method though, since the current component node also gets automatically injected into the Java class and can be accessed through the content variable. So in a component model content and MgnlContext.getAggregationState().getCurrentContent().getJCRNode() are the same.

String uuid1 = MgnlContext.getAggregationState().getCurrentContent().getJCRNode().getIdentifier();
String uuid2 = content.getIdentifier();

// This evaluates to true
uuid1.equals(uuid2);

Get the main content node

Calling MgnlContext.getAggregationState().getMainContent() in your code returns you the page node (mgnl:page) of the currently rendered component node. For example website:/demo-project/de/services/contact[mgnl:page] when you call it in your component model which is currently rendering /demo-project/de/services/contact/content/0.

Expose a node to your Freemarker template

Now if you want to access a node’s properties in a Freemarker template (some other node than the already injected content), it’s best to return the node as a info.magnolia.jcr.util.ContentMap. Like this you can access its properties with the short notation ${model.methodName.myProperty}.

/**
 * Map based representation of JCR content. This class is for instance used in template scripts to allow notations like
 * <code>content.propName</code>. It first tries to read a property with name (key) and if not present checks for the
 * presence of child node. Few special property names map to the JCR methods: \@name, \@id, \@path, \@level, \@nodeType
 *
 * @version $Id$
 */
public ContentMap getTest() throws RepositoryException {
    Node node = MgnlContext.getJCRSession("website").getNode("/demo-project/de");
    return new ContentMap(node);
}

This would expose the node as a ContentMap and would allow you to access its properties in Freemarker like this: ${model.test.myProperty!}

Now instead of exposing the node as a ContentMap you could also expose only certain properties via a method.

public String getMyProperty() {
    Node node = MgnlContext.getJCRSession("website").getNode("/demo-project/de");

    return PropertyUtil.getString(node, "myProperty", "defaultValue");
}

This would return the String value of the property “myProperty” and if non-existant the String “defaultValue”. I’d recommend using the info.magnolia.jcr.util.PropertyUtil here because else you’d have more code to write:

public String getMyProperty() {
    Node node = MgnlContext.getJCRSession("website").getNode("/demo-project/de");

    return node.hasProperty("myProperty") ? node.getProperty("myProperty").getString() : "defaultValue";
}

Utility classes

Since the JCR API may not always be the most elegant way in terms of lines of code, I suggest you to use the available utility classes.

For your Java code

  • info.magnolia.jcr.util.NodeUtil
  • info.magnolia.jcr.util.PropertyUtil
  • info.magnolia.jcr.util.SessionUtil
  • info.magnolia.jcr.util.MetaDataUtil

For your Freemarker templates

Accessable via cmsfn and stkfn.

  • info.magnolia.templating.functions.TemplatingFunctions (cmsfn)
  • info.magnolia.module.templatingkit.functions.STKTemplatingFunctions (stkfn)