Page tree
Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 14 Next »

GREYSimplification of our current tag library. See MAGNOLIA-2993@jira.GREY

Goals

  • (tick) Integration with other templating engines
  • (tick) Testability
  • (tick) Maintainability
  • Customizability - still need to provide guidelines for this - Corner cases that are currently possible to reach, by accident or not, will probably be not supported. On the other hand, it should be easy to extend/replace/add functionality such that such corner cases can provided for on a case-by-case basis.
  • (tick) Hide complexity, sensible defaults - no extraneous parameters "just in case"; parameters are "typed".
  • Magnolia 5 - Whatever we introduce should still make sense with Magnolia 5: the underlying implementation can be changed/swapped, but the templates should ideally be 1:1 compatible.
  • Deprecate current tags - at least those we provide a direct replacement for.
  • Documentation - We currently have 2 different formats that document the taglib; neither is great. We should stick to a single one, and improve it. (for ex: http://dev.magnolia-cms.com/ref/latest/magnolia-taglib-cms/tagreference.html has some html tags in the attributes' description which should not have been encoded)

Status

  • Can be used
  • Can replace existing tags or be used in conjunction with.
  • Both taglibs can co-exist. See below.

The following components have been introduced:

  • PageEditBar (~= MainBar)
  • EditBar
  • NewBar
  • SingletonParagraphBar (wrap a paragraph containing an edit bar; displays a new bar with a specific label if the paragraph does not exist yet)

The following constructs have not been introduced yet:

  • include/render
  • iterate
  • header tags (cms:links)

Some shortcomings:

  • JSP: we currently use NodeMapWrapper, which causes two problems:
    • it does not expose subnodes
    • nodes returned by mgnl or models or whatever are not magically wrapped, unlike what happens with FreeMarker.
  • JSP: despite 2.2 supporting method calls in EL, it seems (maybe it's a bug?) that it can not differentiate between methods with the same name but different parameter types (probably related to coercion of null types)
    • we use explicit method names
    • we provide a wrapper around MagnoliaTemplatingUtilities which hides the "duplicate" methods and exposes only one with Object argument(s)

Implementation

  • UI component classes are in info.magnolia.module.templatingcomponents.components; they extend AbstractAuthoringUiComponent, implement AuthoringUiComponent. When we introduce non-ui components, some renaming might be in order.
  • JSP and FreeMarker wrapper are in their respective subpackages, extending their respective abstract class, which provides helper method.
  • FreeMarker wrappers are {{TemplateDirectiveModel}}s, which are like macros to templates.
  • Wrappers currently simply rely on static factory methods (make) to pass their parameters; all they have to care about is validate the parameters' types and mandatoriness; any other special treatment should happen in the factory method. Default values are also provided by the make() methods, except certain cases(booleans for instance)

Where is X?

Depending on the templating language in use, one should be able to reproduce the behavior of "missing" tags using mgnl.

Examples

Using mgnl

FreeMarker

[#if mgnl.editMode]this only appears on author instances, in edit mode.[/#if]

You can also call methods:

${mgnl.getContent("data", "/contacts/JMustermann").officePhone}

JSP

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
[...]
<c:if test="${mgnl.editMode}">this only appears on author instances, in edit mode.</c:if>

With JSP2.2 and EL2.2, you can also call methods:

${mgnl.getContent("data", "/contacts/JMustermann").officePhone}

If you're stuck with JSP 2.1, you can still do the following and use scriplets:

<% MagnoliaTemplatingUtilities mgnl = (MagnoliaTemplatingUtilities) pageContext.findAttribute("mgnl"); %>
[...]
<%=mgnl.getContent("data", "/contacts/JMustermann").officePhone%>

Taglib declarations

<%@ taglib prefix="cms" uri="http://magnolia-cms.com/taglib/templating-components" %>
<%@ taglib prefix="old" uri="cms-taglib" %>

OR:

<%@ taglib prefix="new" uri="http://magnolia-cms.com/taglib/templating-components" %>
<%@ taglib prefix="cms" uri="cms-taglib" %>

work equally.

Tooling support

JSP:

In IntelliJ, you can add the following at the top of your JSP files: (the editor will hint you to do it anyway)

<%--@elvariable id="mgnl" type="info.magnolia.module.templating.MagnoliaTemplatingUtilities"--%>

FreeMarker:

With IntelliJ, add a file called freemarker_implicit.ftl in a source root of your project (src/main/resources for example) to get autocompletion:

[#ftl]
[#-- @implicitly included --]
[#-- @ftlvariable name="page" type="info.magnolia.cms.core.Content" --]
[#-- @ftlvariable name="content" type="info.magnolia.cms.core.Content" --]
[#-- @ftlvariable name="ctx" type="info.magnolia.context.Context" --]
[#-- @ftlvariable name="state" type="info.magnolia.cms.core.AggregationState" --]
[#-- @ftlvariable name="def" type="info.magnolia.module.templating.RenderableDefinition" --]
[#-- @ftlvariable name="mgnl" type="info.magnolia.module.templating.MagnoliaTemplatingUtilities" --]
[#-- @ftlvariable name="model" type="..." --]
[#-- @ftlvariable name="actionResult" type="java.lang.String" --]

It's also something IntelliJ will hint you about, so no panic. As you can see, the one variable that can't be defined here is model, since it's an arbitrary class, configured per paragraph. Also note that if needed, you can override these definitions (they're only hints for the editor), for example if you need auto-completion for a paragraph that uses a specific RenderableDefinition subclass. Lastly, note that this does not provide auto-completion for content property names, so ${content.foobar} will still be highlighted, unless you provide hints for each and every property you want to use in your template.

Eclipse provides similar functionality, via the FreeMarker plugin configuration dialog; it saves a .freemarker-ui.xml in your project.





Rationale

The current tag library has a few shortcomings. It is outdated, too complex to use and to maintain. New concepts have been introduced at higher levels to solve similar problems.

  • it has complex inheritance logic, which can now be enabled by InheritanceContentWrapper; this is for instance used with info.magnolia.module.templating.MagnoliaTemplatingUtilities#inherit, STKUtil, templating models, ...
  • legacy code for support of nested paragraphs, which is not needed anymore.
  • it is only a tag library, so it's not useable outside the context of JSP templates. We're "lucky" that FreeMarker provides support for taglibs; support for other templating engines would require duplicating everything that's in those tags)
  • the templating module now provides a set of objects (content, mgnl(MagnoliaTemplatingUtilities), ctx, ...) to templates.
  • the templating module now supports model classes, thus giving the ability to move complex logic out of the templates.

We need to provide solutions for this; we will most likely deprecate a bunch of the existing tags in favor of new ones.

Future improvements

Some tags (adminOnly, publicOnly, ...) are redundant with facilities provided by MagnoliaTemplatingUtilities.

Other tags, like ifEmpty, etc, can also be avoided by using EL/jstl (which was not supported in earlier, JSP1.x-based, versions of Magnolia).

Some tags are still useful in the context of JSP (not so much in FreeMarker; for other templating engines, they may or may not be, but are generally simple enough to be reimplemented)

Such tags could/should be kept around, but simplified, too. There's a whole lot of legacy in there that we could get rid of.

Subclasses of BaseContentTag are the most "complex" one because they rely on the complex inheritance patterns provided by getFirstMatchingNode() and resolveNode() - which use different parameters and combinations thereof.

Cms:out and cms:include are also quite complex, and could most likely be simplified. The latter's behaviour is already in part reproduced with MgnlTemplatingUtilities.renderTemplate() methods.

Some other tags should be replaced/reimplemented/split away:

  • cms:links (html head tags for js and css)
  • set, setNode, user, loadPage, ...
  • Provide examples for replacements of AdminOnly or IsEmpty and similar tags.

Concepts

The rendering, or output, of these is mostly irrelevant for this issue; for 4.3, this will most likely still simply delegate to the magnolia-gui components. The important thing being that this becomes a hidden implementation detail, which we will be able to replace/update/improve later without the hassle of having to figure out each and every corner case the current taglib covers.

Buttons have actions, but these do nothing on their own other than generate links to servlets, so this can be considered an implementation detail, hidden in the rendering.

DONE: permissions check
DONE: i18n

Another point to take into consideration is permissions. Currently, some of the tags or magnolia-gui components do check for permissions, for instance before rendering the editBar, but it doesn't seem to be a consistent behavior.

Common properties of items:

  • label, description (help). i18n is implied. This can even exist at "container"-level.
  • target: the node which the action is about (i.e for "edit", the paragraph node we're editing; for "new", the node into which we'll add a new child; etc.)

Ideally, the above should be enough. The dialog name for edit-paragraph can be deduced, and allowed paragraphs for new-button could be deduced from configuration, ... There are however sensible, often used, properties we can support too:

  • dialog: the name of the dialog to use (page properties, page header, ...)
  • allowed paragraphs: in current templates, this list is always passed to the jsp tag (even though with STK its value comes from the configuration). In some cases, it's just convenient if the template itself can enforce which paragraphs are added, even if not exactly the most elegant thing. (and that's our only option outside STK at the moment)

FreeMarker

DONE * allow configuration of object pushed in the rendering context? This could also be done for jsp.

JSP

  • JSP tags - coded (should be fairly straightforward, just a little repetitive), could be generated.
  • Using the JSP 2.0 SimpleTag API - removes concerns about pooling, etc.
  • I'd rather keep the jsp-specific implementation separate from the new components, and keep those completely independant: testability, reusability.

Naming

Templating components.
"UI components" and "constructs"

Current implementation

Some details about the current implementation:

  • nodeCollectionName is used inconsistently. In most cases, it ends up being concatenated with a node path (in some cases in javascript, for example with mgnlDeleteNode(), in other instances the final path is "calculated" by the end point (servlet), and in yet other instances, it's simply ignored.

Migration/update path

  • adapter (which warns/throws on author instance or develop mode but silently delegate to "old" taglib in public instances)
  • documentation / examples for replacements etc!

Additional notes

Just some random bits of things I need to look at:

TODOs:

// TODO if ((!adminOnly || ? with jsp tag EditBar, you can do adminOnly="false" and get the button/bar on public instance !?

// TODO - deduce page dialog from target node...?
bar.setDialog(dialogName);

  • no support for comma-delimited strings? currently not supported by FM directives (but could) but still supported by tags
  • i18n

future:

  • templating made area-aware; area be a 1st class citizen; perhaps currentArea and currentNode/Paragraph be added to an hypothetical TemplatingState; or currentNode be updated with area when iterating, etc.: target=always current node, no need for "container"
  • templating components - how they would still be used
  • No labels