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

AEM 62 - Touch UI Assets Console Cards View Thumbnail Zoom Slider

$
0
0

Goal


Add a Slider in Cards View of Assets Console to Zoom In on the Asset Cards (similar to Mac / Windows Thumbnail Zoom)

Demo | Package Install | GitHub




Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-card-thumbnail-zoom

2) Create clientlib (type cq:ClientLibraryFolder/apps/eaem-card-thumbnail-zoom/clientlib and set a property categories of String type to dam.gui.actions.coraldependencies of type String[] with value underscore

3) Create file ( type nt:file ) /apps/eaem-card-thumbnail-zoom/clientlib/js.txt, add the following

                         card-thumbnail-zoom.js

4) Create file ( type nt:file ) /apps/eaem-card-thumbnail-zoom/clientlib/card-thumbnail-zoom.js, add the following code

(function ($, $document) {
var FOUNDATION_MODE_CHANGE = "foundation-mode-change",
DAM_ADMIN_CHILD_PAGES = ".cq-damadmin-admin-childpages",
ACTION_BAR = ".foundation-collection-actionbar",
$zoomSlider,
LAYOUT_CARD_VIEW = "card";

$document.on(FOUNDATION_MODE_CHANGE, function(event){
_.defer(function(){
contentLoaded(event);
});
});

function contentLoaded(){
var $childPage = $(DAM_ADMIN_CHILD_PAGES),
foundationLayout = $childPage.data("foundation-layout");

if(_.isEmpty(foundationLayout)){
return;
}

var layoutId = foundationLayout.layoutId;

if(layoutId !== LAYOUT_CARD_VIEW){
if(!_.isEmpty($zoomSlider)){
$zoomSlider.hide();
}

return;
}

addZoomSlider();
}

function addZoomSlider(){
if(!_.isEmpty($zoomSlider)){
$zoomSlider.show();
$zoomSlider.find("input").trigger("change.slider");
return;
}

var $actionBar = $(ACTION_BAR),
$graniteRight = $actionBar.find(".granite-actionbar-right");

$zoomSlider = $(getSliderHtml()).prependTo($graniteRight).find(".coral-Slider");

CUI.Slider.init($zoomSlider, $document);

$zoomSlider.find("input").on("change.slider", function(){
$(DAM_ADMIN_CHILD_PAGES).attr("columnwidth", $(this).val());
});

$zoomSlider = $zoomSlider.parent("div");
}

function getSliderHtml(){
return '<div class="granite-actionbar-item">' +
'<div style="margin: 10px 10px 0 0;display: inline-block;font-weight: bold;">Zoom</div>' +
'<div class="coral-Slider" data-init="slider">' +
'<input type="range" value="242" min="242" max="1642" step="200">' +
'</div>' +
'</div>'
}
})(jQuery, jQuery(document));






AEM 62 - Touch UI Show Folders or Assets Alphabetically in List View on Page Load

$
0
0

Goal


TouchUI folders are by default sling:OrderedFolder; so user defines the order and can reorder the way they showup in List View. To sort them alphabetically on page load or mode change to list view, this extension can help...

Demo | Package Install | GitHub



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-touchui-assets-list-load-alpha

2) Create clientlib (type cq:ClientLibraryFolder/apps/eaem-touchui-assets-list-load-alpha/clientlib and set a property categories of String type to dam.gui.admin.coraldependencies of type String[] with value underscore

3) Create file ( type nt:file ) /apps/eaem-touchui-assets-list-load-alpha/clientlib/js.txt, add the following

                         sort-alpha.js

4) Create file ( type nt:file ) /apps/eaem-touchui-assets-list-load-alpha/clientlib/sort-alpha.js, add the following code

(function ($, $document) {
var FOUNDATION_CONTENT_LOADED = "foundation-contentloaded",
FOUNDATION_MODE_CHANGE = "foundation-mode-change",
LAYOUT_LIST_VIEW = "list",
TITLE_COLUMN = "Title",
DAM_ADMIN_CHILD_PAGES = ".cq-damadmin-admin-childpages";

$document.on(FOUNDATION_CONTENT_LOADED, sortListItems);

$document.on(FOUNDATION_MODE_CHANGE, sortListItems);

function sortListItems(event){
var $childPage = $(DAM_ADMIN_CHILD_PAGES),
foundationLayout = $childPage.data("foundation-layout");

if(_.isEmpty(foundationLayout)){
return;
}

var layoutId = foundationLayout.layoutId;

if(layoutId !== LAYOUT_LIST_VIEW){
return;
}

var $listViewHead = $childPage.find("thead");

var $colSpan = $listViewHead.find("coral-th-label:contains('" + TITLE_COLUMN + "')").filter(function(){
return ($(this).text() === TITLE_COLUMN);
});

$colSpan.closest("th").click();
}
})(jQuery, $(document));



AEM 62 - Touch UI Branding Login and Consoles

$
0
0

Goal


Add logo on Login page and Touch UI console banners - Projects, Sites, Assets, Tools etc.

Demo | Package Install | Git Hub


Login Screen



AEM Consoles



Solution


1) Login to CRXDE Lite, create nt:folder - /etc/clientlibs/eaem-branding-login

2) Create nt:folder - /etc/clientlibs/eaem-branding-login/img, add the logo large (for login screen) and small (for console banners) images

               /etc/clientlibs/eaem-branding-login/img/eaem-logo-large.png
               /etc/clientlibs/eaem-branding-login/img/eaem-logo-small.png

3) Create cq:ClientLibraryFolder/etc/clientlibs/eaem-branding-login/logo with categories String[] set to granite.core.login.extensiongranite.ui.coral.foundation and dependencies String[] set to underscore

4) Create nt:file /etc/clientlibs/eaem-branding-login/logo/css.txt add the following content

               add-logo.css

5) Create nt:file /etc/clientlibs/eaem-branding-login/logo/add-logo.css add the following code

.eaem-logo{
position:absolute;
background-repeat: no-repeat;
}

.eaem-login-logo {
top: 3%;
left: 3%;
height: 200px;
width: 250px;
background-image:url('/etc/clientlibs/eaem-branding-login/img/eaem-logo-large.png');
}

.eaem-aem-logo{
transform: translate(-50%, 0%);
top: 2px;
left: 50%;
height: 50px;
width: 100px;
background-image:url('/etc/clientlibs/eaem-branding-login/img/eaem-logo-small.png');
}

6) Create nt:file - /etc/clientlibs/eaem-branding-login/logo/js.txt add the following content

               add-logo.js

7) Create nt:file - /etc/clientlibs/eaem-branding-login/logo/add-logo.js add the following code

(function($, $document){
var FOUNDATION_CONTENT_LOADED = "foundation-contentloaded",
LOGIN_PAGE = "/libs/granite/core/content/login.html";

addLogo();

function addLogo(){
if(isLoginPage()){
$document.ready(addLoginPageLogo);
}else{
addConsoleLogo();
}
}

function addConsoleLogo(){
$document.on(FOUNDATION_CONTENT_LOADED, function(){
var html = "<div class='eaem-logo eaem-aem-logo'></div>";
$(html).prependTo($("coral-shell-header"));
})
}

function addLoginPageLogo(){
loadCss();

var html = "<div class='eaem-logo eaem-login-logo'></div>";

$(html).appendTo($("#backgrounds"));
}

function loadCss(){
$("<link/>", {
rel: "stylesheet",
type: "text/css",
href: "/etc/clientlibs/eaem-branding-login/logo/add-logo.css"
}).appendTo("head");
}

function isLoginPage(){
return (window.location.pathname === LOGIN_PAGE);
}
}($, $(document)));

8) The login extension paths should be added to Sling Authentication Servicehttp://localhost:4502/system/console/configMgr/org.apache.sling.engine.impl.auth.SlingAuthenticator Authentication Requirements property (paths that donot need authentication are prefixed with a "-")



9) To make the authentication requirements part of build process, create nt:folder - /apps/eaem-branding-login/config and sling:OsgiConfig - /apps/eaem-branding-login/config/org.apache.sling.engine.impl.auth.SlingAuthenticator with the following properties

<?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:OsgiConfig"
sling.auth.requirements="[+/,-/libs/granite/core/content/login,-/etc/clientlibs/granite,-/libs/dam/gui/components/admin/adhocassetshare/clientlibs,-/etc/clientlibs/eaem-branding-login]"/>


AEM 62 - Sample 3 Step Wizard Selecting Assets in Column View

$
0
0

Goal


A sample 3 step wizard for selecting assets from different folders, provide additional information in steps and submit for project specific processing; useful for creating custom screens the AEM way

For selecting assets using list viewcheck this post

Demo | Package Install | Git Hub


Wizardhttp://localhost:4502/apps/eaem-wizard-select-assets-column-view/eaem-3-step-wizard.html



Structure in CRX




Solution


1) Login to CRXDE Lite - http://localhost:4502/crx/de/index.jsp and create nt:folder /apps/eaem-wizard-select-assets-column-view

2) Create /apps/eaem-wizard-select-assets-column-view/eaem-3-step-wizard of type cq:Page with the following content

<?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="EAEM 3 Step Wizard"
sling:resourceType="/apps/eaem-wizard-select-assets-column-view/page">
<head jcr:primaryType="nt:unstructured">
<viewport
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/admin/page/viewport"/>
<favicon
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/page/favicon"/>
<clientlibs
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/includeclientlibs"
categories="[coralui3,granite.ui.coral.foundation,eaem.wizard]"/>
</head>
<body
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/page/body">
<items jcr:primaryType="nt:unstructured">
<form
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form"
action="/apps/eaem-wizard-select-assets-column-view/eaem-3-step-wizard/jcr:content"
foundationForm="{Boolean}true"
maximized="{Boolean}true"
method="post"
novalidate="{Boolean}true"
style="vertical">
<successresponse
jcr:primaryType="nt:unstructured"
jcr:title="Success"
sling:resourceType="granite/ui/components/coral/foundation/form/responses/openprompt"
open="/apps/eaem-wizard-select-assets-column-view/eaem-3-step-wizard.html"
redirect="/apps/eaem-wizard-select-assets-column-view/eaem-3-step-wizard.html"
text="Your data was submitted"/>
<items jcr:primaryType="nt:unstructured">
<charset
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/hidden"
name="_charset_"
value="utf-8"/>
<wizard
jcr:primaryType="nt:unstructured"
jcr:title="Experience AEM 3 Step Wizard"
sling:resourceType="granite/ui/components/coral/foundation/wizard">
<items jcr:primaryType="nt:unstructured">
<step1
jcr:primaryType="nt:unstructured"
jcr:title="Select Folder"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<title
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/section">
<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">
<assetsPath
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/pathbrowser"
fieldLabel="Assets Path"
name="eaemAssetsPath"
rootPath="/content/dam"/>
</items>
</column>
</items>
</title>
</items>
<parentConfig jcr:primaryType="nt:unstructured">
<next
granite:class="foundation-wizard-control"
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/button"
text="Next"
variant="primary">
<granite:data
jcr:primaryType="nt:unstructured"
foundation-wizard-control-action="next"/>
</next>
</parentConfig>
</step1>
<step2
jcr:primaryType="nt:unstructured"
jcr:title="Column View"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/include"
path="/libs/dam/gui/content/assets/jcr:content/views/column"/>
</items>
<parentConfig jcr:primaryType="nt:unstructured">
<next
granite:class="foundation-wizard-control"
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/button"
text="Next"
variant="primary">
<granite:data
jcr:primaryType="nt:unstructured"
foundation-wizard-control-action="next"/>
</next>
</parentConfig>
</step2>
<step3
jcr:primaryType="nt:unstructured"
jcr:title="Additional Info"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<footer
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/section">
<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">
<text
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
fieldLabel="Text"
name="eaemText"/>
</items>
</column>
</items>
</footer>
</items>
<parentConfig jcr:primaryType="nt:unstructured">
<next
granite:class="foundation-wizard-control"
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/button"
text="GO"
type="submit"
variant="primary">
<granite:data
jcr:primaryType="nt:unstructured"
foundation-wizard-control-action="next"/>
</next>
</parentConfig>
</step3>
</items>
<granite:data
jcr:primaryType="nt:unstructured"
suffix="${requestPathInfo.suffix}"/>
</wizard>
</items>
</form>
</items>
</body>
</jcr:content>
</jcr:root>

3) The extension clientlib #19, for page created above - eaem.wizard is added in next steps

4) #69, in step 1, select the folder path from where assets are to be picked by default



5) #98, in step 2, provide a column view of the assets in folder selected in step 1; column view here includes otb column view from Assets Console http://localhost:4502/assets.html/content/dam -  /libs/dam/gui/content/assets/jcr:content/views/column and extends it to provide the Selected Files section



6)  #133, in step 3, provide a text area widget for entering additional information



7) #28 has the wizard form action /apps/eaem-wizard-select-assets-column-view/eaem-3-step-wizard/jcr:content of sling:resourceType /apps/eaem-wizard-select-assets-column-view/page

8) Create /apps/eaem-wizard-select-assets-column-view/page with the following content

<?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"
sling:resourceSuperType="granite/ui/components/coral/foundation/page"/>

9) Create nt:file /apps/eaem-wizard-select-assets-column-view/page/POST.jsp with the necessary logic to process selected assets...

<%@include file="/libs/granite/ui/global.jsp"%>
<%@page session="false"%>

<%
String eaemSelectedAssets = request.getParameter("eaemSelectedAssets");
String eaemText = request.getParameter("eaemText");

log.info("eaemText-------" + eaemText);
log.info("eaemSelectedAssets-------" + eaemSelectedAssets);
%>

10) Create clientlib (type cq:ClientLibraryFolder) /apps/eaem-wizard-select-assets-column-view/clientlib with categories String[] eaem.wizard and dependencies String[] underscore to add some dynamic behavior to the wizard while navigating between the steps

11) Create nt:file /apps/eaem-wizard-select-assets-column-view/clientlib/js.txt and add the following content

              handle-wizard.js

12) Create nt:file /apps/eaem-wizard-select-assets-column-view/clientlib/handle-wizard.js, add the following logic

(function ($, $document) {
var WIZARD_URL = "/apps/eaem-wizard-select-assets-column-view/eaem-3-step-wizard.html",
COLUMN_VIEW = "coral-columnview",
FOUNDATION_CONTENT_LOADED = "foundation-contentloaded",
FOUNDATION_WIZARD_STEPCHANGE = "foundation-wizard-stepchange",
FOUNDATION_WIZARD = "foundation-wizard",
FOUNDATION_SELECTIONS_CHANGE = "foundation-selections-change",
FOUNDATION_SELECTIONS_ITEM = "foundation-selections-item",
FOUNDATION_COLLECTION = ".foundation-collection",
FOUNDATION_COLLECTION_ITEM_ID = "foundation-collection-item-id",
CORAL_COLUMN_VIEW_LOAD_ITEMS = 'coral-columnview:loaditems',
CORAL_COLUMNVIEW_ITEM = "coral-columnview-item",
SELECTION_MODE = "selectionmode",
SUFFIX = "suffix",
TABLES_DIV = "eaem-column-view-selections",
TABLES_IN_ROW = 5,
EAEM_ASSETS_PATH = "eaemAssetsPath",
EAEM_SELECTED_ASSETS = "eaemSelectedAssets";

var selectedAssets = {}, $sfContainer,
tableWidth = (($(window).width() / TABLES_IN_ROW) - 5),
itemTpl = Handlebars.compile(getItemTemplate()),
sectionTpl = Handlebars.compile(getSectionTemplate());

$document.on(FOUNDATION_CONTENT_LOADED, ifSuffixAvailableBuildUIMoveToStep2);

$document.on(FOUNDATION_WIZARD_STEPCHANGE, handleSteps);

function ifSuffixAvailableBuildUIMoveToStep2(){
var $wizard = $("." + FOUNDATION_WIZARD);

if(_.isEmpty($wizard)){
return;
}

var suffix = $wizard.data(SUFFIX);

if(_.isEmpty(suffix)){
return;
}

var $columnView = $(COLUMN_VIEW);

if(_.isEmpty($columnView)){
return;
}

createSelectedAssetsUI($columnView);

registerListeners($columnView);

$wizard.adaptTo(FOUNDATION_WIZARD).next();
}

function handleSteps(event, nextStep, currentStep){
if(_.isUndefined(currentStep)){
return;
}

var $eaemAssetsPath = $("[name=" + EAEM_ASSETS_PATH + "]");

if(isSecondStep() && !_.isEmpty($eaemAssetsPath.val())){
$(nextStep).hide();
redirectTo(WIZARD_URL + $eaemAssetsPath.val());
}else if(isThirdStep()){
addSelectedAssetsToForm();
}
}

function registerListeners($columnView){
$document.on(FOUNDATION_SELECTIONS_CHANGE, FOUNDATION_COLLECTION, collectAssets);

$columnView.on(CORAL_COLUMN_VIEW_LOAD_ITEMS, listenerOnLoadedItems);
}

function addSelectedAssetsToForm(){
if(_.isEmpty(selectedAssets)){
return;
}

var $form = $("form"),
$selectedAssets = $("[name=" + EAEM_SELECTED_ASSETS + "]");

if(_.isEmpty($selectedAssets)){
$selectedAssets = $('<input />').attr('type', 'hidden')
.attr('name', EAEM_SELECTED_ASSETS)
.appendTo($form);
}

var assetPaths = Object.keys(selectedAssets);

$selectedAssets.val(assetPaths.join(","));

if(_.isEmpty(assetPaths)){
disableWizardNext();
}else{
enableWizardNext();
}
}

function createSelectedAssetsUI($columnView){
var height = $(window).height() - 350;

$columnView.height(height).attr(SELECTION_MODE, Coral.ColumnView.selectionMode.SINGLE);

$sfContainer = $("<div/>").appendTo($columnView.parent());

addHeader($sfContainer);

addNoSelFilesDiv($sfContainer);
}

function collectAssets(){
var $asset, path;

$("." + FOUNDATION_SELECTIONS_ITEM).each(function(index, asset){
$asset = $(asset);

path = $asset.data(FOUNDATION_COLLECTION_ITEM_ID);

if(selectedAssets.hasOwnProperty(path)){
return;
}

if(!isAsset($asset)){
$asset.removeAttr("selected").removeClass(FOUNDATION_SELECTIONS_ITEM);
return;
}

selectedAssets[path] = {
path: path,
thumbnail: $asset.find("coral-columnview-item-thumbnail > img").attr("src"),
text: $asset.data("item-title")
};
});

buildTable(selectedAssets, $sfContainer);
}

function buildTable(selectedAssets, $container) {
if(_.isEmpty(selectedAssets)){
addNoSelFilesDiv($sfContainer);
return;
}

enableWizardNext();

var itemHtml = "", index = 0,
rowsInTable = getNoOfRowsInEachTable(selectedAssets),
$tablesDiv = getTablesDiv();

_.each(selectedAssets, function(itemData){
itemHtml = itemHtml + itemTpl(itemData);

if( (++index % rowsInTable) !== 0 ){
return;
}

addToSection($tablesDiv, itemHtml);

itemHtml = "";

index = 0;
});

if(itemHtml){
addToSection($tablesDiv, itemHtml);
}

$container.find("#" + TABLES_DIV).remove();

$tablesDiv.appendTo($container);

handleSelections($tablesDiv);
}

function handleSelections($tablesDiv){
_.defer(function(){
$tablesDiv.find("coral-checkbox").click().on('change', handleChecks);
});

function handleChecks(){
var path = $(this).closest("tr").data("path");

delete selectedAssets[path];

buildTable(selectedAssets, $sfContainer);
}
}

function addToSection($tablesDiv, itemHtml){
var sectionHtml = sectionTpl({
width: tableWidth + "px",
itemHtml: itemHtml
});

$(sectionHtml).appendTo($tablesDiv);
}

function listenerOnLoadedItems(){
$(CORAL_COLUMNVIEW_ITEM).click(ignoreFolderSelections);
}

function ignoreFolderSelections(){
var $item = $(this);

if(isAsset($item)){
return;
}

_.defer(function(){
$item.removeAttr("selected");
});
}

function getNoOfRowsInEachTable(selectedAssets){
return Math.ceil(Object.keys(selectedAssets).length / TABLES_IN_ROW);
}

function addHeader($container) {
var html = "<div style='text-align:center; padding: 5px; background-color: rgba(0,0,0,0.05)'>" +
"<h3>Selected Files</h3>" +
"</div>";

return $(html).appendTo($container);
}

function addNoSelFilesDiv($container) {
$container.find("#" + TABLES_DIV).remove();

disableWizardNext();

var html = "<div style='text-align:center' id='" + TABLES_DIV + "'>" +
"<h4>No files have been added." +
"To add files, select the file by clicking on file thumnbnail</h4>" +
"</div>";

return $(html).appendTo($container);
}

function getItemTemplate() {
return '<tr is="coral-tr" data-path="{{path}}">' +
'<td is="coral-td" coral-tr-select></td>' +
'<td is="coral-td">' +
'<img src="{{thumbnail}}" class="image">' +
'</td>' +
'<td is="coral-td">{{text}}</td>' +
'</tr>'
}

function getSectionTemplate(){
return '<coral-table selectionmode="row" multiple style="display:inline-block">' +
'<table is="coral-table-inner" style="width: {{width}}; overflow: hidden">' +
'<colgroup>' +
'<col is="coral-col" fixedwidth>' +
'<col is="coral-col" fixedwidth>' +
'<col is="coral-col" sortable alignment="left">' +
'</colgroup>' +
'<tbody is="coral-tbody">' +
'{{{itemHtml}}}' +
'</tbody>' +
'</table>' +
'</coral-table>'
}

function isAsset($item){
return ($item.data("item-type") === "asset");
}

function getTablesDiv(){
return $("<div id='" + TABLES_DIV + "'></div>");
}

function disableWizardNext(){
toggleWizard(false);
}

function enableWizardNext(){
toggleWizard(true);
}

function toggleWizard(isEnable){
var $wizard = $("." + FOUNDATION_WIZARD);

if(_.isEmpty($wizard)){
return;
}

var wizardApi = $wizard.adaptTo(FOUNDATION_WIZARD);
wizardApi.toggleNext(isEnable);
}

function getStepNumber(){
var $wizard = $("." + FOUNDATION_WIZARD),
currentStep = $wizard.find(".foundation-wizard-step-active"),
wizardApi = $wizard.adaptTo(FOUNDATION_WIZARD);

return wizardApi.getPrevSteps(currentStep).length + 1;
}

function isSecondStep(){
return (getStepNumber() === 2);
}

function isThirdStep(){
return (getStepNumber() === 3);
}

function redirectTo(url){
var ui = $(window).adaptTo("foundation-ui");

ui.wait($("form"));

window.location = url;
}
}(jQuery, jQuery(document)));



AEM 62 - Extending Open Prompt Dialog in Form Success Response

$
0
0

Goal


Extend Projects Open Prompt dialog (handler - projects.openprompt) to change the button Done text to Back to Projects

Demo | Package Install | Git Hub


Product



Extension



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-extend-create-project-open-prompt

2) Create clientlib (type cq:ClientLibraryFolder/apps/eaem-extend-create-project-open-prompt/clientlib and set a property categories of String type to cq.projects.admin.formdependencies of type String[] with value underscore

3) Create file ( type nt:file ) /apps/eaem-extend-create-project-open-prompt/clientlib/js.txt, add the following

                         open-prompt-button.js

4) Create file ( type nt:file ) /apps/eaem-extend-create-project-open-prompt/clientlib/open-prompt-button.js, add the following code

(function ($, $document) {
var OPEN_PROMPT = "projects.openprompt",
SUCCESS_RESPONSE = "foundation.form.response.ui.success",
CREATE_PROJECT_URL = "/mnt/overlay/cq/core/content/projects/wizard/newproject.html";

if(!shouldExecuteForPage()){
return;
}

extendOpenPromptHandler();

function extendOpenPromptHandler(){
var openPromptHandler = findOpenPromptHandler();

if(!openPromptHandler){
return;
}

openPromptHandler.handler = getExtendedHandler(openPromptHandler.handler);
}

function getExtendedHandler(origHandler){
return function(form, config, data, textStatus, xhr){
origHandler.call(this, form, config, data, textStatus, xhr);

getUIWidget(".coral-Dialog--success").done(function($dialog){
$dialog.find("coral-dialog-footer")
.find(".coral-Button:first")
.children("coral-button-label").html("Back To Projects");
});
}
}

function findOpenPromptHandler(){
var registry = $(window).adaptTo("foundation-registry"),
handlers = registry.get(SUCCESS_RESPONSE),
handler;

_.each(handlers, function(handlerObj){
if(handlerObj.name !== OPEN_PROMPT){
return;
}

handler = handlerObj;
});

return handler;
}

function getUIWidget(selector){
if(_.isEmpty(selector)){
return;
}

var deferred = $.Deferred();

var INTERVAL = setInterval(function(){
var $widget = $(selector);

if(_.isEmpty($widget)){
return;
}

clearInterval(INTERVAL);

deferred.resolve($widget);
}, 250);

return deferred.promise();
}

function shouldExecuteForPage() {
return (window.location.pathname.indexOf(CREATE_PROJECT_URL) === 0);
}
})(jQuery, jQuery(document));

AEM 62 - Classic UI Extend Create Paragraph For Custom Name Hint

$
0
0

Goal


Extend Create Paragraph of Classic UI Editables to provide custom names for par nodes, say eaem_

Demo | Package Install | GitHub



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-classicui-par-node-name

2) Create clientlib (type cq:ClientLibraryFolder/apps/eaem-classicui-par-node-name/clientlib and set property categories of String type to cq.widgets and dependencies to lodash

3) Create file ( type nt:file ) /apps/eaem-classicui-par-node-name/clientlib/js.txt, add the following

                         node-name.js

4) Create file ( type nt:file ) /apps/eaem-classicui-par-node-name/clientlib/node-name.js, add the following code

(function(){
var PREFIX = "eaem_";

CQ.Ext.onReady(function () {
if(!CQ.WCM.isEditMode()){
return;
}

CQ.WCM.on("editablesready", iterateEditables, this);
});

function iterateEditables(){
var editables = CQ.utils.WCM.getEditables();

_.each(editables, function (editable) {
extendCreateParagraph(editable);
});
}

function extendCreateParagraph(editable){
if(!editable.createParagraph){
return;
}

var cqCreateParagraph = editable.createParagraph;

editable.createParagraph = function extCreateParagraph(definition, extraParams, noEvent, loadAnnotations,
ignoreTemplate, preventUndo, undoCfg) {
var resType = definition.virtual ? definition.virtualResourceType : definition.resourceType;

if (!resType || !this.isInsertAllowed(resType)) {
return null;
}

extraParams = extraParams || {};

extraParams[":nameHint"] = getNameHint(resType);

cqCreateParagraph.call(this, definition, extraParams, noEvent, loadAnnotations,
ignoreTemplate, preventUndo, undoCfg);
}
}

function getNameHint(resType){
return (PREFIX + resType.substring(resType.lastIndexOf('/') + 1));

//var nameHint = PREFIX + resType.substring(resType.lastIndexOf('/') + 1);
//return _.camelCase(nameHint); AEM includes lodash 2.4.1, camelCase is available in 3.0.0
}
})();

AEM 62 - Touch UI Show Total Assets Count and Size in List View, Card View

$
0
0

Goal


Add Asset Count, Size column to List view and Card view to show the count of assets and total size of a folder

Demo | Package Install | Source Code | GitHub


Configuration & Column Visibility



List View



Card View



Solution


1) Create a servlet apps.experienceaem.assets.GetFolderSizeServlet for getting the assets count and size in folder with following code; sample call - http://localhost:4502/content/dam/experience-aem.size.json

package apps.experienceaem.assets;

import com.day.cq.dam.api.Asset;
import org.apache.felix.scr.annotations.sling.SlingServlet;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.commons.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.servlet.ServletException;
import java.util.Iterator;
import java.util.Map;

@SlingServlet(
label = "Experience AEM - Folder Size Servlet",
description = "Experience AEM - Servlet to return folder size",
resourceTypes = { "sling:OrderedFolder", "sling:Folder" },
selectors = { GetFolderSizeServlet.SEL_FOLDER_SIZE },
methods = "GET")
public class GetFolderSizeServlet extends SlingAllMethodsServlet {
private static final Logger log = LoggerFactory.getLogger(GetFolderSizeServlet.class);

public static final String SEL_FOLDER_SIZE = "size";

@Override
protected void doGet(final SlingHttpServletRequest request, final SlingHttpServletResponse response)
throws ServletException {
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");

String folderPath = request.getRequestPathInfo().getResourcePath();

try{
String stmt = "SELECT * FROM [dam:Asset] WHERE ISDESCENDANTNODE('" + folderPath + "')";

ResourceResolver resolver = request.getResourceResolver();
QueryManager qm = getQueryManager(request);

JSONObject folderJSON = getFolderMapJSON(resolver.getResource(folderPath));
JSONObject sizeObj = null;

Query query = qm.createQuery(stmt, Query.JCR_SQL2);

NodeIterator results = query.execute().getNodes();
Node node = null; Asset asset = null;

Long totalSize = 0L, size;
Integer totalAssets = 0, countedAssets = 0;

String superParentPath;

while (results.hasNext()) {
node = results.nextNode();

superParentPath = getSuperParentPath(folderPath, node);

if(!folderJSON.has(superParentPath)){
continue;
}

sizeObj = (JSONObject) folderJSON.get(superParentPath);

totalSize = (Long)sizeObj.get("size");
totalAssets = (Integer)sizeObj.get("totalAssets");
countedAssets = (Integer)sizeObj.get("countedAssets");

asset = resolver.getResource(node.getPath()).adaptTo(Asset.class);

if(asset.getMetadata("dam:size") instanceof Long){
size = (Long)asset.getMetadata("dam:size");
countedAssets++;
totalSize = totalSize + size;
}

totalAssets++;

sizeObj.put("size", totalSize);
sizeObj.put("totalAssets", totalAssets);
sizeObj.put("countedAssets", countedAssets);
}

response.getWriter().print(folderJSON);
}catch(Exception e){
log.error("Error getting size for - " + folderPath, e);
}
}

private String getSuperParentPath(String folderPath, Node node) throws Exception{
String assetPath = node.getPath();

if(node.getParent().getPath().equals(folderPath)){
return folderPath;
}

return folderPath + "/" + assetPath.substring(folderPath.length() + 1, assetPath.indexOf("/", folderPath.length() + 1));
}

private JSONObject getFolderMapJSON(Resource folderResource) throws Exception{
Iterator<Resource> childrenItr = folderResource.listChildren();
Resource child = null;

JSONObject folderJSON = new JSONObject();

while(childrenItr.hasNext()){
child = childrenItr.next();

if(child.isResourceType("sling:OrderedFolder") || child.isResourceType("sling:Folder")){
folderJSON.put(child.getPath(), getSizeObj());
}
}

return folderJSON;
}

private JSONObject getSizeObj() throws Exception{
JSONObject sizeObj = new JSONObject();

Long totalSize = 0L, size;
Integer totalAssets = 0, countedAssets = 0;

sizeObj.put("totalAssets", totalAssets);
sizeObj.put("countedAssets", countedAssets);
sizeObj.put("size", totalSize);

return sizeObj;
}

private QueryManager getQueryManager(SlingHttpServletRequest request)throws Exception {
ResourceResolver resolver = request.getResourceResolver();

Session session = resolver.adaptTo(Session.class);

return session.getWorkspace().getQueryManager();
}
}


2) To add the necessary columns (here eaemAssetsCount) overlay /libs/dam/gui/content/commons/availablecolumns and create /apps/dam/gui/content/commons/availablecolumns

3) Create nt:unstructured node /apps/dam/gui/content/commons/availablecolumns/eaemAssetsCount with the following properties

<?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"
jcr:primaryType="nt:unstructured">
<eaemAssetsCount
jcr:primaryType="nt:unstructured"
jcr:title="Assets Count, Size"
columnGroup="Experience AEM"
default="{Boolean}false"
sortable="{Boolean}true"
sortType="number"/>
</jcr:root>

4) To add dynamic content (Asset count, size) to the column, create clientlib /apps/eaem-touchui-assets-folder-size/clientlib with JS logic and categories - dam.gui.admin.util, dependencies - underscore executed on view load

5) Create nt:file /apps/eaem-touchui-assets-folder-size/clientlib/js.txt with the following content

                        asset-count.js

6) Create nt:file /apps/eaem-touchui-assets-folder-size/clientlib/asset-count.js, add the following code

(function ($, $document) {
"use strict";

var firstLoad = true,
COOKIE_AEM_ASSETS_LIST_VIEW_COLUMNS = "aem.assets.listview.columns",
EAEM_ASSETS_COUNT = "eaemAssetsCount",
LAYOUT_COL_VIEW = "column",
LAYOUT_LIST_VIEW = "list",
LAYOUT_CARD_VIEW = "card",
DIRECTORY = "directory",
FOUNDATION_CONTENT_LOADED = "foundation-contentloaded",
SEL_DAM_ADMIN_CHILD_PAGES = ".cq-damadmin-admin-childpages",
DIR_CELL_HTML = '<td is="coral-td" class="coral-Table-cell coral-Table-cell--left" alignment="column">' +
'<coral-td-label class="coral-Table-cellLabel">Total Count: ASSET_COUNT, Size: ASSETS_SIZE</coral-td-label>' +
'</td>',
LIST_CELL_HTML = '<td is="coral-td" class="coral-Table-cell coral-Table-cell--left" alignment="column">' +
'<coral-td-label class="coral-Table-cellLabel"></coral-td-label>' +
'</td>';

$document.on(FOUNDATION_CONTENT_LOADED, SEL_DAM_ADMIN_CHILD_PAGES, addAssetCount);

$document.on("cui-contentloaded", function (e) {
if(!firstLoad){
return;
}

var $childPages = $(e.currentTarget).find(SEL_DAM_ADMIN_CHILD_PAGES);

if(_.isEmpty($childPages)){
return;
}

firstLoad = false;

$childPages.trigger(FOUNDATION_CONTENT_LOADED);
});

function isFolderCountEnabled(){
var cookies = document.cookie.split(";"), tokens, isEnabled = false;

_.each(cookies, function(cookie){
tokens = cookie.split("=");

if(tokens[0].trim() !== COOKIE_AEM_ASSETS_LIST_VIEW_COLUMNS){
return;
}

isEnabled = tokens[1].trim().indexOf(EAEM_ASSETS_COUNT) > 0;
});

return isEnabled;
}

function addAssetCount(e) {
if(!e.currentTarget || !isFolderCountEnabled()){
return;
}

var $currentTarget = $(e.currentTarget),
foundationLayout = $currentTarget.data("foundation-layout");

if(_.isEmpty(foundationLayout)){
return;
}

var layoutId = foundationLayout.layoutId;

if(layoutId == LAYOUT_COL_VIEW){
return;
}

var path = $currentTarget.data("foundation-collection-id");

$.ajax(path + ".size.json").done(function(data){
var $dragHandles = $(".cq-damadmin-admin-childpages .coral-Icon--dragHandle");

if(_.isEmpty($dragHandles) || ($dragHandles.closest("td").css("display") == "none")){
//adjust UI removing drag handle column header
$(".cq-damadmin-admin-childpages thead th:last").prev().remove();
}

$(".foundation-collection-item").each(function(index, item){
itemHandler(data, layoutId, $(item) );
});
});

function itemHandler(data, layoutId, $item){
var itemPath = $item.data("foundation-collection-item-id"), isFolder;

var sizeObj = data[itemPath], size = "0 MB",
notExact, totalAssets, html;

if(!_.isEmpty(sizeObj)){
notExact = (sizeObj["totalAssets"] !== sizeObj["countedAssets"]);

if(sizeObj["size"] !== "0"){
size = (notExact ? "~" : "") + ((parseFloat(sizeObj["size"]) / (1024 * 1024))).toFixed(2) + " MB";
}
}

if(layoutId == LAYOUT_LIST_VIEW){
isFolder = $item.data("item-type") == DIRECTORY;

if(!isFolder){
html = LIST_CELL_HTML;
}else{
html = DIR_CELL_HTML.replace("ASSET_COUNT", sizeObj["totalAssets"]).replace("ASSETS_SIZE", size);
}

$item.append(html);
}else if(layoutId == LAYOUT_CARD_VIEW){
var $itemMeta = $item.find(".foundation-collection-assets-meta"), $cardTitle;

isFolder = $itemMeta.data("foundation-collection-meta-type") == DIRECTORY;

if(!isFolder){
return;
}

$cardTitle =$item.find("coral-card-content > coral-card-title");
$cardTitle.html($cardTitle.html() + " - Assets: " + sizeObj["totalAssets"] + ", " + size);
}
}

function getStringAfterLastSlash(str){
if(!str || (str.indexOf("/") == -1)){
return "";
}

return str.substr(str.lastIndexOf("/") + 1);
}
}
})(jQuery, jQuery(document));

AEM 62 - Touch UI Extend Rich Text Link Dialog, Add Rel Attribute Select

$
0
0

Goal


Extend the Link Dialog of Touch UI Rich Text Editor to add Coral Select widget for selecting rel attribute of anchor tag

Demo | Package Install | GitHub


rel Attribute select in Link Dialog



rel Applied to Anchor Tag



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-touchui-extend-rte-link-options

2) Create clientlib (type cq:ClientLibraryFolder/apps/eaem-touchui-extend-rte-link-options/clientlib and set property categories of String type to cq.authoring.dialog and dependencies String[] to underscore

3) Create file ( type nt:file ) /apps/eaem-touchui-extend-rte-link-options/clientlib/js.txt, add the following

                         link-options.js

4) Create file (type nt:file) /apps/eaem-touchui-extend-rte-link-options/clientlib/link-options.js, add the following code

(function ($) {
"use strict";

var _ = window._,
Class = window.Class,
CUI = window.CUI,
REL_FIELD = "rel",
RTE_LINK_DIALOG = "rtelinkdialog";

if(CUI.rte.ui.cui.CuiDialogHelper.eaemExtended){
return;
}

var EAEMLinkBaseDialog = new Class({
extend: CUI.rte.ui.cui.LinkBaseDialog,

toString: "EAEMLinkBaseDialog",

initialize: function(config) {
this.superClass.initialize.call(this, config);

this.$rteDialog = this.$container.find("[data-rte-dialog=link]");

this.$rteDialog.find(".coral-Popover-content").append(getLinkRelOptionsHtml());
},

dlgToModel: function() {
this.superClass.dlgToModel.call(this);

var relField = this.getFieldByType(REL_FIELD);

if(_.isEmpty(relField)){
return;
}

var relVal = relField.val();

if (_.isEmpty(relVal)) {
return;
}

this.objToEdit.attributes["rel"] = relVal;
}
});

CUI.rte.ui.cui.CuiDialogHelper = new Class({
extend: CUI.rte.ui.cui.CuiDialogHelper,

toString: "EAEMCuiDialogHelper",

instantiateDialog: function(dialogConfig) {
var type = dialogConfig.type;

if(type !== RTE_LINK_DIALOG){
this.superClass.instantiateDialog.call(this, dialogConfig);
return;
}

var $editable = $(this.editorKernel.getEditContext().root),
$container = CUI.rte.UIUtils.getUIContainer($editable),
dialog = new EAEMLinkBaseDialog();

dialog.attach(dialogConfig, $container, this.editorKernel);

return dialog;
}
});

function getLinkRelOptionsHtml(){
var html = "<div class='coral-RichText-dialog-columnContainer'>" +
"<div class='coral-RichText-dialog-column'>" +
"<coral-select data-type='rel' placeholder='Choose \"rel\" attribute'>";

var options = ["alternate", "author", "bookmark", "external", "help",
"license", "next", "nofollow", "noreferrer", "noopener", "prev", "search", "tag" ];

_.each(options, function(option){
html = html + getOptionHtml(option);
});

html = html + "</coral-select></div></div>";

return html;

function getOptionHtml(option){
return "<coral-select-item>" + option + "</coral-select-item>"
}
}

CUI.rte.ui.cui.CuiDialogHelper.eaemExtended = true;
})(jQuery);


AEM 62 - Touch UI Extend Create Paragraph For Custom Name Hint

$
0
0

Goal


Extend Create Paragraph of Touch UI Editables to provide custom names for par nodes, say eaem_

For Classic UI check this post

Demo | Package Install | GitHub




Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-touchui-par-node-name

2) Create clientlib (type cq:ClientLibraryFolder/apps/eaem-touchui-par-node-name/clientlib and set property categories of String type to cq.widgets and dependencies to lodash

3) Create file ( type nt:file ) /apps/eaem-touchui-par-node-name/clientlib/js.txt, add the following

                         par-node-name.js

4) Create file ( type nt:file ) /apps/eaem-touchui-par-node-name/clientlib/par-node-name.js, add the following code

(function(){
var PREFIX = "eaem_",
PostRequest = Granite.author.persistence.PostRequest,
prepareCreateParagraph = PostRequest.prototype.prepareCreateParagraph;

PostRequest.prototype.prepareCreateParagraph = function(config){
config.nameHint = getNameHint(config.resourceType);

return prepareCreateParagraph.call(this, config);
};

function getNameHint(resType){
return (PREFIX + resType.substring(resType.lastIndexOf('/') + 1));

//var nameHint = PREFIX + resType.substring(resType.lastIndexOf('/') + 1);
//return _.camelCase(nameHint); AEM includes lodash 2.4.1, camelCase is available in 3.0.0
}
}());

AEM 62 - Touch UI Parsys Restrict Add or Remove Components for specific User Groups

$
0
0

Goal


Restrict users from adding or deleting components in Parsys in Touch UI

Demo | Package Install | Git Hub


Configuration



Stored in CRX



Insert Error



Delete Error



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-touchui-parsys-restrict-add-remove

2) Create clientlib (type cq:ClientLibraryFolder/apps/eaem-touchui-parsys-restrict-add-remove/clientlib and set property categories of String[] type to cq.authoring.dialog, cq.compat.authoring.widgets and dependencies to underscore

3) Create file ( type nt:file ) /apps/eaem-touchui-parsys-restrict-add-remove/clientlib/js.txt, add the following

                         restrict-parsys.js

4) Create file ( type nt:file ) /apps/eaem-touchui-parsys-restrict-add-remove/clientlib/restrict-parsys.js, add the following code

// for touchui design mode
(function(){
var pathName = window.location.pathname,
EAEM_PARSYS_ADD_REMOVE_GROUPS = "eaemParsysAddRemoveGroups";

if( !pathName.endsWith("dialogwrapper.html") ){
return;
}

CQ.Ext.onReady(function () {
findDesignDialogWindow();
});

function findDesignDialogWindow(){
var wMgr = CQ.Ext.WindowMgr, winId;

var W_INTERVAL = setInterval(function () {
wMgr.each(function (win) {
if(!win || !win.id){
return;
}

clearInterval(W_INTERVAL);

addLimitTextField(win);
});
}, 250);
}

function addLimitTextField(win){
var compSelector = win.findByType("componentselector");

if(compSelector.length == 0){
return;
}

compSelector = compSelector[0];

var dialog = compSelector.findParentByType("dialog");

$.ajax( dialog.path + ".2.json" ).done(handler);

function handler(data){
var limitField = new CQ.Ext.form.TextArea({
value: data[EAEM_PARSYS_ADD_REMOVE_GROUPS] || "",
fieldLabel: "Comma Separated Groups for adding/deleting components",
name: "./" + EAEM_PARSYS_ADD_REMOVE_GROUPS,
style: {
marginBottom: '10px'
}
});

compSelector.ownerCt.insert(2, limitField);

compSelector.ownerCt.doLayout();
}
}
}());

// for touchui edit mode
(function ($document, gAuthor) {
var pathName = window.location.pathname;

if( pathName.endsWith("dialogwrapper.html") ){
return;
}

var EAEM_PARSYS_ADD_REMOVE_GROUPS = "eaemParsysAddRemoveGroups",
userGroups = [];

$(extendComponentDrop);

function getDesignPath(editable){
var parsys = editable.getParent();

var designSrc = parsys.config.designDialogSrc, result = {}, param;

designSrc = designSrc.substring(designSrc.indexOf("?") + 1);

designSrc.split(/&/).forEach( function(it) {
if (_.isEmpty(it)) {
return;
}
param = it.split("=");
result[param[0]] = param[1];
});

return decodeURIComponent(result["content"]);
}

function extendComponentDrop(){
var dropController = gAuthor.ui.dropController,
compDragDrop = dropController.get(gAuthor.Component.prototype.getTypeName());

compDragDrop.handleDrop = function(dropFn){
return function (event) {
if(!isAllowed(event.currentDropTarget.targetEditable, "Insert")){
return;
}

return dropFn.call(this, event);
};
}(compDragDrop.handleDrop);

gAuthor.edit.ToolbarActions.INSERT.handler = function eaemOpenInsertDialog(executeDlgFn){
return function (editable) {
if(!isAllowed(editable, "Insert")){
return;
}

return executeDlgFn.call(this, editable);
}
}(gAuthor.edit.ToolbarActions.INSERT.handler);

gAuthor.edit.ToolbarActions.DELETE.handler = function eaemDeleteConfirm(executeDlgFn){
return function (editable, historyConfig) {
if(!isAllowed(editable, "Delete")){
return;
}

return executeDlgFn.call(this, editable, historyConfig);
}
}(gAuthor.edit.ToolbarActions.DELETE.handler);

function isAllowed(editable, action){
if(_.isEmpty(userGroups)){
userGroups = getUserGroups();
}

var allowed = isUserGroupAllowed(editable, userGroups);

if(!allowed){
showErrorAlert(action + " not allowed for user : " + gAuthor.ContentFrame.getUserID());
}

return allowed;
}
}

function isUserGroupAllowed(editable, userGroups){
var path = getDesignPath(editable), isAllowed = false;

$.ajax( { url: path + ".2.json", async: false } ).done(function(data){
if(_.isEmpty(data) || !data[EAEM_PARSYS_ADD_REMOVE_GROUPS]){
isAllowed = true;
return;
}

var groups = data[EAEM_PARSYS_ADD_REMOVE_GROUPS];

if(_.isEmpty(groups)){
isAllowed = true;
return;
}

groups = groups.split(",");

for(var x = 0; x < groups.length; x++ ){
if(userGroups.indexOf(groups[x]) < 0){
continue;
}

isAllowed = true;

break;
}
});

return isAllowed;
}

function getUserGroups(){
var userID = Granite.author.ContentFrame.getUserID();

$.ajax( {
url: "/bin/security/authorizables.json?filter=" + userID,
async: false
} ).done(handler);

function handler(data){
if(!data || !data.authorizables){
return;
}

_.each(data.authorizables, function(authObj){
if( (authObj.id !== userID) || _.isEmpty(authObj.memberOf)){
return;
}

userGroups = _.pluck(authObj.memberOf, "id");
});
}

return userGroups;
}

function showErrorAlert(message, title){
var fui = $(window).adaptTo("foundation-ui"),
options = [{
text: "OK",
warning: true
}];

message = message || "Unknown Error";
title = title || "Error";

fui.prompt(title, message, "error", options);
}
})($(document), Granite.author);



AEM 62 - Touch UI Create Page Wizard Select Target Path

$
0
0

Goal


Extend Touch UI Create Page Wizard to select target path

Demo | Package Install | GitHub




Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de), create folder /apps/eaem-touch-ui-create-page-select-loc

2) Create node /apps/eaem-touch-ui-create-page-select-loc/clientlib of type cq:ClientLibraryFolder, add String property categories with value granite.ui.coral.foundation, String property dependencies with value lodash

3) For the path browser html, create sling:Folder /apps/eaem-touch-ui-create-page-select-loc/content and  nt:unstructured /apps/eaem-touch-ui-create-page-select-loc/content/path with the following xml

<?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/coral/foundation/form/pathbrowser"
fieldLabel="Path"
name="eaemPath"
rootPath="/content"/>

4) Create file (nt:file) /apps/eaem-touch-ui-create-page-select-loc/clientlib/js.txt, add

                       select-loc.js

5) Create file (nt:file) /apps/eaem-touch-ui-create-page-select-loc/clientlib/select-loc.js, add the following code

(function ($, $document) {
var CREATE_PAGE_WIZARD_URL = "/mnt/overlay/wcm/core/content/sites/createpagewizard.html",
PATH_BROWSER = "/apps/eaem-touch-ui-create-page-select-loc/content/path.html",
PATH_BROWSE_NAME = "[name='eaemPath']",
FORM_CREATE_PAGE = "form.cq-siteadmin-admin-createpage",
FOUNDATION_CONTENTLOADED = "foundation-contentloaded",
PARENT_PATH = "parentPath",
TAGS_FIELD = "[data-fieldname='./cq:tags']";

if(window.location.pathname.indexOf(CREATE_PAGE_WIZARD_URL) !== 0){
return;
}

$document.on(FOUNDATION_CONTENTLOADED, addPathBrowser);

function addPathBrowser(){
if(!_.isEmpty($(PATH_BROWSE_NAME))){
return;
}

$.ajax(PATH_BROWSER).done(handler);

function handler(html){
var $tagsField = $(TAGS_FIELD).closest(".foundation-field-editable");

if(_.isEmpty($tagsField)){
return;
}

var $pathBrowser = $(html).insertAfter($tagsField),
currentPath = $("[name='" + PARENT_PATH + "']").val();

$pathBrowser.find('input').val(currentPath);

$document.trigger(FOUNDATION_CONTENTLOADED);

$(FORM_CREATE_PAGE).submit(function() {
$("[name='" + PARENT_PATH + "']").val($pathBrowser.find('input').val());
});
}
}
}(jQuery, jQuery(document)));

AEM 62 - Touch UI Add Custom Columns to Assets Omni Search List View

$
0
0

Goal


Give AEM admin the flexibility to add additional columns in Assets Omni Search List View - http://localhost:4502/assets.html

Thank you ACS Commons for the Generic Lists

For adding custom columns to Assets List Viewcheck this post

For adding custom columns to Reports Consolecheck this post

Demo | Package Install | Source Code | GitHub


Configure Columns

                http://localhost:4502/etc/experience-aem/omni-search-columns.html



Custom Omni Search Columns



Solution


1) Create the extension nt:folder /apps/eaem-omni-search-dynamic-columns

2) Add the ACS Commons Generic List in nt:folder /apps/eaem-omni-search-dynamic-columns/components/apps/eaem-omni-search-dynamic-columns/templates (available in Package Install if installing the extension)

3) Create OSGI servlet apps.experienceaem.omnisearch.MetadataResultsServlet with the following code, to get the custom column values; eg. http://localhost:4502/bin/eaem/metadataResults.json?paths=/content/dam/geometrixx/shapes/cir_circle.png,/content/dam/geometrixx/shapes/sq_cube.png

package apps.experienceaem.omnisearch;

import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.sling.SlingServlet;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.commons.json.JSONArray;
import org.apache.sling.commons.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

@SlingServlet(paths = "/bin/eaem/metadataResults",
methods = "GET",
metatype = true,
label = "Experience AEM Metadata Results Servlet",
extensions = "json")
public class MetadataResultsServlet extends SlingAllMethodsServlet{
private static final Logger log = LoggerFactory.getLogger(MetadataResultsServlet.class);

private static String OMNI_SEARCH_COL_CONFIG = "/etc/experience-aem/omni-search-columns/jcr:content/list";

@Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}

@Override
protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response)
throws ServletException, IOException {
ResourceResolver resourceResolver = request.getResourceResolver();

try{
JSONObject jsonResponse = new JSONObject();

String paths = request.getParameter("paths");

if(StringUtils.isEmpty(paths)){
return;
}else{
List<String> configList = getOmniSearchConfigValues(resourceResolver.getResource(OMNI_SEARCH_COL_CONFIG));

Resource resource, propResource;
String parentPath, propName, propValue;

JSONObject valueObj;

for(String path : paths.split(",")){
resource = resourceResolver.getResource(path);

if(resource == null){
continue;
}

valueObj = new JSONObject();

for(String relPath : configList){
parentPath = relPath.substring(0, relPath.lastIndexOf("/"));
propName = relPath.substring(relPath.lastIndexOf("/") + 1);

propResource = resource.getChild(parentPath);

if(propResource == null){
continue;
}

propValue = propResource.adaptTo(ValueMap.class).get(propName, "");

valueObj.put(relPath, propValue);
}

jsonResponse.put(path, valueObj);
}
}

jsonResponse.write(response.getWriter());
}catch(Exception e){
log.error("Error reading collections", e);
}

}

public List<String> getOmniSearchConfigValues(Resource managedListResource) {
List<String> configList = new ArrayList<String>();

try {
Iterator<Resource> managedList = managedListResource.listChildren();

while (managedList.hasNext()) {
Resource r = managedList.next();
Node resNode = r.adaptTo(Node.class);

if (!resNode.hasProperty("value")) {
continue;
}

configList.add(resNode.getProperty("value").getString());
}

} catch (RepositoryException re) {
log.error("Error getting list values", re);
}

return configList;
}
}


3) Create cq:ClientLibraryFolder /apps/eaem-omni-search-dynamic-columns/clientlib with categories dam.gui.admin.util and dependencies underscore

4) Create nt:file /apps/eaem-omni-search-dynamic-columns/clientlib/js.txt with the following content

                  dynamic-columns.js

5) Create nt:file /apps/eaem-omni-search-dynamic-columnss/clientlib/dynamic-columns.js with the following code

(function ($, $document) {
var FOUNDATION_CONTENT_LOADED = "foundation-contentloaded",
ROW_SELECTOR = "tr.foundation-collection-item",
GRANITE_OMNI_SEARCH_RESULT = "#granite-omnisearch-result",
COLUMN_LIST = "/etc/experience-aem/omni-search-columns/_jcr_content.list.json",
COLUMN_CONFIG = {},
METADATA_MAPPING = "data-metadata-mapping",
RESULTS_URL = "/bin/eaem/metadataResults.json",
GRANITE_OMNI_SEARCH_CONTENT = ".granite-omnisearch-content";

loadColumnsConfiguration();

$document.on(FOUNDATION_CONTENT_LOADED, GRANITE_OMNI_SEARCH_CONTENT, function(event){
_.defer(function(){
handleContentLoad(event);
});
});

function handleContentLoad(event){
var layout = $(GRANITE_OMNI_SEARCH_RESULT).data("foundationLayout");

if(!layout || (layout.layoutId !== "list")){
return;
}

addColumnHeaders();

fillColumnData();
}

function fillColumnData(){
var $fui = $(window).adaptTo("foundation-ui");

$fui.wait();

$.ajax({
type: "POST",
dataType: "json",
url: RESULTS_URL,
data: {
paths: getPaths().join(",")
}
}).done(collectionIterate);

function collectionIterate(data){
$(ROW_SELECTOR).each(function(index, item){
itemHandler(data, $(item) );
});

$fui.clearWait();
}

function itemHandler(data, $row){
if(!_.isEmpty($row.find("[" + METADATA_MAPPING + "]"))){
return;
}

var itemPath = $row.data("foundation-collection-item-id"), metaValue;

_.each(COLUMN_CONFIG, function(colName, colMetaPath){
metaValue = data[itemPath][colMetaPath] || "";
$row.append(getListCellHtml(colMetaPath, metaValue));
});
}
}

function getPaths(){
var paths = [], $item;

$(ROW_SELECTOR).each(function(index, item){
$item = $(item);

if(!_.isEmpty($item.find("td[" + METADATA_MAPPING + "]"))){
return;
}

paths.push($item.data("foundation-collection-item-id"));
});

return paths;
}

function addColumnHeaders(){
if(checkIFHeadersAdded()){
return;
}

var headerHtml,
$container = $(GRANITE_OMNI_SEARCH_CONTENT),
$headRow = $container.find("thead > tr");

_.each(COLUMN_CONFIG, function(headerText, metaRelPath){
headerHtml = getTableHeader(metaRelPath, headerText);
$headRow.append(headerHtml);
});
}

function checkIFHeadersAdded(){
return !_.isEmpty($(GRANITE_OMNI_SEARCH_CONTENT).find("tr").find("[" + METADATA_MAPPING + "]"));
}

function getListCellHtml(colMapping, colValue){
return '<td is="coral-td" class="coral-Table-cell coral-Table-cell--left" alignment="column"'
+ METADATA_MAPPING + '="' + colMapping + '">' +
'<coral-td-label class="coral-Table-cellLabel">'
+ colValue +
'</coral-td-label>' +
'</td>';
}

function getTableHeader(colMapping, colText) {
return '<th is="coral-th"'
+ METADATA_MAPPING + '="' + colMapping + '">'
+ colText
+ '</th>';
}

function loadColumnsConfiguration(){
$.ajax({
url: COLUMN_LIST
}).done(function(data){
_.each(data, function(item){
COLUMN_CONFIG[item.value] = item.text;
})
});
}
})(jQuery, jQuery(document));



AEM 62 - TouchUI Assets Console show node name instead of dc:title

$
0
0

Goal


In all 3 views (card, list, column) of Touch UI Assets Console, show the node name (filename) and NOT dc:title for assets

For AEM 62 Path Browser check this post

For AEM 61 check this post

Demo | Package Install


Column View




Card View




List View



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-touchui-assets-show-filenames

2) Create clientlib (type cq:ClientLibraryFolder/apps/eaem-touchui-assets-show-filenames/clientlib, set a property categories of String type to dam.gui.actions.coral and dependencies of type String[] to underscore

3) Create file ( type nt:file ) /apps/eaem-touchui-assets-show-filenames/clientlib/js.txt, add the following

                         show-file-name.js

4) Create file ( type nt:file ) /apps/eaem-touchui-assets-show-filenames/clientlib/show-file-name.js, add the following code

(function ($, $document) {
"use strict";

var FOUNDATION_MODE_CHANGE = "foundation-mode-change",
EVENT_COLUMNVIEW_LOAD_ITEMS = "coral-columnview:loaditems",
FOUNDATION_COLLECTION_ID = "foundation-collection-id",
FOUNDATION_COLLECTION_ITEM_ID = "foundation-collection-item-id",
DAM_ADMIN_CHILD_PAGES = ".cq-damadmin-admin-childpages",
META_TYPE = "data-foundation-collection-meta-type",
FOUNDATION_COLLECTION_ITEM = ".foundation-collection-item",
COLUMN_VIEW = "coral-columnview",
LAYOUT_COL_VIEW = "column",
LAYOUT_LIST_VIEW = "list",
LAYOUT_CARD_VIEW = "card";

$document.on(FOUNDATION_MODE_CHANGE, modeChangeHandler);

function modeChangeHandler(){
var folderPath = $(DAM_ADMIN_CHILD_PAGES).data(FOUNDATION_COLLECTION_ID);

if(_.isEmpty(folderPath)){
return;
}

if(isColumnView()){
handleColumnView();
}else if(isCardView()){
handleCardView();
}else if(isListView()){
handleListView();
}
}

function handleColumnView(){
var $columnView = $(COLUMN_VIEW);

$columnView.on(EVENT_COLUMNVIEW_LOAD_ITEMS, function(){
$(FOUNDATION_COLLECTION_ITEM).each(handler);
});

function handler(index, item){
var $item = $(item);

if($item.data("item-type") !== "asset"){
return;
}

var assetPath = $item.data(FOUNDATION_COLLECTION_ITEM_ID),
$title = $item.find("coral-columnview-item-content");

$title.html(getStringAfterLastSlash(assetPath));
}
}

function handleCardView(){
$(FOUNDATION_COLLECTION_ITEM).each(handler);

function handler(index, item){
var $item = $(item);

if($item.find("[" + META_TYPE + "]").attr(META_TYPE) !== "asset"){
return;
}

var assetPath = $item.data(FOUNDATION_COLLECTION_ITEM_ID),
$cardTitle =$item.find("coral-card-content > coral-card-title");

$cardTitle.html(getStringAfterLastSlash(assetPath));
}
}

function handleListView(){
$(FOUNDATION_COLLECTION_ITEM).each(handler);

function handler(index, item){
var $item = $(item);

if($item.data("item-type") !== "asset"){
return;
}

var assetPath = $item.data(FOUNDATION_COLLECTION_ITEM_ID),
$title = $item.find(".foundation-collection-item-title");

$title.html(getStringAfterLastSlash(assetPath));
}
}

function isColumnView(){
return ( getAssetsConsoleLayout() === LAYOUT_COL_VIEW );
}

function isListView(){
return ( getAssetsConsoleLayout() === LAYOUT_LIST_VIEW );
}

function isCardView(){
return (getAssetsConsoleLayout() === LAYOUT_CARD_VIEW);
}

function getAssetsConsoleLayout(){
var $childPage = $(DAM_ADMIN_CHILD_PAGES),
foundationLayout = $childPage.data("foundation-layout");

if(_.isEmpty(foundationLayout)){
return "";
}

return foundationLayout.layoutId;
}

function getStringAfterLastSlash(str){
if(!str || (str.indexOf("/") == -1)){
return "";
}

return str.substr(str.lastIndexOf("/") + 1);
}
})(jQuery, jQuery(document));


AEM 62 - Touch UI Rich Text (RTE) in Asset Metadata Editor

$
0
0

Goal


Add Rich Text Editor (RTE) in Asset Metadata Editor for editing metadata fields

Demo | Package Install | GitHub


RTE in Metadata Editor



Data in CRX



Solution


1) To add a metadata field to the image/jpeg editor; access jpeg metadata schema editor

http://localhost:4502/mnt/overlay/dam/gui/content/metadataschemaeditor/schemadetails.html/default/image/jpeg?formPath=/conf/global/settings/dam/adminui-extension/metadataschema

2) Drag & Drop a Text Field, add label EAEM Summary mapping to the property ./jcr:content/metadata/eaemSummary in new tab, here EAEM





3) Saved in CRX eg. /conf/global/settings/dam/adminui-extension/metadataschema/default/image/jpeg/items/tabs/items/tab4/items/col1/items/1493448609617; manually, change the saved metadata field sling:resourceType to   cq/gui/components/authoring/dialog/richtext



4) In CRXDE Lite, create folder (nt:folder) /apps/eaem-metadata-editor-rte

5) Create clientlib (type cq:ClientLibraryFolder/apps/eaem-metadata-editor-rte/clientlib, set property categories of String type to dam.gui.coral.metadataeditor and dependencies of type String[] to rte.coralui2,underscore

6) Create file ( type nt:file ) /apps/eaem-metadata-editor-rte/clientlib/js.txt, add the following

                         metadata-editor-rte.js

7) Create file ( type nt:file ) /apps/eaem-metadata-editor-rte/clientlib/metadata-editor-rte.js, add the following code

(function($document, $) {
"use strict";

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

function initRTE(){
var $rteContainer = $document.find(".richtext-container");

if(_.isEmpty($rteContainer)){
return;
}

CUI.util.plugClass(CUI.RichText, "richEdit", function(rte) {
CUI.rte.ConfigUtils.loadConfigAndStartEditing(rte, $(this));
});

handleStartFinish($rteContainer);

styleUI($rteContainer);
}

function handleStartFinish($rteContainer){
$rteContainer.find(".coral-RichText").each(function() {
($(this)).richEdit();
});

var $valueField = $rteContainer.find("input[type=hidden]");

$rteContainer.each(function() {
$(this).find(".coral-RichText-editable").empty().append($valueField.val());
});

$rteContainer.on("editing-finished", ".coral-RichText-editable", function(e, editedContent) {
$valueField.val(editedContent);
});
}

function styleUI($rteContainer){
var $richTextDiv = $rteContainer.find(".coral-RichText");

$rteContainer.find("[name='./textIsRich']").remove();

$richTextDiv.css("height", "180px").closest(".aem-assets-metadata-form-column").css("width", "80%");
}
})(Granite.$(document), Granite.$);


AEM 62 - Touch UI Sort Components by Title in Insert New Component Dialog

$
0
0

Goal


Sort the components alphabetically in Insert New Component dialog of Touch UI Editor

Demo | Package Install | GitHub


Product



Extension



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-touch-parsys-insert-comp-sort-title

2) Create clientlib (type cq:ClientLibraryFolder/apps/eaem-touch-parsys-insert-comp-sort-title/clientlib and set property categories of String[] type to cq.authoring.dialog and dependencies to underscore

3) Create file ( type nt:file ) /apps/eaem-touch-parsys-insert-comp-sort-title/clientlib/js.txt, add the following

                         sort-components.js

4) Create file ( type nt:file ) /apps/eaem-touch-parsys-insert-comp-sort-title/clientlib/sort-components.js, add the following code

(function ($document, gAuthor) {
$(extendComponentInsert);

function extendComponentInsert(){
gAuthor.edit.ToolbarActions.INSERT.handler = function eaemOpenInsertDialog(executeDlgFn){
return function (editable) {
gAuthor.components.allowedComponents.sort(sortFn);

executeDlgFn.call(this, editable);
}
}(gAuthor.edit.ToolbarActions.INSERT.handler);
}

function sortFn(comp1, comp2){
try{
return comp1.componentConfig.title.localeCompare(comp2.componentConfig.title)
}catch(err){
console.log("Error doing compare", err);
}
}
})($(document), Granite.author);


AEM 62 - Touch UI Extend RTE List Plugin for adding selected CSS class

$
0
0

Goal


In Touch UI RTE (Rich Text Editor) extend the List plugin to apply user selected css class

Demo | Package Install | GitHub






Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-touch-rte-list-plugin-css

2) Create dialog /apps/eaem-touch-rte-list-plugin-css/dialog/cq:dialog with the allowed css classes for user selection (applied on list)

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/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="nt:unstructured"
jcr:title="Css Class"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<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">
<css-classes
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/select"
emptyText="Select css class..."
name="./cssClass">
<items jcr:primaryType="nt:unstructured">
<red
jcr:primaryType="nt:unstructured"
text="coral-Tag--red"/>
<magenta
jcr:primaryType="nt:unstructured"
text="coral-Tag--magenta"/>
<fuchsia
jcr:primaryType="nt:unstructured"
text="coral-Tag--fuchsia"/>
<plum
jcr:primaryType="nt:unstructured"
text="coral-Tag--plum"/>
<blue
jcr:primaryType="nt:unstructured"
text="coral-Tag--blue"/>
</items>
<select
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/button"
class="coral-Button--primary"
text="Select"/>
</css-classes>
</items>
</column>
</items>
</content>
</jcr:root>

3) Create clientlib (type cq:ClientLibraryFolder/apps/eaem-touch-rte-list-plugin-css/clientlib and set property categories of String[] type to cq.authoring.dialog and dependencies to underscore

4) Create file ( type nt:file ) /apps/eaem-touch-rte-list-plugin-css/clientlib/js.txt, add the following

                         list-plugin-css.js

5) Create file ( type nt:file ) /apps/eaem-touch-rte-list-plugin-css/clientlib/list-plugin-css.js, add the following code

(function ($) {
"use strict";

var _ = window._,
Class = window.Class,
GROUP = "experience-aem",
CSS_DIALOG = "/apps/eaem-touch-rte-list-plugin-css/dialog/cq:dialog.html",
CUI = window.CUI;

var EAEMCSSListCmd = new Class({
extend: CUI.rte.commands.List,

toString: "EAEMCSSListCmd",

execute: function(execDef) {
this.superClass.execute.call(this, execDef);

var list = this.getDefiningListDom(execDef.editContext, execDef.nodeList);

if(!list){
return;
}

registerReceiveDataListener(receiveMessage);

if(!this.eaemSelectCssDialog){
this.eaemSelectCssDialog = showDialog();
}

var eaemSelectCssDialog = this.eaemSelectCssDialog;

eaemSelectCssDialog.show();

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

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

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

$(list).addClass(message.data.cssClass);

eaemSelectCssDialog.hide();

removeReceiveDataListener(receiveMessage);
}
}
});

function showDialog(){
var html = "<iframe width='540px' height='250px' frameBorder='0' src='"
+ CSS_DIALOG + "?requester=" + GROUP + "'>"
+ "</iframe>";

var cuiDialog = new Coral.Dialog().set({
backdrop: 'static',
header: {
innerHTML: 'Select css class'
},
content: {
innerHTML: html
}
});

document.body.appendChild(cuiDialog);

return cuiDialog;
}

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

CUI.rte.commands.CommandRegistry.register("_list", EAEMCSSListCmd);
})(jQuery);

(function($, $document){
var SENDER = "experience-aem",
REQUESTER = "requester",
CSS_CLASS = "cssClass";

if(queryParameters()[REQUESTER] !== SENDER ){
return;
}

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

function queryParameters() {
var 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 stylePopoverIframe(){
var $dialog = $(".cq-dialog").css("background", "white"),
$selectBut = $dialog.find(".coral-Button--primary");

$dialog.find(".cq-dialog-header").hide();
$dialog.find(".cq-dialog-content").css("top", ".1rem");

$selectBut.css("margin", "120px 0 0 380px").click(sendDataMessage);
}

function sendDataMessage(){
var message = {
sender: SENDER,
data: {}
}, $dialog, cssClass;

$dialog = $(".cq-dialog");

cssClass = $dialog.find("[name='./" + CSS_CLASS + "']").val();

message.data[CSS_CLASS] = cssClass;

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

AEM 63 - Touch UI make General group Components available to demo site We.Retail

$
0
0

Goal


In vanilla AEM 63, the components of General group available to site /content/we-retail are Content Fragment, Experience Fragment and Layout Container

This simple post is on configuring We.Retail template Hero Page - /conf/we-retail/settings/wcm/templates/hero-page, the template for page - /content/we-retail/language-masters/en.html, to have the text component - /libs/foundation/components/text from General group available for drag and drop

Package Install




Solution


1) Access We.Retail site templates, Start -> Tools -> General -> Templates -> We.Retail - http://localhost:4502/libs/wcm/core/content/sites/templates.html/conf/we-retail



2) Edit Hero Page template - http://localhost:4502/editor.html/conf/we-retail/settings/wcm/templates/hero-page/structure.html



3) In the Structure mode of Hero Page template, click on the Policy icon of Layout container component



4) In the Layout Container Design -> Properties -> Allowed Components, select the components of General to be made available to pages of type Hero Page template, click Save



5) The Layout container policy setting and the policy allowed components in CRXDE http://localhost:4502/crx/de/index.jsp#/conf/we-retail/settings/wcm/policies/wcm/foundation/components/responsivegrid/we-retail-default





6) Foundation text component is now available to Hero pages - http://localhost:4502/editor.html/content/we-retail/language-masters/en.html


AEM 63 - Touch UI RTE (Rich Text Editor) Color Picker Plugin for InPlace and Dialog Editing

$
0
0

Goal


Touch UI Color Picker Plugin for RTE (Rich Text Editor) InPlace and Dialog Edit  /libs/cq/gui/components/authoring/dialog/richtext

For a similar extension on 62 check this post, 61 check this post; to add General group components on We.Retail pages check this post

For demo purposes, dialog of foundation text component was modified to add the color picker configuration - /libs/foundation/components/text/cq:dialog/content/items/text/items/column/items/text/rtePlugins

Demo | Package Install | Github


Plugin Configuration - InPlace Editing




Plugin Configuration - Dialog









Picker with Free Style Palette - InPlace Editing Maximized




Picker with Free Style Palette - Inline Dialog




Picker with Free Style Palette - Full Screen Dialog



Color Applied






Solution


1) Login to CRXDE Lite, add nt:folder /apps/eaem-touchui-dialog-rte-color-picker

2) To show the color picker in a dialog create /apps/eaem-touchui-dialog-rte-color-picker/color-picker-popover of type sling:Folder and /apps/eaem-touchui-dialog-rte-color-picker/color-picker-popover/cq:dialog of type nt:unstructured




                             
3) Create clientlib (cq:ClientLibraryFolder) /apps/eaem-touchui-dialog-rte-color-picker/clientlib set property categories to cq.authoring.dialog.all and dependencies to [underscore]

4) Create file (nt:file) /apps/eaem-touchui-dialog-rte-color-picker/clientlib/js.txt, add the following content

                   color-picker.js

5) Create file (nt:file) /apps/eaem-touchui-dialog-rte-color-picker/clientlib/color-picker.js, add the following code

(function($, CUI){
var GROUP = "experience-aem",
COLOR_PICKER_FEATURE = "colorPicker",
TCP_DIALOG = "eaemTouchUIColorPickerDialog",
PICKER_NAME_IN_POPOVER = "color",
REQUESTER = "requester",
PICKER_URL = "/apps/eaem-touchui-dialog-rte-color-picker/color-picker-popover/cq:dialog.html";

addPluginToDefaultUISettings();

addDialogTemplate();

var EAEMColorPickerDialog = new Class({
extend: CUI.rte.ui.cui.AbstractDialog,

toString: "EAEMColorPickerDialog",

initialize: function(config) {
this.exec = config.execute;
},

getDataType: function() {
return TCP_DIALOG;
}
});

var TouchUIColorPickerPlugin = new Class({
toString: "TouchUIColorPickerPlugin",

extend: CUI.rte.plugins.Plugin,

pickerUI: null,

getFeatures: function() {
return [ COLOR_PICKER_FEATURE ];
},

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

if (!this.isFeatureEnabled(COLOR_PICKER_FEATURE)) {
return;
}

this.pickerUI = tbGenerator.createElement(COLOR_PICKER_FEATURE, this, false, { title: "Color Picker" });
tbGenerator.addElement(GROUP, plg.Plugin.SORT_FORMAT, this.pickerUI, 10);

var groupFeature = GROUP + "#" + COLOR_PICKER_FEATURE;
tbGenerator.registerIcon(groupFeature, "textColor");
},

execute: function (id, value, envOptions) {
if(!isValidSelection()){
return;
}

var context = envOptions.editContext,
selection = CUI.rte.Selection.createProcessingSelection(context),
ek = this.editorKernel,
startNode = selection.startNode;

if ( (selection.startOffset === startNode.length) && (startNode != selection.endNode)) {
startNode = startNode.nextSibling;
}

var tag = CUI.rte.Common.getTagInPath(context, startNode, "span"), plugin = this, dialog,
color = $(tag).css("color"),
dm = ek.getDialogManager(),
$container = CUI.rte.UIUtils.getUIContainer($(context.root)),
propConfig = {
'parameters': {
'command': this.pluginId + '#' + COLOR_PICKER_FEATURE
}
};

if(this.eaemColorPickerDialog){
dialog = this.eaemColorPickerDialog;
}else{
dialog = new EAEMColorPickerDialog();

dialog.attach(propConfig, $container, this.editorKernel);

dialog.$dialog.css("-webkit-transform", "scale(0.8)").css("-webkit-transform-origin", "0 0")
.css("-moz-transform", "scale(0.8)").css("-moz-transform-origin", "0px 0px");

dialog.$dialog.find("iframe").attr("src", getPickerIFrameUrl(color));

this.eaemColorPickerDialog = dialog;
}

dm.show(dialog);

registerReceiveDataListener(receiveMessage);

function isValidSelection(){
var winSel = window.getSelection();
return winSel && winSel.rangeCount == 1 && winSel.getRangeAt(0).toString().length > 0;
}

function getPickerIFrameUrl(color){
var url = PICKER_URL + "?" + REQUESTER + "=" + GROUP;

if(!_.isEmpty(color)){
url = url + "&" + PICKER_NAME_IN_POPOVER + "=" + color;
}

return url;
}

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

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

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

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

action = message.action;

if (action === "submit") {
if (!_.isEmpty(message.data)) {
ek.relayCmd(id, message.data);
}
}else if(action === "remove"){
ek.relayCmd(id);
}else if(action === "cancel"){
plugin.eaemColorPickerDialog = null;
}

dialog.hide();

removeReceiveDataListener(receiveMessage);
}
},

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

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

CUI.rte.plugins.PluginRegistry.register(GROUP,TouchUIColorPickerPlugin);

var TouchUIColorPickerCmd = new Class({
toString: "TouchUIColorPickerCmd",

extend: CUI.rte.commands.Command,

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

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

_getTagObject: function(color) {
return {
"tag": "span",
"attributes": {
"style" : "color: " + color
}
};
},

execute: function (execDef) {
var color = execDef.value ? execDef.value[PICKER_NAME_IN_POPOVER] : undefined,
selection = execDef.selection,
nodeList = execDef.nodeList;

if (!selection || !nodeList) {
return;
}

var common = CUI.rte.Common,
context = execDef.editContext,
tagObj = this._getTagObject(color);

//if no color value passed, assume delete and remove color
if(_.isEmpty(color)){
nodeList.removeNodesByTag(execDef.editContext, tagObj.tag, undefined, true);
return;
}

var tags = common.getTagInPath(context, selection.startNode, tagObj.tag);

//remove existing color before adding new color
if (tags != null) {
nodeList.removeNodesByTag(execDef.editContext, tagObj.tag, undefined, true);
}

nodeList.surround(execDef.editContext, tagObj.tag, tagObj.attributes);
}
});

CUI.rte.commands.CommandRegistry.register(COLOR_PICKER_FEATURE, TouchUIColorPickerCmd);

function addPluginToDefaultUISettings(){
var toolbar = CUI.rte.ui.cui.DEFAULT_UI_SETTINGS.inline.toolbar;
toolbar.splice(3, 0, GROUP + "#" + COLOR_PICKER_FEATURE);

toolbar = CUI.rte.ui.cui.DEFAULT_UI_SETTINGS.fullscreen.toolbar;
toolbar.splice(3, 0, GROUP + "#" + COLOR_PICKER_FEATURE);
}

function addDialogTemplate(){
var url = PICKER_URL + "?" + REQUESTER + "=" + GROUP;

var html = "<iframe width='410px' height='450px' frameBorder='0' src='" + url + "'></iframe>";

if(_.isUndefined(CUI.rte.Templates)){
CUI.rte.Templates = {};
}

if(_.isUndefined(CUI.rte.templates)){
CUI.rte.templates = {};
}

CUI.rte.templates['dlg-' + TCP_DIALOG] = CUI.rte.Templates['dlg-' + TCP_DIALOG] = Handlebars.compile(html);

//Coral.templates.RichTextEditor['dlg_' + TCP_DIALOG] = Handlebars.compile(html);
}
}(jQuery, window.CUI,jQuery(document)));

(function($, $document){
var SENDER = "experience-aem",
REQUESTER = "requester",
COLOR = "color",
ADD_COLOR_BUT = "#EAEM_CP_ADD_COLOR",
REMOVE_COLOR_BUT = "#EAEM_CP_REMOVE_COLOR";

if(queryParameters()[REQUESTER] !== SENDER ){
return;
}

$(function(){
_.defer(stylePopoverIframe);
});

function queryParameters() {
var 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 stylePopoverIframe(){
var queryParams = queryParameters(),
$dialog = $(".coral-Dialog");

if(_.isEmpty($dialog)){
return;
}

$dialog.css("overflow", "hidden");

$dialog[0].open = true;

var $addColor = $dialog.find(ADD_COLOR_BUT),
$removeColor = $dialog.find(REMOVE_COLOR_BUT),
$colorPicker = $document.find(".coral-ColorPicker"),
pickerInstance = $colorPicker.data("colorpicker");

if(!_.isEmpty(queryParameters()[COLOR])){
pickerInstance._setColor(decodeURIComponent(queryParams[COLOR]));
}

adjustHeader($dialog);

$dialog.find(".coral-Dialog-wrapper").css("margin","0").find(".coral-Dialog-content").css("padding","0");

$colorPicker.closest(".coral-Form-fieldwrapper").css("margin-bottom", "285px");

$(ADD_COLOR_BUT).css("margin-left", "150px");

$addColor.click(sendDataMessage);

$removeColor.click(sendRemoveMessage);
}

function adjustHeader($dialog){
var $header = $dialog.css("background-color", "#fff").find(".coral-Dialog-header");

$header.find(".cq-dialog-submit").remove();

$header.find(".cq-dialog-cancel").click(function(event){
event.preventDefault();

$dialog.remove();

sendCancelMessage();
});
}

function sendCancelMessage(){
var message = {
sender: SENDER,
action: "cancel"
};

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

function sendRemoveMessage(){
var message = {
sender: SENDER,
action: "remove"
};

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

function sendDataMessage(){
var message = {
sender: SENDER,
action: "submit",
data: {}
}, $dialog, color;

$dialog = $(".cq-dialog");

color = $dialog.find("[name='./" + COLOR + "']").val();

if(color && color.indexOf("rgb") >= 0){
color = CUI.util.color.RGBAToHex(color);
}

message.data[COLOR] = color;

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

AEM 63 - Touch UI Nested ( Multi-Multi ) Coral 2 Composite Multifield

$
0
0

Goal


AEM 63 Touch UI Nested Composite Multifield (Multi-Multi Field) storing the entered data as Child Nodes.

This implementation uses Coral 2 multifield/libs/granite/ui/components/foundation/form/multifield; AEM 63 provides otb implementation of composite multifield (NOT nested composite multifield) using Coral 3 widget - /libs/granite/ui/components/coral/foundation/form/multifield

This post is on nested composite multifield; For composite multifield use otb widget /libs/granite/ui/components/coral/foundation/form/multifield (set property composite=true).

For AEM 62 check this post

Demo | Package Install | Github


Nested Composite Multifield



Stored as Child Nodes



Sample Dialog 



Sample Dialog XML

#45 eaem-nested=NODE_STORE makes the multifield widget, nested composite multifield

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/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="nt:unstructured"
jcr:title="EAEM TouchUI Nested Multifield"
sling:resourceType="cq/gui/components/authoring/dialog"
helpPath="en/cq/current/wcm/default_components.html#Text">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<fieldset
jcr:primaryType="nt:unstructured"
jcr:title="Sample Dashboard"
sling:resourceType="granite/ui/components/foundation/form/fieldset">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<dashboard
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldDescription="Enter Dashboard name"
fieldLabel="Dashboard name"
name="./dashboard"/>
<countries
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/multifield"
class="full-width"
fieldDescription="Click '+' to add a new page"
fieldLabel="Countries">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/fieldset"
eaem-nested="NODE_STORE"
name="./countries">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"
method="absolute"/>
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<country
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldDescription="Name of Country"
fieldLabel="Country Name"
name="./country"/>
<states
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/multifield"
class="full-width"
fieldDescription="Click '+' to add a new page"
fieldLabel="States">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/fieldset"
name="./states">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"
method="absolute"/>
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<state
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldDescription="Name of State"
fieldLabel="State Name"
name="./state"/>
<path
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/pathbrowser"
fieldDescription="Select Path"
fieldLabel="Path"
name="./path"
rootPath="/content"/>
<startDate
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/datepicker"
class="field"
displayedFormat="YYYY-MM-DD HH:mm"
fieldLabel="Start Date"
name="./startDate"
type="datetime"/>
<show
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/checkbox"
name="./show"
text="Show?"
value="yes"/>
<type
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/select"
fieldDescription="Select Size"
fieldLabel="Size"
name="./size">
<items jcr:primaryType="nt:unstructured">
<def
jcr:primaryType="nt:unstructured"
text="Select Size"
value=""/>
<small
jcr:primaryType="nt:unstructured"
text="Small"
value="small"/>
<medium
jcr:primaryType="nt:unstructured"
text="Medium"
value="medium"/>
<large
jcr:primaryType="nt:unstructured"
text="Large"
value="large"/>
</items>
</type>
<tags
jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/common/tagspicker"
allowCreate="{Boolean}true"
fieldLabel="Tags"
name="./tags"/>
</items>
</column>
</items>
</field>
</states>
</items>
</column>
</items>
</field>
</countries>
</items>
</column>
</items>
</fieldset>
</items>
</column>
</items>
</content>
</jcr:root>

                                                             

Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de), create folder /apps/eaem-touch-ui-nested-multi-field-node-store

2) Create node /apps/eaem-touch-ui-nested-multi-field-node-store/clientlib of type cq:ClientLibraryFolder, add String property categories with value cq.authoring.dialog.all, String property dependencies with value underscore

3) Create file (nt:file) /apps/eaem-touch-ui-nested-multi-field-node-store/clientlib/js.txt, add

                       nested-multifield.js

4) Create file (nt:file) /apps/eaem-touch-ui-nested-multi-field-node-store/clientlib/nested-multifield.js, add the following code

(function ($, $document) {
var EAEM_NESTED = "eaem-nested",
DATA_EAEM_NESTED = "data-" + EAEM_NESTED,
CFFW = ".coral-Form-fieldwrapper",
NODE_STORE = "NODE_STORE";

if(CUI.Multifield.eaemNMFExtended){
return;
}

CUI.Multifield.eaemNMFExtended = true;

function isNodeStoreMultifield(type) {
return (type === NODE_STORE);
}

function isSelectOne($field) {
return !_.isEmpty($field) && ($field.prop("type") === "select-one");
}

function setSelectOne($field, value) {
var select = $field.closest(".coral-Select").data("select");

if (select) {
select.setValue(value);
}
}

function isCheckbox($field) {
return !_.isEmpty($field) && ($field.prop("type") === "checkbox");
}

function setCheckBox($field, value) {
$field.prop("checked", $field.attr("value") === value);
}

function isDateField($field) {
return !_.isEmpty($field) && $field.parent().hasClass("coral-DatePicker");
}

function setDateField($field, value) {
var date = moment(new Date(value)),
$parent = $field.parent();

$parent.find("input.coral-Textfield").val(date.format($parent.data("displayed-format")));

$field.val(date.format($parent.data("stored-format")));
}

function isTagsField($fieldWrapper) {
return !_.isEmpty($fieldWrapper) && ($fieldWrapper.children(".js-cq-TagsPickerField").length > 0);
}

function getTagsFieldName($fieldWrapper) {
return $fieldWrapper.children(".js-cq-TagsPickerField").data("property-path").substr(2);
}

function getTagObject(tag){
var tagPath = "/etc/tags/" + tag.replace(":", "/");
return $.get(tagPath + ".tag.json");
}

function setTagsField($fieldWrapper, tags) {
if(_.isEmpty(tags)){
return;
}

var cuiTagList = $fieldWrapper.find(".coral-TagList").data("tagList");

_.each(tags, function(tag){
getTagObject(tag).done(function(data){
cuiTagList._appendItem( { value: data.tagID, display: data.titlePath} );
});
});
}

function isMultifield($formFieldWrapper){
return ($formFieldWrapper.children("[data-init='multifield']").length > 0);
}

function setWidgetValue($field, value) {
if (_.isEmpty($field)) {
return;
}

if(isSelectOne($field)) {
setSelectOne($field, value);
}else if(isCheckbox($field)) {
setCheckBox($field, value);
}else if(isDateField($field)) {
setDateField($field, value);
}else {
$field.val(value);
}
}

function getMultifields($formField, isInner){
var mNames = {}, mName, $multifield, $template,
$multiTemplates = $formField.find(".js-coral-Multifield-input-template");

$multiTemplates.each(function (i, template) {
$template = $(template);
$multifield = $($template.html());

if(!isInner && !isNodeStoreMultifield($multifield.data(EAEM_NESTED))){
return;
}

mName = $multifield.data("name").substring(2);

mNames[mName] = $template.closest(".coral-Multifield");
});

return mNames;
}

function buildMultifield(data, $multifield, mName){
var $formFieldWrapper, $field, $fieldSet, name,
innerMultifields;

_.each(data, function (value, key) {
if(key.indexOf("jcr:") === 0){
return;
}

$multifield.children(".js-coral-Multifield-add").click();

$fieldSet = $multifield.find(".coral-Form-fieldset").last();

_.each($fieldSet.find(CFFW), function (formFieldWrapper) {
$formFieldWrapper = $(formFieldWrapper);

if(isMultifield($formFieldWrapper)){
innerMultifields = getMultifields($formFieldWrapper, true);

_.each(innerMultifields, function($innerMultifield, nName){
buildMultifield(value[nName], $innerMultifield, nName);
});

return;
}else if(isTagsField($formFieldWrapper)){
setTagsField($formFieldWrapper, value[getTagsFieldName($formFieldWrapper)]);
return;
}

$field = $formFieldWrapper.find("[name]");

if(_.isEmpty($field)){
return;
}

name = $field.attr("name").substr(2);

if(_.isEmpty(value[name])){
return;
}

setWidgetValue($field, value[name]);
});
})
}

function addDataInFields() {
$document.on("dialog-ready", dlgReadyHandler);

function dlgReadyHandler() {
var outerMultifields = getMultifields($(this), false),
$form = $("form.cq-dialog"),
actionUrl = $form.attr("action") + ".infinity.json";

$.ajax(actionUrl).done(postProcess);

function postProcess(data){
_.each(outerMultifields, function($outerMultifield, mName){
buildMultifield(data[mName], $outerMultifield, mName);
});
}
}
}

function fillValue($form, fieldSetName, $field, counter){
var name = $field.attr("name"), value;

if (!name) {
return;
}

if (name.indexOf("./") === 0) {
name = name.substring(2);
}

value = $field.val();

if (isCheckbox($field)) {
value = $field.prop("checked") ? $field.val() : "";
}

//remove the field, so that individual values are not POSTed
$field.remove();

$('<input />').attr('type', 'hidden')
.attr('name', fieldSetName + "/" + counter + "/" + name)
.attr('value', value)
.appendTo($form);
}

function addNestedMultifieldData($form, outerMultiName, $nestedMultiField){
var $fieldSets = $nestedMultiField.find("[class='coral-Form-fieldset']"),
nName = $fieldSets.data("name"), $fields;

if(!nName){
return;
}

nName = outerMultiName + "/" + nName.substring(2);

$fieldSets.each(function (iCounter, fieldSet) {
$fields = $(fieldSet).find("[name]");

$fields.each(function (counter, field) {
fillValue($form, nName, $(field), (iCounter + 1));
});
});
}

function collectDataFromFields(){
$document.on("click", ".cq-dialog-submit", collectHandler);

function collectHandler() {
var $form = $(this).closest("form.foundation-form"),
mName = $("[" + DATA_EAEM_NESTED + "]").data("name"),
$fieldSets = $("[" + DATA_EAEM_NESTED + "][class='coral-Form-fieldset']");

var $fields, $field, name, $nestedMultiField;

$fieldSets.each(function (oCounter, fieldSet) {
$fields = $(fieldSet).children().children(CFFW);

$fields.each(function (counter, field) {
$field = $(field);

//may be a nested multifield
$nestedMultiField = $field.find("[data-init='multifield']");

if($nestedMultiField.length == 0){
fillValue($form, mName, $(field).find("[name]"), (oCounter + 1));
}else{
addNestedMultifieldData($form, mName + "/" + (oCounter + 1) , $nestedMultiField);
}
});
});
}
}

$document.ready(function () {
addDataInFields();
collectDataFromFields();
});

//extend otb multifield for adjusting event propagation when there are nested multifields
//for working around the nested multifield add and reorder
CUI.Multifield = new Class({
toString: "Multifield",
extend: CUI.Multifield,

construct: function () {
this.script = this.$element.find(".js-coral-Multifield-input-template:last");
},

_addListeners: function () {
this.superClass._addListeners.call(this);

//otb coral event handler is added on selector .js-coral-Multifield-add
//any nested multifield add click events are propagated to the parent multifield
//to prevent adding a new composite field in both nested multifield and parent multifield
//when user clicks on add of nested multifield, stop the event propagation to parent multifield
this.$element.on("click", ".js-coral-Multifield-add", function (e) {
e.stopPropagation();
});

this.$element.on("drop", function (e) {
e.stopPropagation();
});
}
});

CUI.Widget.registry.register("multifield", CUI.Multifield);
})(jQuery, jQuery(document));


5) Create file (nt:file) /apps/eaem-touch-ui-nested-multi-field-node-store/eaem-sample-nested-multi-field/eaem-sample-nested-multi-field.jsp for rendering the stored multifield data

<%@ page import="java.io.PrintWriter" %>
<%@ page import="org.apache.commons.lang.StringUtils" %>
<%@include file="/libs/foundation/global.jsp" %>
<%@page session="false" %>

<div style="display: block; border-style: solid; border-width: 1px; margin: 10px; padding: 10px">
<b>Countries and States</b>

<%
try {
if (currentNode.hasNode("countries")) {
Node countriesNode = currentNode.getNode("countries"), cNode;
int counter = 1; PropertyIterator itr = null; Property property;

while(true){
if(!countriesNode.hasNode(String.valueOf(counter))){
break;
}

cNode = countriesNode.getNode(String.valueOf(counter));

itr = cNode.getProperties();

while(itr.hasNext()){
property = itr.nextProperty();

if(property.getName().equals("jcr:primaryType")){
continue;
}
%>
<%=property.getName()%> : <b><%=property.getString()%></b>
<%
}

if(cNode.hasNode("states")){
Node statesNode = cNode.getNode("states"), sNode;
int sCounter = 1; PropertyIterator sTtr = null; Property sProperty;

while(true){
if(!statesNode.hasNode(String.valueOf(sCounter))){
break;
}

sNode = statesNode.getNode(String.valueOf(sCounter));

itr = sNode.getProperties();

while(itr.hasNext()){
sProperty = itr.nextProperty();

if(sProperty.getName().equals("jcr:primaryType")){
continue;
}

String value = null;

if (sProperty.isMultiple()) {
Value[] values = sProperty.getValues();
value = StringUtils.join(values, ",");
} else {
value = sProperty.getString();
}

%>
<div style="margin-left:30px">
<%=sProperty.getName()%> : <b><%=value%></b>
</div>
<%
}

%>

<%

sCounter = sCounter + 1;
}
}

counter = counter + 1;
}
} else {
%>
Add countries and states in dialog</b>
<%
}
} catch (Exception e) {
e.printStackTrace(new PrintWriter(out));
}
%>
</div>

AEM 63 - This and That

$
0
0


FIND_BINARY_LOCATION_ON_FILE_SYSTEM

With AEM 63, the default store for binaries is File Data Store - FDS; to find the location of binary on file system

     1) Get the jcr:content/metadata/dam:sha1 of binary from CRXDE eg. 4608b968ad44797313ee7d6710be9c3dfba1e937



     2) Search for the file with name as dam:shalvalue in <path_to_aem_author_install>/author/crx-quickstart/repository



     3) To be sure, just add the extension of binary eg. 4608b968ad44797313ee7d6710be9c3dfba1e937.jpg, dont forget to change it back to no extension


Viewing all 525 articles
Browse latest View live


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