Recently, we had a requirement to show specific page properties for community pages that are based on the community editable template. As you know, in AEM 6.4, editable templates usually share the same page component, which means the same page properties dialog. Now, you could create a new page component (community-page) and inherit the master page component then ad the desired page properties. But there is another way!
Photo by Max van den Oetelaar on Unsplash
Granite Render Condition
The granite render conditions allow you to show/hide resources based on a condition. Nate Yolles has an excellent post on the topic. Additionally there is a path based ACS commons render condition.
Reading the render condition docs and Nate Yolles post are essential to understanding the rest of this article.
Handling my specific use-case
back to my use case, I want to hide page property resources based on the template of the page authored. In AEM 6.4, page properties are handled in a specific UI with a specific url:
http://localhost:4502/mnt/overlay/wcm/core/content/sites/properties.html?item=/content/my-company/my-page
Now, I just need to write a render condition that will obtain the “item” param (page path) then from there I can get the page and its template and compare it to my specific template path. Here is what I arrived at:
<%@include file="/libs/foundation/global.jsp"%> <%@page session="false" import="com.adobe.granite.ui.components.Config, com.adobe.granite.ui.components.rendercondition.RenderCondition, com.adobe.granite.ui.components.rendercondition.SimpleRenderCondition, com.mycompany.core.utils.TemplateRenderConditionUtil, org.apache.sling.api.resource.Resource, org.apache.sling.api.resource.ValueMap, com.adobe.granite.ui.components.ComponentHelper, com.adobe.granite.xss.XSSAPI, com.day.cq.i18n.I18n"%> <%--### Template ====== .. granite:servercomponent:: /apps/my-company/granite/rendercondition/template :rendercondition: A condition that renders page properties based on page template path It has the following content structure: .. gnd:gnd:: [granite:RenderConditionsTemplate] /** * The template path to match */ - templatePath (String) ###--%> <sling:defineObjects/> <% final ComponentHelper cmp = new ComponentHelper(pageContext); Config cfg = cmp.getConfig(); String path = cfg.get("templatePath", ""); boolean vote = TemplateRenderConditionUtil.isTemplate(slingRequest, request, path); request.setAttribute(RenderCondition.class.getName(), new SimpleRenderCondition(vote)); %>
As you can see, I get the configured templatePath
then pass it to TemplateRenderConditionUtil#isTemplate
.
here is the code for TemplateRenderConditionUtil
Please note: the code here should be straight-forward to follow assuming you are familiar with java 8 lambdas.
package com.mycompany.core.utils; import com.day.cq.wcm.api.Page; import com.day.cq.wcm.api.Template; import java.util.Optional; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.SlingHttpServletRequest; public class TemplateRenderConditionUtil { private static final String PAGE_PROPERTIES = "wcm/core/content/sites/properties"; public static boolean isTemplate( SlingHttpServletRequest slingHttpServletRequest, HttpServletRequest httpServletRequest, String templatePath) { // error if any of the passed params is null. if (slingHttpServletRequest == null || httpServletRequest == null || StringUtils.isBlank(templatePath)) { throw new IllegalArgumentException("One of the passed parameters is null."); } // the dialog is a page properties dialog if (StringUtils.contains(httpServletRequest.getPathInfo(), PAGE_PROPERTIES)) { // get the actual page path String pagePath = httpServletRequest.getParameter("item"); // get page template path and check it return Optional.ofNullable(slingHttpServletRequest.getResourceResolver()) .map(resourceResolver -> resourceResolver.getResource(pagePath)) .map(pageResource -> pageResource.adaptTo(Page.class)) .map(Page::getTemplate) .map(Template::getPath) .map(path -> StringUtils.contains(path, templatePath)) .orElse(false); } return false; } }
Now you can add a granite:rendercondition
node under any of your properties to show/hide them based on template path. Here is an example:
<community jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/fieldset" jcr:title="Community Properties"> <items jcr:primaryType="nt:unstructured"> <communityName jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/textfield" disabled="{Boolean}true" emptyText="Community ShortName" fieldLabel="Community Shortname" name="./communityShortname" /> <communityColor jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/select" fieldLabel="Community Color" name="./communityColor"> <datasource jcr:primaryType="nt:unstructured" sling:resourceType="acs-commons/components/utilities/genericlist/datasource" path="/etc/acs-commons/lists/community-color" /> </communityColor> </items> <granite:rendercondition jcr:primaryType="nt:unstructured" sling:resourceType="my-company/granite/rendercondition/template" templatePath="/conf/my-company/settings/wcm/templates/community-page"/> </community>
my community
fieldset will now only show for pages that are created from template: /conf/my-company/settings/wcm/templates/community-page
where to write the first jsp. I mean location.
Where to write the jsp. I mean location?
^ I have the same question. Where in the code base should this be located?
As you can see in the post, the render condition on granite widget (in dialog) is used in this manner:
granite:rendercondition
jcr:primaryType="nt:unstructured"
sling:resourceType="my-company/granite/rendercondition/template"
templatePath="/conf/my-company/settings/wcm/templates/community-page"
You can see the `sling:resourceType` is `my-company/granite/rendercondition/template`
this means it will first look under /libs then /apps.
I put mine, as you should under `/apps/my-company/granite/rendercondition/template`
You can put yours in any path you like.
I just tried it locally (referencing the jsp path in the sling:resourceType attribute of the grante:rendercondition node) and it worked great, thanks for the post! This opens up a lot of opportunities.
Hi,
Is there any possibilty to provide mutiple template paths for “templatePath”. I want to achieve same feature for more than 1 template. Thank you in advance.
Does it work with AEM 6.5?
Imported class – com.kf.core.utils.TemplateRenderConditionUtil
ERROR-
rg.apache.sling.api.scripting.ScriptEvaluationException: org.apache.sling.scripting.jsp.jasper.JasperException: Unable to compile class for JSP:
An error occurred at line: 26 in the jsp file: /apps/kf/granite/rendercondition/template/template.jsp
TemplateRenderConditionUtil cannot be resolved
23: final ComponentHelper cmp = new ComponentHelper(pageContext);
24: Config cfg = cmp.getConfig();
25: String path = cfg.get(“templatePath”, “”);
26: boolean vote = TemplateRenderConditionUtil.isTemplate(slingRequest, request, path);
27: request.setAttribute(RenderCondition.class.getName(), new SimpleRenderCondition(vote));
28: %>
Could you please suggest me solutions.
Thanks in advance.