AEM allows for customization of the TouchUI to fit the client’s needs through client libraries and overlays. Combined with AEM’s tagging capabilities, information that needs to be directly displayed to users can be made instantly available through labels and adding additional fields to the asset and collection views.
In this example a client needs to communicate to their marketing team that certain assets have restrictions on where they can be used or require credit to the image’s owner. To do this we create a new set of tags, modify the metadata schema, and then create the necessary client libraries, overlays, and servlets for handling the logic.
Tagging system
First, create the tags that will be used for labeling. This can be a single tag or a series of tags. Using tags makes it easy for users to add (or remove) new properties as they are needed. Then, add a new field to your metadata schema. In our case, we have a simple Yes/No dropdown and then a tag Standard Tags selector that appears when Yes is selected from Restrictions dropdown.
Asset View Overlays
Now that we have the tags created, we can move on to labeling the assets. There are two approaches to this: clientlibs or overlays. In this case, we used overlays because they ended up having better performance for these asset views. The following jsp files were overlaid to add labels (code cut down for brevity). The general logic here is to take the resource and check its metadata for restriction tags and then add the labels/formatting.
apps/dam/gui/content/assetdetails/.content.xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="cq:Page">
<jcr:content
jcr:mixinTypes="[sling:VanityPath]"
jcr:primaryType="nt:unstructured"
jcr:title="AEM Assets"
sling:resourceType="granite/ui/components/shell/propertiespage"
sling:vanityOrder="{Long}291"
sling:vanityPath="/assetdetails"
backHref="${granite:concat("/assets.html", granite:encodeURIPath(granite:relativeParent(empty requestPathInfo.suffix ? param.item : requestPathInfo.suffix, 1)))}">
<title
jcr:primaryType="nt:unstructured"
sling:resourceType="/apps/dam/components/assets/assetdetails/title/asset-title.jsp"
path="${requestPathInfo.suffix}"/>
</jcr:content>
</jcr:root
libs/dam/components/assets/assetdetails/title/asset-title.jsp
%><%@include file="/libs/granite/ui/global.jsp" %><%
%><%@page session="false"
import="org.apache.sling.api.resource.Resource,
javax.jcr.Node,
com.adobe.granite.ui.components.AttrBuilder,
com.adobe.granite.ui.components.Config,
com.adobe.granite.ui.components.ExpressionHelper,
com.adobe.granite.ui.components.Tag,
org.apache.sling.api.resource.ValueMap,
com.day.cq.dam.commons.util.UIHelper" %><%
%><%@taglib prefix="cq" uri="http://www.day.com/taglibs/cq/1.0" %><%
%><%
Config cfg = cmp.getConfig();
ExpressionHelper ex = cmp.getExpressionHelper();
String contentPath = ex.getString(cfg.get("path", String.class));
Resource assetResource = resourceResolver.getResource(contentPath);
Node resourceNode = assetResource.adaptTo(Node.class);
String resTitle = "";
if (contentPath != null && !contentPath.trim().isEmpty()) {
Resource contentResource = resourceResolver.getResource(contentPath);
if (contentResource != null) {
resTitle = UIHelper.getTitle(contentResource);
}
}
String title;
if(resTitle != null && !resTitle.trim().isEmpty()){
title = resTitle;
}
else {
title = "";
}
boolean restricted = isRestricted(resourceNode);//call restriction check
AttrBuilder attrs = new AttrBuilder(request, xssAPI);
attrs.add("role", "heading");
attrs.add("aria-level", 1);
if(restricted == true) { //if restricted is true add red background to ttile
attrs.add("style", "background-color: #ff7f7f");
}
attrs.addClass("assetdetails-asset-title");
%><div class="assetdetails-title-container"><div <%= attrs.build() %>><%= xssAPI.encodeForHTML(title) %></div>
<sling:include resourceType="dam/gui/coral/components/admin/assetdetails/assetnavigator" />
</div>
<%!
private boolean isRestricted (Node resourceNode) { //check restricted tags
try {
if (resourceNode.hasProperty("jcr:content/metadata/restricted")) {
if (resourceNode.getProperty("jcr:content/metadata/restricted").getValue().getString()
.equals("yes")){return true;}
else {return false;}
} else {
return false;
}
} catch (Exception e){
return false;
}
}
%>
libs/dam/gui/coral/components/admin/contentrenderer/card/common/card-banner.jsp
%><%@include file="/libs/granite/ui/global.jsp"%><%
%><%@page import="static com.day.cq.commons.jcr.JcrConstants.JCR_CONTENT,
static com.day.cq.dam.api.DamConstants.RENDITIONS_FOLDER,
static com.day.cq.dam.api.DamConstants.ORIGINAL_FILE,
com.day.cq.workflow.status.WorkflowStatus,
com.day.cq.dam.api.ui.editor.metadata.MetadataEditorHelper,
com.day.cq.dam.entitlement.api.EntitlementConstants,
com.day.cq.dam.commons.util.S73DConstants,
com.day.cq.dam.commons.util.S73DHelper,
com.day.cq.i18n.I18n,
org.apache.sling.event.jobs.JobManager,
org.apache.sling.event.jobs.Job,
org.apache.sling.event.jobs.Job.JobState,
com.adobe.granite.xss.XSSAPI,
com.adobe.granite.ui.components.AttrBuilder,
org.apache.sling.api.resource.ValueMap,
org.apache.sling.featureflags.Features,
com.day.cq.dam.commons.util.DamUtil"%>
<%
%><%@taglib prefix="cq" uri="http://www.day.com/taglibs/cq/1.0"%><%
%><%@include file="/libs/dam/gui/coral/components/admin/contentrenderer/base/base.jsp"%><%
%><%@include file="/apps/dam/gui/coral/components/admin/contentrenderer/base/assetBase.jsp"%><%
ResourceResolver resolver = slingRequest.getResourceResolver();
MetadataEditorHelper meh = sling.getService(MetadataEditorHelper.class);
boolean missingMeta = !hasValidMetadata(resource, meh);
boolean isRestricted = isRestricted(resourceNode);//call restriction check
attrAlert.add("variant", variant);
AttrBuilder attrAlertContent = new AttrBuilder(request, xssAPI);
AttrBuilder attrRes = new AttrBuilder(request, xssAPI);
String resAlertContent = "";
if(hasDiv) {
attrAlert.add("style", alertStyle);
attrAlertContent.add("class","coral-Alert-title");
attrDiv = new AttrBuilder(request, xssAPI);
attrDiv.add("class","text-ellipsis v3D-Banner");
attrDiv.add("data-path", encodedResourcePath);
attrSpan = new AttrBuilder(request, xssAPI);
attrDiv.add("class","v3d-ProgressBanner-progress");
attrDiv.add("style", "width: " + progress + "%");
}
if(isRestricted){ //if restricted add restrictions to attr builder
attrRes.add("variant", "warning");
resAlertContent = i18n.get("Restrictions");
}
%>
<coral-card-info <%= attrNone3DAlert != null ? attrNone3DAlert.build() : "" %>>
<% if(attrNewTag != null) { %>
<coral-tag <%= attrNewTag.build()%>> <%= contentNewTag%></coral-tag>
<% }
if(hasAlert) { %>
<coral-alert <%= attrAlert.build()%>>
<% if(hasDiv) { %>
<div <%= attrDiv.build()%>>
<span <%= attrSpan.build()%>></span>
<strong>
<% } %>
<coral-alert-content <%= attrAlertContent.build()%>><%= alertContent %></coral-alert-content>
<% if(hasDiv) { %>
</strong>
</div>
<% } %>
</coral-alert>
<% }
if(isRestricted) { %> //generate restrictions banner
<coral-alert <%= attrRes.build()%>>
<coral-alert-content <%= attrAlertContent.build()%>><%= resAlertContent %></coral-alert-content>
</coral-alert>
<% } %>
</coral-card-info>
private boolean isRestricted (Node resourceNode) { //check asset restricitons
try {
if (resourceNode.hasProperty("jcr:content/metadata/restricted")) {
if (resourceNode.getProperty("jcr:content/metadata/restricted").getValue().getString()
.equals("yes")){return true;}
else {return false;}
} else {
return false;
}
} catch (Exception e){
return false;
}
}
libs/dam/gui/components/admin/collections/childasset/card-banner.jsp
<%@page session="false" %>
<%@page import="static com.day.cq.commons.jcr.JcrConstants.JCR_CONTENT,
static com.day.cq.dam.api.DamConstants.RENDITIONS_FOLDER,
static com.day.cq.dam.api.DamConstants.ORIGINAL_FILE,
javax.jcr.Node,
javax.jcr.query.Query,
javax.jcr.NodeIterator,
java.util.List,
com.day.cq.workflow.status.WorkflowStatus,
com.day.cq.workflow.exec.Workflow,
com.day.cq.i18n.I18n,
org.apache.sling.api.resource.Resource,
org.apache.sling.api.resource.ResourceResolver,
javax.jcr.Session,
com.adobe.cq.commerce.api.collection,
com.day.cq.dam.commons.util.DamUtil" %>
<%@ page import="org.apache.sling.api.resource.ValueMap" %>
<%@taglib prefix="cq" uri="http://www.day.com/taglibs/cq/1.0" %>
<cq:defineObjects/><%
I18n i18n = new I18n(slingRequest);
Node resourceNode = request.getAttribute(RESOURCE_NODE) != null ? (Node) request.getAttribute(RESOURCE_NODE) : null;
boolean isRestricted = isRestricted(resourceNode);//call restrictions test
if(resource.getResourceType().equals("dam/gui/components/admin/collections/childasset")) {
WorkflowStatus wfState = resource.adaptTo(WorkflowStatus.class);
if (isRestricted == true){
<div class="coral-Alert coral-Alert--info">
<i class="coral-Icon coral-Icon--sizeS coral-Icon--infoCircle"></i>
<div class="text-ellipsis">
<div><strong class="coral-Alert-title"><%= i18n.get("Restricted")%> //Add restricted banner
</strong>
</div>
</div>
</div>
}
<%!
private boolean isRestricted (Node resourceNode) { //check for restrictions
try {
if (resourceNode.hasProperty("jcr:content/metadata/restricted")) {
if (resourceNode.getProperty("jcr:content/metadata/restricted").getValue().getString()
.equals("yes")){return true;}
else {return false;}
} else {
return false;
}
} catch (Exception e){
return false;
}
}
%>
libs/dam/gui/coral/components/admin/collections/contentrenderer/rowasset/reorder.jsp
<%
final String ASSET_RES_TYPE = "dam/gui/coral/components/admin/collections/contentrenderer/rowasset";
Logger logger = Logger.getLogger(reorder_jsp.class);
Resource assetResource = resource;
String[] restrictionsTags = new String[10];
String restrictions = "";
try{ //gets a list of restrictions to display
if(assetResource.getResourceType().equals(ASSET_RES_TYPE) && assetResource.getChild("jcr:content/metadata/restricted") != null){ if(assetResource.getChild("jcr:content/metadata/restricted").getValueMap() != null){
ValueMap vm = assetResource.getChild("jcr:content/metadata").getValueMap();
restrictionsTags = vm.get("restricted", String[].class);
restrictions = String.join(" , ", restrictionsTags);
restrictions = restrictions.replaceAll("asset-info:restricted/","");
}
}
} catch(Exception e){
logger.error("Unable to get restrictions ", e);
}
%>
//generate restrictions cell with list of tags
<td is="coral-table-cell" <% if(restrictions.length() > 1) { %> style="background-color: #ff7f7f" <% } %> value="<%= restrictions %>">
<%= restrictions %>
</td>
<cq:include script = "/libs/dam/gui/coral/components/admin/collections/contentrenderer/rowasset/reorder.jsp"/>
/libs/dam/gui/coral/components/admin/contentrenderer/row/common/reorder.jsp
<%
final String ASSET_RES_TYPE = "dam/gui/coral/components/admin/contentrenderer/row/asset";
Logger logger = Logger.getLogger(reorder_jsp.class);
Resource assetResource = resource;
String[] restrictionsTags = new String[10];
String restrictions = "";
try{ //gets a list of restrictions
if(assetResource.getResourceType().equals(ASSET_RES_TYPE) && assetResource.getChild("jcr:content/metadata/restricted") != null){ if(assetResource.getChild("jcr:content/metadata/restricted").getValueMap() != null){
ValueMap vm = assetResource.getChild("jcr:content/metadata").getValueMap();
restrictionsTags = vm.get("restricted", String[].class);
restrictions = String.join(" , ", restrictionsTags);
restrictions = restrictions.replaceAll("asset-info:restricted/","");
}
}
} catch(Exception e){
logger.error("Unable to get restrictions ", e);
}
%>
//create table cell with restrictions
<td is="coral-table-cell" <% if(restrictions.length() > 1) { %> style="background-color: #ff7f7f" <% } %> value="<%= restrictions %>">
<%= restrictions %>
</td>
<cq:include script = "/libs/dam/gui/coral/components/admin/contentrenderer/row/common/reorder.jsp"/>
apps/dam/gui/content/commons/availablecolumns/.content.xml
Add column
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured">
<tags
jcr:primaryType="nt:unstructured"
jcr:title="Restricted Tags"
columnGroup="Restricted Tags"
configurable="{Boolean}true"
default="{Boolean}true"/>
</jcr:root>
apps/dam/gui/coral/components/admin/contentrenderer/column/columnpreview/columnpreview.jsp
<%@include file="/libs/granite/ui/global.jsp"%>
<%@include file="/libs/dam/gui/coral/components/admin/contentrenderer/base/base.jsp"%><%
%><%@page session="false"%><%
%><%@page import="java.util.Date,
org.apache.commons.io.FilenameUtils,
org.apache.jackrabbit.JcrConstants,
org.apache.sling.api.resource.SyntheticResource,
com.adobe.granite.security.user.UserPropertiesManager,
com.adobe.granite.ui.components.Config,
com.adobe.granite.ui.components.ValueMapResourceWrapper,
com.day.cq.dam.api.checkout.AssetCheckoutService,
com.day.cq.dam.commons.util.StockUtil,
com.day.cq.dam.commons.util.DamUtil,
com.day.cq.dam.commons.util.UIHelper,
com.day.cq.dam.commons.util.S73DHelper,
org.apache.log4j.Logger" %><%
%><%@taglib prefix="cq" uri="http://www.day.com/taglibs/cq/1.0"%><%
%><cq:includeClientLib categories="dam.gui.columnrenderer.columnpreview"/><%
String restrictions = "";
boolean restricted = false;
boolean isDirectory = false;
Logger logger = Logger.getLogger(columnpreview_jsp.class);
Node resourceNode = request.getAttribute(RESOURCE_NODE) != null ? (Node) request.getAttribute(RESOURCE_NODE) : null;
restrictions = isRestricted(contentResource, restrictions, logger);
%><coral-columnview-preview><coral-columnview-preview-content>
<%
String metaRT = cfg.get("metaResourceType", String.class);
if (metaRT != null) {
%><sling:include resource="<%= contentResource %>" resourceType="<%= metaRT %>" /><%
}
%>
<coral-columnview-preview-asset>
<img src="<%= xssAPI.getValidHref(thumbnailUrl) %>" alt="<%=xssAPI.encodeForHTMLAttr(UIHelper.getAltText(contentResource))%>">
</coral-columnview-preview-asset>
<coral-columnView-preview-label><%= i18n.get("Title") %></coral-columnView-preview-label>
<coral-columnview-preview-value><%= xssAPI.encodeForHTML(title) %></coral-columnview-preview-value><%
if (name != null && !name.equals(title)) {%>
<coral-columnView-preview-label><%= i18n.get("Name") %></coral-columnView-preview-label>
<coral-columnview-preview-value><%= xssAPI.encodeForHTML(name) %></coral-columnview-preview-value><%
}
if (modified != null && restricted == true && modifiedBy.trim().length() > 0) {
%><coral-columnView-preview-label><%= i18n.get("Modified") %></coral-columnView-preview-label>
<coral-columnview-preview-value><%= xssAPI.filterHTML(modified) %></coral-columnview-preview-value>
<coral-columnView-preview-label><%= i18n.get("Modified By") %></coral-columnView-preview-label>
<coral-columnview-preview-value><%= xssAPI.encodeForHTML(modifiedBy) %></coral-columnview-preview-value><%
} %>
<%if (!isDirectory && isCheckedOut) {
%><coral-columnView-preview-label><%= i18n.get("Checked Out By") %></coral-columnView-preview-label>
<coral-columnview-preview-value><%= xssAPI.encodeForHTML(formattedCheckedOutBy) %></coral-columnview-preview-value><%
}%>
<%if (width != 0 && height != 0) {
%><coral-columnView-preview-label><%= i18n.get("Dimensions") %></coral-columnView-preview-label>
<coral-columnview-preview-value><%= xssAPI.encodeForHTML(width + " x " + height+" px") %></coral-columnview-preview-value><%
}%>
<%if (publicationState != null && !isDeactivated) {%>
<coral-columnView-preview-label><%= i18n.get("Publication") %></coral-columnView-preview-label>
<coral-columnview-preview-value><%= xssAPI.filterHTML(i18n.getVar(publicationState)) %></coral-columnview-preview-value>
<%}%>
<%if (publicationState != null && isDeactivated) {%>
<coral-columnView-preview-label><%= i18n.get("Un-publication") %></coral-columnView-preview-label>
<coral-columnview-preview-value><%= xssAPI.filterHTML(i18n.getVar(publicationState)) %></coral-columnview-preview-value>
<%}%>
<%if (size.length() > 0) { %>
<coral-columnView-preview-label><%= i18n.get("Size") %></coral-columnView-preview-label>
<coral-columnview-preview-value><%= size %></coral-columnview-preview-value><%
}%>
<%if (!restrictions.isEmpty()) { %>
<coral-columnView-preview-label><%= i18n.get("Restricted") %></coral-columnView-preview-label>
<coral-columnview-preview-value style="background-color: #ff7f7f"><%= xssAPI.encodeForHTML(restrictions) %></coral-columnview-preview-value><%
}%>
<%if (!isDirectory && displayMimeType != null) {
%><coral-columnView-preview-label><%= i18n.get("Type") %></coral-columnView-preview-label>
<coral-columnview-preview-value><%= xssAPI.encodeForHTML(displayMimeType) %></coral-columnview-preview-value><%
}%>
%>
</coral-columnview-preview-content></coral-columnview-preview><%!
private String isRestricted (Resource resource, String strict, Logger log) {
try {
ValueMap vm = resource.getChild("jcr:content/metadata").getValueMap();
if (vm.get("restricted", String[].class) != null){
String[] restrictionsTags = new String[10];
restrictionsTags = vm.get("restricted", String[].class);
strict = String.join(" , ", restrictionsTags);
strict = strict.replaceAll("asset-info:restricted/","");
}
if (strict != null){
return strict;
}
else{
return null;
}
} catch (Exception e){
return null;
}
}
%>
apps/dam/gui/coral/components/admin/contentrenderer/column/asset/.content.xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:Folder"
sling:resourceSuperType="dam/gui/coral/components/admin/contentrenderer/column/common"/>
apps/dam/gui/coral/components/admin/contentrenderer/column/asset/asset.jsp
<%@page import="org.apache.sling.api.resource.Resource,
com.adobe.granite.security.user.util.AuthorizableUtil,
org.apache.sling.api.resource.ValueMap,
com.day.cq.dam.commons.util.UIHelper"%><%
%><%@include file="/libs/dam/gui/coral/components/admin/contentrenderer/base/init/assetBase.jsp"%><%
%><%@include file="/libs/dam/gui/coral/components/admin/contentrenderer/column/common/common.jsp"%><%
String name = resource.getName();
ValueMap assetMap = resource.getChild("jcr:content/metadata").getValueMap();
boolean restricted = isRestricted(assetMap);
%><coral-columnview-item <%= attrs.build() %>>
<cq:include script = "meta.jsp"/>
<coral-columnview-item-thumbnail><%
if (isArchive) {
%><coral-icon icon="fileZip" size="S"></coral-icon><%
} else {%>
<img src="<%= xssAPI.getValidHref(thumbnailUrl) %>" alt="" itemprop="thumbnail" style="vertical-align: middle; width: auto; height: auto; max-width: 3rem; max-height: 3rem;"><%
}%>
</coral-columnview-item-thumbnail>
<coral-columnview-item-content>
<div class="foundation-collection-item-title" itemprop="title" title="<%= xssAPI.encodeForHTMLAttr(resourceTitle) %>" <% if(restricted == true) { %> style="color: #ff7f7f" <% } %>>
<%= xssAPI.encodeForHTML(resourceTitle) %>
</div><%
if (name != null && !name.equals(resourceTitle)) {
%><div class="foundation-layout-util-subtletext">
<%= xssAPI.encodeForHTML(name) %>
</div><%
}%>
</coral-columnview-item-content>
<cq:include script = "applicableRelationships.jsp"/>
<cq:include script = "link.jsp"/>
<meta itemprop="lastmodified" content="<%= lastModified %>">
<meta itemprop="lastmodifiedby" content="<%= xssAPI.encodeForHTMLAttr(lastModifiedBy) %>">
</coral-columnview-item><%!
//Add private methods here
%>
<%!
private boolean isRestricted (ValueMap vm) {
try {
if (vm.get("restricted", String[].class) != null){
return true;
}
else{
return false;
}
} catch (Exception e){
return false;
}
}
%>
Collection Views
For collection views, we will be using a servlet and clientlib to generate the labels. To reduce the work being done, lazy loading is implemented to speed up load times. The overall logic works as follows: it checks for items in the viewport and then sends an ajax call containing the list of collections in the viewport; then, the servlet runs a query to check if any of the assets within the collection(s) have restrictions; the servlet then returns then returns a list of collections with restrictions; Finally, the clientlib adds the labels to the collections. The list of restriction tags is stored in our CollectionRestrictionsConf, which allows us to easily add/remove new tags as needs change.
apps/dam/gui/coral/components/admin/collections/contentrenderer/collectioncard/collectioncard.jsp
<%@page session="false" %>
<%@taglib prefix="ui" uri="http://www.adobe.com/taglibs/granite/ui/1.0"%>
<ui:includeClientLib categories="dam.gui.collection.labels"/>
<%@include file="/libs/dam/gui/coral/components/admin/collections/contentrenderer/collectionBase.jsp"%
com/my/aem/dam/core/servlets/CollectionRestrictionServlet.java
The servlet runs a query for assets in the asset paths that are members of the collections we have sent over from the clientlib. It retrieves the list of restriction tags from the config.
package com.my.aem.dam.core.servlets;
import com.my.aem.dam.core.configs.CollectionRestrictionsConfig;
import com.day.cq.search.PredicateGroup;
import com.day.cq.search.Query;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.result.SearchResult;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.request.RequestParameter;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.metatype.annotations.Designate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Session;
import javax.servlet.Servlet;
import java.io.IOException;
import java.io.Writer;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Servlet for querying assets to check if they have any restricted tags
*
* @author David Nestor (david.nestor@kbwebconsult.com)
* */
@Component(service= Servlet.class,
property={
Constants.SERVICE_DESCRIPTION + "=Check collection restrictions.",
"sling.servlet.methods=" + HttpConstants.METHOD_POST,
"sling.servlet.paths="+"/bin/my/check-restrictions"
})
@Designate(ocd = CollectionRestrictionsConfig.class)
public class CollectionRestrictionServlet extends SlingAllMethodsServlet {
private final transient Logger logger = LoggerFactory.getLogger(CollectionRestrictionServlet.class);
private String[] restrictionList;
@Reference
transient QueryBuilder queryBuilder;
@Activate
public void activate(CollectionRestrictionsConfig config) {
restrictionList = config.collectionRestrictions();
}
/**
* Checks if collections sent from check-restriciton.js clientlib
* have assets with restricted tags and writes out a list of collections that have restricted assets
*
* @param request the request from check-restrictions.js
* @param response writes out response to ajax call in check-restrictions.js
* */
@Override
protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response)
throws IOException {
try {
Map<String, String> querymap = new ConcurrentHashMap<>();
Session session = request.getResourceResolver().adaptTo(Session.class);
RequestParameter[] param = request.getRequestParameters("item");
List<String> collections = new ArrayList<>();
List<String> resCollections = new ArrayList<>();
if (param != null) {
for (RequestParameter par : param) {
if (URLDecoder.decode(par.getString(), "UTF-8").length() > 1) {
collections.add(URLDecoder.decode(par.getString(), "UTF-8"));
}
}
Writer writer = response.getWriter();
for (String col : collections) {
if (col.length() > 3) {
querymap.put("group.p.or", "true");
querymap.put("group.1_path", "/content/dam/my-dam");
querymap.put("group.2_path", "/content/dam/webgems");
querymap.put("type", "dam:Asset");
querymap.put("memberOf", col);
querymap.put("property", "jcr:content/metadata/restricted");
for(int i = 0; i < restrictionList.length; i++){
querymap.put("property." + i + "_value", restrictionList[i]);
}
querymap.put("p.limit", "1");
Query query = queryBuilder.createQuery(PredicateGroup.create(querymap), session);
SearchResult result = query.getResult();
long resNum = result.getTotalMatches();
if (resNum > 0) {
resCollections.add(col);
}
querymap.clear();
}
}
if (!resCollections.isEmpty()) {
String out = resCollections.toString();
writer.write(out);
} else {
writer.write("");
}
writer.close();
}
}
catch(Exception e){
logger.error("Unable to obtain asset restrictions for collection ", e );
}
}
}
com/my/aem/dam/core/configs/CollectionRestrictionsConfig.java
package com.my.aem.dam.core.configs;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
/**
* Interface to handle configurations regarding the Collection Restrictions as
* modifiable OSGi configurations
*
* */
@ObjectClassDefinition(name="Collection Restrictions Config", description = "Configurations for CollectionRestrictionServlet ")
public @interface CollectionRestrictionsConfig {
/**
* List of restrictions to be checked for adding labels
*
* */
@AttributeDefinition(name = "AssetRestrictions", description = "Restrictions that are checked for labeling collections")
String[] collectionRestrictions() default {"my-asset-info:restricted/do-not-modify", "my-asset-info:restricted/credit-to-photographer", "my-asset-info:restricted/credit-to-designer",
"my-asset-info:restricted/digital-use-only", "my-asset-info:restricted/social-media-use-only"};
}
apps/my-dam/clientlibs/collection-restrictions/js/check-restrictions.js
/*
* check-retsrictions.js
*
* Hides or displays the Restore Button from the Actions menu bar depending on if the
* selected asset(s) are in the Recycle Bin
*
* @author David Nestor (david.nestor@kbwebconsult.com)
*/
(function($, $document) {
"use strict";
let $collections = [];
let smartCollections = [];
//Adds event listener in collection view and initializes the first check on load
$document.on("foundation-contentloaded", function() {
if (!isCollectionsPage()) {
return;
}
let $view = checkLayout();
addListenerForImageVisibilityCheck($view);
initCheck();
});
//iterates through collections adds all that are in viewport and passes these collections to showlabelarr
//Smart Collections aren't compatible, so they are excluded
function initCheck(){
if (!isCollectionsPage()) {
return;
}
let $view = checkLayout();
let $items, $item, itemId, itemType, $colCell, $colrowCells;
let isSmart = false;
if ($view === "card"){
$items = $("coral-masonry-item");
}
else if ($view === "list"){
$items = $("tr");
}
$items.each(function(){
$item = $(this);
itemId = $item.attr("data-foundation-collection-item-id");
if ($view === "card" && !$item.hasClass("reschecked") && !smartCollections.includes(itemId)){
itemType = $item.find("coral-Card-context").text();
if(itemType.toLowerCase().includes("smart")){
isSmart = true;
smartCollections.push(itemId);
}
}
if ($view === "list" && !$item.hasClass("reschecked")){
$colrowCells = $item.children("td");
$colCell = $($colrowCells.get(4));
if ($colCell.text().includes("SMART")){
isSmart = true;
}
}
if (isImageInViewport($item) && !$item.hasClass("reschecked") && isSmart === false && !smartCollections.includes(itemId)){
$collections.push(itemId);
$item.addClass("reschecked");
isSmart = false;
}
isSmart = false;
});
if ($collections.length > 0){
showLabelArr($collections);
}
}
//performs ajax call to collections to check for restrictions and then adds banners/text to those that have restrictions
function showLabelArr(colarr){
let $view = checkLayout();
$.ajax({
method: "POST",
url: "/bin/my/check-restrictions",
data: {"item" : colarr}
})
.done(function(msg){
if (msg !== ""){
msg = msg.replace(",", "");
msg = msg.replace("]", "");
msg = msg.replace("[", "");
msg = msg.trimStart();
let rescollections = msg.split(" ");
for (let col of rescollections){
let colPath = col.replace(",", "");
if ($view === "card"){
let $collection = $("coral-masonry-item[data-granite-collection-item-id$='" + colPath + "']");
if ($collection.find("coral-alert").length < 1){
let text = "<coral-alert style='background-color: #ff7f7f' class='my-banner'>" +
"<coral-alert-content>Contains Restricted Assets</coral-alert-content>" +
"</coral-alert>";
$collection.find("coral-card-content").prepend(text);
}
}
if ($view === "list"){
let $rowCells = $("tr[data-granite-collection-item-id$='" + colPath + "']").children("td");
let $cell = $($rowCells.get(3));
if (!$cell.text().includes("Contains Assets with Restrictions")){
$cell.append(" - Contains Assets with Restrictions");
$cell.attr("style", "background-color: #ff7f7f");
}
}
}
}
});
$collections = [];
}
//checks if in collection page
function isCollectionsPage() {
let path = window.location.pathname;
path = decodeURIComponent(path);
let isColDetail = path.includes("collectiondetails")
let $overlay = $("coral-masonry[data-foundation-collection-id*='/mnt/overlay/cq/core/content/nav/tools/']");
return path.indexOf("/content/dam/collections") > 0 && !isColDetail && $overlay.length < 1;
}
//checks what layout is being used
function checkLayout(){
return $("body").attr("data-shell-collectionpage-view-layoutid");
}
//viewport check
function isImageInViewport(el) {
const rect = el[0].getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
//addlistener
function addListenerForImageVisibilityCheck(view){
if (view === "list"){
$(".coral-Table-wrapper-container").on('scroll', initCheck);
}
else {
$(".foundation-layout-panel-content").on('scroll', initCheck);
}
}
})(jQuery, jQuery(document));
With all of these complete, our asset labeling system is complete. If we need to add more restrictions, simply add new tags and update the config in the system console.
Below are a few resources used while creating this feature.
Tagging:
Labeling:
KBWEB Consult is an experienced Adobe Experience Cloud consulting firm, with expertise in AEM, Adobe Target, and related technologies, and a proven track record of implementations for multiple firms.
If you need to improve your marketing, personalize your communications to your customers, and automate your processes for better ROI, please contact us and request a free consultation.
One Comment on “AEM Assets Labeling System for Restricted Assets”
Pingback: Implement AEM Assets Properly to Ensure a Happy Holiday