Touch UI / Author UI
Touch UI là tên cũ. Tên chính thức của giao diện authoring "modern" (post Classic UI) là Author UI .
Trong AEM 6.5 trở đi, Author UI là giao diện duy nhất được Adobe support cho việc tác giả nội dung. Classic UI đã bị remove hoàn toàn, nên mọi customisation UI phải build trên nền Author UI.
Classic UI (ExtJS) → removed
Touch UI / Author UI → default + duy nhất trong AEM 6.5
Author UI được dựng trên 2 layer:
Nói ngắn: viết dialog XML là làm việc với Granite UI , viết JS cho dialog là làm việc với Coral UI .
Add Styles Plugin vào RTE
Một use case phổ biến của Author UI customisation là thêm Styles Plugin vào Rich Text Editor (RTE). Plugin này cho phép author chọn một đoạn text và áp dụng một CSS class đã định nghĩa sẵn. AEM sẽ wrap selection bằng <span> với class đó.
< span class = " your-defined-class " >your text selection</ span >
Result mong muốn
Sau khi cấu hình, dialog RTE sẽ có thêm dropdown Styles :
Khi author chọn một đoạn text rồi pick style, HTML output sẽ là:
Setup từng bước
Ví dụ dưới đây extend Text Core Component v2 (core/wcm/components/text/v2/text). Sling Resource Merger sẽ tự pull các node còn lại từ dialog gốc, nên overlay của mình chỉ cần chứa node liên quan đến Styles Plugin.
Tham khảo:
Bước 1: Tạo component extend Text Core
File: apps/myproject/components/text/.content.xml
<? xml version = " 1.0 " encoding = " UTF-8 " ?>
< jcr:root xmlns : jcr = " http://www.jcp.org/jcr/1.0 "
xmlns : cq = " http://www.day.com/jcr/cq/1.0 "
xmlns : sling = " http://sling.apache.org/jcr/sling/1.0 "
jcr : primaryType = " cq:Component "
jcr : title = " Text with Styles "
sling : resourceSuperType = " core/wcm/components/text/v2/text "
componentGroup = " content " />
Bước 2: Thêm node styles vào rtePlugins
Trong apps/myproject/components/text/_cq_dialog/.content.xml, thêm node styles vào rtePlugins:
< rtePlugins jcr : primaryType = " nt:unstructured " >
< styles
jcr : primaryType = " nt:unstructured "
features = " * " >
< styles jcr : primaryType = " cq:WidgetCollection " >
< p1
jcr : primaryType = " nt:unstructured "
cssName = " p1 "
text = " P1 " />
< p2
jcr : primaryType = " nt:unstructured "
cssName = " p2 "
text = " P2 " />
< p3
jcr : primaryType = " nt:unstructured "
cssName = " p3 "
text = " P3 " />
</ styles >
</ styles >
</ rtePlugins >
Cả inline toolbar và fullscreen toolbar đều phải có #styles thì button mới hiện:
< inline
jcr : primaryType = " nt:unstructured "
toolbar = " [format#bold,format#italic,format#underline,#justify,#lists,links#modifylink,links#unlink,#paraformat,#styles] " >
<!-- [...] -->
</ inline >
Bước 4: Add styles vào popovers
Popover định nghĩa cách hiển thị danh sách styles dưới dạng pulldown:
< popovers jcr : primaryType = " nt:unstructured " >
< styles
jcr : primaryType = " nt:unstructured "
items = " styles:getStyles:styles-pulldown "
ref = " styles " />
</ popovers >
items="styles:getStyles:styles-pulldown" là ID của command trả về danh sách styles, dạng UI là pulldown.
Bước 5: Định nghĩa CSS class
Các class p1, p2, p3 phải được định nghĩa trong site clientlib để render đúng ở cả author preview lẫn publish:
.p1 { font-size : 1.25 rem ; font-weight : 600 ; }
.p2 { font-size : 0.875 rem ; font-style : italic ; }
.p3 { font-size : 0.75 rem ; color : #999 ; }
Full example dialog
Toàn bộ dialog apps/myproject/components/text/_cq_dialog/.content.xml:
<? xml version = " 1.0 " encoding = " UTF-8 " ?>
< jcr:root xmlns : jcr = " http://www.jcp.org/jcr/1.0 "
xmlns : nt = " http://www.jcp.org/jcr/nt/1.0 "
xmlns : cq = " http://www.day.com/jcr/cq/1.0 "
xmlns : granite = " http://www.adobe.com/jcr/granite/1.0 "
xmlns : sling = " http://sling.apache.org/jcr/sling/1.0 "
jcr : primaryType = " nt:unstructured "
jcr : title = " Text with Styles "
helpPath = " https://www.aemcomponents.dev/content/core-components-examples/library/page-authoring/text.html "
sling : resourceType = " cq/gui/components/authoring/dialog " >
< content
jcr : primaryType = " nt:unstructured "
sling : resourceType = " granite/ui/components/coral/foundation/container " >
< items jcr : primaryType = " nt:unstructured " >
< tabs
jcr : primaryType = " nt:unstructured "
sling : resourceType = " granite/ui/components/coral/foundation/tabs "
maximized = " {Boolean}true " >
< items jcr : primaryType = " nt:unstructured " >
< properties
jcr : primaryType = " nt:unstructured "
jcr : title = " Properties "
sling : resourceType = " granite/ui/components/coral/foundation/container "
margin = " {Boolean}true " >
< items jcr : primaryType = " nt:unstructured " >
< columns
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 "
granite : class = " cq-RichText-FixedColumn-column "
sling : resourceType = " granite/ui/components/coral/foundation/container " >
< items
jcr : primaryType = " nt:unstructured "
sling : hideChildren = " [id] " >
< text
jcr : primaryType = " nt:unstructured "
sling : resourceType = " cq/gui/components/authoring/dialog/richtext "
name = " ./text "
useFixedInlineToolbar = " {Boolean}true " >
< rtePlugins jcr : primaryType = " nt:unstructured " >
< styles
jcr : primaryType = " nt:unstructured "
features = " * " >
< styles jcr : primaryType = " cq:WidgetCollection " >
< p1
jcr : primaryType = " nt:unstructured "
cssName = " p1 "
text = " P1 " />
< p2
jcr : primaryType = " nt:unstructured "
cssName = " p2 "
text = " P2 " />
< p3
jcr : primaryType = " nt:unstructured "
cssName = " p3 "
text = " P3 " />
</ styles >
</ styles >
</ 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,#styles] " >
< popovers jcr : primaryType = " nt:unstructured " >
< styles
jcr : primaryType = " nt:unstructured "
items = " styles:getStyles:styles-pulldown "
ref = " styles " />
</ popovers >
</ inline >
< dialogFullScreen
jcr : primaryType = " nt:unstructured "
toolbar = " [format#bold,format#italic,format#underline,#justify,#lists,links#modifylink,links#unlink,#paraformat,#styles] " >
< 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 " />
< styles
jcr : primaryType = " nt:unstructured "
items = " styles:getStyles:styles-pulldown "
ref = " styles " />
</ popovers >
</ dialogFullScreen >
</ cui >
</ uiSettings >
</ text >
</ items >
</ column >
</ items >
</ columns >
</ items >
</ properties >
</ items >
</ tabs >
</ items >
</ content >
</ jcr:root >
Lưu ý các điểm quan trọng:
sling:resourceSuperType ở component XML → kéo theo dialog gốc.
sling:hideChildren="[id]" ẩn field id từ parent dialog (tuỳ nhu cầu).
Cấu trúc tabs → properties → columns → column → text phải khớp với dialog gốc, không được rút gọn.
Pitfalls thường gặp
Checklist khi cấu hình thật
Component có sling:resourceSuperType="core/wcm/components/text/v2/text" (hoặc Text component có RTE).
Dialog overlay nằm đúng path _cq_dialog/.content.xml.
Có node rtePlugins/styles với features="*".
Có cq:WidgetCollection chứa các style items với cssName + text.
#styles xuất hiện trong toolbar của cả inline và dialogFullScreen.
Có popovers/styles với items="styles:getStyles:styles-pulldown" và ref="styles".
CSS class được define trong site clientlib.
Deploy package lên local AEM, test author tạo text mới và verify dropdown hiện đủ styles.
Verify HTML output có <span class="..."> đúng class đã chọn.
Tài liệu tham khảo