Goal
Add Rich Text Editor to the Multiline TextField widget of Assets Metadata Editor. Extension adds button Open Editor to all Multiline TextFields for formatting the content in RTE
Demo | Package Install | Github
Button in Multiline Text
Rich Text Editor
Solution
1) Create clientlib ui.apps\src\main\content\jcr_root\apps\eaem-assets-metadata-rte\clientlibs\metadata-rte\clientlib\.content.xml for the RichText editor
<?xml version="1.0"encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0"xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="cq:ClientLibraryFolder"
categories="[dam.gui.actions.coral, eaem.metadata.editor.rte]"
dependencies="eaem.lodash"/>
2) Add JS file ui.apps\src\main\content\jcr_root\apps\eaem-assets-metadata-rte\clientlibs\metadata-rte\clientlib\metadata-rte-edit.js for adding the button and opening editor
(function ($, $document) {
"use strict";
const METADATA_EDITOR_PAGE = "/mnt/overlay/dam/gui/content/assets/metadataeditor.external.html",
META_RTE_URL = "/apps/eaem-assets-metadata-rte/clientlibs/metadata-rte/rte-modal.html",
CANCEL_CSS = "[data-foundation-wizard-control-action='cancel']",
RTE_VALUE_SEL = "[name='./text']",
RICH_TEXT_EDITABLE = ".cq-RichText-editable",
url = document.location.pathname,
DATA_RTE_INSTANCE = "rteinstance",
MULTI_LINE_TEXT_SEL = ".aem-assets-metadata-form-column textarea";
let $rteModal;
if (isMetadataEditPage()) {
$document.on("foundation-contentloaded", addRichTextButtonToTMultiLineText);
}else if(url.indexOf(META_RTE_URL) == 0){
$document.on("foundation-contentloaded", initRTE);
$document.on("click", CANCEL_CSS, sendCancelMessage);
$document.submit(postRTEContent);
}
function initRTE(){
const queryParams = queryParameters();
const INIT_INTERVAL = setInterval(() =>{
const rteInstance = $(RICH_TEXT_EDITABLE).data(DATA_RTE_INSTANCE);
if(rteInstance && rteInstance.editorKernel){
rteInstance.setContent(decodeURIComponent(queryParams.content));
clearInterval(INIT_INTERVAL);
}
}, 500);
}
function postRTEContent(){
const queryParams = queryParameters(),
rteInstance = $(RICH_TEXT_EDITABLE).data(DATA_RTE_INSTANCE);
let message = {
action: "save",
name: decodeURIComponent(queryParams.name),
content: rteInstance ? rteInstance.getContent() : $(RTE_VALUE_SEL).val()
};
getParent().postMessage(JSON.stringify(message), "*");
}
function addRichTextButtonToTMultiLineText() {
let $multiLines = $(MULTI_LINE_TEXT_SEL);
if ($multiLines.length === 0) {
return;
}
_.each($multiLines, (multiLine) => {
const $multiline = $(multiLine);
const editInRTE = new Coral.Button().set({
variant: 'secondary',
innerText: "Open Editor"
});
$(editInRTE).click((event) => {
event.preventDefault();
openRTEModal($multiline.attr("name"), $multiline.val());
});
$multiline.closest(".coral-Form-fieldwrapper").append(editInRTE);
})
addRTEDataListener();
$(window).off('message', closeRTEModal).on('message', closeRTEModal);
}
function addRTEDataListener(){
$(window).off('message', receiveMessage).on('message', receiveMessage);
function receiveMessage(event) {
event = event.originalEvent || {};
if (_.isEmpty(event.data)) {
return;
}
let message;
try{
message = JSON.parse(event.data);
}catch(err){
return;
}
if(message.action !== "save"){
return;
}
$("[name='"+ message.name + "']").val(message.content);
forceCloseRTEModal();
}
}
function closeRTEModal(event){
event = event.originalEvent || {};
if (_.isEmpty(event.data) || _.isEmpty($rteModal)) {
return;
}
var message;
try { message = JSON.parse(event.data); }catch(e){ return; }
if (!message || message.action !== "cancel") {
return;
}
forceCloseRTEModal();
}
function forceCloseRTEModal(){
var modal = $rteModal.data('modal');
modal.hide();
modal.$element.remove();
}
function openRTEModal(name, rteContent) {
let $iframe = $('<iframe>'),
$modal = $('<div>').addClass('eaem-metadata-rte-modal coral-Modal');
let src = META_RTE_URL + "?name="+ encodeURIComponent(name) + "&content="+ encodeURIComponent(rteContent);
$iframe.attr('src', src).appendTo($modal);
$modal.appendTo('body').modal({
type: 'default',
buttons: [],
visible: true
});
$rteModal = $modal;
}
function sendCancelMessage(){
var message = {
action: "cancel"
};
getParent().postMessage(JSON.stringify(message), "*");
}
function getParent() {
if (window.opener) {
return window.opener;
}
return parent;
}
function queryParameters() {
let 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 isMetadataEditPage() {
return (window.location.pathname.indexOf(METADATA_EDITOR_PAGE) >= 0);
}
}(jQuery, jQuery(document)));
3) Add the configuration for modal frame hosting the RTE in ui.apps\src\main\content\jcr_root\apps\eaem-assets-metadata-rte\clientlibs\metadata-rte\rte-modal\.content.xml
<?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="Richtext Editor"
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, cq.authoring.dialog,cq.authoring.dialog.rte.coralui3,eaem.metadata.editor.rte]"/>
</head>
<body
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/page/body">
<items jcr:primaryType="nt:unstructured">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form"
action=""
foundationForm="{Boolean}true"
maximized="{Boolean}true"
method="post"
novalidate="{Boolean}true"
style="vertical">
<items jcr:primaryType="nt:unstructured">
<wizard
jcr:primaryType="nt:unstructured"
jcr:title="Richtext Editor"
sling:resourceType="granite/ui/components/coral/foundation/wizard">
<items jcr:primaryType="nt:unstructured">
<container
granite:class="eaem-metadata-rte-form"
jcr:primaryType="nt:unstructured"
jcr:title="Richtext Editor"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<actionbar
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<text
jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/authoring/dialog/richtext"
name="./text"
useFixedInlineToolbar="{Boolean}true">
<rtePlugins jcr:primaryType="nt:unstructured">
<format
jcr:primaryType="nt:unstructured"
features="bold,italic"/>
<justify
jcr:primaryType="nt:unstructured"
features="-"/>
<links
jcr:primaryType="nt:unstructured"
features="modifylink,unlink"/>
<lists
jcr:primaryType="nt:unstructured"
features="*"/>
<misctools jcr:primaryType="nt:unstructured">
<specialCharsConfig jcr:primaryType="nt:unstructured">
<chars jcr:primaryType="nt:unstructured">
<default_copyright
jcr:primaryType="nt:unstructured"
entity="&copy;"
name="copyright"/>
<default_euro
jcr:primaryType="nt:unstructured"
entity="&euro;"
name="euro"/>
<default_registered
jcr:primaryType="nt:unstructured"
entity="&reg;"
name="registered"/>
<default_trademark
jcr:primaryType="nt:unstructured"
entity="&trade;"
name="trademark"/>
</chars>
</specialCharsConfig>
</misctools>
<paraformat
jcr:primaryType="nt:unstructured"
features="*">
<formats jcr:primaryType="nt:unstructured">
<default_p
jcr:primaryType="nt:unstructured"
description="Paragraph"
tag="p"/>
<default_h1
jcr:primaryType="nt:unstructured"
description="Heading 1"
tag="h1"/>
<default_h2
jcr:primaryType="nt:unstructured"
description="Heading 2"
tag="h2"/>
<default_h3
jcr:primaryType="nt:unstructured"
description="Heading 3"
tag="h3"/>
<default_h4
jcr:primaryType="nt:unstructured"
description="Heading 4"
tag="h4"/>
<default_h5
jcr:primaryType="nt:unstructured"
description="Heading 5"
tag="h5"/>
<default_h6
jcr:primaryType="nt:unstructured"
description="Heading 6"
tag="h6"/>
<default_blockquote
jcr:primaryType="nt:unstructured"
description="Quote"
tag="blockquote"/>
<default_pre
jcr:primaryType="nt:unstructured"
description="Preformatted"
tag="pre"/>
</formats>
</paraformat>
<table
jcr:primaryType="nt:unstructured"
features="-">
<hiddenHeaderConfig
jcr:primaryType="nt:unstructured"
hiddenHeaderClassName="cq-wcm-foundation-aria-visuallyhidden"
hiddenHeaderEditingCSS="cq-RichText-hiddenHeader--editing"/>
</table>
<tracklinks
jcr:primaryType="nt:unstructured"
features="*"/>
</rtePlugins>
<uiSettings jcr:primaryType="nt:unstructured">
<cui jcr:primaryType="nt:unstructured">
<inline
jcr:primaryType="nt:unstructured"
toolbar="[format#bold,format#italic,format#underline,#justify,#lists,links#modifylink,links#unlink,#paraformat]">
<popovers jcr:primaryType="nt:unstructured">
<justify
jcr:primaryType="nt:unstructured"
items="[justify#justifyleft,justify#justifycenter,justify#justifyright,justify#justifyjustify]"
ref="justify"/>
<lists
jcr:primaryType="nt:unstructured"
items="[lists#unordered,lists#ordered,lists#outdent,lists#indent]"
ref="lists"/>
<paraformat
jcr:primaryType="nt:unstructured"
items="paraformat:getFormats:paraformat-pulldown"
ref="paraformat"/>
</popovers>
</inline>
<tableEditOptions
jcr:primaryType="nt:unstructured"
toolbar="[table#insertcolumn-before,table#insertcolumn-after,table#removecolumn,-,table#insertrow-before,table#insertrow-after,table#removerow,-,table#mergecells-right,table#mergecells-down,table#mergecells,table#splitcell-horizontal,table#splitcell-vertical,-,table#selectrow,table#selectcolumn,-,table#ensureparagraph,-,table#modifytableandcell,table#removetable,-,undo#undo,undo#redo,-,table#exitTableEditing,-]"/>
</cui>
</uiSettings>
</text>
</items>
</actionbar>
</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"
text="Save"
type="submit"
variant="primary">
<granite:data
jcr:primaryType="nt:unstructured"
foundation-wizard-control-action="next"/>
</next>
</parentConfig>
</container>
</items>
</wizard>
</items>
</content>
</items>
</body>
</jcr:content>
</jcr:root>