AEM 61 - Touch UI Limit the Number of Components Added in Parsys

Goal


Limit the number of Components added in a Parsys of Touch UI

For Classic UI check this post

Demo | Package Install


Bug Fixes

Applying limit to copy / paste of components - Demo | Package Install


Set the Limit - Design Mode



Stored in CRX



Limit Exceeded Error on Component Drop or Insert - Edit Mode



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/touchui-limit-parsys

2) Create clientlib (type cq:ClientLibraryFolder/apps/touchui-limit-parsys/clientlib and set property categories of String[] type to cq.authoring.dialog, cq.compat.authoring.widgets and dependencies to underscore

3) Create file ( type nt:file ) /apps/touchui-limit-parsys/clientlib/js.txt, add the following

                         limit-parsys.js

4) Create file ( type nt:file ) /apps/touchui-limit-parsys/clientlib/limit-parsys.js, add the following code

// for touchui design mode
(function(){
    var pathName = window.location.pathname,
        EAEM_COMPONENT_LIMIT = "eaemComponentLimit";

    if( !pathName.endsWith("dialogwrapper.html") ){
        return;
    }

    CQ.Ext.onReady(function () {
        findDesignDialogWindow();
    });

    function findDesignDialogWindow(){
        var wMgr = CQ.Ext.WindowMgr, winId;

        var W_INTERVAL = setInterval(function () {
            wMgr.each(function (win) {
                if(!win || !win.id){
                    return;
                }

                clearInterval(W_INTERVAL);

                addLimitTextField(win);
            });
        }, 250);
    }

    function addLimitTextField(win){
        var compSelector = win.findByType("componentselector");

        if(compSelector.length == 0){
            return;
        }

        compSelector = compSelector[0];

        var dialog = compSelector.findParentByType("dialog");

        $.ajax( dialog.path + ".2.json" ).done(handler);

        function handler(data){
            var limitField = new CQ.Ext.form.TextField({
                value: data[EAEM_COMPONENT_LIMIT] || "",
                fieldLabel: "Limit Components to ",
                name: "./" + EAEM_COMPONENT_LIMIT,
                style: {
                    marginBottom: '10px'
                }
            });

            compSelector.ownerCt.insert(2, limitField);

            compSelector.ownerCt.doLayout();
        }
    }
}());

// for touchui edit mode
(function ($document, gAuthor) {
    var pathName = window.location.pathname;

    if( pathName.endsWith("dialogwrapper.html") ){
        return;
    }

    var EAEM_COMPONENT_LIMIT = "eaemComponentLimit";

    $(extendComponentDrop);

    function getDesignPath(editable){
        var parsys = editable.getParent(),
            designSrc = parsys.config.designDialogSrc,
            result = {}, param;

        designSrc = designSrc.substring(designSrc.indexOf("?") + 1);

        designSrc.split(/&/).forEach( function(it) {
            if (_.isEmpty(it)) {
                return;
            }
            param = it.split("=");
            result[param[0]] = param[1];
        });

        return decodeURIComponent(result["content"]);
    }

    function extendComponentDrop(){
        var dropController = gAuthor.ui.dropController,
            compDragDrop = dropController.get(gAuthor.Component.prototype.getTypeName());

        //handle drop action
        compDragDrop.handleDrop = function(dropFn){
            return function (event) {
                if(showError(event.currentDropTarget.targetEditable)){
                    return;
                }

                return dropFn.call(this, event);
            };
        }(compDragDrop.handleDrop);

        //handle insert action
        gAuthor.edit.actions.openInsertDialog = function(openDlgFn){
            return function (editable) {
                if(showError(editable)){
                    return;
                }

                return openDlgFn.call(this, editable);
            }
        }(gAuthor.edit.actions.openInsertDialog);

        //handle paste action
        var insertAction = gAuthor.edit.Toolbar.defaultActions["INSERT"];

        insertAction.handler = function(insertHandlerFn){
            return function(editableBefore, param, target){
                if(showError(editableBefore)){
                    return;
                }

                return insertHandlerFn.call(this, editableBefore, param, target)
            }
        }(insertAction.handler);

        function showError(editable){
            var limit = isWithinLimit(editable);

            if(!limit.isWithin){
                showErrorAlert("Limit exceeded, allowed - " + limit.currentLimit);
                return true;
            }

            return false;
        }
    }

    function getChildEditables(parsys){
        var editables = gAuthor.edit.findEditables(),
            children = [], parent;

        _.each(editables, function(editable){
            parent = editable.getParent();

            if(parent && (parent.path === parsys.path)){
                children.push(editable);
            }
        });

        return children;
    }

    function showErrorAlert(message, title){
        var fui = $(window).adaptTo("foundation-ui"),
            options = [{
                text: "OK",
                warning: true
            }];

        message = message || "Unknown Error";
        title = title || "Error";

        fui.prompt(title, message, "error", options);
    }

    function isWithinLimit(editable){
        var path = getDesignPath(editable),
            children = getChildEditables(editable.getParent()),
            isWithin = true, currentLimit = "";

        $.ajax( { url: path + ".2.json", async: false } ).done(function(data){
            if(_.isEmpty(data) || !data[EAEM_COMPONENT_LIMIT]){
                return;
            }

            currentLimit = data[EAEM_COMPONENT_LIMIT];

            var limit = parseInt(data[EAEM_COMPONENT_LIMIT]);

            isWithin = children.length <= limit;
        });

        return {
            isWithin: isWithin,
            currentLimit: currentLimit
        };
    }
})($(document), Granite.author);


3 comments:

  1. Hello,

    This is breaking in AEM 6.3.
    could you please help us with it.

    Thank you.
    saurabh

    ReplyDelete
    Replies
    1. One good option is to use ACS Commons and the property acsComponentsLimit. More info here: https://github.com/Adobe-Consulting-Services/acs-aem-commons/pull/639

      Delete
  2. Hello,

    This is breaking in AEM 6.5. We are using editable templates where we have responsive grid as reourceType
    Can you please help with suggestion?

    Thanks.

    ReplyDelete