Showing posts with label Ext Js. Show all posts
Showing posts with label Ext Js. Show all posts

Saturday, 18 March 2017

Customize AEM Sidekick

Normally, once you activates a new page in author instance, in order to view it in publish instance you probably copy page its URL, paste it in new tab and change the host-name and port name to see the results. To simplify it here, we will add a custom button in our sidekick and convert it into a useful features.
The button we are going to add here will automatically give you the link to the page in publish instance once you activates it. With one click you can see your current page in publish instance.
The path where AEM sidekick code is present is: /libs/cq/ui/widgets/source/widgets/wcm/Sidekick.js
Follow the below steps :-
  1. Create a folder inside /apps with name customsidekick
  2. Create a node with type cq:ClientLibraryFolder inside /apps/customsidekick
  3. Create a file name customSidekick.js
  4. Create a file name js.txt inside /apps/customsidekick and write customSidekick.js in the this file
  5. Add categories property with value cq.widgets to the clientlibrary created in step 2
  6. Open the customSidekick.js file created in step 3 and paste the below code

Saturday, 4 March 2017

Things to know about AEM Touch UI

Adobe introduced a new touch-optimized UI with AEM 5.6 for the author environment. This differs considerably from the original classic UI as it is designed to operate on both touch and desktop devices. The basic principles of the touch-optimized UI are:
  • Mobile first (with desktop in mind)
  • Responsive design
  • Context relevant display
  • Reusable
  • Include embedded reference documentation
  • Include embedded tests
  • Bottom-up design to ensure these principles are applied to every element and component

Thursday, 1 December 2016

Create a Nested Multi-Field CQ Dialog Widget

Nested multi-field cq dialog widget  or custom Adobe Experience Manager (AEM) component is one that uses a nested multi-field control located in a dialog. A nested multi-field control is an inner multi-field control within an outer multi-field control and lets an author dynamically enter data. 

For example, assume the AEM control lists developers and each developer has an unknown number of skills to display. That is, within the inner multi-field, the author enter details such as professional skill set. The outer multi-field determines how may developers to display. 

Lets create Nested multi-field cq dialog widget as shown below:

Friday, 25 November 2016

Uncaught TypeError: Cannot read property 'extend' of undefined' - cq.extend

When we try to create a custom widget we extend the widget from the CompositeField. When we render this custom widget script, we may get error like "Uncaught TypeError: Cannot read property 'extend' of undefined' ".


When custom component adds a dependency on cq.widgets it will always fail in the publisher.

When we include below statement in clientlibs, this script is also evaluated in the publisher for the purpose of assigning the variable, even if it is never used.

Search.CustomWidget = CQ.Ext.extend(CQ.form.CompositeField, { 
You can make this work by having the authoring part completely separated from the rest of the component, so that the publisher never tries to execute any part of it. For example by using a different category which is only included using WCMMode.fromRequest(request) != WCMMode.DISABLED

Wednesday, 23 November 2016

Hide a field based on the User Role in a Dialog

If you need to hide a field in a dialog based on User role i.e. user group, you can do it through an Ajax call-

    type: "GET",
    url: "/libs/granite/security/",
    cache: false
}).done(function(data, textStatus, jqXHR) {
    var isMember = false;
    /* The Group ID for which the widget has to be disabled */
    var groupId = "site-editor";
    var membershipInfo = data.declaredMemberOf;
    if (membershipInfo) {
        for (var membershipIdx = 0; membershipIdx < membershipInfo.length; membershipIdx++) {
            if (membershipInfo[membershipIdx].authorizableId == groupId) {
                isMember = true;
    /*This example disables the Hide in Navigation checkbox on Page Properties dialog of the page*/
    if (isMember) {
        $('[name="./hideInNav"]').prop('disabled', 'true');

Ajax call should be invoked at "dialog-ready" event of document. You need to put the above code in a clientlibs JS file with category "cq.authoring.dialog". 

Restriction on size in multifield

Following sample code can be used to put size restriction in a multifield -

1. Add a new property- "maxlinksallowed" to the multifield node in your cq:dialog. Refer the screenshot below-

2. Use this JS code-

$(document).on("dialog-ready", function () {
$(".js-coral-Multifield-add").click(function() {
    var field = $(this).parent();
    var size = field.attr("data-maxlinksallowed");
    if (size) {
        var ui = $(window).adaptTo("foundation-ui");
        var totalLinkCount = $(this).prev('ol').children('li').length;
        if (totalLinkCount >= size) {
            ui.alert("Warning", "Maximum " + size + " links are allowed!", "notice");
            return false;

You need to put the above code in a clientlibs JS file with category "cq.authoring.dialog".

Auto populate title field based on Path browser path selection

To auto populate page title in a title field based on path value selected in path browser field, you can refer following code snippet-

$(document).ready(function() {
    var textInput;
    var textToInput;
    var inputText;
    var ui = $(window).adaptTo("foundation-ui");
    var s = $(document).on("dialog-ready", function() {
        textInput = $('.js-coral-pathbrowser-button').parent().prev();
        textToInput = $('.js-coral-pathbrowser-button').parent().parent().parent().parent().parent().parent().parent().find("input[name='./page'].coral-Form-field");
        $(document).on("click", ".js-coral-pathbrowser-confirm", function() {
            setTimeout(function() {
                inputText = $(textInput).val();
                if (!$(textToInput).val()) {
                    $(textToInput).val(inputText.substring(inputText.lastIndexOf("/") + 1));
            }, 1000);
        $(document).on('click', '.js-coral-pathbrowser-button', function() {
            textInput = $(this).parent().prev();
            textToInput = $(this).parent().parent().parent().parent().parent().parent().parent().find("input[name='./page'].coral-Form-field");
        $(document).on("change", " .js-coral-pathbrowser-input", function() {
            inputText = $(this).val();
            if (inputText.indexOf("/content") > 0) {
                textToInput = $(this).parent().parent().parent().parent().parent().parent().parent().find("input[name='./page'].coral-Form-field");
                if (!$(textToInput).val()) {
                    $(textToInput).val(inputText.substring(inputText.lastIndexOf("/") + 1));

Change the field selector input[name='./page'].coral-Form-field as per your dialog configuration where the title value is to be auto populated.

You need to put the above code in a clientlibs JS file with category "cq.authoring.dialog".

Prefill text field with http, mailto

You can use the below sample code to prefill an input text field with URL/ EMail prefixes based on user input. It prefixes mailto syntax if user input forms an email pattern.

(function($) {
    "use strict";
    $(document).on("change", ".js-coral-pathbrowser-input", function(e) {
        // If it's a relative path - do nothing
        if (this.value.indexOf('/') != 0) {
            // It's not a relative path - treat it as either a mail address or webb address
            if ((this.value.indexOf('@') > -1) && (this.value.indexOf('mailto:') == -1)) {
                // It's a mail address
                $(this).prop("value", 'mailto:' + this.value);
            } else if ((this.value.indexOf('http://') == -1) && (this.value.indexOf('https://') == -1) && (this.value != "") && (this.value.indexOf('mailto:') == -1) && ($(this).parent().parent().hasClass('js-cq-TagsPickerField') == false)) {
                // It's a webb address
                $(this).prop("value", 'http://' + this.value);
        if (this.value.indexOf(' ') >= 0) {
            $(this).prop("value", $.trim(this.value));
            $(this).prop("value", this.value.replace(new RegExp(' ', 'g'), '%20'));

You need to put the above code in a clientlibs JS file with category "cq.authoring.dialog".

RTE validator plugin

You can use the belowJS for sample validator plugin for Rich text editor-

CUI.rte.plugins.ValidatorPlugin = new Class({

    toString: "ValidatorPlugin",

    extend: CUI.rte.plugins.Plugin,

     * RTE plugin for maximun length check. If maxlength is set to 0 the validation will not happen. Set a number 
     * greater than 0 for maxlength property.

     * @private
    alertShown: false,
    errorEl: null,

Friday, 25 March 2016

Populate Tags based on Selection in Pathfield – Classic UI

This simple use case has been taken from AEM community, I thought an article for this would really help people who are looking for this or may need in future.
Component Overview
We have two field, one is pathfield (which helps to browse repository) and another a tag input field. Author selects an asset from pathfield and based on the the selection Tag input field is populated with tags available for selected assets.
Authoring Experience
Component dialogComponent

Sunday, 22 November 2015

Customizing the Adobe Experience Manager Sidekick to improve the activate process

You can customize the AEM sidekick to add functionality tio meet your business requirements. For example, typically, once you activate an AEM page in author, in order to view it in publish, you typically copy its URL, paste it in new tab and change the host-name and port name to see the results. To simplify this use case, you can add a custom button to the AEM sidekick that performs these actions with 1 click.

The new sidekick button automatically give you the link to the page in publish instance once you activate it. That is, with a single click, you can see your current page in publish instance.

Wednesday, 18 November 2015

How to Use ExtJS Widgets

Widgets, also known as xtypes, are a core feature of Adobe Experience Manager (AEM formerly Adobe CQ), and understanding them is crucial. AEM provides many different widgets for use out of the box. While it is nice to have so many to choose from to create your site, it can be difficult to know which one best fits your needs. There are two great ways to find out what widgets exist and how to use them. The first is Adobe’s online documentation, and the second is trial and error.
Learning by Documentation
Adobe’s Widget documentation is a great resource for reading about the features and functionality of each widget type. You can browse by hierarchy or directly search for the name of the widget you are looking for. Each widget description page contains detailed information about the widget. You can see its class ancestry, descendants, configuration options, properties, events, and more. Each description gives information about the method/property such as common uses and provided parameters.
Note that the link above provides documentation on the current version of AEM. If you want to find information about a specific version, modify the “current” portion of the URL with the version you are looking for (e.g.
Learning by Use
The other way to learn more about out of the box widgets is to simply try them. It can often be best to see them in action before attempting to use them yourself. One way to find an already existing component containing a widget is to query the JCR for its use with the query tool provided in AEM. This tool can be located from the Tools->Query… link on the CRXDE Lite page (highlighted below). Using the query tool, you can search for any widget you want, and it will display all components that use that widget. A sample of the query to execute for SQL2 would be SELECT * FROM [nt:base] AS s WHERE [xtype] = ‘pathfield’. For XPath, you could use //*[xtype=’pathfield’] (highlighted below).
Once you know of an existing component that uses the widget you want to use, you can create an instance of that component on a page. You can then experiment with the dialog and see the way the widget functions.
As you become familiar with the out of the box widgets that AEM provides, you will be able to use multiple and various widgets in your dialogs in unity to create the authoring experience you are looking for while being able to best leverage the native power of AEM and its flexible dialog/widget system.
In many situations, the out of the box widgets will be sufficient for what you will need. If your situation requires a more complex widget, AEM allows for the creation of powerful, highly customizable widgets. Instructions on sample custom widget creation will be provided in another tutorial.

Friday, 13 November 2015

Customization of parsys to restrict number of compnents in CQ 5.6.1

  1. Copy the parsys component from(/libs/foundation/components/parsys) to your poroject(/apps/<project nmae>/components/).
     2.  In the design dialog add number filed configuration to enter the number of components.

           Crate node noofcomp of type cq:widget under /apps/<project nmae>/components/parsys
           Add the following properties

           xtype : numberfield
           name : noOfComp
           maxValue : 20
           fieldLabel : No of components

     3.   Create ajax.jsp and write the below logic.
           <%@page import=""%>
           <%@include file="/libs/foundation/global.jsp"%>
                 ParagraphSystem parSys = ParagraphSystem.create(resource, slingRequest);
                 int totalComponents=parSys.paragraphs().size();
                 int restrictCompoNo=Integer.parseInt(currentStyle.get("noOfComp","20"));

                if(totalComponents >= restrictCompoNo){

    4.  You need to overlay the EditBase.js and Sidekick.js

          Create the following structure   /apps/cq/ui/widgets/source/widgets/wcm

         then copy the EditBase.js and Sidekick.js form libs . Place under /wcm /
         In Sidekic.js at line 4098 in set timeout method add this snippet.

            var parentPath=editComponent.path;
            var ajaxUrl=parentPath.substr(0,parentPath.lastIndexOf("/"))+".ajax";
            var notAllowToCreate = CQ.HTTP.eval(ajaxUrl);

                                   msg: 'You reached  the maximun limit. You can set the limit in disign mode.',
                                   buttons: CQ.Ext.Msg.OK,
                                   icon: CQ.Ext.MessageBox.WARNING


         In EditBase.js at line 1181  in set timeout method add this snippet.
           var parentPath=e.path;
            var ajaxUrl=parentPath.substr(0,parentPath.lastIndexOf("/"))+".ajax";
            var notAllowToCreate = CQ.HTTP.eval(ajaxUrl);    

                    msg: 'You reached  the maximun limit. You can set the limit in disign mode.',
                    buttons: CQ.Ext.Msg.OK,
                    icon: CQ.Ext.MessageBox.WARNING

Colorpicker RTE plugin for Richtext

1.  Create a clientlibs node (nodeTypecq:ClientLibraryFolder) under the root node of the project.
e.g. /apps/training
Assign the following properties to the newly created clientlibs node 
the property that identifies the category these custom Widgets will be referenced by

Name  = categories
Type  String []
Value  colorpicker.widgets
the property that defines JS library dependencies

Name  dependencies
Type  String[]
Value  cq.jquery#cq.foundaion-main

2.  Create a css folder under the newly created clientlibs node.
3.  Create a js folder under the newly created clientlibs node.
4.  Create a css.txt file under the newly created clientlibs node.
5.  Create a js.txt file under the newly created clientlibs node.
6.  Create colorpickerplugin.Js and colorpickerdialog.js in js folder under  clientlibs


 * @class CQ.form.rte.plugins.HtmlColorPickerPlugin
 * @extends CQ.form.rte.plugins.Plugin
 * <p>This class builds a Colorpicker pop up as a plugin.</p>
 * <p>The plugin ID is "<b>Colorpicker</b>".</p>
 * <p><b>Features</b></p>
 * <ul>
 *   <li><b>colorpicker</b> - pops up Colorpicker dialog</li>
 * </ul>
CQ.form.rte.plugins.HtmlColorPickerPlugin = CQ.Ext.extend(CQ.form.rte.plugins.Plugin, {

 * @private
htmlColorPickerDialog: null,
 * @private
colorPickerText: null,
 * @private

constructor: function(editorKernel) {,
callDialog: function(context) {

if (CQ.Ext.isIE) {
this.savedRange = context.doc.selection.createRange();
var editorKernel = this.editorKernel;

var configdialog = {
"editContext": context,
"title": CQ.I18n.getMessage("Color Picker"),
"colorPickerText": this.colorPickerText,
"insertContentIntoRTE": this.insertContentIntoRTE.createDelegate(this),
"cancelFn": this.execCancel.createDelegate(this),
"listeners": {
"show": function() {
"hide": function() {
this.htmlColorPickerDialog =
                                        new CQ.form.rte.plugins.HtmlColorPickerDialog(configdialog);
window.setTimeout(function() {
}.createDelegate(this), 10);

getYourData: function(){

 // You might want to do something here and populate a value that needs to go inside your dialog.
this.colorPickerText = "Color Picker RTE Plugin";

// This gets called when you click OK button from your dialog
insertContentIntoRTE: function(context, options, dialog) {
 var selectionDef = this.editorKernel.analyzeSelection();
 var nodelist = selectionDef["nodeList"];
 var node = nodelist["nodes"][0];
 var nodeDom = node["dom"];
 var nodeText = nodeDom["nodeValue"];
 var selectedText=nodeText.substring(node["startPos"], node["startPos"] +
 var color;
 for(var opt in options){
if(opt!=undefined && opt!="" && opt!='remove' && opt!='indexOf'){
                                              //for IE alone indexOf comes as a function
color = options[opt].value;
var style="style=color:#"+color;
var htmlCode = '<span '+style+'>' + selectedText + '</span>';

this.editorKernel.execCmd("inserthtml", htmlCode);

// This gets called when you click Cancel button from your dialog
execCancel: function() {


getFeatures: function() {
return [ "colorpicker" ];

initializeUI: function(tbGenerator) {
var plg = CQ.form.rte.plugins;
var ui = CQ.form.rte.ui;
if (this.isFeatureEnabled("colorpicker")) {
this.checkTextUI = new ui.TbElement("colorpicker", this,
tbGenerator.addElement("htmlColorPicker", plg.Plugin.SORT_LISTS,


notifyPluginConfig: function(pluginConfig) {
pluginConfig = pluginConfig || { };
CQ.Util.applyDefaults(pluginConfig, {
"tooltips": {
"colorpicker": {
"title": CQ.I18n.getMessage("Color Picker"),
"text":  CQ.I18n.getMessage("Color Picker pop-up")
this.config = pluginConfig;

execute: function(id, value, env) {
switch (id) {
case "colorpicker":
this.envEditContext = env.editContext;


updateState: function(selDef) {

// todo implement


// register plugin



 * @class CQ.form.rte.plugins.HtmlColorPickerDialog
 * @extends CQ.Ext.Window
 * @private
 * This class implements the Hello World dialog.
CQ.form.rte.plugins.HtmlColorPickerDialog = CQ.Ext.extend(CQ.Ext.Window, {
editContext: null,
insertContentIntoRTE: null,
constructor: function(config) {
var dialogRef = this;
var dialogItems = [ ];
var buttonItems = [ ];
var defaults = {
"title": "Html Color Picker"
CQ.Util.applyDefaults(config, defaults);
CQ.Ext.apply(this, config);
"itemId": "colorPickerField",
"name": "colorPickerField",
"xtype": "colorfield",           
"fieldLabel": "colorPickerField",
"colors":['000000', '993300', '333300', '003300', '003366', '000080', '333399',
                                                    '333333','800000', 'FF6600', '808000', '008000', '008080', '0000FF',
                                                   '666699', '808080','FF0000', 'FF9900', '99CC00', '339966', '33CCCC',
                                                   '3366FF', '800080', '969696','FF00FF', 'FFCC00', 'FFFF00', '00FF00',
                                                   '00FFFF', '00CCFF', '993366', 'C0C0C0','FF99CC', 'FFCC99',
                                                    'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF' ]
// Buttons
"itemId": "ok",
"name": "ok",
"text": "OK",
"handler": function() {
this.applyDialog(this.insertContentIntoRTE, null);
"scope": this
"text": "Cancel",
"handler": function() {
if (this.cancelFn) {
"scope": this
});, {
"renderTo": CQ.Util.ROOT_ID,
"title": this.title,
"stateful": false,
"minWidth": 400,
"minHeight": 170,
"width": 400,
"height": 310,
"plain": true,
"layout": "fit",
"items": [ {
"xtype": "panel",
"layout": "fit",
"stateful": false,
"items": [ {
"border": false,
"xtype": "form",
"itemId": "ColorPickerForm",
"name": "ColorPickerForm",
"stateful": false,
"autoHeight": true,
"items": dialogItems,
"bodyStyle": "overflow: auto;",
"afterRender": function() {;
dialogRef.findItems = this.items;
"buttons": buttonItems,
"modal": true
applyDialog: function(fn, options) {
options = options || [];
if (fn) {
var colorPickerTextValue = this.findItems.get("colorPickerField").getValue();
options.push({value: colorPickerTextValue, name: 'colorPickerTextValue'});
fn(this.editContext, options, this);
cancelFn: function(){

//Implement here

7.  List colorpickerplugin.js and  colorpickerdialog.js in js.txt file.
8.  Create cloorpicker.css in css folder under  clientlibs.


#CQ .x-html-editor-tb .x-edit-colorpicker{
  background: url(/libs/cq/ui/widgets/themes/default/icons/16x16/colorpicker.jpg)
                           center no-repeat;

9.  List cloorpicker.css in css.txt file underclientlibs.

10. Create a htmlColorPicker node (nodeType nt:unstructured) under the Training Complex Component's Dialog tab 1 widget collection.

 Assign the following properties to the newly created htmlColorPicker node:

 the property that will define where the content is stored

Name  features
Type  String
Value  *