AEM 65 - Assets Admin Search Rail Predicate to check if Metadata Property exists

Goal


Add a new predicate to Assets Admin Search rail to filter assets based on the existence of metadata

Requirement could be to find all assets with specific metadata missing. Here a filter (predicate) is added to filter assets containing / not containing metadata property (configurable) jcr:content/metadata/dc:title

For 6430 add the workaround discussed at the end of this article...

To check for missing renditions or nodes check this post

Demo | Package Install | Github


Search Filter



Search form Configuration




Solution


1) Login to CRXDe Lite and create the metadata exists predicate configuration metadataexists  in /apps/settings/dam/search/facets/formbuilderconfig/predicatetypes/items

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root 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">
    <adminui/>
    <search jcr:primaryType="nt:unstructured">
        <facets jcr:primaryType="nt:unstructured">
            <formbuilderconfig jcr:primaryType="nt:unstructured">
                <predicatetypes jcr:primaryType="nt:unstructured">
                    <items jcr:primaryType="nt:unstructured">
                        <metadataexists
                            jcr:primaryType="nt:unstructured"
                            fieldLabel="Metadata Property"
                            fieldPropResourceType="/apps/eaem-search-form-metadata-exists-predicate/metadata-exists-field"
                            fieldResourceType="/apps/eaem-search-form-metadata-exists-predicate/metadata-exists-predicate"
                            fieldTitle="Experience AEM Metadata Exists Predicate"
                            fieldViewResourceType="granite/ui/components/foundation/form/formbuilder/checkbox"
                            renderType="checkbox"/>
                    </items>
                </predicatetypes>
            </formbuilderconfig>
        </facets>
    </search>
</jcr:root>


2) Create the configuration page for predicate (fieldPropResourceType in step 1) /apps/eaem-search-form-metadata-exists-predicate/metadata-exists-field/metadata-exists-field.jsp with the following code (metadataexists is for identifying the config page to render under /mnt/overlay/settings/dam/search/facets/formbuilderconfig/predicatetypes/items )

<%@include file="/libs/granite/ui/global.jsp" %>
<%@ page session="false" contentType="text/html" pageEncoding="utf-8"
         import="com.adobe.granite.ui.components.Config" %>

<%
    String key = resource.getName();
    Config cfg = new Config(resource);
    String metaType = "metadataexists";

    String listOrder = cfg.get("listOrder", String.class);
    listOrder = (listOrder == null) ? "" : listOrder;

    String fieldLabel = i18n.get("Metadata Exists Predicate");
%>

<input type="hidden" name="<%= xssAPI.encodeForHTMLAttr("./items/" + key) %>">
<input type="hidden" name="<%= xssAPI.encodeForHTMLAttr("./items/" + key + "/jcr:primaryType") %>" value="nt:unstructured">
<input type="hidden" name="<%= xssAPI.encodeForHTMLAttr("./items/" + key + "/sling:resourceType") %>" value="/apps/eaem-search-form-metadata-exists-predicate/metadata-exists-predicate">
<input type="hidden" name="<%= xssAPI.encodeForHTMLAttr("./items/" + key + "/fieldLabel") %>" value="<%= fieldLabel %>">
<input type="hidden" name="<%= xssAPI.encodeForHTMLAttr("./items/" + key + "/metaType") %>" value="<%= metaType %>">
<input type="hidden" name="<%= xssAPI.encodeForHTMLAttr("./items/" + key + "/listOrder@Delete") %>">
<input type="hidden" name="<%= xssAPI.encodeForHTMLAttr("./items/" + key + "/listOrder@TypeHint") %>" value="String">
<input type="hidden" class="listOrder" name="<%= xssAPI.encodeForHTMLAttr("./items/" + key + "/listOrder") %>" value="<%= xssAPI.encodeForHTMLAttr(listOrder) %>">

<div><h3><%= i18n.get("Metadata Exists Predicate")%></h3></div>

<% request.setAttribute ("com.adobe.cq.datasource.fieldtextplaceholder", i18n.get("Metadata Property"));%>

<sling:include resource="<%= resource %>" resourceType="dam/gui/coral/components/admin/customsearch/formbuilder/predicatefieldproperties/fieldlabelpropertyfield"/>

<sling:include resource="<%= resource %>" resourceType="dam/gui/coral/components/admin/customsearch/formbuilder/predicatefieldproperties/maptopropertyfield"/>

<sling:include resource="<%= resource %>" resourceType="granite/ui/components/foundation/form/formbuilder/formfieldproperties/titlefields"/>

<sling:include resource="<%= resource %>" resourceType="granite/ui/components/foundation/form/formbuilder/formfieldproperties/deletefields"/>

3) Create the predicate widget render html (fieldResourceType in step 1) /apps/eaem-search-form-metadata-exists-predicate/metadata-exists-predicate/metadata-exists-predicate.jsp with following code (it includes clientlib eaem.dam.admin.metadataexists)

<%@include file="/libs/granite/ui/global.jsp" %>
<%@ page session="false" contentType="text/html" pageEncoding="utf-8"
           import="com.adobe.granite.ui.components.Config"%>

<%
    Config cfg = new Config(resource);
    String metaPropName = cfg.get("name", "");

    long predicateIndex = cfg.get("listOrder", 5000L);

    String indexGroup = predicateIndex + "_group";
    String predicateName = indexGroup + ".property";
    String propertyOperation = predicateName + ".operation";

    boolean foldableOpen = cfg.get("open", true);
    String selected = foldableOpen ? "selected":"";
%>

<ui:includeClientLib categories="eaem.dam.admin.metadataexists" />

<coral-accordion variant="large">
    <coral-accordion-item "<%=selected%>" data-metaType="checkboxgroup" data-type="metadataexists">
        <coral-accordion-item-label><%= xssAPI.encodeForHTML(cfg.get("text", i18n.get("Property"))) %></coral-accordion-item-label>
        <coral-accordion-item-content class="coral-Form coral-Form--vertical" id="<%= xssAPI.encodeForHTMLAttr(resource.getPath()) %>">
            <input type="hidden" name="<%=predicateName%>" value="<%= xssAPI.encodeForHTMLAttr(metaPropName) %>">
            <coral-checkbox class="coral-Form-field eaem-metadata-exists-predicate" name="<%=propertyOperation%>" value="not">
                Not Exists
            </coral-checkbox>
            <coral-checkbox class="coral-Form-field eaem-metadata-exists-predicate" name="<%=propertyOperation%>" value="exists">
                Exists
            </coral-checkbox>
        </coral-accordion-item-content>
    </coral-accordion-item>
</coral-accordion>

4) Create node /apps/eaem-search-form-metadata-exists-predicate/metadata-exists-predicate/clientlib of type cq:ClientLibraryFolder, add String property categories with value eaem.dam.admin.metadataexists, String[] property dependencies with value lodash

6) Create file (nt:file) /apps/eaem-search-form-metadata-exists-predicate/metadata-exists-predicate/clientlib/js.txt, add

                        metadata-exists.js

7) Create file (nt:file) /apps/eaem-search-form-metadata-exists-predicate/metadata-exists-predicate/clientlib/metadata-exists.js, add the following code

(function($, $document) {
    var EAEM_METADATA_EXISTS_PREDICATE = ".eaem-metadata-exists-predicate";

    $document.on("change", EAEM_METADATA_EXISTS_PREDICATE, function(event) {
        $(EAEM_METADATA_EXISTS_PREDICATE).removeAttr("checked");
        event.currentTarget.checked = true;

        var $form = $(this).closest(".granite-omnisearch-form");
        $form.submit();
    })

})(jQuery, jQuery(document));


6430 Solution


1) In addition to the above package install, add the following filter

package com.experienceaem.assets;

import com.adobe.granite.ui.components.ds.AbstractDataSource;
import com.adobe.granite.ui.components.ds.DataSource;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceWrapper;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;

import javax.servlet.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

@Component(
        service = Filter.class,
        immediate = true,
        name = "Experience AEM Assets Search Facets Filter",
        property = {
                Constants.SERVICE_RANKING + ":Integer=-99",
                "sling.filter.scope=COMPONENT",
                "sling.filter.resourceTypes=cq/gui/components/common/admin/customsearch/facetconfiguration/facetconfigdatasource"
        }
)
public class SearchFacetsFilter implements Filter {
    public static String DATA_SOURCE_NAME = DataSource.class.getName();
    public static String ASSETS_SEARCH_FACETS_PATH = "/settings/dam/search/facets/assets";
    public static String LIBS_ASSETS_SEARCH_FACETS_RT = "/libs/cq/gui/components/common/admin/customsearch/facetconfiguration/childfacetconfig";

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        SlingHttpServletRequest sfrWrapper = (SlingHttpServletRequest)request;

        chain.doFilter(sfrWrapper, response);

        ResourceResolver resolver = sfrWrapper.getResourceResolver();

        Resource assetsFacetsResource = resolver.getResource("/libs" + ASSETS_SEARCH_FACETS_PATH);

        if(assetsFacetsResource == null){
            return;
        }

        DataSource ds = (DataSource)sfrWrapper.getAttribute(DATA_SOURCE_NAME);

        if(ds == null){
            return;
        }

        final List<Resource> resultList = new ArrayList<Resource>();
        Iterator<Resource> items = ds.iterator();

        ResourceWrapper assetsFacetsResourceWrapper = new ResourceWrapper(assetsFacetsResource) {
            public String getResourceType() {
                return LIBS_ASSETS_SEARCH_FACETS_RT;
            }
        };

        Resource resource = null;
        boolean exists = false;

        while(items.hasNext()){
            resource = items.next();

            if(resource.getPath().endsWith(ASSETS_SEARCH_FACETS_PATH)){
                exists = true;
            }

            resultList.add(resource);
        }

        if(!exists && !resultList.isEmpty()){
            resultList.add(assetsFacetsResourceWrapper);
        }

        ds = new AbstractDataSource() {
            public Iterator<Resource> iterator() {
                return resultList.iterator();
            }
        };

        sfrWrapper.setAttribute(DATA_SOURCE_NAME, ds);
    }

    @Override
    public void destroy() {
    }
}

No comments:

Post a Comment