Force Selection for Tags Field
Exposing hidden features of the CFM editor in AEM - Part 2
With the tag field inside the AEM Content Fragment Models (CFMs) you can add tags as content to Content Fragments (CFs). This is a very helpful component to reuse the powerful AEM tags functionality also inside content.
But the field has one big disadvantage: the author can input their own tags that will be stored at a default namespace and not in your probably well thought out tags structure.
How to add a force selection functionality
So now we need to think about a solution on how to force the user to select only the already created tags to prevent chaos in our tags. And here a little bit of AEM reverse engineering can help big time.
Let's have a look at the component AEM is using for this tag field. You can find the CFM components under this path:
- /libs/settings/dam/cfm/models/formbuilderconfig/datatypes/items
And here we see now that the tag field is of resourceType
- /libs/cq/gui/components/coral/common/form/tagfield
Now we can look inside the implementation and look at the render.jsp of this field:
Isn't that exactly what we are searching for? 🥳
Great - but how can we use it now in our Content Fragment Model? For this we need to do some more reverse engineering.
Let's have a look again at the CFM tag field and see how it's built together. For that we can have a look at the fieldProperties at
- /libs/settings/dam/cfm/models/formbuilderconfig/datatypes/items/tagfield/tagsfields.jsp (see the picture above)
In the tagsfields.jsp we can see that "forceSelection" property is already set to false permanently. So we need to overlay this file in order to expose the property to the CFM author.
To do that you can create a file "tagsfields.jsp" at the exact same path but under /apps
- /apps/settings/dam/cfm/models/formbuilderconfig/datatypes/items/tagfield/tagsfields.jsp
And inside this file we will copy the content from the /libs file and modify it accordingly:
For copy pasting:
<%@ page import="org.apache.sling.api.resource.ValueMap" %><%
%><%@include file="/libs/granite/ui/global.jsp" %><%
%><%@ page session="false" contentType="text/html" pageEncoding="utf-8"
import="com.adobe.granite.ui.components.formbuilder.FormResourceManager,
org.apache.sling.api.resource.Resource,
org.apache.sling.api.resource.ValueMap,
java.util.HashMap"%><%
ValueMap fieldProperties = resource.adaptTo(ValueMap.class);
String key = resource.getName();
String xssKey = xssAPI.encodeForHTMLAttr(key);
%>
<input type="hidden" name="./content/items/<%= xssKey %>/multiple" value="false">
<input type="hidden" name="./content/items/<%= xssKey %>/cq:showOnCreate" value="true">
<input type="hidden" name="./content/items/<%= xssKey %>/autocreateTag" value="true">
<!-- BEGIN ORIGINAL FIELD -->
<!--<input type="hidden" name="./content/items/<%= xssKey %>/forceSelection" value="false">-->
<!-- END ORIGINAL FIELD -->
<!-- BEGIN CUSTOM CODE -->
<%
HashMap<String, Object> values = new HashMap<String, Object>();
values.put("granite:class", "checkbox-label forceSelection");
values.put("text", i18n.get("Force Selection"));
values.put("value", true);
values.put("uncheckedValue", false);
values.put("checked", Boolean.valueOf(fieldProperties.get("forceSelection", String.class)));
values.put("name", "./content/items/" + xssKey + "/forceSelection");
FormResourceManager formResourceManager = sling.getService(FormResourceManager.class);
Resource placeholderFieldResource = formResourceManager.getDefaultPropertyFieldResource(resource, values);
%><sling:include resource="<%= placeholderFieldResource %>" resourceType="granite/ui/components/coral/foundation/form/checkbox"/>
<!-- END CUSTOM CODE -->
With that custom code we now have the forceSelection property as checkbox inside the CFM editor and the user can only select the tags but not create new ones.
Disclaimer
By extending AEM out-of-the-box functionality we always need to question ourselves if it's worth the risk to overlay. Because a new AEM release can contain changes at our changed files and could lead to unforeseen problems as there is no way to know what changed if you are not looking into the jsp/html/.. files in folder /libs directly.
In this case the risk is fairly low because we are changing only a single small jsp file. But also for this jsp we need to see what changes have been made with every AEM upgrade we do.
Tested with AEM 6.5.17
More on extending the Content Fragment functionality can be found here: