Goal
Give AEM admin the flexibility to add additional asset metadata columns in Reporting Console - http://localhost:4502/mnt/overlay/dam/gui/content/reports/reportspage.html
The custom column configuration is stored in cookies, so the configuration is per report and per user
For more information on Assets Reporting check documentation
Thank you ACS Commons for the Generic Lists
For adding columns to Search Results check this post
For adding custom columns to Assets List View check this post
Demo | Package Install | Github
Configure Columns
http://localhost:4502/etc/experience-aem/report-columns.html
Enable Columns
Columns in Report
Report Exported as CSV
Solution
1) Create the extension nt:folder /apps/eaem-asset-reports-dynamic-columns
2) Add the ACS Commons Generic List in nt:folder /apps/eaem-asset-reports-dynamic-columns/components, /apps/eaem-asset-reports-dynamic-columns/templates (available in Package Install if installing the extension)
3) Create sling:Folder /apps/eaem-asset-reports-dynamic-columns/dialog/columns and nt:unstructured /apps/eaem-asset-reports-dynamic-columns/dialog/columns/column with the following properties for creating column html
<?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" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" jcr:primaryType="nt:unstructured" sling:resourceType="cq/gui/components/siteadmin/admin/pages/headers/deflt" class="type small-col" modalResourceType="granite/ui/components/foundation/form/checkbox" name="./columns" sort-selector=".label .eaemCol" text="eaemCol" title="eaemCol"/>
4) Create nt:unstructured /apps/eaem-asset-reports-dynamic-columns/dialog/modal with following properties, for showing the column names in Configure Columns modal (set in property columnspath)
<?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" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" jcr:primaryType="nt:unstructured" sling:resourceType="dam/gui/coral/components/admin/reports/customcolumnmodal" columnspath="/apps/eaem-asset-reports-dynamic-columns/dialog/columns"/>
5) Create cq:ClientLibraryFolder /apps/eaem-asset-reports-dynamic-columns/clientlib with categories cq.dam.admin.reports.customcolumns.coral and dependencies underscore
6) Create nt:file /apps/eaem-asset-reports-dynamic-columns/clientlib/js.txt with the following content
dynamic-report-columns.js
7) Create nt:file /apps/eaem-asset-reports-dynamic-columns/clientlib/dynamic-report-columns.js with the following code
(function ($, $document) { var VIEW_SETTINGS_COLUMN_CONFIG = {}, FOUNDATION_CONTENT_LOADED = "foundation-contentloaded", REPORTS_FORM = "#customcolumnsForm", DAM_ADMIN_REPORTS_PAGE = ".cq-damadmin-admin-reportspage", METADATA_COL_MAPPING = "data-eaem-col-mapping", EXPORT_BUTTON = ".dam-admin-reports-export", QB = "/bin/querybuilder.json?", EXPORT_URL = "/apps/eaem-asset-reports-dynamic-columns/dialog/export.html", REPORT_RESULT_URL = "/mnt/overlay/dam/gui/content/reports/reportsresult.html", EAEM_COL = "eaemCol", FORM_FIELD_WRAPPER = ".coral-Form-fieldwrapper", COOKIE_REPORT_LIST_VIEW_COLUMNS = "eaem.report.listview.columns", REPORTS_CONFIGURE_COLUMNS_DIALOG = "reports-configure-columns-dialog", COLUMN_URL = "/apps/eaem-asset-reports-dynamic-columns/dialog/columns/column.html", COLUMNS_MODAL = "/apps/eaem-asset-reports-dynamic-columns/dialog/modal.html", DYNAMIC_COLS_CONFIG_URL = "/etc/experience-aem/report-columns/jcr:content/list.infinity.json", MAIN_COLUMN = "dam/gui/coral/components/admin/reports/columns/main", MAIN_COLUMN_WIDTH = 22, COLUMN_CACHE = [], COLUMN_WIDTH = "0%", CELL_DATA = [], searchUrl; if (typeof EAEM == "undefined") { EAEM = { REPORT : {} }; } EAEM.REPORT.storeEnabledColumns = storeEnabledColumns; loadColumnsConfiguration(); $document.on(FOUNDATION_CONTENT_LOADED, function(event){ _.defer(function(){ handleContentLoad(event); }); }); $document.on("submit", "form.foundation-form", function(e) { searchUrl = getSearchUrl(); }); function loadColumnsConfiguration() { if(!_.isEmpty(VIEW_SETTINGS_COLUMN_CONFIG)){ return; } $.ajax(DYNAMIC_COLS_CONFIG_URL).done(function(data){ if(_.isEmpty(data)){ return; } _.each(data, function(obj, key){ if(key.indexOf("item") !== 0){ return; } VIEW_SETTINGS_COLUMN_CONFIG[obj.value] = obj["jcr:title"]; }); }); } function handleContentLoad(event){ var target = event.target; if(isConfigureColumnsDialog(target)){ addDynamicColumnsInModal(); }else if(isReportsPage(event)){ handleReportsPage(); } } function handleReportsPage(){ var $reportsPage = $(DAM_ADMIN_REPORTS_PAGE); if(_.isEmpty($reportsPage)){ return; } var enabledColumns = getEnabledColumnsObj()[getReportPath()]; if(_.isEmpty(enabledColumns)){ return; } handleHeaders(); } function handleHeaders(){ var $reportsPage = $(DAM_ADMIN_REPORTS_PAGE), $hContainers = $reportsPage.find("header > .label"), $aContainers = $reportsPage.find("article"); if(customHeadersAdded($hContainers)){ handlePagination($aContainers); return; } handleExport(); var enabledColumns = getEnabledColumnsObj()[getReportPath()]; $.ajax(COLUMN_URL).done(function(colHtml) { _.each(enabledColumns, function(colMetaPath){ addColumnHeaders($hContainers, $aContainers, colHtml, colMetaPath); }); if(!searchUrl){ return; } $.get(searchUrl).done(function(data){ if(_.isEmpty(data.hits)){ return; } CELL_DATA = data.hits; addCellValues(); }); }); } function handlePagination($aContainers){ var $labelContainers = $aContainers.find(".label"), $lContainer; $labelContainers.each(function(index, aContainer){ $lContainer = $(aContainer); if(customHeadersAdded($lContainer)){ return; } _.each(COLUMN_CACHE, function(cellHtml){ $lContainer.append(cellHtml); }) }); fixCellWidths($labelContainers, COLUMN_WIDTH); addCellValues(); } function addColumnHeaders($hContainers, $aContainers, colHtml, colMetaPath){ var $columnHeader, $labelContainers = $aContainers.find(".label"), cellHtml; $columnHeader = $(colHtml).appendTo($hContainers); $columnHeader.attr(METADATA_COL_MAPPING, colMetaPath) .html(VIEW_SETTINGS_COLUMN_CONFIG[colMetaPath]); COLUMN_WIDTH = fixHeaderWidths($hContainers); if(_.isEmpty($labelContainers)){ return; } cellHtml = getCellHtml(colMetaPath, ""); $labelContainers.append(cellHtml); fixCellWidths($labelContainers, COLUMN_WIDTH); COLUMN_CACHE.push(cellHtml); } function addCellValues( ){ var $reportsPage = $(DAM_ADMIN_REPORTS_PAGE), $aContainers = $reportsPage.find("article"), $aParent = $aContainers.parent(), $article, $cell, enabledColumns = getEnabledColumnsObj()[getReportPath()]; _.each(CELL_DATA, function(hit){ $article = $aParent.find("article[data-path='" + hit["jcr:path"] + "']"); if(_.isEmpty($article)){ return; } _.each(enabledColumns, function(colMetaPath){ $cell = $article.find("[" + METADATA_COL_MAPPING + "='" + colMetaPath + "']"); $cell.html(nestedPluck(hit, colMetaPath)); }) }) } function fixCellWidths($lContainers, colWidth){ $lContainers.children("div").removeClass("small-col large-col") .css("width", MAIN_COLUMN_WIDTH + "%"); $lContainers.children("p").removeClass("small-col large-col") .css("width", colWidth).css("float", "left"); } function getCellHtml(colMapping, colText){ return "<p " + METADATA_COL_MAPPING + "='" + colMapping + "'>" + colText + "</p>"; } function fixHeaderWidths($hContainer){ var $hDivs = $hContainer.children("div"), $hDiv, colWidth = ((100 - MAIN_COLUMN_WIDTH) / ($hDivs.length - 1)) + "%"; $hDivs.each(function(index, hDiv){ $hDiv = $(hDiv); $hDiv.removeClass("small-col large-col"); if( $hDiv.data("itemresourcetype") === MAIN_COLUMN){ $hDiv.css("width", MAIN_COLUMN_WIDTH + "%"); return; } $hDiv.css("width", colWidth); }); return colWidth; } function customHeadersAdded($hContainers){ return !_.isEmpty($hContainers.find("[" + METADATA_COL_MAPPING + "]")); } function isReportsPage(event){ var target = event.target; if(!target){ return false; } var $target = (!target.$ ? $(target) : target.$); return (!_.isEmpty($target.find(DAM_ADMIN_REPORTS_PAGE)) || $target.hasClass(DAM_ADMIN_REPORTS_PAGE.substr(1))); } function isConfigureColumnsDialog(target){ if(!target || (target.tagName !== "CORAL-DIALOG")){ return false; } var $target = (!target.$ ? $(target) : target.$); return $target.hasClass(REPORTS_CONFIGURE_COLUMNS_DIALOG); } function addDynamicColumnsInModal(){ var url = COLUMNS_MODAL + getReportPath(); $.ajax(url).done(handler); function handler(html){ if(_.isEmpty(html)){ return; } var $html, $column, $input, $form = $(REPORTS_FORM), enabledColumns = getEnabledColumnsObj()[getReportPath()]; _.each(VIEW_SETTINGS_COLUMN_CONFIG, function(colTitle, colPath){ $html = $(html); $input = $html.find("input[title='" + EAEM_COL + "']") .attr(METADATA_COL_MAPPING, colPath) .val(""); if(contains(enabledColumns, colPath)){ $input.attr("checked", "checked"); } $input.attr("onchange", "EAEM.REPORT.storeEnabledColumns()"); $column = $input.closest(FORM_FIELD_WRAPPER); $column.find(".coral-Checkbox-description").html(colTitle); $form.append($column[0].outerHTML); }); styleDialog(); } } function handleExport(){ $document.off("click", EXPORT_BUTTON).fipo("tap", "click", EXPORT_BUTTON, handler); function handler(){ var currReportPath = getReportPath(); if(currReportPath.indexOf("default") !== -1) { return; } var $form = $(".dam-admin-reports").find("[report-path='" + currReportPath + "']").find('form'), $sliderrange = $form.find(".sliderrange"); setSliderValue($sliderrange); var url = Granite.HTTP.externalize(EXPORT_URL + currReportPath + "?" + $form.serialize()); var downloadURL = function (url) { $('<iframe>', { id: 'idown', src: url }).hide().appendTo('body'); }; downloadURL(url); } function setSliderValue($form){ var tickValues = $form.find(".coral-Slider").attr("data-tickvalues").split(","), lowerInd = $form.find(".coral-Slider.lower").attr('value'), upperInd = $form.find(".coral-Slider.upper").attr('value'), order = $form.find(".coral-Slider").attr("data-order"), tmp; if (order && order === "increasing") { if (lowerInd > upperInd) { tmp = lowerInd; lowerInd = upperInd; upperInd = tmp; } } else if (lowerInd < upperInd) { tmp = lowerInd; lowerInd = upperInd; upperInd = tmp; } $form.find(".coral-Slider.lower .lowervalue").val(tickValues[lowerInd]); $form.find(".coral-Slider.upper .uppervalue").val(tickValues[upperInd]); } } function getReportPath(){ return $('input[name=dam-asset-report]:checked').attr("report-path"); } function getEnabledColumnsObj(){ var cookieValue = getCookie(COOKIE_REPORT_LIST_VIEW_COLUMNS); if(!cookieValue){ cookieValue = {}; }else{ cookieValue = JSON.parse(decodeURIComponent(cookieValue)); } return cookieValue; } function storeEnabledColumns(){ var $input, columns = [], colMapping; $(REPORTS_FORM).find("input:checked").each(function(index, input){ $input = $(input); colMapping = $input.attr(METADATA_COL_MAPPING); if(_.isEmpty(colMapping)){ return; } columns.push(colMapping); }); var cookieObj = getEnabledColumnsObj(); cookieObj[getReportPath()] = columns; addCookie(COOKIE_REPORT_LIST_VIEW_COLUMNS, JSON.stringify(cookieObj)); } function getCookie(cookieName){ var cookieValue = ""; if(_.isEmpty(cookieName)){ return; } var cookies = document.cookie.split(";"), tokens; _.each(cookies, function(cookie){ tokens = cookie.split("="); if(tokens[0].trim() !== cookieName){ return; } cookieValue = tokens[1].trim(); }); return cookieValue; } function addCookie(cookieName, value){ if(_.isEmpty(cookieName)){ return; } $.cookie(cookieName, value, { expires: 365, path: "/" } ); } function contains(arrOrObj, key){ var doesIt = false; if(_.isEmpty(arrOrObj) || _.isEmpty(key)){ return doesIt; } if(_.isArray(arrOrObj)){ doesIt = (arrOrObj.indexOf(key) !== -1); } return doesIt; } function styleDialog(){ var $form = $(REPORTS_FORM); $form.css("max-height", "21.5rem").css("overflow-y", "auto"); } function getSearchUrl(){ var $form = $("form[action='" + REPORT_RESULT_URL + getReportPath() + "']"); return QB + $form.serialize() + "&p.nodedepth=2&p.hits=full"; } function nestedPluck(object, key) { if (!_.isObject(object) || _.isEmpty(object) || _.isEmpty(key)) { return []; } if (key.indexOf("/") === -1) { return object[key]; } var nestedKeys = _.reject(key.split("/"), function(token) { return token.trim() === ""; }), nestedObjectOrValue = object; _.each(nestedKeys, function(nKey) { if(_.isUndefined(nestedObjectOrValue)){ return; } if(_.isUndefined(nestedObjectOrValue[nKey])){ nestedObjectOrValue = undefined; return; } nestedObjectOrValue = nestedObjectOrValue[nKey]; }); return nestedObjectOrValue; } })(jQuery, jQuery(document));
8) For exporting the report with configured custom columns, create nt:unstructured /apps/eaem-asset-reports-dynamic-columns/dialog/export with property sling:resourceType set to /apps/eaem-asset-reports-dynamic-columns/export
9) Create /apps/eaem-asset-reports-dynamic-columns/export/export.jsp with the following code
<%@ page import="java.io.PrintWriter" %> <%@ page import="java.io.IOException" %> <%@ page import="java.io.StringWriter" %> <%@ page import="org.apache.commons.lang3.StringUtils" %> <%@ page import="org.apache.sling.api.SlingHttpServletRequest" %> <%@ page import="org.apache.sling.commons.json.JSONObject" %> <%@ page import="org.slf4j.Logger" %> <%@ page import="org.slf4j.LoggerFactory" %> <%@ page import="java.net.URLDecoder" %> <%@ page import="org.apache.sling.commons.json.JSONArray" %> <%@ page import="org.apache.sling.api.resource.ResourceResolver" %> <%@ page import="org.apache.sling.api.resource.Resource" %> <%@ page import="java.util.*" %> <%@ page import="org.apache.sling.api.resource.ValueMap" %> <%@ page import="javax.jcr.Session" %> <%@taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.2"%> <%@taglib prefix="cq" uri="http://www.day.com/taglibs/cq/1.0"%> <cq:defineObjects /> <%! final Logger log = LoggerFactory.getLogger(this.getClass()); final String EXPORT_JSP = "/libs/dam/gui/content/reports/export"; final String COLUMNS_CONFIG = "/etc/experience-aem/report-columns/jcr:content/list"; static String COOKIE_REPORT_LIST_VIEW_COLUMNS = "eaem.report.listview.columns"; static String PATH_HEADER = "\"PATH\""; %> <% HttpServletResponseWrapper responseWrapper = getWrapper(response); request.getRequestDispatcher(EXPORT_JSP).include(request, responseWrapper); String csvContent = responseWrapper.toString(); csvContent = addCustomColumns(slingRequest, csvContent); PrintWriter o = response.getWriter(); o.write(csvContent); o.flush(); %> <%! private String addCustomColumns(SlingHttpServletRequest sRequest, String csvContent){ Cookie cookie = sRequest.getCookie(COOKIE_REPORT_LIST_VIEW_COLUMNS); if(cookie == null){ return csvContent; } String enabledColumns = cookie.getValue(); if(StringUtils.isEmpty(enabledColumns)){ return csvContent; } String reportSelected = sRequest.getRequestPathInfo().getSuffix(); try{ JSONObject enabled = new JSONObject(URLDecoder.decode(enabledColumns, "UTF-8")); if(!enabled.has(reportSelected)){ return csvContent; } JSONArray reportCustomColumns = enabled.getJSONArray(reportSelected); String[] lines = csvContent.split("\r\n"); StringWriter customCsvContent = new StringWriter(); if(lines.length == 0){ return csvContent; } Map<String, String> colConfig = getCustomColumnsConfig(sRequest.getResourceResolver()); addHeaderLine(colConfig, reportCustomColumns, customCsvContent, lines[0]); addAssetLines(sRequest, reportCustomColumns, customCsvContent, lines); csvContent = customCsvContent.toString(); }catch(Exception e){ log.warn("Error adding columns", e); } return csvContent; } private void addAssetLines(SlingHttpServletRequest sRequest, JSONArray reportCustomColumns, StringWriter gieCsvContent, String[] csvLines) throws Exception { if(csvLines.length == 1){ return; } int pathIndex = -1; String[] headers = csvLines[0].split(","); for(int i = 0; i < headers.length; i++){ if(headers[i].equalsIgnoreCase(PATH_HEADER)){ pathIndex = i; } } if(pathIndex == -1){ return; } String line = null, assetPath = null, colMapPath = null, colMetaName; ValueMap assetMetadata = null; ResourceResolver resolver = sRequest.getResourceResolver(); Session session = resolver.adaptTo(Session.class); for(int l = 1; l < csvLines.length; l++){ line = csvLines[l]; try{ assetPath = line.split(",")[pathIndex]; assetPath = assetPath.replaceAll("\"", ""); assetPath = assetPath + "/jcr:content/metadata"; if(!session.nodeExists(assetPath)){ continue; } assetMetadata = resolver.getResource(assetPath).getValueMap(); for(int index = 0, len = reportCustomColumns.length(); index < len; index++){ colMapPath = reportCustomColumns.getString(index); colMetaName = colMapPath.substring(colMapPath.lastIndexOf("/") + 1); line = line + "," + assetMetadata.get(colMetaName, ""); } }catch(Exception e){ } gieCsvContent.append(line); gieCsvContent.append("\r\n"); } } private Map<String, String> getCustomColumnsConfig(ResourceResolver resolver){ Resource mlResource = resolver.getResource(COLUMNS_CONFIG); Map<String, String> colConfig = new HashMap<String, String>(); if(mlResource == null){ return colConfig; } try{ Iterator<Resource> managedList = mlResource.listChildren(); Resource resource; ValueMap vm; while (managedList.hasNext()) { resource = managedList.next(); vm = resource.getValueMap(); colConfig.put(vm.get("value", ""), vm.get("jcr:title", "")); } }catch(Exception e){ log.warn("Error reading managed list"); } return colConfig; } private void addHeaderLine(Map<String, String> colConfig, JSONArray customColumns, StringWriter customCsvContent, String headerLine) throws Exception{ String colMapPath = null; if(headerLine.indexOf("\"\"") != -1){ headerLine = headerLine.substring(0, headerLine.indexOf("\"\"") - 1); } for(int index = 0, len = customColumns.length(); index < len; index++){ colMapPath = customColumns.getString(index); headerLine = headerLine + ",\"" + colConfig.get(colMapPath) + "\""; } customCsvContent.append(headerLine).append("\r\n");; } private static HttpServletResponseWrapper getWrapper(HttpServletResponse response){ return new HttpServletResponseWrapper(response) { public final StringWriter sw = new StringWriter(); @Override public PrintWriter getWriter() throws IOException { return new PrintWriter(sw); } @Override public String toString() { return sw.toString(); } }; } %>
This comment has been removed by a blog administrator.
ReplyDelete