Creating Custom Intershop ISML Functions With CustomTag
In this post, I’ll look at leveraging Intershop’s CustomTag
to create custom
functionality in ISML templates.
The problem
I wanted to capitalize a certain value from the pipeline dictionary in
an ISML template. Intershop provides ISML functions of the kind ucase
and lcase
to transform a string to uppercase or lowercase
respectively. When the value of the dictionary name Product:Name
is
AwesomeNotebook
:
<isprint value="#ucase(Product:Name)#">
This will print AWESOMENOTEBOOK
. Unfortunately, a similar function to
capitalize a string is not provided by Intershop. Ideally, you would
want to be able to implement your own methods, so that you could type:
<isprint value="#capitalize(Product:Name)#">
What about custom modules?
Intershop does provide the ability to create custom modules. These are
basically just tags you can give whatever name you like, and you can
provide an implementation in Java or ISML. The interface is defined in a
modules.isml
file. You could, for example, define a custom tag
isprintdefault
that prints a default value whenever the value
passed
to it is null
or the empty string:
<isprintdefault default="(unknown)" value="#Product:Price">
This would print (unknown)
every time Product:Price
does not contain
a value.
This solution, however, has two drawbacks:
-
For a Java implementation, this would come down to writing scriptlet. A few lines of scriptlet is fine ofcourse, but as the size of the code increases, it becomes harder and harder to maintain, because there is no compilation-time check and debugging is nearly impossible (say hello to a thousand print statements).
-
Also, it is cumbersome for implementing something like the capitalize function mentioned above, because you would have to provide the input to the custom module, put the result in the pipeline dictionary, and then use the pipeline dictionary name:
<iscapitalize dictname="capitalizedStr" input="#Product:Name#"> <isprint value="#capitalizedStr#">
Instead of just using the returned capitalized string directly:
<isprint value="#capitalize(Product:Name)#">
Solution: ISML Custom Tag
An ISML custom tag looks just like a custom module:
<ISUUID name="CategoryRenderEntityID">
(Incidently, this creates a UUID and stores it in the pipeline
dictionary variable name CategoryRenderEntityID
). According to the
documentation, the difference between a custom tag and a custom
module is that the former “provides a significant performance increase”
compared to the latter.
Two purposes
Custom tags can be used for two purposes:
- Performing a single operation, like we saw with the
ISUUID
custom tag above, where the tag performs a single action (creating a new UUID and putting it in the pipeline dictionary). - Inserting an instance of a helper or utilities class into the pipeline dictionary.
Creating an ISML Custom Tag
Say, we want to create an ISML Custom Tag named <ishelper>
. First, we
would define it in a modules.properties
file, located at
<cartridge>/staticfiles/cartridge/config/modules.properties
:
ismodules.helper.class=custom_base.internal.modules.Helper
ismodules.helper.isStrict=false
ismodules.helper.parameters=alias
ismodules.helper.parameter.alias.type=java.lang.String
ismodules.helper.parameter.alias.isRequired=false
The class
property should point to the implementing Java class. Since
a pipeline dictionary is available to custom tags, we can define whether
we want a clean, strict dictionary or not, much like we can do in
pipelines. Parameters and return values can be defined by providing
their name, type and whether or not they are mandatory.
So, the properties file above defines a custom tag <ishelper>
(put a
leading is
to your lowercase class name), that has an optional
attribute alias
of type String
. Other parameters can be added by
using comma’s:
ismodules.helper.parameters=alias,more,params
Don’t forget to also add a type and whether the parameter is required or not. The type of the parameter can be any Java class, so you’re not limited to just strings.
The Java class with the implementing code must extend the abstract class
CustomTag
. It is required to override the method processOpenTag
,
which will be called every time the custom tag is encountered in the
ISML template.
Now then, we can create “custom ISML functions” by adding an instance of
our Helper
class to the pipeline dictionary:
public class Helper extends CustomTag {
@Override
public void processOpenTag(PageContext paramPageContext,
ServletResponse paramServletResponse,
AbstractTemplate paramAbstractTemplate,
int paramInt)
throws IOException, ServletException
{
PipelineDictionary dict = AbstractTemplate
.getTemplateExecutionConfig()
.getPipelineDictionary();
String alias = dict.getOptional("alias", "Helper");
dict.put(alias, new Helper());
}
}
If we wanted a function capitalize
in our templates, we could add such
a method to our Java class:
public String getCapitalize(String str) {
/* do groundbreaking stuff */
return capitalizedStr;
}
Note that the method name should start with get
in order for it to be
available in the pipeline dictionary in our templates. We can now use
the method as follows:
<isprint value="#Helper:Capitalize(Product:Name)#">
Don’t forget, however, to add the custom tag somewhere at the beginning of the template before using it:
<ishelper alias="MyCustomUtilsClass">
<!-- snip -->
<isloop iterator="MyProducts" alias="product">
<isprint value="#MyCustomUtilsClass:Capitalize(product:Name)#"
</isloop>
(Note that the alias
attribute is optional. If you leave it out, it
will default to Helper
: see the implementation above. This will be the
alias of the class we’re using to invoke methods on:
<isprint value="#MyCustomUtilsClass:noArgMethod()#">
Remember that the name of the actual class that contains the custom tag
is part of the tag name: ishelper
.)
Alternative method of making a Java class available in the pipeline dictionary
Obviously, we do not have to rely on an ISML custom tag to put an
instance of a Java class in the pipeline dictionary. We could just
create a pipelet that does this, and put this pipelet in a pipeline that
gets called a lot (a Prefix
pipeline would be good). This would remove
the need to put <ishelper>
(or whatever we named the custom tag) at
the top of our templates. But the helper class would only be available
if the Prefix
pipeline was called, so you would need to check that
carefully. Putting the custom tag at the top of the template makes the
import explicit and easily verifiable.
A word of caution
Custom tags are very handy, but they extend the internal CustomTag
class. This means Intershop does not encourage using them in custom
project development. In fact, you won’t find any information about them
in the documentation.