AEM 61 - Touch UI Multiple Root Paths in Tags Picker

Goal


Support multiple root paths in Tags Picker of Touch UI. For Classic UI check this post

This is a picker extension only, searching inline still returns every tag available

For a similar Path Browser Picker extension check this post

Demo |  Package Install


Tags Picker (Product)



Tags Picker with Multiple Root Paths Configuration (Extension)





Tags Picker with Multiple Root Paths (Extension) eaemTagsRootPaths -  /etc/tags/geometrixx-outdoors/activity, /etc/tags/geometrixx-media/entertainment




Solution


1) Login to CRXDE Lite http://localhost:4502/crx/de, create folder /apps/touchui-tags-picker-custom-root-paths

2) Create folder /apps/touchui-tags-picker-custom-root-paths/tags-picker and file /apps/touchui-tags-picker-custom-root-paths/tags-picker/tags-picker.jsp, add the following code. This picker jsp extends ootb tags picker jsp /libs/cq/gui/components/common/tagspicker to set the data source url /apps/touchui-tags-picker-custom-root-paths/content/tag-column-wrapper.html (ootb its /libs/wcm/core/content/common/tagbrowser/tagbrowsercolumn.html set in /libs/cq/gui/components/common/tagspicker/render.jsp). Had the pickerSrc attribute been available as configuration param like rootPath, this step would have not been required...

<%@ page import="com.adobe.granite.ui.components.Config" %>
<%@ page import="org.apache.commons.lang3.StringUtils" %>
<%@ page import="org.apache.commons.lang3.ArrayUtils" %>
<%@include file="/libs/granite/ui/global.jsp" %>

<sling:include resourceType="/libs/cq/gui/components/common/tagspicker" />

<%
    Config cfg = cmp.getConfig();
    String[] eaemTagsPaths = cfg.get("eaemTagsRootPaths", String[].class);

    //tags paths not set, continue with ootb functionality
    if(ArrayUtils.isEmpty(eaemTagsPaths)){
        return;
    }
%>

<script type="text/javascript">
    (function(){
        var EAEM_TAGS_PATHS = "eaemtagsrootpaths",
            BROWSER_COLUMN_PATH = "/apps/touchui-tags-picker-custom-root-paths/content/tag-column-wrapper.html";

        function changeTagsPickerSrc(){
            var $eaemTagsPicker = $("[data-" + EAEM_TAGS_PATHS + "]");

            if($eaemTagsPicker.length == 0){
                return;
            }

            var browserCP = BROWSER_COLUMN_PATH + '<%=StringUtils.join(eaemTagsPaths, ",")%>';

            $eaemTagsPicker.attr("data-picker-src", browserCP);
        }

        changeTagsPickerSrc();
    }());
</script>

3) Create sling:Ordered folder /apps/touchui-tags-picker-custom-root-paths/content and nt:unstructured node /apps/touchui-tags-picker-custom-root-paths/content/tag-column-wrapper with sling:resourceType /apps/touchui-tags-picker-custom-root-paths/tag-browser-column

4) To add the picker browser column renderer extension, create folder /apps/touchui-tags-picker-custom-root-paths/tag-browser-column and file /apps/touchui-tags-picker-custom-root-paths/tag-browser-column/tag-browser-column.jsp with the following code. It includes /libs/wcm/core/content/common/tagbrowser/tagbrowsercolumn.html for getting the columns html and later removes unwanted root nodes

<%@ page import="org.apache.sling.api.request.RequestPathInfo" %>
<%@ page import="org.apache.sling.commons.json.JSONArray" %>
<%@include file="/libs/granite/ui/global.jsp" %>

<%!
    String TAG_BROWSER_COLUMN_PATH = "/libs/wcm/core/content/common/tagbrowser/tagbrowsercolumn.html";
    String TAG_NAV_MARKER = "eaemTagNavMarker";

    private String getParentTagPath(String tagPath) {
        return tagPath.substring(0, tagPath.lastIndexOf("/"));
    }

    private JSONArray getTagPathsJson(String[] tagPaths){
        JSONArray array = new JSONArray();

        for(String tagPath: tagPaths){
            array.put(tagPath);
        }

        return array;
    }
%>

<%
    RequestPathInfo pathInfo = slingRequest.getRequestPathInfo();
    String tagPaths[] = pathInfo.getSuffix().split(",");

    for(String tagPath: tagPaths){
        String includePath = TAG_BROWSER_COLUMN_PATH + getParentTagPath(tagPath);

%>
        <sling:include path="<%=includePath%>"  />
<%
    }
%>
<div id="<%=TAG_NAV_MARKER%>">
</div>

<script type="text/javascript">
    (function(){
        function removeAddnNavsGetColumn($navs){
            $navs.not(":first").remove(); //remove all additional navs
            return $navs.first().children(".coral-ColumnView-column-content").html("");//get the column of first nav
        }

        function addRootTags(){
            var $tagMarker = $("#<%=TAG_NAV_MARKER%>"),
                $navs = $tagMarker.prevAll("nav"),
                tagPaths = <%=getTagPathsJson(tagPaths)%>,
                rootTags = [];

            //find the root tags
            $.each(tagPaths, function(index, tagPath){
                rootTags.push($navs.find("[data-value='" + tagPath + "']"));
            });

            removeAddnNavsGetColumn($navs).append(rootTags);

            //remove the tag marker div
            $tagMarker.remove();
        }

        addRootTags();
    }());
</script>

5) In the component dialog add necessary configuration eaemTagsRootPaths and sling:resourceType /apps/touchui-tags-picker-custom-root-paths/tags-picker. For example, here is a sample configuration added on ootb page properties /libs/foundation/components/page/cq:dialog/content/items/tabs/items/basic/items/column/items/title/items/tags. This is for demonstration only, never touch /libs





10 comments:

  1. Thanks ! this was informative ! Can you suggest about how can we restrict same from inline search

    ReplyDelete
  2. Hi, This is working fine but one namespace is getting displayed twice. In jsp $navs.find("[data-value='/etc/tags']"), this is returning the size as 2.

    ReplyDelete
  3. Hi, how can this be modified to get the internationalized tag values for the selected language?

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Try to make such changes in tag-browser-column.jsp. Most likely, changes are needed elsewhere, but it works for me.
    <%
    RequestPathInfo pathInfo = slingRequest.getRequestPathInfo();
    String tagPaths[] = pathInfo.getSuffix().split(",");
    String includePath = TAG_BROWSER_COLUMN_PATH + getParentTagPath(tagPaths[0]);
    %>

    ReplyDelete
  6. In tag-browser-column.jsp
    by replacing the line
    rootTags.push($navs.find("[data-value='" + tagPath + "']"));
    with
    rootTags.push($navs.find("[data-value='" + tagPath + "']")[index]);
    resolved duplicate issue.

    ReplyDelete
  7. We are seeing that the order of the tags in the CRXDE have been changed when they are displayed below the tag field when the page properties are displayed. Then, when we click Save, that new order is written into the CRXDE. In thoughts on how we could keep that re-ordering from happening? I'm not seeing how the tags are read and displayed in the page properties dialog in the touch ui.

    ReplyDelete
  8. Thank you for the customization. It's really helpful. I have added this customization to the assets metadata and it works fine if you have one field, but if you have multiple properties in the same schema, it is displaying the same tags for all of them. I have fixed it by changing the code as below:
    Use the property-path instead of eaemtagsrootpaths to retrieve the required property to set the tags.
    Iterated through all the div attributes and set the data-picker-src where the name tag matches.

    Final Code changes are as below: (Removed the script tag as the comment did not allowed it)
    <%@ page import="com.adobe.granite.ui.components.Config" %>
    <%@ page import="org.apache.commons.lang3.StringUtils" %>
    <%@ page import="org.apache.commons.lang3.ArrayUtils" %>
    <%@include file="/libs/granite/ui/global.jsp" %>



    <%
    Config cfg = cmp.getConfig();
    String[] eaemTagsPaths = cfg.get("eaemTagsRootPaths", String[].class);
    String tagPropertyName = cfg.get("name", String.class);

    //tags paths not set, continue with ootb functionality
    if(ArrayUtils.isEmpty(eaemTagsPaths)){
    return;
    }
    %>


    (function(){
    var BROWSER_COLUMN_PATH = "/apps/tags-picker-custom-root-paths/content/tag-column-wrapper.html",
    propertyPath = "property-path";
    function changeTagsPickerSrc(){
    var browserCP1 = BROWSER_COLUMN_PATH + '<%=StringUtils.join(eaemTagsPaths, ",")%>';
    var eaemTagPropertyPath = $("[data-" + propertyPath + "]");

    eaemTagPropertyPath.each(function() {
    if(this.dataset.propertyPath == '<%=tagPropertyName%>') {
    this.dataset.pickerSrc = browserCP1;
    }
    });

    }
    changeTagsPickerSrc();
    }());





    One issue I am still not able to fix is having different level of root paths.

    Scenario1: (Works fine)
    /content/cq:tags/we-retail/activity
    /content/cq:tags/we-retail/gender
    Scenario2: (Does not work)
    /content/cq:tags/we-retail/activity
    /content/cq:tags/we-retail/simple/tagparent

    In Scenario one, both the namespaces displays fine, but in the second scenario, the activity tag children item displays but not the tagparent tags.
    Still trying to see if there is a possible option and can achieve it as they are different hierarchies. I guess I have to iterate through all the children for we-retail and hide them in the second/third level except the configured one to display both tagParent and activity tags.
    It is picking the root path for the first item configured in the list and ignoring the second one. If anyone have came across similar issue and fixed, could you please share the fix?

    ReplyDelete
    Replies
    1. For the different hierarchical paths, in tag-browser-column.jsp, reverted to the original file and removed the [index] which fixed the issue.
      Changed
      rootTags.push($navs.find("[data-value='" + tagPath + "']")[index]);
      back to
      rootTags.push($navs.find("[data-value='" + tagPath + "']"));

      Along with the above mentioned changes in tag-picker.jsp, now the code works for both multiple properties and different hierarchies.

      Delete