AEM 61 - This And That



AEM_61_RECOMPILE_JSPS

1)  In AEM <= 6.0 the compiled classes are placed in CRX /var/classes - http://localhost:4502/crx/de/index.jsp#/var/classes. To force recompile any jsp files, deleting the node in /var/classes helped; with AEM 61, the compiled class files are NOT placed in /var/classes any more, so to recompile jsps, use Felix Console - http://localhost:4502/system/console/slingjsp or to be 100% sure to the naked eye, follow these steps

             a. Stop bundle org.apache.sling.commons.fsclassloader
             b. Search with keyword classes in CQ install folder <author>\crx-quickstart\launchpad\felix
             c. Delete generated java/class files from <author>\crx-quickstart\launchpad\felix\<bundleXYZ>\data\classes
             d. Delete generated java files from /var/classes in CRX (sightly generated java files are placed here)
             e. Restart bundle org.apache.sling.commons.fsclassloader




AEM_61_OSGI_INSTALL_ACTION

2)  OSGI action when updating packages in AEM. Packages with SNAPSHOT in name have special meaning; SNAPSHOT packages are only for Dev environment. Thanks to Ian Boston for the tip

Current VersionNew VersionAction
1.0.221.0.23Install
1.0.221.0.23-SNAPSHOTInstall
1.0.231.0.23-SNAPSHOTIgnore
1.0.23-SNAPSHOT1.0.23-SNAPSHOTInstall
1.0.23-SNAPSHOT1.0.23-r231423423Install
1.0.23-r2314234231.0.23-r231423423Ignore
1.0.23-r2314234231.0.23-r231423424Install (last digit 4 > 3)
1.0.23-SNAPSHOT1.0.23-T1r231423423Install
1.0.23-T1r2314234231.0.23-T2r231423423Install (Patch revision number - T2r > T1r)




AEM_61_ADD_WORKFLOW_ADMINISTRATORS

3)  By default, only administrators can view all workflow instancesto add users/groups for administering workflows (view, terminate, monitor workflows created by other users), give necessary read/write permissions on /etc/workflow/instances and add user/group to Superuser of Adobe Granite Workflow Service - http://localhost:4502/system/console/configMgr/com.adobe.granite.workflow.core.WorkflowSessionFactory







4)  By default, when html tags like hyperlink from an external website are copied into Rich Text Editor (not source edit mode) all attributes are NOT copied (so you may only see href copied and not others ). To copy other attributes like say class, role etc. htmlPasteRules have to be defined in the edit plugin of RTE. For more options check the documentation



To copy css classes, make sure cssMode is defined






5)  To know the hotfixes and feature packs installed on AEM 

curl -u admin -s http://localhost:4502/crx/packmgr/list.jsp | jshon -e results -a -e downloadName -u | grep -E "hotfix|feature"

Sample output:
                     cq-6.1.0-apps-featurepack-1.0.10.zip
                     cq-6.1.0-apps-featurepack-2.0.4.zip
                     cq-6.1.0-featurepack-6563-1.0.0.zip
                     cq-6.1.0-hotfix-6445-1.0.zip
                     cq-6.1.0-hotfix-6449-1.2.zip
                     cq-6.1.0-hotfix-6500-1.0.zip
                     cq-6.1.0-hotfix-6500-1.5.zip
                     cq-6.1.0-hotfix-6570-1.2.zip
                     cq-6.1.0-hotfix-6680-1.0.zip
                     cq-6.1.0-hotfix-6680-1.2.zip
                     cq-6.1.0-hotfix-7285-1.0.zip
                     cq-6.1.0-hotfix-7432-1.0.2.zip
                     cq-6.1.0-hotfix-6446-1.0.zip





6)  In order to reduce time spent on offline compaction, make sure the customer has latest Oak version and also they use memory mapped file access option with compaction command.  Since memory mapped file access would use off-heap memory, it would benefit to have high memory on the server - thanks Can Altuner for the tip

java -Dtar.memoryMapped=true -Dupdate.limit=5000000 -Dcompaction-progress-log=1500000 
     -Dcompress-interval=10000000 -Doffline-compaction=true -Xmx8G -jar oak-run-1.2.7.jar 
     compact /crx-quickstart/repository/segmentstore





7)  To validate user's password during reset or while creation, use password validation action of Authorizable Action Provider - http://localhost:4502/system/console/configMgr/org.apache.jackrabbit.oak.spi.security.user.action.DefaultAuthorizableActionProvider

For detailed error description on Touch UI - check this post, Classic UI - check this post

Configure



TouchUI Error



ClassicUI Error








8)  When a 300 Multiple choices status code is returned by json renderer of GET servlet, try changing your application logic or increase the JSON Max Results of Apache Sling GET Servlet - http://localhost:4502/system/console/configMgr/org.apache.sling.servlets.get.DefaultGetServlet

Demo






9)  CRXDE Lite Tools -> Query provides interface for running search queries; if a query is running for long time (could be because of missing index) and consuming system resources, the following Traversing Cursor warnings are logged in error.log

29.02.2016 11:48:22.174 *WARN* [0:0:0:0:0:0:0:1 [1456768102069] GET /bin/querybuilder.json HTTP/1.1] org.apache.jackrabbit.oak.spi.query.Cursors$TraversingCursor Traversed 12000 nodes with filter Filter(query=select [jcr:path], [jcr:score], * from [nt:base] as a where [modelId] = '/etc/workflow/models/request_for_activation/jcr:content/model' and [data/payload/path] = '/content/dam/geometrixx/portraits/alison_parker.jpg' and isdescendantnode(a, '/etc/workflow/instances') order by [endTime] desc /* xpath: /jcr:root/etc/workflow/instances//*[@modelId = '/etc/workflow/models/request_for_activation/jcr:content/model' and data/payload/@path = '/content/dam/geometrixx/portraits/alison_parker.jpg'] order by @endTime descending */, path=/etc/workflow/instances//*, property=[modelId=[/etc/workflow/models/request_for_activation/jcr:content/model], data/payload/path=[/content/dam/geometrixx/portraits/alison_parker.jpg]]); consider creating an index or changing the query

To kill such queries without restarting CQ

       a. Access QueryEngineSettings MBean - http://localhost:4502/system/console/jmx/org.apache.jackrabbit.oak%3Aname%3Dsettings%2Ctype%3DQueryEngineSettings
       b. Set the values LimitInMemory and LimitReads to some lower number like 10000
       c. Queries that exceed one of the limits are cancelled with an UnsupportedOperationException



To automatically kill long running queries - set the startup system properties -Doak.queryLimitInMemory and -Doak.queryLimitReads to some lower number like 10000 (requires restart)

For more information check helpx.adobe.com and jackrabbit.apache.org




10) To workaround org.apache.sling.api.request.TooManyCallsException, increase the Number of Calls per request - sling.max.calls, setting of Apache Sling Main Servlet http://localhost:4502/system/console/configMgr/org.apache.sling.engine.impl.SlingMainServlet

`27.01.2016 15:29:50.021 *ERROR* [0:0:0:0:0:0:0:1 [1453930186556] GET /apps/geometrixx/components/testComponent/_cq_dialog.html/content/geometrixx/en/jcr:content/par/testComponent HTTP/1.1] org.apache.sling.engine.impl.SlingRequestProcessorImpl service: Uncaught SlingException
org.apache.sling.api.request.TooManyCallsException: /libs/granite/ui/components/foundation/form/fieldset/fieldset.jsp
at org.apache.sling.engine.impl.request.RequestData.service(RequestData.java:517)
at org.apache.sling.engine.impl.filter.SlingComponentFilterChain.render(SlingComponentFilterChain.java:44)
at org.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:77)
at com.day.cq.personalization.impl.TargetComponentFilter.doFilter(TargetComponentFilter.java:96)
at org.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:68)
at com.day.cq.wcm.core.impl.WCMDebugFilter.doFilter(WCMDebugFilter.java:146)








11)  To find the jar file in cq install folder, containing a specific class - look for the bundle id folder


 

AEM 61 - TouchUI Date Picker Validator Comparing Two Date Fields in Dialog

Goal


Add a Validator on Granite (Coral) Date Picker in TouchUI Dialogs

Page properties dialog has two fields OnTime and OffTime for Activation / Deactivation; for demo purposes, validator here checks if date of OffTime is lesser than OnTime

Demo shows dialog of foundation page component modified to add the eaemCheckDateAfter config (/libs/foundation/components/page/cq:dialog/content/items/tabs/items/basic/items/column/items/onofftime/items/offdate). This is just for demonstration only (on Geometrixx pages), ideally the foundation components should never be altered

Demo | Package Install


Configuration

Set the property eaemCheckDateAfter value to the name of dialog property to check against. Here its set to ./onTime



Inline Dialog - Error Shown when Off Time < On Time



Full Screen Dialog - Error Shown when Off Time < On Time



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/touchui-date-picker-validator

2) Create clientlib (type cq:ClientLibraryFolder/apps/touchui-date-picker-validator/clientlib and set a property categories of String type to cq.authoring.dialogdependencies of type String[] with value underscore

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

                         date-validator.js

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

(function ($) {
    var EAEM_CHECK_DATE_AFTER = "eaemcheckdateafter",
        fieldErrorEl = $("<span class='coral-Form-fielderror coral-Icon coral-Icon--alert coral-Icon--sizeS' " +
                            "data-init='quicktip' data-quicktip-type='error' />");

    $.validator.register({
        selector: "input",
        validate: validate, //if validate() returns a non empty value, show() is called
        show: show,
        clear: clear
    });

    function validate($el){
        //if not date widget or widget value is empty, return
        if(!$el.parent().hasClass("coral-DatePicker") || _.isEmpty($el.val())){
            return;
        }

        var $datePicker = $el.parent(),
            $form = $datePicker.closest("form"),
            checkDateAfter = $datePicker.data(EAEM_CHECK_DATE_AFTER);

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

        var $toCompareField = $form.find("[name='" + checkDateAfter + "']");

        if(_.isEmpty($toCompareField) || _.isEmpty($toCompareField.val())){
            return;
        }

        var toCompareMillis = new Date($toCompareField.val()).getTime(),
            compareWithMillis = new Date($el.val()).getTime(),
            text = $toCompareField.closest(".coral-Form-fieldwrapper").find(".coral-Form-fieldlabel").html();

        return ( compareWithMillis < toCompareMillis) ? "Should not be less than '" + text + "'" : null;
    }

    function show($el, message){
        if(!$el.parent().hasClass("coral-DatePicker")){
            return;
        }

        var $datePicker = $el.parent();

        this.clear($el);

        var arrow = $datePicker.closest("form").hasClass("coral-Form--vertical") ? "right" : "top";

        $el.attr("aria-invalid", "true").toggleClass("is-invalid", true);

        fieldErrorEl.clone()
            .attr("data-quicktip-arrow", arrow)
            .attr("data-quicktip-content", message)
            .insertAfter($datePicker);
    }

    function clear($el){
        if(!$el.parent().hasClass("coral-DatePicker")){
            return;
        }

        var $datePicker = $el.parent();

        $el.removeAttr("aria-invalid").removeClass("is-invalid");

        $datePicker.nextAll(".coral-Form-fielderror").tooltip("hide").remove();
    }
}(jQuery));

AEM 61 - TouchUI Slide Show Component with Image Multifield

Goal


Create a TouchUI slideshow component rendered using Sightly, images added using Image Multifield

This post focuses on creating a slideshow using sightly templating language with the images added in multifield. For more information on image multifield widget check this post

For ClassicUI slide show component check this post

To learn more about sightly controls check adobe documentation

Hotfix 6670 must be installed for this widget extension to work

Demo | Package Install

For 61 SP1 (no hotfixes necessary) - Package Install







Solultion


1) Assuming user added the images for slideshow component using image multifield widget; the following structure gets created in CRX



2) To render images using html, create the component /apps/touchui-sightly-image-multifield/sample-image-multifield with following structure



3)  The file /apps/touchui-sightly-image-multifield/sample-image-multifield/sample-image-multifield.html uses sightly to create html. Add the following code to prepare html for slideshow..

<div    data-sly-use.clientLib="${'/libs/granite/sightly/templates/clientlib.html'}"
        style="display: block; padding: 10px">

    <sly data-sly-call="${clientLib.all @ categories='eaem.slideshow'}" />

    <b>Image Multi Field Sample</b>

    <div data-sly-use.images="imgHelper.js" data-sly-unwrap>
        <div data-sly-test="${images.gallery}" style="margin-bottom: 15px">
            Gallery - ${images.gallery}
        </div>

        <div data-sly-test="${!images.artists && wcmmode.edit}">
            Add images using component dialog
        </div>

        <div data-sly-test="${images.artists}" class="eaem-slideshow">
            <div data-sly-list.artist="${images.artists}">
                <div class="show-pic ${artistList.first ? 'active' : ''}">
                    <img src="${artist.painting}" height="530px" width="700px"/>
                    <div class="overlayText">
                        <span class="overlayTitle">${artist.name}</span>
                        <div class="overlayDesc">${artist.desc}</div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

4) #4 adds the clientlib eaem.slideshow with necessary css and js logic (available in package install) to run a simple slideshow with images. #8 initializes helper object (defined in java script) and exposes it through the variable images using sightly use-api

5) Create file /apps/touchui-sightly-image-multifield/sample-image-multifield/imgHelper.js with necessary logic to read the CRX node structure and create a js object for sightly script added in step3

"use strict";

use( ["/libs/wcm/foundation/components/utils/ResourceUtils.js" ], function(ResourceUtils){
    var images = {}, properties = granite.resource.properties,
        artistsPath = granite.resource.path + "/artists", counter = 1, artist;

    images.gallery = properties["gallery"];
    images.artists = undefined;

    function recursiveImageRead(path){
        ResourceUtils.getResource(path)
                        .then(addImage);
    }

    function addImage(artistRes){
        if(!images.artists){
            images.artists = [];
        }

        properties = artistRes.properties;

        artist = {
            painting: properties["paintingRef"],
            desc: properties["desc"],
            name: properties["artist"]
        };

        images.artists.push(artist);

        recursiveImageRead(artistsPath + "/" + counter++);
    }

    recursiveImageRead(artistsPath + "/" + counter++);

    return images;
} );


AEM 61 - TouchUI Assets Console show node name instead of dc:title or jcr:title

Goal


In all 3 views (card, list, column) of Touch UI Assets Console, show the node name (filename) and NOT dc:title for assets or jcr:title for folders

For AEM 62 check this post

Demo | Package Install


Product



Extension



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/touchui-assets-show-filenames

2) Create clientlib (type cq:ClientLibraryFolder/apps/touchui-assets-show-filenames/clientlib, set a property categories of String type to granite.ui.foundation.admin and dependencies of type String[] to underscore

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

                         show-file-name.js

4) Create file ( type nt:file ) /apps/touchui-assets-show-filenames/clientlib/show-file-name.js, add the following code

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

    var DAM_ADMIN_CHILD_PAGES = "cq-damadmin-admin-childpages",
        LOAD_EVENT = "coral-columnview-load",
        FOUNDATION_LAYOUT_CARD = ".foundation-layout-card",
        NS_COLUMNS = ".foundation-layout-columns";

    $document.on("foundation-mode-change", function(e, mode, group){
        //not assets console, return
        if(group != DAM_ADMIN_CHILD_PAGES){
            return;
        }

        showFileName();

        var $collection = $(".foundation-collection[data-foundation-mode-group=" + group + "]");

        //for column view
        $collection.on(LOAD_EVENT, function(){
            setTimeout( showFileName ,200);
        });

        //for column view select
        $collection.on("coral-columnview-item-select" + NS_COLUMNS, showFileName);

        if (!$collection.is(FOUNDATION_LAYOUT_CARD)) {
            return;
        }

        var $scrollContainer = $collection.parent();

        //for card view scroll
        $scrollContainer.on("scroll" + FOUNDATION_LAYOUT_CARD, _.throttle(function(){
            var paging = $collection.data("foundation-layout-card.internal.paging");

            if(!paging.isLoading){
                return;
            }

            var INTERVAL = setInterval(function(){
                if(paging.isLoading){
                    return;
                }

                clearInterval(INTERVAL);

                showFileName();
            }, 250);
        }, 100));
    });

    function showFileName(){
        var $articles = $("article"), $article, name;

        $articles.each(function(index, article){
            $article = $(article);

            name = getStringAfterLastSlash($article.data("path"));

            $article.find("h4").html(name);
            $article.find(".coral-ColumnView-label").html(name);
        });
    }

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

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

AEM 61 - TouchUI Rich Text Editor Remove Unused Plugins

Goal


A sample extension for removing unused plugins in RTE (Rich Text Editor) of Touch UI. With the extension Inline Editor is stripped to have buttons for just full screen, close & save; Fullscreen Editor with only switch to inline...

Demo | Package Install


Inline Editor



Fullscreen Editor



Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/touchui-hide-default-rte-plugins

2) Create node /apps/touchui-hide-default-rte-plugins/clientlib of type cq:ClientLibraryFolder and add a String property categories with value rte.coralui2

3) Create file (nt:file) /apps/touchui-hide-default-rte-plugins/clientlib/js.txt and add

                       remove-plugins.js

4) Create file (nt:file) /apps/touchui-hide-default-rte-plugins/clientlib/remove-plugins.js and add the following code.

(function(){
    var INLINE_TOOLBAR = [ "fullscreen#start", "control#close", "control#save"],
        FULLSCREEN_TOOLBAR = [ "fullscreen#finish"];

    var EAMCuiToolbarBuilder = new Class({
        toString: "EAEMCuiToolbarBuilder",

        extend: CUI.rte.ui.cui.CuiToolbarBuilder,

        _getUISettings: function(options) {
            var uiSettings = this.superClass._getUISettings(options);

            //inline toolbar - "#format", "#justify", "#lists", "links#modifylink",
            // "links#unlink", "fullscreen#start", "control#close", "control#save"
            uiSettings["inline"]["toolbar"] = INLINE_TOOLBAR.slice(0);

            //fullscreen toolbar - "format#bold", "format#italic", "format#underline",
            // "fullscreen#finish"....
            uiSettings["fullscreen"]["toolbar"] = FULLSCREEN_TOOLBAR.slice(0);

            return uiSettings;
        }
    });

    var EAEMToolkitImpl = new Class({
        toString: "EAEMToolkitImpl",

        extend: CUI.rte.ui.cui.ToolkitImpl,

        createToolbarBuilder: function() {
            return new EAMCuiToolbarBuilder();
        }
    });

    CUI.rte.ui.ToolkitRegistry.register("cui", EAEMToolkitImpl);
}());




AEM 61 - Classic UI Disable Drag and Drop from Desktop to DAM Admin Console

Goal


Disable upload of assets using Drag and Drop from Desktop to DAM Admin Console - http://localhost:4502/damadmin

For TouchUI check this post

Demo | Package Install




Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/classicui-assets-disable-desktop-drop

2) Create node /apps/classicui-assets-disable-desktop-drop/clientlib of type cq:ClientLibraryFolder and add a String property categories with value cq.widgets

3) Create file (nt:file) /apps/classicui-assets-disable-desktop-drop/clientlib/js.txt and add

                       disable-drop.js

4) Create file (nt:file) /apps/classicui-assets-disable-desktop-drop/clientlib/disable-drop.js and add the following code.

(function () {
    if (window.location.pathname !== "/damadmin") {
        return;
    }

    //id set in /libs/wcm/core/content/damadmin
    var DAM_ADMIN_ID = "cq-damadmin";

    function handleDrops() {
        var damAdmin = CQ.Ext.getCmp(DAM_ADMIN_ID);

        damAdmin.html5UploadFiles = function (files) {
            CQ.Ext.Msg.alert("Drop Error", "Drop from desktop disabled");
        }
    }

    var INTERVAL = setInterval(function () {
        var grid = CQ.Ext.getCmp(DAM_ADMIN_ID + "-grid");

        if (!grid) {
            return;
        }

        clearInterval(INTERVAL);

        try {
            handleDrops();
        } catch (err) {
            console.log("error executing drag drop disable extension");
        }
    }, 250);
})();

AEM 61 - TouchUI Disable Drag and Drop from Desktop to Assets Console

Goal


Disable upload of assets using Drag and Drop from Desktop to Assets Console - http://localhost:4502/assets.html/content/dam

For ClassicUI check this post

Demo | Package Install



Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/touchui-assets-disable-desktop-drop

2) Create node /apps/touchui-assets-disable-desktop-drop/clientlib of type cq:ClientLibraryFolder and add a String property categories with value cq.gui.damadmin.admin

3) Create file (nt:file) /apps/touchui-assets-disable-desktop-drop/clientlib/js.txt and add

                       disable-drop.js

4) Create file (nt:file) /apps/touchui-assets-disable-desktop-drop/clientlib/disable-drop.js and add the following code.

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

    $document.on("foundation-contentloaded", function () {
        $document.off("dropzonedragover", "span.coral-FileUpload");
        $document.on("dropzonedragover", "span.coral-FileUpload", showDisabledMessage);

        //triggered by coral when dropped files are added in queue and ready to process
        $document.off("filelistprocessed", "span.coral-FileUpload")
                        .on("filelistprocessed", "span.coral-FileUpload", clearAndRefresh);
    });

    function clearAndRefresh(event){
        var fileUpload = event.fileUpload;

        if(fileUpload && (fileUpload.isDragOver === true)){
            fileUpload.uploadQueue.splice(0, fileUpload.uploadQueue.length);
            $(".foundation-content").adaptTo("foundation-content").refresh();
        }
    }

    function showDisabledMessage() {
        var message = $('<div class=\"drag-drop-message\"><h1>'
                            + '<span>{</span>Drop from desktop disabled<span>}</span>' +
                        '</h1></div>');
        $('.foundation-collection-container').overlayMask('show', message);
    }
})(jQuery, $(document));


AEM 61 - Touch UI Rich Text Editor Remove Bold and Add Strong Plugin

Goal


Remove the Bold plugin in TouchUI Rich Text Editor and add Strong plugin for semantic markup

AEM RTE Bold Plugin wraps text with "b" tag by default, to make the plugin wrap text with strong tag, add nt:unstructured nodes htmlRules/docType/typeConfig with property useSemanticMarkup=true; if you are happy with it, avoid this extension

Demo shows dialog of foundation text component modified to add the plugin config (/libs/foundation/components/text/dialog/items/tab1/items/text/rtePlugins). This is just for demonstration only (on Geometrixx pages), ideally the foundation components should never be altered...

Demo (download for better view) | Package Install


Plugin Configuration



Semantic Markup Configuration



Inline Editor

Full Screen Editor




Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/touchui-rte-strong-plugin

2) Create node /apps/touchui-rte-strong-plugin/clientlib of type cq:ClientLibraryFolder and add a String property categories with value rte.coralui2

3) Create file (nt:file) /apps/touchui-rte-strong-plugin/clientlib/js.txt and add

                       strong.js

4) Create file (nt:file) /apps/touchui-rte-strong-plugin/clientlib/strong.js and add the following code.

(function($, CUI){
    var ExperienceAEM = {
        GROUP: "experience-aem",
        STRONG_FEATURE: "strong",
        BOLD: "format#bold"
    };

    function getStrongFeature() {
        return ExperienceAEM.GROUP + "#" + ExperienceAEM.STRONG_FEATURE;
    }
 
    //extend toolbar builder to register strong styles
    ExperienceAEM.CuiToolbarBuilder = new Class({
        toString: "EAEMCuiToolbarBuilder",
 
        extend: CUI.rte.ui.cui.CuiToolbarBuilder,
 
        //add strong icon to the existing set
        _getUISettings: function(options) {
            var uiSettings = this.superClass._getUISettings(options),
                strongFeature = getStrongFeature();

            //inline toolbar
            var items = uiSettings["inline"]["popovers"]["format"].items;

            //insert strong feature
            if(items.indexOf(strongFeature) == -1){
                items.push(strongFeature);
            }

            //remove bold feature
            if(items.indexOf(ExperienceAEM.BOLD) >= 0){
                items = items.splice(items.indexOf(ExperienceAEM.BOLD), 1);
            }

            //fullscreen toolbar
            items = uiSettings["fullscreen"]["toolbar"];

            //insert strong feature
            if(items.indexOf(strongFeature) == -1){
                items.splice(3, 0, strongFeature);
            }

            //remove bold feature
            if(items.indexOf(ExperienceAEM.BOLD) >= 0){
                items = items.splice(items.indexOf(ExperienceAEM.BOLD), 1);
            }

            if(!this._getClassesForCommand(strongFeature)){
                this.registerAdditionalClasses(strongFeature, "coral-Icon coral-Icon--textStyle");
            }

            return uiSettings;
        }
    });
 
    ExperienceAEM.ToolkitImpl = new Class({
        toString: "EAEMToolkitImpl",
 
        extend: CUI.rte.ui.cui.ToolkitImpl,
 
        createToolbarBuilder: function() {
            return new ExperienceAEM.CuiToolbarBuilder();
        }
    });
 
    CUI.rte.ui.ToolkitRegistry.register("cui", ExperienceAEM.ToolkitImpl);
 
    ExperienceAEM.TouchUIStrongPlugin = new Class({
        toString: "TouchUIStrongPlugin",
 
        extend: CUI.rte.plugins.Plugin,
 
        pickerUI: null,
 
        getFeatures: function() {
            return [ ExperienceAEM.STRONG_FEATURE ];
        },
 
        initializeUI: function(tbGenerator) {
            var plg = CUI.rte.plugins;
 
            if (this.isFeatureEnabled(ExperienceAEM.STRONG_FEATURE)) {
                this.pickerUI = tbGenerator.createElement(ExperienceAEM.STRONG_FEATURE, this, true, "Wrap Strong");
                tbGenerator.addElement("format", plg.Plugin.SORT_FORMAT, this.pickerUI, 120);
            }
        },
 
        execute: function(id) {
            this.editorKernel.relayCmd(id);
        },
 
        //to mark the strong icon selected/deselected
        updateState: function(selDef) {
            var hasUC = this.editorKernel.queryState(ExperienceAEM.STRONG_FEATURE, selDef);
 
            if (this.pickerUI != null) {
                this.pickerUI.setSelected(hasUC);
            }
        },
 
        notifyPluginConfig: function(pluginConfig) {
            pluginConfig = pluginConfig || { };
 
            var defaults = {
                "tooltips": {
                    "strong": {
                        "title": "Wrap Strong",
                        "text": "Wrap Strong"
                    }
                }
            };
 
            CUI.rte.Utils.applyDefaults(pluginConfig, defaults);
 
            this.config = pluginConfig;
        }
    });
 
    CUI.rte.plugins.PluginRegistry.register(ExperienceAEM.GROUP, ExperienceAEM.TouchUIStrongPlugin);
 
    ExperienceAEM.StrongCmd = new Class({
        toString: "StrongCmd",
 
        extend: CUI.rte.commands.Command,
 
        isCommand: function(cmdStr) {
            return (cmdStr.toLowerCase() == ExperienceAEM.STRONG_FEATURE);
        },
 
        getProcessingOptions: function() {
            var cmd = CUI.rte.commands.Command;
            return cmd.PO_SELECTION | cmd.PO_BOOKMARK | cmd.PO_NODELIST;
        },
 
        _getTagObject: function() {
            return {
                "tag": "strong"
            };
        },
 
        execute: function(execDef) {
            var selection = execDef.selection;
 
            if (!selection) {
                return;
            }
 
            var nodeList = execDef.nodeList;
 
            if (!nodeList) {
                return;
            }
 
            var common = CUI.rte.Common;
            var context = execDef.editContext;
 
            var tagObj = this._getTagObject();
 
            var tags = common.getTagInPath(context, selection.startNode, tagObj.tag, tagObj.attributes);
 
            if (tags == null) {
                nodeList.surround(execDef.editContext, tagObj.tag, tagObj.attributes);
            } else {
                nodeList.removeNodesByTag(execDef.editContext, tagObj.tag, tagObj.attributes, true);
            }
        },
 
        queryState: function(selectionDef) {
            var common = CUI.rte.Common;
            var context = selectionDef.editContext;
 
            var selection = selectionDef.selection;
            var tagObj = this._getTagObject();
 
            return (common.getTagInPath(context, selection.startNode, tagObj.tag, tagObj.attributes) != null);
        }
    });
 
    CUI.rte.commands.CommandRegistry.register(ExperienceAEM.STRONG_FEATURE, ExperienceAEM.StrongCmd);
})($, window.CUI);