Goal
AEM Cloud Version : 2021.2.4944.20210221T230729Z-210225 (Feb 21, 2021)
This post details steps for adding a Custom Metadata Field in Asset Metadata Editor. If an asset is used in external sites, the steps below explain showing asset usage in the external site pages...
Demo | Package Install | Github
External Usage in Metadata Editor
Curl command to post Usage
Stored in CRX
Metadata Schema field
Solution
1) Add a service user eaem-service-user in repo init script ui.config\src\main\content\jcr_root\apps\eaem-custom-metadata-asset-usage\osgiconfig\config.author\org.apache.sling.jcr.repoinit.RepositoryInitializer-eaem.config
scripts=[ " create service user eaem-service-user with path system/cq:services/experience-aem set principal ACL for eaem-service-user allow jcr:all on /conf end # below registers a namespace with the prefix 'eaem' and the uri 'http://experience-aem/aem'. register namespace ( eaem ) http://experience-aem/aem " ]
2) Provide the service user to bundle mapping in ui.config\src\main\content\jcr_root\apps\eaem-custom-metadata-asset-usage\osgiconfig\config.author\org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-ea.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:OsgiConfig" user.mapping="[eaem-custom-metadata-asset-usage.core:eaem-service-user=[eaem-service-user]]"/>
3) Add the External Asset Usage metadata render script /apps/eaem-custom-metadata-asset-usage/components/asset-external-references/asset-external-references.jsp
<%@ page import="com.adobe.granite.ui.components.Config" %> <%@ page import="org.apache.sling.api.SlingHttpServletRequest" %> <%@ page import="org.apache.sling.api.resource.ResourceResolver" %> <%@ page import="java.util.Iterator" %> <%@ page import="java.util.Date" %> <%@include file="/libs/granite/ui/global.jsp" %> <%@page session="false"%> <% final String META_ASSET_USAGE_PATH = "jcr:content/metadata/asset-usage"; final String EAEM_PUBLISH_PAGE = "eaem:external-page"; Config cfg = new Config(resource); String fieldLabel = cfg.get("fieldLabel", String.class); String contentPath = (String)request.getAttribute("granite.ui.form.contentpath"); ResourceResolver resolver = slingRequest.getResourceResolver(); Resource eaemResource = resolver.getResource(contentPath); if(eaemResource == null){ return; } Resource metadataRes = eaemResource.getChild(META_ASSET_USAGE_PATH); Iterator<Resource> usagesItr = ((metadataRes != null) ? metadataRes.getChildren().iterator() : null); %> <div> <label class="coral-Form-fieldlabel"> <h3><%= outVar(xssAPI, i18n, fieldLabel) %></h3> </label> <div style="margin-bottom: 10px"> <% if( (usagesItr == null) || !usagesItr.hasNext()){ %> <div>None</div> <% }else{ while(usagesItr.hasNext()){ Resource usageRes = usagesItr.next(); ValueMap usageResVM = usageRes.getValueMap(); %> <div> <%= usageResVM.get(EAEM_PUBLISH_PAGE) %> </div> <% } } %> </div> </div>
4) Create a schema listener apps.experienceaem.assets.core.listeners.MetadataSchemaListener to update the External Asset Usage field resource type when schema is modified for other purposes. Since there is no custom field create support in schema form builder, we use an existing field eg. Asset Referenced by (dam/gui/coral/components/admin/references) and change it to the custom resource type /apps/eaem-custom-metadata-asset-usage/components/asset-external-references on save using a Osgi Event Listener
package apps.experienceaem.assets.core.listeners; import com.day.cq.search.PredicateGroup; import com.day.cq.search.Query; import com.day.cq.search.QueryBuilder; import com.day.cq.search.result.Hit; import com.day.cq.search.result.SearchResult; import org.apache.commons.lang.StringUtils; import org.apache.sling.api.SlingConstants; import org.apache.sling.api.resource.*; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.service.event.Event; import org.osgi.service.event.EventConstants; import org.osgi.service.event.EventHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jcr.RepositoryException; import javax.jcr.Session; import java.util.HashMap; import java.util.Map; @Component(service = EventHandler.class, immediate = true, property = { EventConstants.EVENT_TOPIC + "=" + "org/apache/sling/api/resource/Resource/ADDED", EventConstants.EVENT_TOPIC + "=" + "org/apache/sling/api/resource/Resource/CHANGED", EventConstants.EVENT_TOPIC + "=" + "org/apache/sling/api/resource/Resource/REMOVED", EventConstants.EVENT_FILTER + "=" + "(path=/conf/global/settings/dam/adminui-extension/metadataschema/experience-aem/*)" } ) public class MetadataSchemaListener implements EventHandler { private final Logger logger = LoggerFactory.getLogger(getClass()); private static final String SDL_REFERENCES_RES_TYPE = "/apps/eaem-custom-metadata-asset-usage/components/asset-external-references"; private static final String ASSET_USAGE_TITLE = "External Asset Usage"; private static final String EAEM_SERVICE_USER = "eaem-service-user"; private static final String REFERENCES_RES_TYPE = "dam/gui/coral/components/admin/references"; @Reference private ResourceResolverFactory factory; @Reference private QueryBuilder builder; /** * Event handler * * @param event */ public void handleEvent(final Event event) { logger.debug("Resource event: {} at: {}", event.getTopic(), event.getProperty(SlingConstants.PROPERTY_PATH)); ResourceResolver resourceResolver = getServiceResourceResolver(factory); try { Query query = builder.createQuery(PredicateGroup.create(getQueryPredicateMap()), resourceResolver.adaptTo(Session.class)); SearchResult result = query.getResult(); ValueMap resVM = null; for (Hit hit : result.getHits()) { Resource res = resourceResolver.getResource(hit.getPath()); if(res == null){ continue; } resVM = res.getValueMap(); if(REFERENCES_RES_TYPE.equals(resVM.get("resourceType", String.class))){ updateReferencesResourceType(res); } } if (resourceResolver.hasChanges()) { resourceResolver.commit(); } } catch (RepositoryException | PersistenceException e) { logger.error("Exception occured at handleEvent() , reason {}", e.getMessage(), e); } } private void updateReferencesResourceType(Resource referencesRes) { ModifiableValueMap mvm = referencesRes.adaptTo(ModifiableValueMap.class); if(ASSET_USAGE_TITLE.equals(mvm.get("fieldLabel", String.class))){ mvm.put("resourceType", SDL_REFERENCES_RES_TYPE); } } /** * Return the query predicate map * * @return */ private Map<String, String> getQueryPredicateMap() { Map<String, String> map = new HashMap<>(); map.put("path", "/conf/global/settings/dam/adminui-extension/metadataschema/experience-aem"); map.put("property", "resourceType"); map.put("property.1_value", "dam/gui/coral/components/admin/references"); return map; } public ResourceResolver getServiceResourceResolver(ResourceResolverFactory resourceResolverFactory) { Map<String, Object> subServiceUser = new HashMap<>(); subServiceUser.put(ResourceResolverFactory.SUBSERVICE, EAEM_SERVICE_USER); try { return resourceResolverFactory.getServiceResourceResolver(subServiceUser); } catch (LoginException ex) { logger.error("Could not login as SubService user {}, exiting SearchService service.", EAEM_SERVICE_USER, ex); return null; } } }
5) If the listener is not executing it might have been denied access, may be because its taking too long to finish, error log logs following warning. In such cases increasing the time out in org.apache.felix.eventadmin.Timeout might help....
*WARN* [FelixLogListener] org.apache.felix.eventadmin EventAdmin: Denying event handler from ServiceReference [[org.osgi.service.event.EventHandler] | Bundle(eaem-custom-metadata-asset-usage.core [551])] due to timeout!
No comments:
Post a Comment