AEM 62 - Touch UI Show Total Assets Count and Size in List View, Card View

Goal


Add Asset Count, Size column to List view and Card view to show the count of assets and total size of a folder

Tested with AEM 63

For adding columns to Search Results check this post

For adding custom columns to Reports Console check this post

Demo | Package Install | Source Code | GitHub


Configuration & Column Visibility



List View



Card View



Solution


1) Create a servlet apps.experienceaem.assets.GetFolderSizeServlet for getting the assets count and size in folder with following code; sample call - http://localhost:4502/content/dam/experience-aem.size.json

package apps.experienceaem.assets;

import com.day.cq.dam.api.Asset;
import org.apache.felix.scr.annotations.sling.SlingServlet;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.commons.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.servlet.ServletException;
import java.util.Iterator;
import java.util.Map;

@SlingServlet(
        label = "Experience AEM - Folder Size Servlet",
        description = "Experience AEM - Servlet to return folder size",
        resourceTypes = { "sling:OrderedFolder", "sling:Folder" },
        selectors = { GetFolderSizeServlet.SEL_FOLDER_SIZE },
        methods = "GET")
public class GetFolderSizeServlet extends SlingAllMethodsServlet {
    private static final Logger log = LoggerFactory.getLogger(GetFolderSizeServlet.class);

    public static final String SEL_FOLDER_SIZE = "size";

    @Override
    protected void doGet(final SlingHttpServletRequest request, final SlingHttpServletResponse response)
            throws ServletException {
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");

        String folderPath = request.getRequestPathInfo().getResourcePath();

        try{
            String stmt = "SELECT * FROM [dam:Asset] WHERE ISDESCENDANTNODE('" + folderPath + "')";

            ResourceResolver resolver = request.getResourceResolver();
            QueryManager qm = getQueryManager(request);

            JSONObject folderJSON = getFolderMapJSON(resolver.getResource(folderPath));
            JSONObject sizeObj = null;

            Query query = qm.createQuery(stmt, Query.JCR_SQL2);

            NodeIterator results = query.execute().getNodes();
            Node node = null; Asset asset = null;

            Long totalSize = 0L, size;
            Integer totalAssets = 0, countedAssets = 0;

            String superParentPath;

            while (results.hasNext()) {
                node = results.nextNode();

                superParentPath = getSuperParentPath(folderPath, node);

                if(!folderJSON.has(superParentPath)){
                    continue;
                }

                sizeObj = (JSONObject) folderJSON.get(superParentPath);

                totalSize = (Long)sizeObj.get("size");
                totalAssets = (Integer)sizeObj.get("totalAssets");
                countedAssets = (Integer)sizeObj.get("countedAssets");

                asset = resolver.getResource(node.getPath()).adaptTo(Asset.class);

                if(asset.getMetadata("dam:size") instanceof Long){
                    size = (Long)asset.getMetadata("dam:size");
                    countedAssets++;
                    totalSize = totalSize + size;
                }

                totalAssets++;

                sizeObj.put("size", totalSize);
                sizeObj.put("totalAssets", totalAssets);
                sizeObj.put("countedAssets", countedAssets);
            }

            response.getWriter().print(folderJSON);
        }catch(Exception e){
            log.error("Error getting size for - " + folderPath, e);
        }
    }

    private String getSuperParentPath(String folderPath, Node node) throws Exception{
        String assetPath = node.getPath();

        if(node.getParent().getPath().equals(folderPath)){
            return folderPath;
        }

        return folderPath + "/" + assetPath.substring(folderPath.length() + 1, assetPath.indexOf("/", folderPath.length() + 1));
    }

    private JSONObject getFolderMapJSON(Resource folderResource) throws Exception{
        Iterator<Resource> childrenItr = folderResource.listChildren();
        Resource child = null;

        JSONObject folderJSON = new JSONObject();

        while(childrenItr.hasNext()){
            child = childrenItr.next();

            if(child.isResourceType("sling:OrderedFolder") || child.isResourceType("sling:Folder")){
                folderJSON.put(child.getPath(), getSizeObj());
            }
        }

        return folderJSON;
    }

    private JSONObject getSizeObj() throws Exception{
        JSONObject sizeObj = new JSONObject();

        Long totalSize = 0L, size;
        Integer totalAssets = 0, countedAssets = 0;

        sizeObj.put("totalAssets", totalAssets);
        sizeObj.put("countedAssets", countedAssets);
        sizeObj.put("size", totalSize);

        return sizeObj;
    }

    private QueryManager getQueryManager(SlingHttpServletRequest request)throws Exception {
        ResourceResolver resolver = request.getResourceResolver();

        Session session = resolver.adaptTo(Session.class);

        return session.getWorkspace().getQueryManager();
    }
}


2) To add the necessary columns (here eaemAssetsCount) overlay /libs/dam/gui/content/commons/availablecolumns and create /apps/dam/gui/content/commons/availablecolumns

3) Create nt:unstructured node /apps/dam/gui/content/commons/availablecolumns/eaemAssetsCount with the following properties

<?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">
    <eaemAssetsCount
        jcr:primaryType="nt:unstructured"
        jcr:title="Assets Count, Size"
        columnGroup="Experience AEM"
        default="{Boolean}false"
        sortable="{Boolean}true"
        sortType="number"/>
</jcr:root>

4) To add dynamic content (Asset count, size) to the column, create clientlib /apps/eaem-touchui-assets-folder-size/clientlib with JS logic and categories - dam.gui.admin.util, dependencies - underscore executed on view load

5) Create nt:file /apps/eaem-touchui-assets-folder-size/clientlib/js.txt with the following content

                        asset-count.js

6) Create nt:file /apps/eaem-touchui-assets-folder-size/clientlib/asset-count.js, add the following code

(function ($, $document) {
    "use strict";

    var firstLoad = true,
        COOKIE_AEM_ASSETS_LIST_VIEW_COLUMNS = "aem.assets.listview.columns",
        EAEM_ASSETS_COUNT = "eaemAssetsCount",
        LAYOUT_COL_VIEW = "column",
        LAYOUT_LIST_VIEW = "list",
        LAYOUT_CARD_VIEW = "card",
        DIRECTORY = "directory",
        FOUNDATION_CONTENT_LOADED = "foundation-contentloaded",
        SEL_DAM_ADMIN_CHILD_PAGES = ".cq-damadmin-admin-childpages",
        DIR_CELL_HTML =    '<td is="coral-td" class="coral-Table-cell coral-Table-cell--left" alignment="column">' +
                                '<coral-td-label class="coral-Table-cellLabel">Total Count: ASSET_COUNT, Size: ASSETS_SIZE</coral-td-label>' +
                            '</td>',
        LIST_CELL_HTML =    '<td is="coral-td" class="coral-Table-cell coral-Table-cell--left" alignment="column">' +
                                '<coral-td-label class="coral-Table-cellLabel"></coral-td-label>' +
                            '</td>';

    $document.on(FOUNDATION_CONTENT_LOADED, SEL_DAM_ADMIN_CHILD_PAGES, addAssetCount);

    $document.on("cui-contentloaded", function (e) {
        if(!firstLoad){
            return;
        }

        var $childPages = $(e.currentTarget).find(SEL_DAM_ADMIN_CHILD_PAGES);

        if(_.isEmpty($childPages)){
            return;
        }

        firstLoad = false;

        $childPages.trigger(FOUNDATION_CONTENT_LOADED);
    });

    function isFolderCountEnabled(){
        var cookies = document.cookie.split(";"), tokens, isEnabled = false;

        _.each(cookies, function(cookie){
            tokens = cookie.split("=");

            if(tokens[0].trim() !== COOKIE_AEM_ASSETS_LIST_VIEW_COLUMNS){
                return;
            }

            isEnabled = tokens[1].trim().indexOf(EAEM_ASSETS_COUNT) > 0;
        });

        return isEnabled;
    }

    function addAssetCount(e) {
        if(!e.currentTarget || !isFolderCountEnabled()){
            return;
        }

        var $currentTarget = $(e.currentTarget),
            foundationLayout = $currentTarget.data("foundation-layout");

        if(_.isEmpty(foundationLayout)){
            return;
        }

        var layoutId = foundationLayout.layoutId;

        if(layoutId == LAYOUT_COL_VIEW){
            return;
        }

        var path = $currentTarget.data("foundation-collection-id");

        $.ajax(path + ".size.json").done(function(data){
            var $dragHandles = $(".cq-damadmin-admin-childpages .coral-Icon--dragHandle");

            if(_.isEmpty($dragHandles) || ($dragHandles.closest("td").css("display") == "none")){
                //adjust UI removing drag handle column header
                $(".cq-damadmin-admin-childpages thead th:last").prev().remove();
            }

            $(".foundation-collection-item").each(function(index, item){
                itemHandler(data, layoutId, $(item) );
            });
        });

        function itemHandler(data, layoutId, $item){
            var itemPath = $item.data("foundation-collection-item-id"), isFolder;

            var sizeObj = data[itemPath], size = "0 MB",
                notExact, totalAssets, html;

            if(!_.isEmpty(sizeObj)){
                notExact = (sizeObj["totalAssets"] !== sizeObj["countedAssets"]);

                if(sizeObj["size"] !== "0"){
                    size = (notExact ? "~" : "") + ((parseFloat(sizeObj["size"]) / (1024 * 1024))).toFixed(2) + " MB";
                }
            }

            if(layoutId == LAYOUT_LIST_VIEW){
                isFolder = $item.data("item-type") == DIRECTORY;

                if(!isFolder){
                    html = LIST_CELL_HTML;
                }else{
                    html = DIR_CELL_HTML.replace("ASSET_COUNT", sizeObj["totalAssets"]).replace("ASSETS_SIZE", size);
                }

                $item.append(html);
            }else if(layoutId == LAYOUT_CARD_VIEW){
                var $itemMeta = $item.find(".foundation-collection-assets-meta"), $cardTitle;

                isFolder = $itemMeta.data("foundation-collection-meta-type") == DIRECTORY;

                if(!isFolder){
                    return;
                }

                $cardTitle =$item.find("coral-card-content > coral-card-title");
                $cardTitle.html($cardTitle.html() + " - Assets: " + sizeObj["totalAssets"] + ", " + size);
            }
        }

        function getStringAfterLastSlash(str){
            if(!str || (str.indexOf("/") == -1)){
                return "";
            }

            return str.substr(str.lastIndexOf("/") + 1);
        }
    }
})(jQuery, jQuery(document));

No comments:

Post a Comment