AEM Cloud Service - RichText Editor in Assets Metadata Edit

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  

(Adobe Experience Manager 2022.11.9832.20221115T164011Z-220900)

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="&amp;copy;"
name="copyright"/>
<default_euro
jcr:primaryType="nt:unstructured"
entity="&amp;euro;"
name="euro"/>
<default_registered
jcr:primaryType="nt:unstructured"
entity="&amp;reg;"
name="registered"/>
<default_trademark
jcr:primaryType="nt:unstructured"
entity="&amp;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>

No comments:

Post a Comment