Goal
Touch UI Color Picker Plugin for RTE (Rich Text Editor) Dialog - /libs/cq/gui/components/authoring/dialog/richtext
In Core Components 2.8.0 uiSettings/cui/dialogFullScreen was removed and the plugin settings were moved to Template (Component) Policies, this post is for making the Font & Color plugin work with Core Components 2.8.0
For a similar extension on 65 and older Core Components check this post 64 check this post 63 check this post
For demo purposes, design dialog of core text component v2 was modified to add the font picker configuration - /apps/core/wcm/components/text/v2/text/cq:design_dialog/content/items/tabs/items/plugins/items/experience-aem-fonts
Demo | Package Install | Github
Plugin Configuration in Design Dialog
Plugin Config in Template Policy
Plugin Picker
Font and Color
Solution
1) Login to CRXDE Lite, add nt:folder /apps/eaem-fonts-plugin
2) To show the color picker in a dialog create cq:Page /apps/eaem-fonts-plugin/font-selector and add eaem.rte.font.plugin categories in /apps/eaem-fonts-plugin/font-selector/jcr:content/head/clientlibs
<?xml version="1.0" encoding="UTF-8"?> <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" jcr:primaryType="cq:Page"> <jcr:content jcr:mixinTypes="[sling:VanityPath]" jcr:primaryType="nt:unstructured" jcr:title="EAEM Font Selector" sling:resourceType="granite/ui/components/coral/foundation/page"> <head jcr:primaryType="nt:unstructured"> <favicon jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/page/favicon"/> <viewport jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/admin/page/viewport"/> <clientlibs jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/includeclientlibs" categories="[coralui3,granite.ui.coral.foundation,granite.ui.shell,dam.gui.admin.coral, eaem.rte.font.plugin]"/> </head> <body jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/page/body"> <items jcr:primaryType="nt:unstructured"> <form jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form" class="foundation-form content-container" maximized="{Boolean}true" style="vertical"> <items jcr:primaryType="nt:unstructured"> <wizard jcr:primaryType="nt:unstructured" jcr:title="Select Text Font Color..." sling:resourceType="granite/ui/components/coral/foundation/wizard"> <items jcr:primaryType="nt:unstructured"> <text jcr:primaryType="nt:unstructured" jcr:title="Select Text Font Color..." sling:resourceType="granite/ui/components/coral/foundation/container"> <items jcr:primaryType="nt:unstructured"> <fixedColumns jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns" margin="{Boolean}true"> <items jcr:primaryType="nt:unstructured"> <column jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container"> <items jcr:primaryType="nt:unstructured"> <fontSize jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/select" fieldDescription="Select text size" fieldLabel="Size" name="./size"> <items jcr:primaryType="nt:unstructured"> <def jcr:primaryType="nt:unstructured" text="Select Size" value=""/> <small jcr:primaryType="nt:unstructured" text="Small (15px)" value="15px"/> <medium jcr:primaryType="nt:unstructured" text="Medium (30px)" value="30px"/> <large jcr:primaryType="nt:unstructured" text="Large (40px)" value="40px"/> </items> </fontSize> <color jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/colorfield" fieldDescription="Select text color" fieldLabel="Text Color" name="./color" showProperties="{Boolean}true"/> <bgColor jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/colorfield" fieldDescription="Select background color" fieldLabel="Background Color" name="./bgColor" showProperties="{Boolean}true"/> </items> </column> </items> </fixedColumns> </items> <parentConfig jcr:primaryType="nt:unstructured"> <prev granite:class="foundation-wizard-control" jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/anchorbutton" text="Cancel"> <granite:data jcr:primaryType="nt:unstructured" foundation-wizard-control-action="cancel"/> </prev> <next granite:class="foundation-wizard-control" jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/button" disabled="{Boolean}true" text="Apply" type="submit" variant="primary"> <granite:data jcr:primaryType="nt:unstructured" foundation-wizard-control-action="next"/> </next> </parentConfig> </text> </items> </wizard> </items> </form> </items> </body> </jcr:content> </jcr:root>
3) Create clientlib (cq:ClientLibraryFolder) /apps/eaem-fonts-plugin/clientlib set property categories to [cq.authoring.dialog.all, eaem.rte.font.plugin] and dependencies to [lodash]
4) Create file (nt:file) /apps/eaem-fonts-plugin/clientlib/js.txt, add the following content
eaem-fonts.js
5) Create file (nt:file) /apps/eaem-fonts-plugin/clientlib/eaem-fonts.js, add the following code
(function($, CUI, $document){ var GROUP = "experience-aem-fonts", FONT_FEATURE = "applyFont", FONT_SIZE_FEATURE = "fontSize", TEXT_COLOR_FEATURE = "textColor", TEXT_BG_COLOR_FEATURE = "textBackgroundColor", EAEM_APPLY_FONT_DIALOG = "eaemTouchUIApplyFontDialog", SENDER = "experience-aem", REQUESTER = "requester", $eaemFontPicker, CANCEL_CSS = "[data-foundation-wizard-control-action='cancel']", FONT_SELECTOR_URL = "/apps/eaem-fonts-plugin/font-selector.html", url = document.location.pathname; if( url.indexOf(FONT_SELECTOR_URL) == 0 ){ handlePicker(); return; } function handlePicker(){ $document.on("foundation-contentloaded", fillDefaultValues); $document.on("click", CANCEL_CSS, sendCancelMessage); $document.submit(sentTextAttributes); } 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 setWidgetValue(form, selector, value, enable){ Coral.commons.ready(form.querySelector(selector), function (field) { field.value = _.isEmpty(value) ? "" : decodeURIComponent(value); if(enable){ delete field.disabled; }else{ field.disabled = "disabled"; } }); } function fillDefaultValues(){ var queryParams = queryParameters(), $form = $("form"); if(_.isEmpty(queryParams.features)){ return; } var features = queryParams.features.split(","); setWidgetValue($form[0], "[name='./color']", queryParams.color, features.includes(TEXT_COLOR_FEATURE)); setWidgetValue($form[0], "[name='./size']", queryParams.size, features.includes(FONT_SIZE_FEATURE)); setWidgetValue($form[0], "[name='./bgColor']", queryParams.bgColor, features.includes(TEXT_BG_COLOR_FEATURE)); $form.css("background-color", "#fff"); } function sentTextAttributes(){ var message = { sender: SENDER, action: "submit", data: {} }, $form = $("form"), $field; _.each($form.find("[name^='./']"), function(field){ $field = $(field); message.data[$field.attr("name").substr(2)] = $field.val(); }); getParent().postMessage(JSON.stringify(message), "*"); } function sendCancelMessage(){ var message = { sender: SENDER, action: "cancel" }; getParent().postMessage(JSON.stringify(message), "*"); } function getParent() { if (window.opener) { return window.opener; } return parent; } addPlugin(); addPluginToDefaultUISettings(); addDialogTemplate(); function addDialogTemplate(){ var url = Granite.HTTP.externalize(FONT_SELECTOR_URL) + "?" + REQUESTER + "=" + SENDER; var html = "<iframe width='700px' 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-' + EAEM_APPLY_FONT_DIALOG] = CUI.rte.Templates['dlg-' + EAEM_APPLY_FONT_DIALOG] = Handlebars.compile(html); } function rgbToHex(color){ if(_.isEmpty(color)){ return color; } if(color.indexOf("rgb") == 0){ color = CUI.util.color.RGBAToHex(color); } return color; } function addPluginToDefaultUISettings(){ var groupFeature = GROUP + "#" + FONT_FEATURE, toolbar = CUI.rte.ui.cui.DEFAULT_UI_SETTINGS.dialogFullScreen.toolbar; if(toolbar.includes(groupFeature)){ return; } toolbar.splice(3, 0, groupFeature); } var EAEMApplyFontDialog = new Class({ extend: CUI.rte.ui.cui.AbstractDialog, toString: "EAEMApplyFontDialog", initialize: function(config) { this.exec = config.execute; }, getDataType: function() { return EAEM_APPLY_FONT_DIALOG; } }); function addPlugin(){ var EAEMTouchUIFontPlugin = new Class({ toString: "EAEMTouchUIFontPlugin", extend: CUI.rte.plugins.Plugin, pickerUI: null, getFeatures: function() { return [ FONT_FEATURE ]; }, initializeUI: function(tbGenerator) { var plg = CUI.rte.plugins; addPluginToDefaultUISettings(); if (!this.isFeatureEnabled(FONT_FEATURE)) { return; } this.pickerUI = tbGenerator.createElement(FONT_FEATURE, this, false, { title: "Select Font..." }); tbGenerator.addElement(GROUP, plg.Plugin.SORT_FORMAT, this.pickerUI, 10); var groupFeature = GROUP + "#" + FONT_FEATURE; tbGenerator.registerIcon(groupFeature, "abc"); }, execute: function (pluginCommand, value, envOptions) { var context = envOptions.editContext, ek = this.editorKernel; if (pluginCommand != FONT_FEATURE) { return; } if(!isValidSelection()){ return; } var selection = CUI.rte.Selection.createProcessingSelection(context), startNode = selection.startNode; if ( (selection.startOffset === startNode.length) && (startNode != selection.endNode)) { startNode = startNode.nextSibling; } var $tag = $(CUI.rte.Common.getTagInPath(context, startNode, "span")), size = $tag.css("font-size"),dialog,dm = ek.getDialogManager(), $container = CUI.rte.UIUtils.getUIContainer($(context.root)), propConfig = { 'parameters': { 'command': this.pluginId + '#' + FONT_FEATURE } }; var color = this.getColorAttributes($tag); if(this.eaemApplyFontDialog){ dialog = this.eaemApplyFontDialog; dialog.$dialog.find("iframe").attr("src", this.getPickerIFrameUrl(this.config.features, size, color.color, color.bgColor)); }else{ dialog = new EAEMApplyFontDialog(); 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", this.getPickerIFrameUrl(this.config.features, size, color.color, color.bgColor)); this.eaemApplyFontDialog = dialog; } dm.show(dialog); $(window).off('message', receiveMessage).on('message', receiveMessage); function isValidSelection(){ var winSel = window.getSelection(); return winSel && winSel.rangeCount == 1 && winSel.getRangeAt(0).toString().length > 0; } function receiveMessage(event) { event = event.originalEvent || {}; if (_.isEmpty(event.data)) { return; } var message, action; try{ message = JSON.parse(event.data); }catch(err){ return; } if (!message || message.sender !== SENDER) { return; } action = message.action; if(action === "submit"){ ek.relayCmd(pluginCommand, message.data); } dialog.hide(); } }, getColorAttributes: function($tag){ var key, color = { color: "", bgColor : ""}; if(!$tag.attr("style")){ return color; } //donot use .css("color"), it returns default font color, if color is not set var parts = $tag.attr("style").split(";"); _.each(parts, function(value){ value = value.split(":"); key = value[0] ? value[0].trim() : ""; value = value[1] ? value[1].trim() : ""; if(key == "color"){ color.color = rgbToHex(value); }else if(key == "background-color"){ color.bgColor = rgbToHex(value); } }); return color; }, showFontModal: function(url){ var self = this, $iframe = $('<iframe>'), $modal = $('<div>').addClass('eaem-cfm-font-size coral-Modal'); $iframe.attr('src', url).appendTo($modal); $modal.appendTo('body').modal({ type: 'default', buttons: [], visible: true }); $eaemFontPicker = $modal; $eaemFontPicker.eaemFontPlugin = self; $modal.nextAll(".coral-Modal-backdrop").addClass("cfm-coral2-backdrop"); }, getPickerIFrameUrl: function(features, size, color, bgColor){ var url = Granite.HTTP.externalize(FONT_SELECTOR_URL) + "?" + REQUESTER + "=" + SENDER; url = url + "&features=" + features.join(","); if(!_.isEmpty(color)){ url = url + "&color=" + encodeURIComponent(color); } if(!_.isEmpty(bgColor)){ url = url + "&bgColor=" + encodeURIComponent(bgColor); } if(!_.isEmpty(size)){ url = url + "&size=" + size; } return url; }, updateState: function(selDef) { var hasUC = this.editorKernel.queryState(FONT_FEATURE, selDef); if (this.pickerUI != null) { this.pickerUI.setSelected(hasUC); } } }); var EAEMTouchUIFontCmd = new Class({ toString: "EAEMTouchUIFontCmd", extend: CUI.rte.commands.Command, isCommand: function (cmdStr) { return (cmdStr.toLowerCase() == FONT_FEATURE); }, getProcessingOptions: function () { var cmd = CUI.rte.commands.Command; return cmd.PO_SELECTION | cmd.PO_BOOKMARK | cmd.PO_NODELIST; }, getTagObject: function(textData) { var style = ""; if(!_.isEmpty(textData.color)){ style = "color: " + textData.color + ";"; } if(!_.isEmpty(textData.size)){ style = style + "font-size: " + textData.size + ";"; } if(!_.isEmpty(textData.bgColor)){ style = style + "background-color: " + textData.bgColor; } return { "tag": "span", "attributes": { "style" : style } }; }, execute: function (execDef) { var textData = execDef.value, selection = execDef.selection, nodeList = execDef.nodeList; if (!selection || !nodeList) { return; } var common = CUI.rte.Common, context = execDef.editContext, tagObj = this.getTagObject(textData); if(_.isEmpty(textData.size) && _.isEmpty(textData.color) && _.isEmpty(textData.bgColor)){ nodeList.removeNodesByTag(execDef.editContext, tagObj.tag, undefined, true); return; } var tags = common.getTagInPath(context, selection.startNode, tagObj.tag); //remove existing color before adding new color if (tags != null) { nodeList.removeNodesByTag(execDef.editContext, tagObj.tag, tags.attributes ? tags.attributes : undefined, true); } nodeList.surround(execDef.editContext, tagObj.tag, tagObj.attributes); }, queryState: function(selectionDef, cmd) { return false; } }); CUI.rte.commands.CommandRegistry.register(FONT_FEATURE, EAEMTouchUIFontCmd); CUI.rte.plugins.PluginRegistry.register(GROUP,EAEMTouchUIFontPlugin); } }(jQuery, window.CUI,jQuery(document)));
Hi, I am using AEM 6.5 and trying to integrate this feature to RTE. I am not able to integrate this. I have installed the package and added 'Plugin Configuration in Design Dialog' and also 'Plugin Config in Template Policy'. However, I don't see option 'AEM Experience Fonts & Colors' while updating the template policy. Am I missing anything?
ReplyDeleteHi there, can someone please let me know how to get 'ABC' icon for the font color picker. I am not able to get the icon. I am using AEM 6.5
ReplyDeleteHi,Same issue i am facing..
DeleteAre u still having same issue or resolved?
facing the same issue . Did you resolve it ? I want to add font size to RTE component
DeleteHi, I am using AEM 6.5 and trying to integrate this feature to RTE. As per above given instructions I have installed the package and added Plugin configurations. However, I don't see option color picker icon in the RTE rich text field.
ReplyDeleteHi Sreekanth,
ReplyDeleteThank you for writing this article. Please can you advise me if this works on a clean installation of AEM 6.5.2.0 with "We-Retail" sample content ?
I have tried following the steps below and I cannot see the Font & Colour icon in the Rich Text Editor ?
(1) Navigate to http://localhost:4502/crx/packmgr/index.jsp
(2) Upload and install package "eaem-fonts-plugin-6530.zip"
(3) Navigate to http://localhost:4502/crx/de/index.jsp#/apps/eaem-fonts-plugin
(4) Verify this contains folder "clientlib" and page "font-selector"
(5) Navigate to http://localhost:4502/crx/de/index.jsp#/apps/core/wcm/components/text/v2/text/cq%3Adesign_dialog/content/items/tabs/items/plugins/items
(6) Verify this contains "experience-aem-fonts" as well as Features , Formatting , Paraformat , Characters
(7) Navigate to http://localhost:4502/editor.html/conf/we-retail/settings/wcm/templates/hero-page/structure.html
(8) Observe error at top of page saying "TEMPLATE EDITORThis template is not a draft anymore and might already be referenced by one or more pages. Editing the structure will affect all the pages referencing it."
(9) Click "Page Policy"
(10) In Page Policy , the right-hand column (Properties) contains tabs Properties and Styles , and not Plugins and Styles as shown in https://1.bp.blogspot.com/-bnmdzZhBq9c/Xi8RhvyX9cI/AAAAAAAA3cI/nJLXjAoDC8o2LXTQ7F_V5pDoM2S406wcQCLcBGAsYHQ/s1600/360%2B-%2B6530%2B-%2Bpolicy.png
(11) Navigate to http://localhost:4502/crx/de/index.jsp#/conf/we-retail/settings/wcm/policies/weretail/components/content
(12) The "content" node does not contain child node "text" as shown in https://1.bp.blogspot.com/-W1EeSIA01lQ/Xi8RfoR7OrI/AAAAAAAA3b0/zhgwNQxIulgwgibtO9Ri9U7Vg7epnKoDACLcBGAsYHQ/s1600/360%2B-%2B6530%2B-%2Bcrx%2Bsave.png
Please can you advise me if there are any additional steps needed to get the Rich Text Editor Font & Colour working on a clean installation of AEM 6.5.2.0 with "We-Retail" sample content ?
Thank you for your time and advise,
Best regards,
James Collett
Hi, I am using AEM 6.5 and trying to integrate this feature to RTE. As per above given instructions I have installed the package and added Plugin configurations. However, I don't see option color picker icon in the RTE rich text field.
ReplyDeleteSame problem here, followed all the instructions but it's not appering the "abc" icon
ReplyDeleteIt is working fine
ReplyDeleteCheck if you have created the rtePlugins/experience-aem-fonts under the text/cq:dialog node.
/apps/core/wcm/components/text/v2/text/cq:dialog/content/items/tabs/items/properties/items/columns/items/column/items/text/rtePlugins/experience-aem-fonts
{"jcr:primaryType":"nt:unstructured","features":"textColor,applyFont,fontSize,textBackgroundColor"}
I tried to replicate these steps but couldnt see ABC in rte. Am I missing something instead of these steps
DeleteThanks for the article.
ReplyDeleteI can see the "abc" option but I have got an error 404: font-selector.html not found!
ReplyDeleteI have checked this file is not part of the package. Where that is come from?
has anyone made this work? I am unable to
ReplyDeleteCan We put more than one custom plugin in RTE ?
ReplyDeleteI have done same step an it is working in my sites pages but it is not working in my experience pages. I am getting error like (uncaught typeerror: eaemapplyfontdialog is not a constructor) and this is coming in eaem-fonts.js file at line number 227 and code is (dialog = new EAEMApplyFontDialog();), i was debug and then getting undefined value of new EAEMApplyFontDialog();. can any one help me on this.
ReplyDeleteFor which AEM instance you have followed these steps and how you are calling the js file. Because I cant see abc in site pages as well
Deletei am getting abc but not working as expecated getting beloe error -->
ReplyDeleterichtext.js:9819
Uncaught TypeError: Cannot read properties of null (reading 'cellSelection')
at Object.createNodeList (richtext.js:9819:23)
at instanceGenerator.execCmd (richtext.js:4368:40)
at instanceGenerator. (richtext.js:4249:26)
at callFn (richtext.js:2673:18)
is anyone got the same issue? or any resolution for same
abc icon is not visible . has anyone solved this issue ?
ReplyDeleteTo show the icon you need to add node in rtePlugins and in inline/fullScreenDialog call each feature - experience-aem-fonts#applyFont,experience-aem-fonts#fontSize,experience-aem-fonts#textColor,experience-aem-fonts#textBackgroundColor
ReplyDeleteAlso have this problem, how can I solve it?
ReplyDeleteclientlib.js:227 Uncaught TypeError: EAEMApplyFontDialog is not a constructor
at instanceGenerator.execute (clientlib.js:227:30)
at HTMLButtonElement. (richtext.js:42078:36)
at HTMLButtonElement.dispatch (jquery.js:5232:27)
at elemData.handle (jquery.js:4884:28)