Here, we’ll focus on touch-enabled UI. Adobe introduced Touch UI in AEM 5.6 and has become the standard UI throughout the product. Classic UI will be deprecated in AEM 6.4 and will be removed in the 2019 release.
You can refer to a complete code sample of RTE in the title text component in my GitHub project.
Get Started with OOTB RTE
In touch UI dialog, you define the sling:resourceType property in _cq_dialog/.content.xml for each field, as touch UI dialog, is rendered on server-side as Granite UI components. It leverages Sling and sling resource type to render the component.
In order to have RTE in touch UI dialog, you can call the OOTB RTE script. The OOTB sling:resourceType for RTE is cq/gui/components/authoring/dialog/richtext. You can refer to the OOTB text component under /libs/wcm/foundation/components/text and /libs/foundation/components/text, where the former uses HTML Template Language (HTL) and the latter uses JSP. There’s also a text component from AEM WCM Core components.
In touch UI dialog, you define the sling:resourceType property in _cq_dialog/.content.xml for each field, as touch UI dialog, is rendered on server-side as Granite UI components. It leverages Sling and sling resource type to render the component.
In order to have RTE in touch UI dialog, you can call the OOTB RTE script. The OOTB sling:resourceType for RTE is cq/gui/components/authoring/dialog/richtext. You can refer to the OOTB text component under /libs/wcm/foundation/components/text and /libs/foundation/components/text, where the former uses HTML Template Language (HTL) and the latter uses JSP. There’s also a text component from AEM WCM Core components.
Touch UI Dialog Mode and In-place Editing Mode
There are two modes the author can use RTE in a touch-enabled UI AEM page. One is touch UI dialog mode, which is the wrench when you click on the component; the other is in-place editing mode, the pencil.
Adobe recommends using in-place editing for RTE. Both in-place editing and touch UI dialog have two views. One is the compact inline view, the other is the full-screen view. They can be configured by the corresponding uiSettings node. It controls the plugins and popovers in the RTE toolbar for each view. A default uiSettings will be used if you don’t have a uiSettings node for your field.
Table plugins and the source-edit feature are not available for in-place editing in the touch-enabled UI. Moreover, the Image plugin is only supported partially (drag-and-drop functionality is not available). Also, the drag-and-drop does not apply to the full-screen mode.
There are two modes the author can use RTE in a touch-enabled UI AEM page. One is touch UI dialog mode, which is the wrench when you click on the component; the other is in-place editing mode, the pencil.
Adobe recommends using in-place editing for RTE. Both in-place editing and touch UI dialog have two views. One is the compact inline view, the other is the full-screen view. They can be configured by the corresponding uiSettings node. It controls the plugins and popovers in the RTE toolbar for each view. A default uiSettings will be used if you don’t have a uiSettings node for your field.
Table plugins and the source-edit feature are not available for in-place editing in the touch-enabled UI. Moreover, the Image plugin is only supported partially (drag-and-drop functionality is not available). Also, the drag-and-drop does not apply to the full-screen mode.
Multiple In-place Editors
In-place editors are defined in cq editconfig node. Multiple in-place editors, no matter if they are all text or different types, require editorType to be hybrid. Cq:childEditors node holds in place editor nodes. And config node holds configurations for in-place editors. Detailed instructions can be found here.
Use Same RTE Plugins and UI Settings in Touch UI Dialog and In-place Editing
Same RTE plugins and UI settings provide the author with a consistent authoring experience in both modes. To achieve that, for touch UI dialog mode, you can use the Sling Resource Merger to call the path to the RTE plugins and UI settings. Sling Resource Merger is introduced since AEM 6.1. It overwrites the default component dialog for touch-enabled UI, using the resource type hierarchy (by means of sling:resourceSuperType property).
For in-place editing, it’s a little bit difficult. Sling Resource Merger doesn’t work in edit config. You can set configpath property in cq:inplaceEditing node, especially when you have only one type of in place editors. The OOTB RTE script works charmingly and will not display the control buttons (like the fullscreen, cancel and save) in touch UI dialog inline view. So you can have one shared set of rtePlugins and uiSettings nodes for both touch UI dialog and in-place editing. However, the configpath doesn’t work quite well if you have different types of in-place editors and need to pass multiple custom configurations, since you can only point to one path in the property. Plus, the configpath property is for backward compatibility only. It’s not recommended by Adobe.
Optionally, you can write a script to encapsulate all your RTE plugins and UI settings and then load them into the RTE config programmatically. Here are some pointers for you.
/libs/cq/gui/components/authoring/editors/clientlibs/core/inlineediting/js/HybridEditor.js /libs/cq/gui/components/authoring/editors/clientlibs/core/inlineediting/rte/coralui3/js/InlineTextEditor.js
/libs/clientlibs/granite/richtext/js/rte/ConfigUtils.js
/libs/cq/gui/components/authoring/editors/clientlibs/core/inlineediting/rte/coralui3/js/RTE.js
The drawback of this approach is that Adobe doesn’t make the RTE js apis public, so you will have to dig into all the OOTB RTE scripts. And you may fix any discrepancies when you do an upgrade.
The recommended way is to use config node in cq:inplaceEditing node. But then you will need to configure rtePlugins and uiSettings in each component.
An example of RTE node for touch UI dialog mode:
In-place editors are defined in cq editconfig node. Multiple in-place editors, no matter if they are all text or different types, require editorType to be hybrid. Cq:childEditors node holds in place editor nodes. And config node holds configurations for in-place editors. Detailed instructions can be found here.
Use Same RTE Plugins and UI Settings in Touch UI Dialog and In-place Editing
Same RTE plugins and UI settings provide the author with a consistent authoring experience in both modes. To achieve that, for touch UI dialog mode, you can use the Sling Resource Merger to call the path to the RTE plugins and UI settings. Sling Resource Merger is introduced since AEM 6.1. It overwrites the default component dialog for touch-enabled UI, using the resource type hierarchy (by means of sling:resourceSuperType property).
For in-place editing, it’s a little bit difficult. Sling Resource Merger doesn’t work in edit config. You can set configpath property in cq:inplaceEditing node, especially when you have only one type of in place editors. The OOTB RTE script works charmingly and will not display the control buttons (like the fullscreen, cancel and save) in touch UI dialog inline view. So you can have one shared set of rtePlugins and uiSettings nodes for both touch UI dialog and in-place editing. However, the configpath doesn’t work quite well if you have different types of in-place editors and need to pass multiple custom configurations, since you can only point to one path in the property. Plus, the configpath property is for backward compatibility only. It’s not recommended by Adobe.
Optionally, you can write a script to encapsulate all your RTE plugins and UI settings and then load them into the RTE config programmatically. Here are some pointers for you.
/libs/cq/gui/components/authoring/editors/clientlibs/core/inlineediting/js/HybridEditor.js /libs/cq/gui/components/authoring/editors/clientlibs/core/inlineediting/rte/coralui3/js/InlineTextEditor.js
/libs/clientlibs/granite/richtext/js/rte/ConfigUtils.js
/libs/cq/gui/components/authoring/editors/clientlibs/core/inlineediting/rte/coralui3/js/RTE.js
The drawback of this approach is that Adobe doesn’t make the RTE js apis public, so you will have to dig into all the OOTB RTE scripts. And you may fix any discrepancies when you do an upgrade.
The recommended way is to use config node in cq:inplaceEditing node. But then you will need to configure rtePlugins and uiSettings in each component.
An example of RTE node for touch UI dialog mode:
<text
jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/authoring/dialog/richtext"
fieldLabel="Text"
name="./text">
<rtePlugins
jcr:primaryType="nt:unstructured"
sling:resourceSuperType="/apps/blog/dialogs/standardRTE/rtePlugins"/>
<uiSettings
jcr:primaryType="nt:unstructured"
sling:resourceSuperType="/apps/blog/dialogs/standardRTE/uiSettings"/>
</text>
An example of RTE config for in-place editing mode:
jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/authoring/dialog/richtext"
fieldLabel="Text"
name="./text">
<rtePlugins
jcr:primaryType="nt:unstructured"
sling:resourceSuperType="/apps/blog/dialogs/standardRTE/rtePlugins"/>
<uiSettings
jcr:primaryType="nt:unstructured"
sling:resourceSuperType="/apps/blog/dialogs/standardRTE/uiSettings"/>
</text>
An example of RTE config for in-place editing mode:
<cq:inplaceEditing
jcr:primaryType="cq:InplaceEditingConfig"
active="{Boolean}true"
editorType="hybrid">
<cq:childEditors jcr:primaryType="nt:unstructured">
<title
jcr:primaryType="cq:ChildEditorConfig"
title="Title"
type="text"/>
<text
jcr:primaryType="cq:ChildEditorConfig"
title="Text"
type="text"/>
</cq:childEditors>
<config jcr:primaryType="nt:unstructured">
<rtePlugins jcr:primaryType="nt:unstructured">
…
</rtePlugins>
<uiSettings jcr:primaryType="nt:unstructured">
…
</uiSettings>
</config>
</cq:inplaceEditing>
An example of shared RTE plugins and UI settings file:
jcr:primaryType="cq:InplaceEditingConfig"
active="{Boolean}true"
editorType="hybrid">
<cq:childEditors jcr:primaryType="nt:unstructured">
<title
jcr:primaryType="cq:ChildEditorConfig"
title="Title"
type="text"/>
<text
jcr:primaryType="cq:ChildEditorConfig"
title="Text"
type="text"/>
</cq:childEditors>
<config jcr:primaryType="nt:unstructured">
<rtePlugins jcr:primaryType="nt:unstructured">
…
</rtePlugins>
<uiSettings jcr:primaryType="nt:unstructured">
…
</uiSettings>
</config>
</cq:inplaceEditing>
An example of shared RTE plugins and UI settings file:
<?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" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured">
<rtePlugins jcr:primaryType="nt:unstructured">
<edit
jcr:primaryType="nt:unstructured"
features="*"/>
...
</rtePlugins>
<uiSettings jcr:primaryType="nt:unstructured">
<cui jcr:primaryType="nt:unstructured">
<inline
jcr:primaryType="nt:unstructured"
...
</inline>
<dialogFullScreen
jcr:primaryType="nt:unstructured"
...
</dialogFullScreen>
<tableEditOptions
jcr:primaryType="nt:unstructured"
...
</tableEditOptions>
</cui>
</uiSettings>
</jcr:root>
<jcr:root 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="nt:unstructured">
<rtePlugins jcr:primaryType="nt:unstructured">
<edit
jcr:primaryType="nt:unstructured"
features="*"/>
...
</rtePlugins>
<uiSettings jcr:primaryType="nt:unstructured">
<cui jcr:primaryType="nt:unstructured">
<inline
jcr:primaryType="nt:unstructured"
...
</inline>
<dialogFullScreen
jcr:primaryType="nt:unstructured"
...
</dialogFullScreen>
<tableEditOptions
jcr:primaryType="nt:unstructured"
...
</tableEditOptions>
</cui>
</uiSettings>
</jcr:root>
RTE Link href Issue and Resolution
With the out of the box richtext resource type defined for touch UI dialog, cq/gui/components/authoring/dialog/richtext, and when the author adds a link from the Touch UI dialog (the wrench), the href of anchor tag will be stripped off upon submitting the dialog.
This issue happens only for RTE in touch UI dialog, not in-place editing. This is a known defect for AEM, also documented on the Adobe website. The root cause is that with the OOTB richtext resource type, an RTE instance is getting created each time the RTE is clicked or just simply scrolling the dialog. There’s a simple fix provided by Adobe for this. When you develop the touch UI dialog for the RTE, you can add a property of useFixedInlineToolbar="{Boolean}true". With this setting, you will see the toolbar is fixed above the text editing area, and if you open the developer console in your browser, you will see only one instance for RTE is created. This setting will also fix the href issue in the touch UI dialog, and other plugins should work as expected, too.
Moreover, if you want to apply this setting on an application level, you can use the sling resource merger and overlay the OOTB richtext script to your app location. I.e., create this structure in your code, apps/cq/gui/components/authoring/dialog/richtext/render.jsp. Now you can add your custom logic in the JSP to render RTE. And what you want to do is that when there’s no useFixedInlineToolbar setting in the dialog configuration, default it to be true.
With the out of the box richtext resource type defined for touch UI dialog, cq/gui/components/authoring/dialog/richtext, and when the author adds a link from the Touch UI dialog (the wrench), the href of anchor tag will be stripped off upon submitting the dialog.
This issue happens only for RTE in touch UI dialog, not in-place editing. This is a known defect for AEM, also documented on the Adobe website. The root cause is that with the OOTB richtext resource type, an RTE instance is getting created each time the RTE is clicked or just simply scrolling the dialog. There’s a simple fix provided by Adobe for this. When you develop the touch UI dialog for the RTE, you can add a property of useFixedInlineToolbar="{Boolean}true". With this setting, you will see the toolbar is fixed above the text editing area, and if you open the developer console in your browser, you will see only one instance for RTE is created. This setting will also fix the href issue in the touch UI dialog, and other plugins should work as expected, too.
Moreover, if you want to apply this setting on an application level, you can use the sling resource merger and overlay the OOTB richtext script to your app location. I.e., create this structure in your code, apps/cq/gui/components/authoring/dialog/richtext/render.jsp. Now you can add your custom logic in the JSP to render RTE. And what you want to do is that when there’s no useFixedInlineToolbar setting in the dialog configuration, default it to be true.
/*
* Get useFixedInlineToolbar value from component configuration, if not found, default it to
* true to fix rte anchor link issue
*/
String useFixedInlineToolbar = cfg.get("useFixedInlineToolbar", String.class);
if (useFixedInlineToolbar == null) {
useFixedInlineToolbar = "true";
}
And then in the markup, you can pass the useFixedInlineToolbar variable in.
<div class="coral-RichText-editable coral-Form-field coral-Textfield coral-Textfield--multiline coral-RichText" data-config-path="<%=resource.getPath()%>.infinity.json" data-use-fixed-inline-toolbar="<%=useFixedInlineToolbar%>"></div>
In this case, if you do have a use case that requires to set the useFixedInlineToolbar to false, you can set it in the component dialog configuration and your custom script will be able to read that configuration value.
* Get useFixedInlineToolbar value from component configuration, if not found, default it to
* true to fix rte anchor link issue
*/
String useFixedInlineToolbar = cfg.get("useFixedInlineToolbar", String.class);
if (useFixedInlineToolbar == null) {
useFixedInlineToolbar = "true";
}
And then in the markup, you can pass the useFixedInlineToolbar variable in.
<div class="coral-RichText-editable coral-Form-field coral-Textfield coral-Textfield--multiline coral-RichText" data-config-path="<%=resource.getPath()%>.infinity.json" data-use-fixed-inline-toolbar="<%=useFixedInlineToolbar%>"></div>
In this case, if you do have a use case that requires to set the useFixedInlineToolbar to false, you can set it in the component dialog configuration and your custom script will be able to read that configuration value.
Custom RTE Plugin and Validation
Both RTE custom plugins and validation can be achieved by clientlibs and JavaScript. Even though OOTB RTE plugins are enough most of the time, I have been in couple projects that required to a custom RTE plugin. One example would be the font color picker. If you do need to develop a custom RTE plugin, you can start with creating a clientlib in your code with category rte.coralui3 (AEM 6.3) and dependencies underscore. This will load your JavaScript when AEM is calling RTE in a touch UI component.
Both RTE custom plugins and validation can be achieved by clientlibs and JavaScript. Even though OOTB RTE plugins are enough most of the time, I have been in couple projects that required to a custom RTE plugin. One example would be the font color picker. If you do need to develop a custom RTE plugin, you can start with creating a clientlib in your code with category rte.coralui3 (AEM 6.3) and dependencies underscore. This will load your JavaScript when AEM is calling RTE in a touch UI component.
Then you will write JS code to build the plugin into toolbar and custom logic and implementation for the plugin. At the end, register your custom plugin with Coral UI RTE api, CUI.rte.commands.CommandRegistry.register(). After your plugin is registered, you can add that into the RTE plugins and author will be able to see it. You can reference to some OOTB plugins scripts under here: /etc/clientlibs/granite/coralui2/optional/rte/js/components/rte/plugins(AEM 6.2)
/libs/clientlibs/granite/richtext/js/rte/plugins (AEM 6.3).
Validation for RTE touch UI dialog should follow the new best practice and use Granite UI foundation validation.
/libs/clientlibs/granite/richtext/js/rte/plugins (AEM 6.3).
Validation for RTE touch UI dialog should follow the new best practice and use Granite UI foundation validation.
No comments:
Post a Comment
If you have any doubts or questions, please let us know.