RTE Runtime Word Count Plugin

This plugin works just like a status plugin reflecting the count of words entered by the editor while entering content in the rich text editor.

How to create ?

Create clientlibs for our text component with
categories[] | String | core.aeexplorers.text

Now, add word-count.js in the above created clientlib and update the file name in js.txt as well.

word-count.js

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

    function word_count(htmlContent){
		htmlContent = htmlContent.replace(/<[^>]*>/g," ");
        htmlContent = htmlContent.replace(/&nbsp;/g, ' ');
        htmlContent = htmlContent.replace(/\s+/g, ' ');
		htmlContent = htmlContent.trim();
		var count = htmlContent.split(" ").length;
        var displayCount=count;
          if(count===1 && htmlContent.split(" ")[0]==='') {
              displayCount=0;
          }
          var htmlStringToShow="<p class=\"word-count\" style=\"color:blue\">Word Count : "+displayCount+"</p>"
          return htmlStringToShow;
    }

    $document.on("dialog-ready", function() {
        var textarea=$(".cq-RichText-editable");
		var htmlContent = textarea.html();
		var htmlStringToShow=word_count(htmlContent);
        $(htmlStringToShow).insertAfter(textarea);

        $(".cq-RichText-editable").keyup(function(){
            var htmlContent = $(this).html();
            var htmlStringToShow=word_count(htmlContent);
            $(".word-count").html(htmlStringToShow);
        });
    });


})($, $(document));

Now, include the above created clientlibs to cq:dialog of text component by adding the property
extraClientlibs[] | String | core.aeexplorers.text

The plugin is now created and ready to use.

How to use ?

Place a text component on the content page and start typing. Just below the text area, a word count status would be displayed which will be updated in real time, as an when you enter a character.

To download the installable jcr package, click on the below button.

RTE Anchor Plugin

The out of the box anchor plugin present in text component is buggy and doesn’t work as expected. The issues identified on the Anchor Plugin are :
1. The “href “attribute is being removed on submit of dialog i.e. the href property is not stored on the text node
2. Modification of the anchor id/href attribute is not possible as the plugin doesn’t load the saved property.

To meet the above requirement, we have developed a custom anchor plugin.

How to create ?

Create a text component and go to rtePlugins node present under cq:dialog of the component.

(/apps/adobeexplorers/components/content/text/cq:dialog/content/items/tabs/items/properties/items/columns/items/column/items/text/rtePlugins)

Create a nt:unstructured node under rtePlugins node > adobe-explorers

(/apps/adobeexplorers/components/content/text/cq:dialog/content/items/tabs/items/properties/items/columns/items/column/items/text/rtePlugins/adobe-explorers)

Add a property to the node > adobe-explorers
features | <String> | *

Enable the plugin feature in dialog inline and full screen mode by adding the feature on below nodes :

  1. inline > on this node go to the multi value property named – “toolbar“. Add the feature in this property > adobe-explorers#anchor
  2. dialogFullScreen > similar to above – > on this node go to the multi value property named – “toolbar“. Add the feature in this property > adobe-explorers#anchor

Now, let’s create a dialog which will be displayed on click of plugin. Here, in our case, we would need to have a textfield and two buttons, one to add anchor tag and another to remove anchor tag.

Now, let’s create clientlibs for our text component with
categories[] | String | core.aeexplorers.text, cq.authoring.dialog.all
and dependencies | String | underscore

To create the Anchor Plugin feature, we would need to add following three js files to above created clientlibs – anchor-plugin.js ( this contains the dialog and the complete code on how to wrap the text with anchot tag) ,dialog-before-submit.js and richtext.js ( The changes in these two js is needed to preserve the href tag on the text node ).

1. anchor-plugin.js

(function($, CUI) {
	var GROUP = "adobe-explorers", 
	ANCHOR_PICKER_FEATURE = "anchor", 
	TCP_DIALOG = "aeTouchUIAnchorPickerDialog", 
	PICKER_NAME_IN_POPOVER = "anchorvalue", 
	REQUESTER = "requester", 
	PICKER_URL = "/apps/adobeexplorers/components/content/text/anchor-popover/cq:dialog.html", 
	url = document.location.pathname;

	if (url.indexOf(PICKER_URL) !== 0) {
		addPluginToDefaultUISettings();
		addDialogTemplate();
	}

	var AEAnchorPickerDialog = new Class({
		extend : CUI.rte.ui.cui.AbstractDialog,
		toString : "AEAnchorPickerDialog",

		initialize : function(config) {
			this.exec = config.execute;
		},

		getDataType : function() {
			return TCP_DIALOG;
		}
	});

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

				extend : CUI.rte.plugins.Plugin,
				pickerUI : null,

				getFeatures : function() {
					return [ ANCHOR_PICKER_FEATURE ];
				},

				initializeUI : function(tbGenerator) {
					var plg = CUI.rte.plugins;

					if (!this.isFeatureEnabled(ANCHOR_PICKER_FEATURE)) {
						return;
					}

					this.pickerUI = tbGenerator.createElement(
							ANCHOR_PICKER_FEATURE, this, false, {
								title : "Anchor Plugin"
							});
					tbGenerator.addElement(GROUP, plg.Plugin.SORT_FORMAT,
							this.pickerUI, 10);

					var groupFeature = GROUP + "#" + ANCHOR_PICKER_FEATURE;
					tbGenerator.registerIcon(groupFeature, "anchor");
				},

				execute : function(id, value, envOptions) {
					if (!isValidSelection()) {
						return;
					}

					var context = envOptions.editContext, selection = CUI.rte.Selection
							.createProcessingSelection(context), ek = this.editorKernel, startNode = selection.startNode;

					if ((selection.startOffset === startNode.length)
							&& (startNode != selection.endNode)) {
						startNode = startNode.nextSibling;
					}

					var tag = CUI.rte.Common.getTagInPath(context, startNode,
							"a"), plugin = this, dialog, anchorval = $(tag)
							.attr("href"), dm = ek.getDialogManager(), $container = CUI.rte.UIUtils
							.getUIContainer($(context.root)), propConfig = {
						'parameters' : {
							'command' : this.pluginId + '#'
									+ ANCHOR_PICKER_FEATURE
						}
					};
					var cond1 = (anchorval != undefined);
					var cond2 = (anchorval != null);

					if (cond1 || cond2) {
						anchorval = anchorval.substring(1);
					}

					if (this.aeAnchorPickerDialog) {
						dialog = this.aeAnchorPickerDialog;
					} else {
						dialog = new AEAnchorPickerDialog();

						dialog
								.attach(propConfig, $container,
										this.editorKernel);

						dialog.$dialog.css("-webkit-transform", "scale(0.9)")
								.css("-webkit-transform-origin", "0 0").css(
										"-moz-transform", "scale(0.9)").css(
										"-moz-transform-origin", "0px 0px");

						dialog.$dialog.find("iframe").attr("src",
								getPickerIFrameUrl(anchorval));
						this.aeAnchorPickerDialog = dialog;
					}

					dm.show(dialog);

					registerReceiveDataListener(receiveMessage);

					function isValidSelection() {
						var winSel = window.getSelection();
						return winSel && winSel.rangeCount == 1
								&& winSel.getRangeAt(0).toString().length > 0;
					}

					function getPickerIFrameUrl(anchorval) {
						var url = PICKER_URL + "?" + REQUESTER + "=" + GROUP;

						if (!_.isEmpty(anchorval)) {
							url = url + "&" + PICKER_NAME_IN_POPOVER + "="
									+ anchorval;
						}
						console.log(url);
						return url;
					}

					function removeReceiveDataListener(handler) {
						if (window.removeEventListener) {
							window.removeEventListener("message", handler);
						} else if (window.detachEvent) {
							window.detachEvent("onmessage", handler);
						}
					}

					function registerReceiveDataListener(handler) {
						if (window.addEventListener) {
							window.addEventListener("message", handler, false);
						} else if (window.attachEvent) {
							window.attachEvent("onmessage", handler);
						}
					}

					function receiveMessage(event) {

						if (_.isEmpty(event.data)) {
							return;
						}

						var message = JSON.parse(event.data), action;

						if (!message || message.sender !== GROUP) {
							return;
						}

						action = message.action;

						if (action === "submit") {
							if (!_.isEmpty(message.data)) {
								ek.relayCmd(id, message.data);
							}

						} else if (action === "remove") {
							ek.relayCmd(id);
							plugin.aeAnchorPickerDialog = null;
						} else if (action === "cancel") {
							plugin.aeAnchorPickerDialog = null;
						}

						dialog.hide();
						removeReceiveDataListener(receiveMessage);
					}
				},

				// to mark the icon selected/deselected
				updateState : function(selDef) {
					var hasUC = this.editorKernel.queryState(
							ANCHOR_PICKER_FEATURE, selDef);

					if (this.pickerUI != null) {
						this.pickerUI.setSelected(hasUC);
					}
				}
			});

	CUI.rte.plugins.PluginRegistry.register(GROUP, TouchUIAnchorPickerPlugin);

	var TouchUIAnchorPickerCmd = new Class(
			{
				toString : "TouchUIAnchorPickerCmd",
				extend : CUI.rte.commands.Command,

				isCommand : function(cmdStr) {
					return (cmdStr.toLowerCase() == ANCHOR_PICKER_FEATURE);
				},

				getProcessingOptions : function() {
					var cmd = CUI.rte.commands.Command;
					return cmd.PO_SELECTION | cmd.PO_BOOKMARK | cmd.PO_NODELIST;
				},

				_getTagObject : function(anchorval) {
					return {
						"tag" : "a",
						"attributes" : {
							"href" : "#" + anchorval
						}
					};
				},

				execute : function(execDef) {
					var anchorval = execDef.value ? execDef.value[PICKER_NAME_IN_POPOVER]
							: undefined, selection = execDef.selection, nodeList = execDef.nodeList;

					if (!selection || !nodeList) {
						return;
					}

					var common = CUI.rte.Common, context = execDef.editContext, tagObj = this
							._getTagObject(anchorval);

					// if no anchorval value passed, assume delete and remove
					// anchorval
					if (_.isEmpty(anchorval)) {
						nodeList.removeNodesByTag(execDef.editContext,
								tagObj.tag, undefined, true);
						return;
					}

					// remove existing anchorval before adding new anchorval
					if (tags != null) {
						nodeList.removeNodesByTag(execDef.editContext,
								tagObj.tag, undefined, true);
						nodeList.commonAncestor = nodeList.nodes[0].dom.parentNode;
					}

					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);
					}
				}
			});

	CUI.rte.commands.CommandRegistry.register(ANCHOR_PICKER_FEATURE,
			TouchUIAnchorPickerCmd);

	function addPluginToDefaultUISettings() {
		var toolbar = CUI.rte.ui.cui.DEFAULT_UI_SETTINGS.inline.toolbar;
		toolbar.splice(3, 0, GROUP + "#" + ANCHOR_PICKER_FEATURE);

		toolbar = CUI.rte.ui.cui.DEFAULT_UI_SETTINGS.fullscreen.toolbar;
		toolbar.splice(3, 0, GROUP + "#" + ANCHOR_PICKER_FEATURE);
	}

	function addDialogTemplate() {
		var url = PICKER_URL + "?" + REQUESTER + "=" + GROUP;

		var html = "<iframe width='600px' height='500px' frameBorder='0' src='"
				+ url + "'></iframe>";

		if (_.isUndefined(CUI.rte.Templates)) {
			CUI.rte.Templates = {};
		}

		if (_.isUndefined(CUI.rte.templates)) {
			CUI.rte.templates = {};
		}

		CUI.rte.templates['dlg-' + TCP_DIALOG] = CUI.rte.Templates['dlg-'
				+ TCP_DIALOG] = Handlebars.compile(html);
	}
}(jQuery, window.CUI, jQuery(document)));

(function($, $document) {
	var SENDER = "adobe-explorers", REQUESTER = "requester", ANCHORVAL = "anchorvalue", ADD_ANCHOR_BUTTON = "#AELINK_ANCHOR_ADD", REMOVE_ANCHOR_BUTTON = "#AELINK_ANCHOR_REMOVE";

	if (queryParameters()[REQUESTER] !== SENDER) {
		return;
	}

	$(function() {
		_.defer(stylePopoverIframe);
	});

	function queryParameters() {
		var result = {}, param, params = document.location.search
				.split(/\?|\&/);

		params.forEach(function(it) {
			if (_.isEmpty(it)) {
				return;
			}

			param = it.split("=");
			result[param[0]] = param[1];
		});

		return result;
	}

	function stylePopoverIframe() {
		var queryParams = queryParameters(), $dialog = $("coral-dialog");

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

		$dialog.css("overflow", "hidden").css("background-color", "#fff");
		$dialog[0].open = true;

		var $addAnchor = $dialog.find(ADD_ANCHOR_BUTTON), $removeAnchor = $dialog
				.find(REMOVE_ANCHOR_BUTTON), anchorval = queryParameters()[ANCHORVAL];
		if (!_.isEmpty(anchorval)) {
			$("input[name='./anchorvalue']").val(anchorval);
		}
		adjustHeader($dialog);
		$(ADD_ANCHOR_BUTTON).css("margin-left", "220px");
		$addAnchor.click(sendDataMessage);
		$removeAnchor.click(sendRemoveMessage);
	}

	function adjustHeader($dialog) {
		var $header = $dialog.css("background-color", "#fff").find(
				".coral3-Dialog-header");
		$header.find(".cq-dialog-submit").remove();
		$header.find(".cq-dialog-cancel").click(function(event) {
			event.preventDefault();
			$dialog.remove();
			sendCancelMessage();
		});
	}

	function sendCancelMessage() {
		var message = {
			sender : SENDER,
			action : "cancel"
		};
		parent.postMessage(JSON.stringify(message), "*");
	}

	function sendRemoveMessage() {
		var message = {
			sender : SENDER,
			action : "remove"
		}, $dialog, anchorval;

		$dialog = $(".cq-dialog");
		anchorval = $dialog.find("[name='./" + ANCHORVAL + "']").val();
		parent.postMessage(JSON.stringify(message), "*");
	}

	function sendDataMessage() {
		var message = {
			sender : SENDER,
			action : "submit",
			data : {}
		}, $dialog, anchorval;

		$dialog = $(".cq-dialog");
		anchorval = $dialog.find("[name='./" + ANCHORVAL + "']").val();
		message.data[ANCHORVAL] = anchorval;
		parent.postMessage(JSON.stringify(message), "*");
	}
})(jQuery, jQuery(document));

2. dialog-before-submit.js

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

    var ui = $(window).adaptTo("foundation-ui");
    var $document = $(document);

    function gotoPageEditor(dialog) {
        if (dialog.data("cqDialogReturntoreferral")){ 
            window.location = document.referrer;
        } else {
            if (dialog.closest(".cq-dialog-page").length) {
                window.location = dialog.data("cqDialogPageeditor");
            } else {
                dialog.remove();
            }
        }

        $document.trigger("dialog-closed");
    }

    function _cancel($dialog) {
        $dialog.trigger("dialog-beforeclose");

        /**
         * @deprecated Since 6.3
         */
        // Still requested by external listeners
        var $dialogForm = $dialog.find("form").eq(0);
        $dialogForm.trigger("dialog-beforeclose");

        gotoPageEditor($dialogForm);
    }

    $document.on("click", ".cq-dialog-cancel", function(e) {
        e.preventDefault();

        var $dialog = $(this).closest(".cq-Dialog");
        _cancel($dialog);
    });

    $document.on("coral-overlay:beforeclose", ".cq-Dialog", function(e) {
        // only triggered when the dialog is closed by the escape key
        if ($(e.target).hasClass("cq-Dialog")) {
            // components in the the dialog like select trigger this event as well
            _cancel($(this));
        }
    });

    $document.on("click", ".cq-dialog-layouttoggle", function(e) {
        var dialog = this.closest("coral-dialog");
        Granite.DialogUtils.unifySpacing(dialog);
        e.preventDefault();
    });

    $document.on("click", ".cq-dialog-help", function(e) {
        e.preventDefault();

        var el = $(this);
        window.open(el.data("href"), "_blank");
    });

    $document.on("foundation-form-submitted", ".cq-Dialog", function(e, status, xhr) {
        if (status === true) {
            var $dialog = $(this);
            $document.trigger("dialog-success");

            // Trigger an event 'dialog-beforeclose' which can be used by the contained
            // fields to finish editing before the dialog closes. (CQ-63409)
            // This event is triggered just before the dialog is surely being closed
            $dialog.trigger("dialog-beforeclose");


            /**
             * @deprecated Since 6.3
             */
            // Still requested by external listeners
            var $dialogForm = $dialog.find("form").eq(0);
            $dialogForm.trigger("dialog-beforeclose");

            gotoPageEditor($dialog.find("form").eq(0));
        } else {
            $document.trigger("dialog-fail", xhr);
        }
    });

    $document.on("foundation-contentloaded", function (event) {
        // dialog may be the page properties form launched from the sites console
        // or the .cq-dialog launched from authoring
        var $dialog = $(event.target).find("#cq-sites-properties-form, .cq-dialog");

        if ($dialog.length) {
            var resourceType = $dialog.find("input[name='./sling:resourceType']").val();

            Granite.DialogUtils.unifySpacing($dialog.parents("coral-dialog").get(0));

            $document.trigger($.Event("dialog-loaded", {
                dialog: $dialog,
                resourceType: resourceType
            }));
        }
    });

    // Force blur on fields before submitting (CQ-55684)
    // Listen on body because form submit is already intercepted to handle tags (see properties.js)
    $("body").on("submit", ".cq-Dialog", function (event) {
        var $dialog = $(this);

//changes by hemant start
        $("input[name$='text']").val($dialog.find(".cq-RichText>.cq-RichText-editable").html());	
//changes by hemant end
        
        $dialog.find("input, textarea").blur();
    });

})(window, document, Granite.$, Granite.author);

3. richtext.js

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

    var rteFixedColumnCss = "cq-RichText-FixedColumn-column";
    var rteFixedColumnCssCompat = "coral-RichText-FixedColumn-column";
    var DATA_RTE_INSTANCE = "rteinstance";

    var startRTE = function($editable, options) {
        var editorType = $editable.data("editorType"), rtePluginsDefaults, configCallBack;
        var externalStyleSheets = $editable.data("externalStyleSheets"), index, $styleSheet, styleElements;
        if (editorType === "table") {
            rtePluginsDefaults = {
                "useColPercentage": false,
                "rtePlugins": {
                    "table": {
                        "features": "*",
                        "defaultValues": {
                            "width": "100%"
                        },
                        "editMode": CUI.rte.plugins.TablePlugin.EDITMODE_TABLE
                    }
                }
            };
            configCallBack = function(config) {
                return Granite.Util.applyDefaults({}, rtePluginsDefaults, config);
            };
        }
        var rte = new CUI.RichText({
            "element": $editable,
            "componentType": editorType,
            "preventCaretInitialize": true
        });
        if (externalStyleSheets && externalStyleSheets.length > 0) {
            externalStyleSheets = externalStyleSheets.split(",");
            for (index = 0; index < externalStyleSheets.length; index++) {
                $styleSheet = $("head link[href='" + externalStyleSheets[index] +"']");
                if ($styleSheet.length <= 0) {
                    $styleSheet = $("<link rel=\"stylesheet\" href=\"" + externalStyleSheets[index] + "\" type=\"text/css\">");
                    $("head").append($styleSheet);
                    styleElements = $editable.data("externalStyleElements");
                    if (!styleElements) {
                        styleElements = [$styleSheet];
                    } else {
                        styleElements.push($styleSheet);
                    }
                    $editable.data("externalStyleElements", styleElements);
                }
            }
        }
        CUI.rte.ConfigUtils.loadConfigAndStartEditing(rte, $editable, configCallBack);
    };

    $(document).on("foundation-contentloaded", function(e) {

        var $container = $(e.target).hasClass(".cq-RichText") ? $(e.target) : $(e.target).find(".cq-RichText");

        // Added in 6.4 to ensure Full BC (CQ-4231708)
        // Should ideally be removed in 6.6
        $container.each(function () {
            var $this = $(this);
            $this.closest("." + rteFixedColumnCss).addClass(rteFixedColumnCssCompat);
            $this.closest("." + rteFixedColumnCssCompat + ":not(." + rteFixedColumnCss + ")").addClass(rteFixedColumnCss);
        });

        // Copy hidden text field value to RTE and stop implicit submission of form, when enter key is pressed in RTE UI.
        // We don't put html value into RTE while rendering, otherwise, the linkchecker
        // converts invalid links to image tags. (See CQ-4219770). So, we add it in the
        // value attribute of hidden input field when rendering and copy it from there now
        $container.each(function() {
            var $this = $(this);
            var $richTextDiv = $this.find(".cq-RichText-editable");
            if (!$richTextDiv.data(DATA_RTE_INSTANCE)) {
                var html = $this.find("input[type='hidden'][data-cq-richtext-input='true']").val();
                $richTextDiv.empty().append(html);
            }
            $this.on("keypress", "input", function (e) {
                if (e.keyCode === 13) {
                    e.preventDefault();
                }
            });
        });

        // Copy RTE text to hidden field
        $container.on("change", "[data-cq-richtext-editable='true']", function() {
            var el = $(this).closest(".cq-RichText");
            var rteInstance = el.find(".cq-RichText-editable").data(DATA_RTE_INSTANCE);
            el.find("input[type=hidden][data-cq-richtext-input='true']").val(rteInstance.getContent());
        });

        var $richTextDiv = $(e.target).find(".cq-RichText>.cq-RichText-editable");
        $richTextDiv.each(function() {
            var $this = $(this);
            if ($this.data("customStart")) {
                $this.on("rte-start", function() {
                    var $this = $(this);
                    if ($this.data("useFixedInlineToolbar") && !$this.data(DATA_RTE_INSTANCE)) {
                        var html = $(this).parent().find("input[type=hidden][data-cq-richtext-input='true']").val();
                        $this.empty().append(html);
                        startRTE($this);
                    }
                });
            } else {
                if ($this.data("useFixedInlineToolbar") && !$this.data(DATA_RTE_INSTANCE)) {
                    startRTE($this);
                }
            }
            $this.on("editing-start", function() {
                var rte = $(this).data(DATA_RTE_INSTANCE);
                var $this = $(this);
                var $coralDialog = $this.closest("coral-dialog");
                rte.editorKernel.getToolbar().hide();
                if ($coralDialog.length && $coralDialog[0].fullscreen === true) {
                    switchToolbar("dialogFullScreen", rte);
                }
                $this.closest("coral-dialog-content").on("click", function(e) {
                    var $target = $(e.target);
                    if (!$target.closest(".cq-RichText").length) {
                        //[Workaround] CQ-4249635 : Safari doesn't blur selection from contenteditable when clicking on radio/checkbox
                        if (CUI.rte.Common.ua.isSafari && $target.is("input[type='radio'], input[type='checkbox']")) {
                            var context = rte.editorKernel.getEditContext();
                            var selection = CUI.rte.Selection.getSelection(context);
                            selection.removeAllRanges();
                        }
                        if (rte.useFixedInlineToolbar && !rte.sourceEditMode) {
                            rte.editorKernel.toolbar.hide();
                        }
                    }
                });
            });
            $(this).on("click", function() {
                var self = this;
                $richTextDiv.each(function() {
                    var rte = $(this).data(DATA_RTE_INSTANCE);

                    if (this !== self && rte && !rte.sourceEditMode) {
                        rte.editorKernel.getToolbar().hide();
                    }
                });
            });
        });
    });

    var rteFinish = function() {
        var $this = $(this), index;
        var rteInstance = $this.data(DATA_RTE_INSTANCE), styleElements = $this.data("externalStyleElements");
        if (rteInstance) {
            rteInstance.finish(false);
            if (styleElements) {
                for (index = 0; index < styleElements.length; index++) {
                    styleElements[index].remove();
                }
            }
            $this.removeData(DATA_RTE_INSTANCE);
        }
    };
//changes by hemant start
    var htmlContent='';
//changes by hemant end

    $(document).on("dialog-beforeclose", ".cq-Dialog", function(e) {

//changes by hemant start
        if(htmlContent===''){
            htmlContent=$(this).closest(".cq-Dialog").find(".cq-RichText>.cq-RichText-editable").html();
        }
		$(this).find(".cq-RichText>.cq-RichText-editable").html(htmlContent);
//changes by hemant end
        
        if (!$(e.target).hasClass("cq-Dialog")) {
            // dialog currently throws dialog-beforeclose twice - once on form and once on coral-dialog element
            // we need to only listen to event on coral-dialog
            return;
        }

        $(this).find(".cq-RichText>.cq-RichText-editable").each(rteFinish);
    });

    $(document).on("dialog-layouttoggle-fullscreen", ".cq-Dialog", function(e) {
        var $richTextDiv = $(e.target).find(".cq-RichText>.cq-RichText-editable");
        $richTextDiv.each(function() {
            var rte = $(this).data(DATA_RTE_INSTANCE);
            if (rte) {
                switchToolbar("dialogFullScreen", rte)
            }
        });
    });

    $(document).on("dialog-layouttoggle-floating", ".cq-Dialog", function(e) {
        var $richTextDiv = $(e.target).find(".cq-RichText>.cq-RichText-editable");
        $richTextDiv.each(function() {
            var rte = $(this).data(DATA_RTE_INSTANCE);
            if (rte) {
                switchToolbar("inline", rte)
            }
        });
    });

    $(document).on("click", ".coral-Wizard-nextButton", function(e) {
        $(this).closest(".foundation-form").find(".cq-RichText>.cq-RichText-editable").each(rteFinish);
    });

    function switchToolbar(toolbarType, rte) {
        var ek = rte.editorKernel;
        if (rte.sourceEditMode) {
            ek.fireUIEvent('disablesourceedit');
        }
        if (!ek.hasBackgroundToolbar(toolbarType)) {
            ek.addBackgroundToolbar({
                "tbType": toolbarType,
                "isFullScreen": ek.getEditContext().getState("fullscreenadapter").isFullScreen(),
                "useFixedInlineToolbar": true
            });
        }
        ek.setActiveToolbar(toolbarType);
    }

    CUI.rte.Theme.BLANK_IMAGE = Granite.HTTP.externalize("/libs/clientlibs/granite/richtext/resources/images/blank.png");

})(window, document, Granite.$);

Now, include the above created clientlibs to cq:dialog of text component by adding the property
extraClientlibs[] | String | cq.authoring.dialog.all, core.aeexplorers.text

The plugin is now created and ready to use.

How to use ?

To create an anchor tag, select the text and click on Anchor Plugin

Fill in the data and click on Create button. The text will now behave as an anchor tag having href set to the tag.

To remove the anchor tag from a text, select the text and click on Anchor Plugin. In the anchor popover dialog, click on Remove button. The anchor tag will be removed from targeted text.

Note : To modify an existing anchor value, remove the existing anchor value by clicking on remove button and then add anchor to it.

To download the installable jcr package, click on the below button.

Mail Server Set up in AEM using GMail

Applicable for versions AEM 6.x

AEM provides OOTB OSGI configuration to enable the mailing service.This service is named as DAY CQ Mailing service. Here the configuration of the SMTP server to be used needs to be added. In this blog, we are going to see how can we use gmail SMTP server to enable the mailing service in AEM.

Following are the steps :

SMTP Server Host Namesmtp.gmail.com
SMTP Server Port465
SMTP User<gmail address>
SMTP Password<gmail password>
“From” address<gmail address>
SMTP use SSLCheck this
SMTP use StartTLSDon’t check
Debug emailDon’t check

Search for the config file using PID. Check in this config file into configuration code base to persist the configuration even after deployment. [com.day.cq.mailer.DefaultMailService.config]

# Configuration created by Apache Sling JCR Installer
smtp.password="secretPassword"
debug.email=B"false"
smtp.port=I"465"
smtp.user="adobeexplorers@gmail.com"
smtp.ssl=B"true"
smtp.starttls=B"false"
from.address="adobe-explorers@gmail.com"
smtp.host="smtp.gmail.com"

The mail server integration is completed and Day CQ Mail service is active now. You can send emails from AEM instance.