Quantcast
Channel: Experiencing Adobe Experience Manager (AEM, CQ)
Viewing all articles
Browse latest Browse all 525

AEM 61 - Touch UI Rich Text Editor (RTE) Browse and Insert Image

$
0
0

Goal


Touch UI Rich Text Editor (RTE) Plugin to open a DialogSelect Image and add it in RTE

Dialog for the plugin can be configured with any standard Touch UI Widgets. In this post, we configure a textfield - /libs/granite/ui/components/foundation/form/textfield for entering alt text and path browser - /libs/granite/ui/components/foundation/form/pathbrowser for selecting image

Demo | Package Install


Component Dialog RTE Config

Add the image insert plugin - touchuiinsertimage, available in group experience-aem in component dialog eg. /libs/foundation/components/text/dialog/items/tab1/items/text/rtePlugins (for demonstration only; never modify foundation components)



Plugin Dialog with Path Browser Config



Plugin Dialog



Plugin Dialog with Picker



Plugin Dialog with Image Selected



Image Shown in RTE


Image Source in RTE


Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/touchui-rte-browse-insert-image

2)  Add the dialog configuration for RTE Plugin, shown in popover window when user clicks on image plugin icon; create node of type sling:Folder /apps/touchui-rte-browse-insert-image/popover with the following configuration

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:Folder"
jcr:title="Pick an Image"
sling:resourceType="cq/gui/components/authoring/dialog"/>

3) Add dialog content /apps/touchui-rte-browse-insert-image/popover/content; #5 attribute eaem-rte-iframe-content marks this dialog RTE specific

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container"
eaem-rte-iframe-content="">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"
margin="{Boolean}false"/>
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<alt
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldLabel="Alt Text"
name="./alt"/>
<image
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/pathbrowser"
fieldLabel="Image"
name="./image"
rootPath="/content/dam"/>
</items>
</column>
</items>
</jcr:root>

4) Dialog in CRXDE Lite



5) Create clientlib (type cq:ClientLibraryFolder/apps/touchui-rte-browse-insert-image/clientlib, set property categories of String type to rte.coralui2

6) Create file ( type nt:file ) /apps/touchui-rte-browse-insert-image/clientlib/js.txt, add the following

                         image-insert.js
                         popover.js

7) Create file ( type nt:file ) /apps/touchui-rte-browse-insert-image/clientlib/image-insert.js, add the following code. This file contains logic for image plugin; receiving selected image from popover iframe showing the dialog and adding it in RTE

(function ($, $document, Handlebars) {
var ExperienceAEM = {
GROUP: "experience-aem",
TIM_FEATURE: "touchuiinsertimage",
TIM_DIALOG: "touchuiinsertimagedialog",
CONTENT_URL: "/apps/touchui-rte-browse-insert-image/popover.html",
EAEM_RTE_IFRAME_CONTENT: "eaem-rte-iframe-content"
};

ExperienceAEM.TIM_UI_SETTING = ExperienceAEM.GROUP + "#" + ExperienceAEM.TIM_FEATURE;

//extend toolbar builder to register insert image
ExperienceAEM.CuiToolbarBuilder = new Class({
toString: "EAEMCuiToolbarBuilder",

extend: CUI.rte.ui.cui.CuiToolbarBuilder,

_getUISettings: function (options) {
var uiSettings = this.superClass._getUISettings(options);

//inline toolbar
var toolbar = uiSettings["inline"]["toolbar"],
feature = ExperienceAEM.TIM_UI_SETTING;

//uncomment this to make image insert available for inline toolbar
/*if (toolbar.indexOf(feature) == -1) {
var index = toolbar.indexOf("fullscreen#start");
toolbar.splice(index, 0, feature);
toolbar.splice(index + 1, 0, "-");
}*/

//add image insert to fullscreen toolbar
toolbar = uiSettings["fullscreen"]["toolbar"];

if (toolbar.indexOf(feature) == -1) {
toolbar.splice(3, 0, feature);
}

if (!this._getClassesForCommand(feature)) {
this.registerAdditionalClasses(feature, "coral-Icon coral-Icon--image");
}

return uiSettings;
}
});

//popover dialog thats hosts iframe
ExperienceAEM.InsertImageDialog = new Class({
extend: CUI.rte.ui.cui.AbstractBaseDialog,

toString: "EAEMInsertImageDialog",

getDataType: function () {
return ExperienceAEM.TIM_DIALOG;
}
});

//extend the CUI dialog manager to register popover dialog
ExperienceAEM.DialogManager = new Class({
toString: "EAEMDialogManager",

extend: CUI.rte.ui.cui.CuiDialogManager,

create: function (dialogId, config) {
if (dialogId !== ExperienceAEM.TIM_DIALOG) {
return this.superClass.create.call(this, dialogId, config);
}

var context = this.editorKernel.getEditContext();
var $container = CUI.rte.UIUtils.getUIContainer($(context.root));

var dialog = new ExperienceAEM.InsertImageDialog();
dialog.attach(config, $container, this.editorKernel, true);

return dialog;
}
});

//extend the toolkit implementation for returning custom toolbar builder and dialog manager
ExperienceAEM.ToolkitImpl = new Class({
toString: "EAEMToolkitImpl",

extend: CUI.rte.ui.cui.ToolkitImpl,

createToolbarBuilder: function () {
return new ExperienceAEM.CuiToolbarBuilder();
},

createDialogManager: function (editorKernel) {
return new ExperienceAEM.DialogManager(editorKernel);
}
});

CUI.rte.ui.ToolkitRegistry.register("cui", ExperienceAEM.ToolkitImpl);

ExperienceAEM.TouchUIInsertImagePlugin = new Class({
toString: "TouchUIInsertImagePlugin",

extend: CUI.rte.plugins.Plugin,

pickerUI: null,

getFeatures: function () {
return [ ExperienceAEM.TIM_FEATURE ];
},

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

if (this.isFeatureEnabled(ExperienceAEM.TIM_FEATURE)) {
this.pickerUI = tbGenerator.createElement(ExperienceAEM.TIM_FEATURE, this, true, "Insert Image");
tbGenerator.addElement(ExperienceAEM.GROUP, plg.Plugin.SORT_FORMAT, this.pickerUI, 120);
}
},

execute: function (id) {
var ek = this.editorKernel,
dm = ek.getDialogManager();

var dialogConfig = {
parameters: {
"command": ExperienceAEM.TIM_UI_SETTING
}
};

var dialog = this.dialog = dm.create(ExperienceAEM.TIM_DIALOG, dialogConfig);

dm.prepareShow(this.dialog);

dm.show(this.dialog);

var $popover = this.dialog.$dialog.find(".coral-Popover-content");

loadPopoverUI($popover);

function loadPopoverUI($popover) {
$popover.parent().css("width", ".1px").height(".1px").css("border", "none");
$popover.css("width", ".1px").height(".1px");

$popover.find("iframe").attr("src", ExperienceAEM.CONTENT_URL);

//receive the dialog values from child window
registerReceiveDataListener(receiveMessage);
}

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

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

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

var action = message.action;

if(action == "submit"){
var data = message.data;

if(!_.isEmpty(data) && !_.isEmpty(data.imagePath)){
ek.relayCmd(id, message.data);
}
}

dialog.hide();

removeReceiveDataListener(receiveMessage);
}

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

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

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

CUI.rte.plugins.PluginRegistry.register(ExperienceAEM.GROUP, ExperienceAEM.TouchUIInsertImagePlugin);

ExperienceAEM.InsertImageCmd = new Class({
toString: "InsertImageCmd",

extend: CUI.rte.commands.Command,

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

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

execute: function (execDef) {
var data = execDef.value, path = data.imagePath, alt = data.altText || "",
width = 100, height = 100,
imageUrl = CUI.rte.Utils.processUrl(path, CUI.rte.Utils.URL_IMAGE),
imgHtml = "";

imgHtml += "<img src=\"" + imageUrl + "\" alt=\"" + alt + "\"";
imgHtml += "" + CUI.rte.Common.SRC_ATTRIB + "=\"" + path + "\"";
imgHtml += " width=\"" + width + "\"";
imgHtml += " height=\"" + height + "\"";
imgHtml += ">";

execDef.editContext.doc.execCommand("insertHTML", false, imgHtml);
}
});

CUI.rte.commands.CommandRegistry.register(ExperienceAEM.GROUP, ExperienceAEM.InsertImageCmd);

//returns the picker dialog html
//Handlebars doesn't do anything useful here, but the framework expects a template
function cpTemplate() {
CUI.rte.Templates["dlg-" + ExperienceAEM.TIM_DIALOG] =
Handlebars.compile('<div data-rte-dialog="' + ExperienceAEM.TIM_DIALOG
+ '" class="coral--dark coral-Popover coral-RichText-dialog">'
+ '<iframe width="1100px" height="700px"></iframe>'
+ '</div>');
}

cpTemplate();
})(jQuery, jQuery(document), Handlebars);

8) Create file ( type nt:file ) /apps/touchui-rte-browse-insert-image/clientlib/popover.js, add the following code. This file contains logic for sending the dialog values like image selected to parent window RTE

(function($, $document){
//dialogs marked with eaem-rte-iframe-content data attribute execute the below logic
//to send dialog values to parent window RTE
var EAEM_RTE_IFRAME_CONTENT = "eaem-rte-iframe-content",
HELP_BUTTON_SEL = ".cq-dialog-help",
CANCEL_BUTTON_SEL = ".cq-dialog-cancel",
SUBMIT_BUTTON_SEL = ".cq-dialog-submit",
ALT_TEXT_NAME = "./alt",
IMAGE_NAME = "./image";

$document.on("foundation-contentloaded", stylePopoverIframe);

function stylePopoverIframe(){
var $iframeContent = $("[" + 'data-' + EAEM_RTE_IFRAME_CONTENT + "]");

if(_.isEmpty($iframeContent)){
return
}

var $form = $iframeContent.closest("form"),
$cancel = $form.find(CANCEL_BUTTON_SEL),
$submit = $form.find(SUBMIT_BUTTON_SEL);

$form.css("border", "solid 2px");
$form.find(HELP_BUTTON_SEL).hide();

$document.off("click", CANCEL_BUTTON_SEL);
$document.off("click", SUBMIT_BUTTON_SEL);
$document.off("submit");

$cancel.click(sendCloseMessage);
$submit.click(sendDataMessage);
}

function sendCloseMessage(){
var message = {
sender: EAEM_RTE_IFRAME_CONTENT,
action: "close"
};

parent.postMessage(JSON.stringify(message), "*");
}

function sendDataMessage(){
var message = {
sender: EAEM_RTE_IFRAME_CONTENT,
action: "submit",
data:{
altText: $("[name='" + ALT_TEXT_NAME + "']").val(),
imagePath: $("[name='" + IMAGE_NAME + "']").val()
}
};

parent.postMessage(JSON.stringify(message), "*");
}
})(jQuery, jQuery(document));


Viewing all articles
Browse latest Browse all 525

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>