This is an initial approach to integrate the Adobe HTL Expression Language (formerly called Sightly) with the Composum Pages framework.
Unfortunately HTL is quite limited in it's expressiveness, and does not (yet?) support an equivalent of JSP tag libraries. Since the Composum Tag libraries are an important part of the Pages framework, this is a bit limited, and thus not recommended for production usage. There is a somewhat limited adapter that allows using the Composum tags within HTL, which uses one HTL template for the tag start and one for the tag end. Obviously this cannot hide or change the content of the tag, as it is possible in JSP, and it is more verbose.
This project provides the UseProviders that enable the creation of Composum models within HTL, and the discussed Tag adapter. As an example, the test artefact defines a search component completely with HTL.
More details please find here.
The HTML Template Language HTL (formerly called Sightly) is a modern replacement for JSP that provides the dynamic behaviour by using HTML5 like data attributes and using HTML elements as block statements, and provides enhanced security against XSS by tightly integrating with HTML syntax and automatically escaping data properly.
Integration with Composum models
The ComposumModelUseProvider
extends the
HTL Java Use-Api such
that an object is also created if it can be created
with BeanContext.adaptTo
, which supports the creation of
both SlingBean
s and Pages Model
s with or without Sling-Models. Thus,
to create a model is as easy as e.g.
// no parameters
<sly data-sly-use.someobject="foo.bar.SomeClass">
// with parameters
<sly data-sly-use.someobject="${'foo.bar.SomeClass' @ parameter=resource}">
Replacement for Composum Tag libraries
Since HTL there is no direct counterpart for JSP tag libraries, there is a partial emulation of the composum tag libraries as HTL templates. Unfortunately the data-sly-call to a template cannot access the elements content where the call is placed at. Thus, the usual JSP mechanisms for wrapping some content into a custom tag that modifies it and possibly adds variables valid in that context cannot be easily be carried over.
As a workaround the tags that expect content have to be split into templates, one for the start and one for the end of the tag. For instance the cpp:element Tag can be used as follows:
<sly data-sly-call="${cpp.startElement @ var='field', type='com.composum.pages.components.model.search.SearchField'}"/>
... content that would be within a cpp:element tag in JSP ...
<sly data-sly-call="${cpp.endElement}"/>
If needed, the defined variable field
(which is set in an emulated page context, to be comparable to the JSP mechanisms) from the HTL, it can be read as follows (see 'Further extensions' below):
<sly data-sly-use.field="${'com.composum.pages.components.model.search.SearchField' @ fromScope='page', key='field'}"/>
To enable communication between templates we implements a kind of pagecontext for HTL by putting a map into a request attribute. There are separate maps for each script name - that is, the resource of the script. The emulated page context can be accessed from the Composum tags, or via the AttributesUseProvider (see below).
The AttributesUseProvider allows reading request- or session-attributes or the EmulatedPageContext with a data-sly-use statement such that the IDE knows the specific class and can provide code-completion etc. This use provider is activated whenever the "fromScope" parameter is present. Usage example (reads the search result from a request attribute searchresult
):
<sly data-sly-use.searchresult="${'com.composum.pages.commons.service.search.SearchService.Result' @ fromScope='request', key='searchresult'}"/>:
Possible scopes (case insensitive) are bindings
for the script bindings, page
for the emilated page context, and request
and session
.
It is also possible to explicitly pass a value with parameter value
to effect a IDE-visible typecast. (The given value is not changed).
The AttributeHelper
allows setting and reading request-/session-attributes, request parameters, script bindings or a simulated pageContext (EmulatedPageContext)
since there is no standard HTL way to read, much less set, those. If there should be something written, the parameter scope
should be one of bindings
, page
, request
, session
(page being the emulated page context), and there can be a key
and value
parameter to set one value, or an arbitrary number key1, key2, key3 ... and corresponding value1, value2, value3, ... parameters to set the values in that scope. Example:
<sly data-sly-use.attrs="${'com.composum.platform.htl.AttributeHelper' @ scope='page', key='themodel', value=model}"></sly>
<sly data-sly-use.attrs="${'com.composum.platform.htl.AttributeHelper @ scope='page', key1='something', value1=foo", key2='else', value2=bar}"></sly>
It also allows reading request and session attributes and the emulated page context by providing maps requestAttributes
and sessionAttributes
and pageContext
:
<sly data-sly-use.attrs="com.composum.platform.htl.AttributeHelper">${attrs.requestAttributes['sling.core.current.servletName']}</sly>
To be able to access the org.apache.sling.scripting.sightly.render.RenderContext from models, these can extend the UseWithRenderContext
interface.
Some of the less obvious things:
If you use of the
injector-specific annotations,
adding @Inject
is unneccesary. These annotations can be recognized by
carrying the @InjectAnnotation annotation.
- https://docs.adobe.com/docs/en/htl/overview.html
- HTL Specification
- http://blogs.adobe.com/experiencedelivers/experience-management/htl-intro-part-1/, incl. some FAQ
- Sling: HTL Scripting Engine
- HTL Repl http://localhost:9090/htl/repl.html
- Slides about AEM Sightly Template Language
- https://sling.apache.org/project-information.html
- Stackoverflow: Tag sling-models, Tag sling
- The JSP Expression language cannot be used in the Composum Tags when these are used in HTL.
- org.apache.sling.scripting.sightly.use.UseProvider provides ways to instantiate the objects for data-sly-use blocks; the ComposumModelUseProvider is a UseProvider that instantiates Sling-Models also from a BeanContext.
- org.apache.sling.scripting.sightly.extension.RuntimeExtension : handler for various built in functions, see RuntimeFunction. Could e.g. be used to extend i18n to the Composum Pages way, or extend uriManipulation or includeResource with additional arguments or functionality.
Defining a global with use within a template doesn't work - that's only
local to the template. Thus, a template cannot declare global variables.
(Idea: special composum variables as one model composum
).
Statements in a template library are not executed when loading - HTL
just takes the templates out of there.
Unknown variables are taken from the bindings, but are initialized
before everything else, so we can't define additional globals.
The bindings passed on by the Java Use-Api are freshly generated - if
it's neccesary to keep something permanently, one needs to put it into
the request attributes.
When trying to carry over the concept of tag libraries to HTL, there are the following limitations that hurt the possibilities:
- A data-sly-call
on an element throws away the content of the element. Thus, it is
not possible to modify, conditionally throw away, or repeat the
content of the element, or easily surround some content with a
computed number of elements.
- Partial solution: split up the tags with content into a start and an end template. Problem: functionality that concerns the content of a tag cannot be implemented, and no variables for the content of the tag can be set. (Possible extension of HTL: the content of the data-sly-call element could be delivered to the template e.g. as a RenderUnit as a binding variable, which could be rendered once or several times via a new mechanism.)
- There seems no way to define something like functions as in a tag library. (Idea: Use-Provider that calls a static function and delivers the result? (→ no IDE support.) Special class for each taglib function with constructor parameters corresponding to the arguments of the taglib function?)
- In a template the parameters cannot be documented. Thus, it is not possible to see documentation within the IDE.
- It is not possible to modify the global variables from a template. Thus, it its not possible to emulate a tag like cpp:defineObjects that defines several objects at once - the closest you can do is to create a model class that has several attributes with the objects to define. (Mostly OK; perhaps HTL could be extended that a template could also define some models, not only templates.).
- data-sly-use for template libraries etc. does not use the resource search path (/apps, /libs, ...)
- To output an attribute of type URI one needs to insert .toString - URI are silently swallowed.
- Model that extends ResourceHandle and can provide e.g.
inheritedValues (while setting the inheritancetype). Partial
solution:
<sly data-sly-use.resourceHandle="com.composum.sling.core.ResourceHandle" />
- EL Function equivalents
- Model that provides the defineObjects functionality