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

AEM 61 - Get reference of Parent Parsys Editable in Component Dialog

$
0
0

Goal


Sample code to get the reference of parent parsys component editable in TouchUI

Demo | Package Install



Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/eaem-parsys-ref-in-component-dialog

2) Create node /apps/eaem-parsys-ref-in-component-dialog/clientlib of type cq:ClientLibraryFolder and add a String property categories with value cq.authoring.dialog

3) Create file (nt:file) /apps/eaem-parsys-ref-in-component-dialog/clientlib/js.txt and add

                       parsys-ref.js

4) Create file (nt:file) /apps/eaem-parsys-ref-in-component-dialog/clientlib/parsys-ref.js and add the following code

(function ($document, gAuthor) {
$document.on("dialog-ready", showParsysPath);

function showParsysPath(){
var dialog = gAuthor.DialogFrame.currentDialog,
parent = dialog.editable.getParent();

if(!parent){
return;
}

showMessage("Parent", parent.path);
}

function showMessage(title, message){
$(window).adaptTo('foundation-ui').prompt(title, message, "default", [{
primary: true,
text: Granite.I18n.get('OK')
}]);
}
}($(document), Granite.author));


AEM 61 - TouchUI Authoring Refresh page when switched to Preview mode

$
0
0

Goal


Refresh page when the layer mode is switched to Preview

Demo | Package Install


Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/touchui-refresh-page-preview-mode

2) Create node /apps/touchui-refresh-page-preview-mode/clientlib of type cq:ClientLibraryFolder and add a String property categories with value cq.authoring.dialog

3) Create file (nt:file) /apps/touchui-refresh-page-preview-mode/clientlib/js.txt and add

                       refresh-on-preview.js

4) Create file (nt:file) /apps/touchui-refresh-page-preview-mode/clientlib/refresh-on-preview.js and add the following code

(function ($, $document) {
$document.on('cq-layer-activated', refreshPage);

function refreshPage(ev){
if ( (ev.prevLayer === "Preview") || (ev.layer !== 'Preview') ) {
return;
}

window.location.reload();
}
}(jQuery, jQuery(document)));

AEM 61 SP1 - TouchUI Configure Component Drop Zone Placeholder Text

$
0
0

Goal


This post is on extendingcomponent drop overlay placeholders to show custom text (and background color) instead of component name (jcr:title)

Foundation Text component (/libs/foundation/components/text) was modified for demonstration only (on Geometrixx pages), ideally the foundation components should never be altered...

Demo | Package Install


Configure Placeholder text

                    eaemPlaceholderText - Custom text to be shown instead of component name

                    eaemPlaceholderBGColor - Placeholder overlay background color




Placeholder on Page



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/touchui-configure-components-placeholder

2) Create clientlib (type cq:ClientLibraryFolder/apps/touchui-configure-components-placeholder/clientlib and set property categories of String[] type to cq.authoring.dialog and dependencies to underscore

3) Create file ( type nt:file ) /apps/touchui-configure-components-placeholder/clientlib/js.txt, add the following

                         configure-placeholder.js

4) Create file ( type nt:file ) /apps/touchui-configure-components-placeholder/clientlib/configure-placeholder.js, add the following code

(function ($, $document, gAuthor) {
var PLACEHOLDER_TEXT = "eaemPlaceholderText",
PLACEHOLDER_BG_COLOR = "eaemPlaceholderBGColor",
PLACE_HOLDER = "cq-Overlay--placeholder",
configCache = {},
//look for configured placeholders only on these components
LOOK_FOR_PLACEHOLDER_COMPONENTS = [
"/libs/foundation/components/text",
"/libs/foundation/components/textimage"
];

$document.on('cq-layer-activated', addPlaceholder);

$document.on('cq-inspectable-added', componentAdded);

function componentAdded(event){
var LM = gAuthor.layerManager;

if (LM.getCurrentLayer() != "Edit") {
return;
}

var editable = event.inspectable;

//placeholder overlay gets added after triggering cq-inspectable-added event
//add a setTimeout workaround
setTimeout(function(){
configurePlaceholder(editable);
}, 500)
}

function addPlaceholder(event){
if(event.layer !== "Edit"){
return;
}

_.each(gAuthor.edit.findEditables(), configurePlaceholder);
}

function prefixLib(type){
type = type.trim();

if(type.indexOf("/") !== 0){
type = "/libs/" + type;
}

return type;
}

function configurePlaceholder(editable){
if(!isAllowedForPlaceholderConfig(editable)){
return;
}

var parent = editable.getParent(),
$overlay = $(parent.overlay.dom),
$placeholder = $overlay.find("[data-path='" + editable.path + "']");

if(!$placeholder.hasClass(PLACE_HOLDER)){
return;
}

var type = prefixLib(editable.type);

if(_.isEmpty(configCache[type])){
$.ajax( type + ".json" ).done(configure);
}else{
configure(configCache[type]);
}

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

configCache[type] = data;

var color;

if(!_.isEmpty(data[PLACEHOLDER_TEXT])){
$placeholder.attr("data-text", data[PLACEHOLDER_TEXT]);
}

if(!_.isEmpty(data[PLACEHOLDER_BG_COLOR])){
$placeholder.css("background-color", data[PLACEHOLDER_BG_COLOR]);
}
}
}

function isAllowedForPlaceholderConfig(editable){
return editable && editable.getParent()
&& editable.getParent().overlay
&& (LOOK_FOR_PLACEHOLDER_COMPONENTS.indexOf(prefixLib(editable.type)) !== -1)
}
})(jQuery, jQuery(document), Granite.author);

AEM 61 - ClassicUI Add Title Column to Damdadmin Search grid

$
0
0

Goal


Add title column (available in Digital Assets tab ootb) to the search grid of Classic UI Damadmin - http://localhost:4502/damadmin

For adding a column to siteadmin search panel check this post

For adding a column to Digital Assets tab grid check this post

Demo | Package Install



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/classicui-damadmin-search-title-column

2) Create clientlib (type cq:ClientLibraryFolder/apps/classicui-damadmin-search-title-column/clientlib and set property categories of String type to cq.widgets

3) Create file ( type nt:file ) /apps/classicui-damadmin-search-title-column/clientlib/js.txt, add the following

                         title-column.js

4) Create file ( type nt:file ) /apps/classicui-damadmin-search-title-column/clientlib/title-column.js, add the following code

(function(){
if(window.location.pathname !== "/damadmin"){
return;
}

// yes cq-siteadminsearchpanel-grid is dam search panel grid in /damadmin
var DA_GRID = "cq-siteadminsearchpanel-grid",
TITLE = "title";

var DA_INTERVAL = setInterval(function(){
var grid = CQ.Ext.getCmp(DA_GRID);

if(grid && (grid.rendered == true)){
clearInterval(DA_INTERVAL);
addTitleColumn(grid);
}
}, 250);

function addTitleColumn(grid) {
var cm = grid.getColumnModel();

var tColumn = new CQ.Ext.grid.Column({
"header": "Title",
"id": TITLE,
width: 100,
"renderer": function (v, params, record) {
if(!record || !record.json || !record.json["jcr:content"]){
return "";
}

var metadata = record.json["jcr:content"]["metadata"];

if(metadata){
return metadata["dc:title"];
}
}
});

cm.columns.splice(2, 0, tColumn);

cm.lookup[TITLE] = tColumn;
}
})();

AEM 61 SP1 - TouchUI Extend User Picker to filter users in New Project Wizard

$
0
0

Goal


Extend User Picker (autocomplete) - /libs/cq/gui/components/projects/admin/userpicker to apply filter function on results (users and groups) in create new project wizard http://localhost:4502/libs/cq/core/content/projects/wizard/newproject.html

Demo | Package Install


Product

                  All matching results (users and groups) shown in user picker



Extension

                  Filter applied on results to show Geometrixx users only



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/touchui-projects-userpicker-filter-users

2) Create clientlib (type cq:ClientLibraryFolder/apps/touchui-projects-userpicker-filter-users/clientlib and set property categories of String[] type to cq.projects.admin.projecteam

3) Create file ( type nt:file ) /apps/touchui-projects-userpicker-filter-users/clientlib/js.txt, add the following

                         filter-users.js

4) Create file ( type nt:file ) /apps/touchui-projects-userpicker-filter-users/clientlib/filter-users.js, add the following code

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

//set in /libs/cq/core/content/projects/properties/jcr:content/body/content/content/items/properties/items/right/items/memberpicker/items/userpicker
var PROJECTS_USER_PICKER = "#collection-settings-userpicker";

$document.on('cui-contentloaded.data-api', function () {
var P_INTERVAL = setInterval(function(){
var cuiPicker = $(PROJECTS_USER_PICKER).data("autocomplete");

if(cuiPicker){
clearInterval(P_INTERVAL);
addFilter(cuiPicker);
}
}, 250);
});

function addFilter(cuiPicker){
var type = cuiPicker._selectListWidget.get('type');

if(type !== "dynamic"){
return;
}

var options = cuiPicker._selectListWidget.options;

options.loadData = extendLoadDataFn(cuiPicker._selectListWidget);
}

function extendLoadDataFn(selWidget){
var loadDataFn = selWidget.options.loadData;

return function (start, end) {
var promise = loadDataFn.call(this, start, end);

promise.done(filter);

return promise;
};

//filter out non geometrixx users
function filter(){
selWidget.filter(function (value) {
return value && (value.indexOf("geometrixx") > 0);
});
}
}
})(jQuery, jQuery(document));




AEM 61 SP1 - TouchUI set User Default Search Keyword in Asset Finder

$
0
0

Goal


Set the user's search keyword and results in Asset Finder when loading TouchUI authoring editorhttp://localhost:4502/editor.html

Demo | Package Install


Keyword in User Profile

          Set the property searchKeyword in user's profile manually or by extending user editor - check this post



TouchUI Editor with Keyword and Results



Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/touchui-asset-finder-set-search-text

2) Create node /apps/touchui-asset-finder-set-search-text/clientlib of type cq:ClientLibraryFolder and add a String property categories with value cq.authoring.editor.hook.assetfinder

3) Create file (nt:file) /apps/touchui-asset-finder-set-search-text/clientlib/js.txt and add

                       set-search-text.js

4) Create file (nt:file) /apps/touchui-asset-finder-set-search-text/clientlib/set-search-text.js and add the following code

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

//id "assetfinder-filter" and "assetsearch" are defined in
///libs/wcm/core/content/editor/jcr:content/sidepanels/edit/items/assetsTab/items/filterPanel/items/views/items/search/items/searchpanel
var ASSET_FINDER_FILTER = "#assetfinder-filter",
KEYWORD_SELECTOR = "#assetsearch",
ASSET_FINDER_CONTAINER = ".assetfinder-content-container",
PROFILE_SEARCH_KEYWORD = "searchKeyword";

$document.on('cq-layer-activated', getDefaultKeyword);

function getDefaultKeyword(ev){
if ( ev.layer !== 'Edit' ) {
return;
}

//Granite.author.ContentFrame.contentWindow.CQ.shared.User.getUserPropsUrl()
$.ajax("/libs/cq/security/userinfo.json").done(function(data){
$.ajax(data.home + ".1.json").done(searchWithKeyword);
});
}

function searchWithKeyword(data){
if(!data || !data.profile || !data.profile[PROFILE_SEARCH_KEYWORD]){
return;
}

var $assetFinderFilter = $(ASSET_FINDER_FILTER),
$assetFinderContainer = $(ASSET_FINDER_CONTAINER),
$assetFinderKeyword = $assetFinderFilter.find(KEYWORD_SELECTOR);

$assetFinderKeyword.val(data.profile[PROFILE_SEARCH_KEYWORD]);

$assetFinderContainer.trigger({
type: "loadAssets",
append: false
})
}
})(jQuery, jQuery(document));



AEM 62 - Add Start Workflow Select to Assets Console Action Bar

$
0
0

Goal


Add Coral Select - granite/ui/components/coral/foundation/form/select to Assets Console Action bar to start workflow on selected assets

For adding a button to 61 Assets action barcheck this post

Demo | Package Install


Product



Extension



Solution


1) Login to CRX DE Lite http://localhost:4502/crx/de/index.jsp and create nt:folder /apps/eaem-assets-action-bar-start-workflow

2) Create sling:Folder /apps/eaem-assets-action-bar-start-workflow/button and nt:unstructured /apps/eaem-assets-action-bar-start-workflow/button/model for rendering start workflow coral select, with the following structure

<?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/select"
emptyText="Select a Workflow Model"
workflowtags="[dam]">
<datasource
jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/coral/common/admin/timeline/events/workflow/datasources/models"/>
</jcr:root>

3) Create a clientlib (type cq:ClientLibraryFolder) /apps/eaem-assets-action-bar-start-workflow/clientlib with categories - dam.gui.admin.util and dependencies - [underscore]

4) Create file /apps/eaem-assets-action-bar-start-workflow/clientlib/js.txt with the following content

                                                start-workflow.js

5) Create file /apps/eaem-assets-action-bar-start-workflow/clientlib/start-workflow.js, add the following code

(function ($, $document) {
// cq-damadmin-admin-actions-share-activator
// defined here /libs/dam/gui/content/commons/sidepanels/search/items/searchpanel/result/header/items/selection/items/editasset
var SHARE_ACTIVATOR = "cq-damadmin-admin-actions-share-activator",
BUTTON_HTML_URL = "/apps/eaem-assets-action-bar-start-workflow/button/model.html",
added = false;

$document.on("foundation-mode-change", function(e, mode){
if(added || (mode !== "selection") ){
return;
}

added = true;

$.ajax(BUTTON_HTML_URL).done(addButton);
});

function addButton(html){
var $eActivator = $("." + SHARE_ACTIVATOR);

if ($eActivator.length == 0) {
return;
}

var $startWorkflow = $(html).css("margin-left", "20px").insertBefore( $eActivator );

CUI.Select.init($startWorkflow, $document);

var cuiSelect = $startWorkflow.data("select");

cuiSelect.on('coral-select:change', handleSelect);
}

function handleSelect(){
var selection = this._getSelection();

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

var wSelect = this, model = selection.value,
wTitle = $(selection).html(),
paths = [], $items = $(".foundation-collection").find(".foundation-selections-item");

$items.each(function(index, item) {
paths.push($(item).data("foundation-collection-item-id"));
});

function startWorkflow(){
var data = [{name: "_charset_", value: "utf-8"},
{name: ":status", value: "browser"},
{name: "payloadType", value: "JCR_PATH"},
{name: "model", value: model}];

_.each(paths, function(path){
data.push( { name: "payload", value: path} )
});

$.ajax( { url: "/etc/workflow/instances" , type: "post", data: data}).done(function(){
showMessage("Success", "Workflow initiated");
wSelect.clear();
})
}

showConfirmation("Workflow", "Run workflow '" + wTitle + "' on selected items?", startWorkflow);
}

function showMessage(title, message){
$(window).adaptTo('foundation-ui').prompt(title, message, "notice", [{
primary: true,
text: Granite.I18n.get('OK')
}]);
}

function showConfirmation(title, message, handler){
var cancel = {
text: Granite.I18n.get('Cancel')
};

var ok = {
text: Granite.I18n.get('OK'),
primary: true,
handler: handler
};

$(window).adaptTo('foundation-ui').prompt(title, message, 'warning', [cancel, ok ]);
}
})(jQuery, jQuery(document));

AEM 62 - Touch UI Path Browser Filter for Autocomplete and Picker Results

$
0
0

Goal


Add a filter function to the Touch UI Path browser widget to filter the results shown in autocomplete dropdown and picker columns. For autocomplete, the results returned as valid by the isValid() function are shown; for picker the results returned by isValid() are enabled (other column values remain disabled)

In the demo, Link to (path browser widget) of Image Component is automatically set to the current page path and the goal is to make sure user can select only the parent, current page path or sub paths

Demo | Package Install


Filtered Autocomplete Results



Disabled Picker Columns



Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/eaem-touchui-pathbrowser-filter-results

2) Create node /apps/eaem-touchui-pathbrowser-filter-results/clientlib of type cq:ClientLibraryFolder and add a String property categories with value cq.authoring.dialog and dependencies to underscore

3) Create file (nt:file) /apps/eaem-touchui-pathbrowser-filter-results/clientlib/js.txt and add

                       pathbrowser-filter.js

4) Create file (nt:file) /apps/eaem-touchui-pathbrowser-filter-results/clientlib/pathbrowser-filter.js and add the following code

(function ($, $document, gAuthor) {
var LINK_URL = "./linkURL",
COMPONENT = "foundation/components/image";

if(!gAuthor){
return;
}

$document.on('dialog-ready', handlePathBrowser);

function handlePathBrowser(){
var $linkUrl = $("[name='" + LINK_URL + "']"),
editable = gAuthor.DialogFrame.currentDialog.editable;

//if not an image component dialog, return
if((editable.type !== COMPONENT) || _.isEmpty($linkUrl)){
return;
}

var cuiPathBrowser = $linkUrl.closest(".coral-PathBrowser").data("pathBrowser");

if(!cuiPathBrowser){
return;
}

//set the default value to current page path
setDefaultValue(cuiPathBrowser);

//handle inline autocomplete results
extendOptionLoader(cuiPathBrowser);

//handle picker columns
extendPicker(cuiPathBrowser);
}

//extend picker to disable columns
function extendPicker(cuiPathBrowser){
var cuiPicker = cuiPathBrowser.$picker.data("picker");

cuiPathBrowser.$button.on("click", function() {
setTimeout(function(){
if(!cuiPicker.columnView){
console.log("EAEM - could not initialize column view");
return;
}

extendColumnView(cuiPicker.columnView);
}, 200);
});
}

function extendColumnView(columnView){
function handler(){
var $items = columnView.$element.find(".coral-ColumnView-item"), $item;

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

if(isValid($item.data("value"))){
return;
}

//remove href link to make it disabled
$item.attr("data-href", "").css("background-color", "#AAAAAA");
});
}

handler();

columnView.$element.on('coral-columnview-load', handler);

columnView.$element.on('coral-columnview-item-select', handler);
}

function setDefaultValue(cuiPathBrowser){
//set the default path to current page path
cuiPathBrowser._setInputValue(gAuthor.getPageInfoLocation(), true);
}

function extendOptionLoader(cuiPathBrowser){
var optionLoader = cuiPathBrowser.optionLoader;

cuiPathBrowser.optionLoader = function(path, callback){
optionLoader.call(this, path, function(data){
callback(dataFilter(path,data));
});
};
}

//filter results function
function dataFilter(path, data){
var filteredData = [];

_.each(data, function(value){
if(!isValid(path + "/" + value)){
return;
}

filteredData.push(value);
});

return filteredData;
}

//if the search result is not sub path of current page path, consider it invalid
function isValid(relPath){
var pagePath = gAuthor.getPageInfoLocation();

if(relPath.length > pagePath.length){
if(relPath.indexOf(pagePath) !== 0){
return false;
}
}else{
if(pagePath.indexOf(relPath) !== 0){
return false;
}
}

return true;
}
}(jQuery, jQuery(document), Granite.author));



AEM 62 - Touch UI Search based Pathbrowser for Autocomplete

$
0
0

Goal


Touch UI pathbrowser widget of AEM - /libs/granite/ui/components/foundation/form/pathbrowser is based on paths, showing the results of path selector - .pages.json; this post is on extending ootb pathbrowser and creating a new search based pathbrowser

For demo purposes, the linkURL property - /libs/foundation/components/image/cq:dialog/content/items/column/items/linkURL, of foundation image component dialog was modified to use search based pathbrowser

Demo | Package Install


Widget and Query Parameters

                          sling:resourceType - String - /apps/eaem-touchui-search-pathbrowser/search-pathbrowser
                          queryParameters - String[] - jcr:content/cq:template=/apps/geometrixx/templates/contentpage
                                                                    jcr:content/cq:lastModifiedBy=admin

 


Inline Dialog



Full Screen Dialog



Solution


1) Login to CRXDE Lite and create folder /apps/eaem-touchui-search-pathbrowser

2) Create sling:Folder /apps/eaem-touchui-search-pathbrowser/search-pathbrowser for adding the widger renderer jsp

3) Add the widget renderer jsp /apps/eaem-touchui-search-pathbrowser/search-pathbrowser/search-pathbrowser.jsp with the following code extending /libs/granite/ui/components/foundation/form/pathbrowser

<%@ page import="com.adobe.granite.ui.components.Config" %>
<%@include file="/libs/granite/ui/global.jsp" %>

<%
Config mCfg = cmp.getConfig();

String SEARCH_PATHBROWSER_WRAPPER_ID = "eaem-search-pathbrowser-wrapper-" + mCfg.get("name", String.class).substring(2);
String EAEM_PREFIX = "eaem.granite.ui.search.pathBrowser";
%>

<div id="<%=SEARCH_PATHBROWSER_WRAPPER_ID%>">
<%--include ootb pathbrowser--%>
<sling:include resourceType="/libs/granite/ui/components/foundation/form/pathbrowser"/>
</div>

<script>
(function($){
var wrapper = $("#<%=SEARCH_PATHBROWSER_WRAPPER_ID%>"),
pathBrowser = wrapper.find("[data-init='pathbrowser']");

if(_.isEmpty(pathBrowser)){
console.log("EAEM - search path browser wrapper not found");
return;
}

//set the search based pathbrowser loaders and renderers defined in search-based-pathbrowser.js
pathBrowser.attr("data-autocomplete-callback", "<%=EAEM_PREFIX%>" + ".autocompletecallback");
pathBrowser.attr("data-option-loader", "<%=EAEM_PREFIX%>" + ".optionLoader");
pathBrowser.attr("data-option-renderer", "<%=EAEM_PREFIX%>" + ".optionRenderer");
}(jQuery));
</script>

4) Create a clientlib /apps/eaem-touchui-search-pathbrowser/clientlib with categories cq.authoring.dialog and dependencies underscore

5) Add file /apps/eaem-touchui-search-pathbrowser/clientlib/js.txt with the following content

                             search-based-pathbrowser.js

6) Add file /apps/eaem-touchui-search-pathbrowser/clientlib/search-based-pathbrowser.js for adding the necessary autocomplete callback, option loader and option renderer used by the widget, with following code

(function(){
var EAEM_PREFIX = "eaem.granite.ui.search.pathBrowser",
ROOT_PATH = "rootPath",
QUERY_PARAMS = "queryparameters", // somehow queryParameters is read as queryparameters
QUERY = "/bin/querybuilder.json?";

//executed when user initiates search in pathbrowser by typing in a keyword
function searchBasedAutocompleteCallback(){
return{
name: EAEM_PREFIX + '.autocompletecallback',
handler: autoCompleteHandler
};

function autoCompleteHandler(searchTerm){
var self = this, deferred = $.Deferred();

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

var searchParams = getSearchParameters(self, searchTerm);

self.optionLoader(searchParams, callback);

function callback(results){
if(_.isEmpty(results)){
deferred.resolve([]);
return;
}

self.options.options = results;
deferred.resolve(_.range(results.length));
}

return deferred.promise();
}

function getSearchParameters(widget,searchTerm){
var searchParams = {
fulltext: searchTerm
};

var path = widget.$element.data(ROOT_PATH), tokens,
queryParams = widget.$element.data(QUERY_PARAMS);

if(!_.isEmpty(path)){
searchParams.path = path;
}

if(!_.isEmpty(queryParams)){
queryParams = queryParams.split("");

_.each(queryParams, function(param, index){
tokens = param.split("=");
searchParams[ (index + 1) + "_property" ] = tokens[0];
searchParams[ (index + 1) + "_property.value" ] = tokens[1];
})
}

return searchParams;
}
}

CUI.PathBrowser.register('autocompleteCallback', searchBasedAutocompleteCallback());

//the option loader for requesting query results
function searchBasedOptionLoader() {
return {
name: EAEM_PREFIX + ".optionLoader",
handler: optionLoaderHandler
};

function optionLoaderHandler(searchParams, callback) {
var query = QUERY;

_.each(searchParams, function(value, key){
query = query + key + "=" + value + "&";
});

query = query.substring(0, query.length - 1);

console.log("EAEM - Search query - " + query);

$.get(query).done(handler);

function handler(data){
var results = [];

if(!_.isEmpty(data.hits)){
results = _.pluck(data.hits, "path");
}

if (callback){
callback(results);
}
}

return false;
}
}

CUI.PathBrowser.register('optionLoader', searchBasedOptionLoader());

//option renderer for creating the option html
function searchBasedOptionRenderer() {
return {
name: EAEM_PREFIX + ".optionRenderer",
handler: optionRendererHandler
};

function optionRendererHandler(iterator, index) {
var value = this.options.options[index];

return $('<li class="coral-SelectList-item coral-SelectList-item--option" data-value="'
+ value + '">' + value + '</li>');
}
}

CUI.PathBrowser.register('optionRenderer', searchBasedOptionRenderer());
}());



AEM 62 - TouchUI Dialog RTE (Mini RTE) To Upper Case Plugin

$
0
0

Goal


RTE (Rich Text Editor) plugin to convert selected text to upper case in the dialog RTE widget - /libs/cq/gui/components/authoring/dialog/richtext, of Touch UI

Dialog of foundation text component was modified for demo purposes only - /libs/foundation/components/text/cq:dialog/content/items/tabs/items/text/items/column/items/text

For a similar extension on AEM 6 - check this post

Demo | Package Install


Configure the RTE Plugin



Add Plugin to RTE Widget Toolbar



Uppercase Plugin in RTE of Dialog




Uppercase Plugin in RTE of Fullscreen Dialog



Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/touchui-dialog-mini-rte-to-upper-case

2) Create node /apps/touchui-dialog-mini-rte-to-upper-case/clientlib of type cq:ClientLibraryFolder and add a String property categories with value rte.coralui2

3) Create file (nt:file) /appstouchui-dialog-mini-rte-to-upper-case/clientlib/js.txt and add

                       to-upper.js

4) Create file (nt:file) /apps/touchui-dialog-mini-rte-to-upper-case/clientlib/to-upper.js and add the following code.

(function(){
var ExperienceAEM = {
GROUP: "experience-aem",
TUC_FEATURE: "touchuitouppercase"
};

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

extend: CUI.rte.plugins.Plugin,

pickerUI: null,

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

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

if (!this.isFeatureEnabled(ExperienceAEM.TUC_FEATURE)) {
return;
}

this.pickerUI = tbGenerator.createElement(ExperienceAEM.TUC_FEATURE, this, true, "To Upper Case");
tbGenerator.addElement(ExperienceAEM.GROUP, plg.Plugin.SORT_FORMAT, this.pickerUI, 120);

var groupFeature = ExperienceAEM.GROUP + "#" + ExperienceAEM.TUC_FEATURE;
tbGenerator.registerIcon(groupFeature, "coral-Icon coral-Icon--thumbUp");
},

execute: function(id) {
this.editorKernel.relayCmd(id);
},

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

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

notifyPluginConfig: function(pluginConfig) {
pluginConfig = pluginConfig || { };

var defaults = {
"tooltips": {
"touchuitouppercase": {
"title": "To Upper Case",
"text": "To Upper Case"
}
}
};

CUI.rte.Utils.applyDefaults(pluginConfig, defaults);

this.config = pluginConfig;
}
});

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

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

extend: CUI.rte.commands.Command,

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

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

_getTagObject: function() {
return {
"tag": "span",
"attributes": {
"style" : "text-transform:uppercase"
}
};
},

execute: function(execDef) {
var selection = execDef.selection;

if (!selection) {
return;
}

var nodeList = execDef.nodeList;

if (!nodeList) {
return;
}

var common = CUI.rte.Common;
var context = execDef.editContext;

var tagObj = this._getTagObject();

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

if (tags == null) {
nodeList.surround(execDef.editContext, tagObj.tag, tagObj.attributes);
} else {
nodeList.removeNodesByTag(execDef.editContext, tagObj.tag, tagObj.attributes, true);
}
},

queryState: function(selectionDef, cmd) {
var common = CUI.rte.Common;
var context = selectionDef.editContext;

var selection = selectionDef.selection;
var tagObj = this._getTagObject();

return (common.getTagInPath(context, selection.startNode, tagObj.tag, tagObj.attributes) != null);
}
});

CUI.rte.commands.CommandRegistry.register(ExperienceAEM.TUC_FEATURE, ExperienceAEM.UpperCaseCmd);
})();



AEM 62 - Touch UI Dialog RTE (Rich Text Editor) Color Picker Plugin

$
0
0

Goal


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

For a similar extension (Inplace editing) on 61 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/tabs/items/text/items/column/items/text/rtePlugins

Thank you Brett Birschbach for the firefox bug fix

Demo on Chrome | Demo on Firefox | Package Install


Plugin Configuration

 


Add Plugin to RTE Toolbar



Picker with Free Style Palette - Inline Dialog



Picker with Free Style Palette - Full Screen Dialog



Picker with Palette Shades - Classic



Palette Edit Mode



RTE text with Color




Solution


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

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




3) XML representation of /apps/touchui-dialog-mini-rte-color-picker/color-picker-popover/cq:dialog

<?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="Color Picker"
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">
<picker
jcr:primaryType="nt:unstructured"
sling:resourceType="/apps/touchui-dialog-mini-rte-color-picker/color-picker"
editType="{Boolean}true"
freestylePaletteType="{Boolean}true"
name="./color">
<colors jcr:primaryType="nt:unstructured">
<red
jcr:primaryType="nt:unstructured"
name="Red"
value="#FF0000"/>
<green
jcr:primaryType="nt:unstructured"
name="Green"
value="#00FF00"/>
<blue
jcr:primaryType="nt:unstructured"
name="Blue"
value="#0000FF"/>
<black
jcr:primaryType="nt:unstructured"
name="Black"
value="#000000"/>
<brown
jcr:primaryType="nt:unstructured"
name="Brown"
value="#996633"/>
<orange
jcr:primaryType="nt:unstructured"
name="Orange"
value="#FF7F00"/>
<purple
jcr:primaryType="nt:unstructured"
name="Purple"
value="#7F007F"/>
<yellow
jcr:primaryType="nt:unstructured"
name="Yellow"
value="#FFFF00"/>
</colors>
</picker>
<add
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/button"
class="coral-Button--primary"
id="EAEM_CP_ADD_COLOR"
text="Add Color"/>
<remove
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/button"
class="coral-Button--warning"
id="EAEM_CP_REMOVE_COLOR"
text="Remove Color"/>
</items>
</column>
</items>
</content>
</jcr:root>

4) Colors shown in picker dialog are added in /apps/touchui-dialog-mini-rte-color-picker/color-picker-popover/cq:dialog/content/items/column/items/picker/colors

5) To workaround the pickerModes bug register a new color picker widget /apps/touchui-dialog-mini-rte-color-picker/color-picker/render.jsp extending ootb color picker widget /libs/granite/ui/components/foundation/form/colorpicker, add the following code

<%@ page import="com.adobe.granite.ui.components.Config" %>
<%@include file="/libs/granite/ui/global.jsp" %>

<%
Config mCfg = cmp.getConfig();

String COLOR_PICKER_WRAPPER_ID = "eaem-color-picker-wrapper-" + mCfg.get("name", String.class).substring(2);
%>

<div id="<%=COLOR_PICKER_WRAPPER_ID%>">
<%--include ootb color picker--%>
<sling:include resourceType="/libs/granite/ui/components/foundation/form/colorpicker"/>
</div>

<script>
(function($){
var wrapper = $("#<%=COLOR_PICKER_WRAPPER_ID%>"),
colorPicker = wrapper.find("[data-init='colorpicker']");

if(_.isEmpty(colorPicker)){
console.log("EAEM - color picker wrapper not found");
return;
}

//extend otb Colorpicker to workaround the pickerModes bug
//in granite/ui/components/foundation/form/colorpicker/render.jsp
//colorpickerJson.put("modes", pickerModes); should have been
//colorpickerJson.put("pickerModes", pickerModes);
var config = colorPicker.data("config");
config.pickerModes = config.modes;

delete config.modes;

colorPicker.attr("data-config", config);
}(jQuery));
</script>

6) Create clientlib (cq:ClientLibraryFolder) /apps/touchui-dialog-mini-rte-color-picker/clientlib set property categories to rte.coralui2 and dependencies to [underscore]

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

                   color-picker.js

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

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

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, true, "Color Picker");
tbGenerator.addElement(GROUP, plg.Plugin.SORT_FORMAT, this.pickerUI, 120);

var groupFeature = GROUP + "#" + COLOR_PICKER_FEATURE;
tbGenerator.registerIcon(groupFeature, "coral-Icon coral-Icon--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"),
content = this.getPickerIFrameContent($(tag).css("color"));

this.addModalDiv();

var modal = new CUI.Modal({
element : '#' + COLOR_PICKER_MODAL_DIV,
heading : "Pick a Color",
content: content
});

registerReceiveDataListener(receiveMessage);

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

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

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

getModalHtml: function(){
return "<div class=\"coral-Modal-header\">"
+ "<h2 class=\"coral-Modal-title coral-Heading coral-Heading--2\"></h2>"
+ "<i class=\"coral-Modal-typeIcon coral-Icon coral-Icon--sizeS\"></i>"
+ "<button type=\"button\""
+ "class=\"coral-MinimalButton coral-Modal-closeButton\""
+ "data-dismiss=\"modal\">"
+ "<i class=\"coral-Icon coral-Icon--sizeXS coral-Icon--close "
+ "coral-MinimalButton-icon\"></i>" + "</button>"
+ "</div>"
+ "<div class=\"coral-Modal-body legacy-margins\"></div>";
},

addModalDiv: function(){
var $modalDiv = $("#" + COLOR_PICKER_MODAL_DIV);

if (!_.isEmpty($modalDiv) ) {
return;
}

var tag = {
class: "coral-Modal",
id: COLOR_PICKER_MODAL_DIV
};

var insertModal = $("<div>", tag).hide().html(this.getModalHtml());

$(document.body).append(insertModal);
},

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

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

return "<iframe width='520px' height='405px' frameBorder='0' src='" + url + "'></iframe>";
}
});

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);
}(jQuery, window.CUI));

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

$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 queryParams = queryParameters();

var $dialog = $(".cq-dialog").css("background", "white"),
$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]));
}

$dialog.find(".cq-dialog-header").hide();
$dialog.find(".cq-dialog-content").css("top", ".1rem");
$colorPicker.closest(".coral-Form-fieldwrapper").css("margin-bottom", "285px");
$(ADD_COLOR_BUT).css("margin-left", "250px");

$addColor.click(sendDataMessage);
$removeColor.click(sendRemoveMessage);
}

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 62 - Add Asset Count Column to List View, Show Asset Count in Card View

$
0
0

Goal


Add Asset Count column to List view and Card view to show the shallow count of assets in a folder

For Classic UI check this post

For AEM 61 Touch UI check this post

Demo | Package Install


Configuration



Column Visibility




List View




Card View



Solution


1) To add the necessary columns (here eaemAssetsCount) overlay /libs/dam/gui/content/commons/availablecolumns and create /apps/dam/gui/content/commons/availablecolumns . Use a simple servlet like this to automatically create the overlay structure in /apps

2) 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"
columnGroup="Experience AEM"
default="{Boolean}false"
sortable="{Boolean}true"
sortType="number"/>
</jcr:root>

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

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

                        asset-count.js

5) Create nt:file /apps/eaem-touchui-assets-folder-count/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",
LIST_CELL_HTML = '<td is="coral-td" class="coral-Table-cell coral-Table-cell--left" alignment="column">' +
'<coral-td-label class="coral-Table-cellLabel">ASSET_COUNT</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 + ".2.json").done(function(data){
$(".foundation-collection-item").each(function(index, item){
itemHandler(data, layoutId, $(item) );
});
});

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

//reject non assets nodes
var assets = _.reject(data[itemName], function(value){
return value["jcr:primaryType"] !== "dam:Asset";
});

count = Object.keys(assets).length;

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

if(!isFolder){
return;
}

$item.append(LIST_CELL_HTML.replace("ASSET_COUNT", count));
}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: " + count);
}
}

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

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




AEM 62 - Touch UI extend Path Browser picker window to show file name (nodename) NOT dc:title or jcr:title

$
0
0

Goal


Touch UI Path Browser widget (/libs/granite/ui/components/foundation/form/pathbrowser) shows dc:title metadata for files and jcr:title for folders, in the picker; this post is on extending the widget to always show node name in picker

Dialog of foundation image component - /libs/foundation/components/image/cq:dialog/content/items/column/items/linkURL was modified for demo purposes only

Demo | Package Install


Product - Path Browser Auto Complete



Product - Path Browser Picker

  


Extension - Path Browser Picker




Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/eaem-touchui-pathbrowser-picker-show-filename

2) Create node /apps/eaem-touchui-pathbrowser-picker-show-filename/clientlib of type cq:ClientLibraryFolder and add a String property categories with value cq.authoring.dialog and dependencies to underscore

3) Create file (nt:file) /apps/eaem-touchui-pathbrowser-picker-show-filename/clientlib/js.txt and add

                       show-file-name.js

4) Create file (nt:file) /apps/eaem-touchui-pathbrowser-picker-show-filename/clientlib/show-file-name.js and add the following code

(function ($, $document, gAuthor) {
var LINK_URL = "./linkURL",
COMPONENT = "foundation/components/image";

if(!gAuthor){
return;
}

$document.on('dialog-ready', handlePathBrowser);

function handlePathBrowser(){
var $linkUrl = $("[name='" + LINK_URL + "']"),
editable = gAuthor.DialogFrame.currentDialog.editable;

//if not an image component dialog, return
if((editable.type !== COMPONENT) || _.isEmpty($linkUrl)){
return;
}

var cuiPathBrowser = $linkUrl.closest(".coral-PathBrowser").data("pathBrowser");

if(!cuiPathBrowser){
return;
}

//handle picker columns
extendPicker(cuiPathBrowser);
}

//extend picker to disable columns
function extendPicker(cuiPathBrowser){
var cuiPicker = cuiPathBrowser.$picker.data("picker");

cuiPathBrowser.$button.on("click", function() {
setTimeout(function(){
if(!cuiPicker.columnView){
console.log("EAEM - could not initialize column view");
return;
}

extendColumnView(cuiPicker.columnView);
}, 200);
});
}

function extendColumnView(columnView){
function handler(event){
var $element = event && event.currentTarget ? $(event.currentTarget) : columnView.$element,
$items = $element.find(".coral-ColumnView-item"),
$item, dataValue;

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

dataValue = $item.data("value");

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

$item.find(".coral-ColumnView-label").html(getStringAfterLastSlash(dataValue));
});
}

handler();

columnView.$element.on('coral-columnview-load', handler);

columnView.$element.on('coral-columnview-item-select', function(){
// event coral-columnview-item-select is triggered before selected column load
// hope the data gets loaded in 1000 msecs
setTimeout(handler, 1000);
});
}

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

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

AEM 62 - Support Required (Validator) on FileUpload (Image) in Touch UI Dialog

$
0
0

Goal


Touch UI extension to support required on FileUpload widget - /libs/granite/ui/components/foundation/form/fileupload using Granite validator. For validation api check documentation

For a similar extension on 61 supporting required on RTE check this post

Dialog of foundation image component /libs/foundation/components/image/cq:dialog/content/items/column/items/file was modified for demonstration purposes only

Demo | Package Install


File Upload widget - required = true



Validation Error

 


Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/eaem-touchui-image-required

2) Create node /apps/eaem-touchui-image-required/clientlib of type cq:ClientLibraryFolder and add a String property categories with value cq.authoring.dialog and dependencies to underscore

3) Create file (nt:file) /apps/eaem-touchui-image-required/clientlib/js.txt and add

                       image-required.js

4) Create file (nt:file) /apps/eaem-touchui-image-required/clientlib/image-required.js and add the following code

(function ($, $document, gAuthor) {
var EAEM_IMAGE_REQ_FIELD_ID_PREFIX = "eaem-img-required-",
FILE_UPLOAD = ".coral-FileUpload",
FILE_UPLOAD_CLEAR = ".cq-FileUpload-clear",
DATA_ATTR_FILE_UPLOAD = "fileUpload",
FILE_UPLOAD_SEL = ".coral-FileUpload-input",
FILE_NAME = "./fileName",
COMPONENT = "foundation/components/image",
FIELD_ERROR_EL = $("<span class='coral-Form-fielderror coral-Icon coral-Icon--alert " +
"coral-Icon--sizeS' data-init='quicktip' data-quicktip-type='error'>" +
"</span>");
if(!gAuthor){
return;
}

$document.on('dialog-ready', checkFileRequired);

function checkFileRequired(){
var $fileUpload = $(FILE_UPLOAD),
$fileName = $("[name='" + FILE_NAME + "']"),
fileReqId = EAEM_IMAGE_REQ_FIELD_ID_PREFIX + getStringAfterLastSlash(FILE_NAME),
editable = gAuthor.DialogFrame.currentDialog.editable;

//if not an image component dialog, return
if((editable.type !== COMPONENT) || _.isEmpty($fileName)){
return;
}

//fileName field is hidden input, for the validator to work, add a invisible text field
//holding the file name
$fileUpload.append("<input type=text style='display:none' id='" + fileReqId + "'/>");

var cuiFileUpload = $fileUpload.data(DATA_ATTR_FILE_UPLOAD),
$fileReqInvisibleField = $("#" + fileReqId);

addValidatorIfRequiredSet(editable, cuiFileUpload, $fileReqInvisibleField);
}

function addValidatorIfRequiredSet(editable, cuiFileUpload, $fileReqInvisibleField){
var $fileUploadInput = cuiFileUpload.$element.find(FILE_UPLOAD_SEL);

if ($fileUploadInput.attr("aria-required") !== "true") {
return;
}

//user can either drop or upload an image; with required set to true
//validator with selector: ".coral-FileUpload-input"
//in /libs/granite/ui/components/foundation/clientlibs/foundation.js
//always checks if there is a file queued for upload, so workaround it by removing
//the required attribute on file upload input
$fileUploadInput.removeAttr( "aria-required" );

cuiFileUpload.$element.find(FILE_UPLOAD_CLEAR).on("click tap", function (e) {
performRequiredCheck($fileReqInvisibleField, '');
});

cuiFileUpload.$element.on("fileuploadsuccess", function (event) {
performRequiredCheck($fileReqInvisibleField, event.item.file.name);
});

cuiFileUpload.$element.on("assetselected", function (event) {
performRequiredCheck($fileReqInvisibleField, event.path);
});

addValidator($fileReqInvisibleField);

initRequiredField($fileReqInvisibleField, editable.path);
}

function initRequiredField($fileReqInvisibleField, path){
var fileName = getStringAfterLastSlash(FILE_NAME);

$.ajax(path + ".json").done(function(data){
if(_.isEmpty(data[fileName])){
return;
}

$fileReqInvisibleField.val(data[fileName]);
})
}

function performRequiredCheck($fileReqInvisibleField, value){
$fileReqInvisibleField.val(value);
$fileReqInvisibleField.checkValidity();
$fileReqInvisibleField.updateErrorUI();
}

function addValidator($fileReqInvisibleField){
$.validator.register({
selector: "#" + $fileReqInvisibleField.attr("id"),

validate: validate ,

show: show ,

clear: clear
});

function validate($fileReqInvisibleField) {
if (_.isEmpty($fileReqInvisibleField.val())) {
return "Drop or upload an image";
}

return null;
}

function show($fileReqInvisibleField, message) {
var $fileUploadField = $fileReqInvisibleField.closest(FILE_UPLOAD),
arrow = $fileUploadField.closest("form").hasClass("coral-Form--vertical") ? "right" : "top",
$error = $fileUploadField.nextAll(".coral-Form-fielderror");

if (!_.isEmpty($error)) {
return;
}

FIELD_ERROR_EL.clone()
.attr("data-quicktip-arrow", arrow)
.attr("data-quicktip-content", message)
.insertAfter($fileUploadField);
}

function clear($fileReqInvisibleField) {
var $fileUploadField = $fileReqInvisibleField.closest(FILE_UPLOAD);
$fileUploadField.nextAll(".coral-Form-fielderror").remove();
}
}

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

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

AEM 62 - Classic UI show Nodename and not dc:title or jcr:title in Path Field of RTE Link Plugin Dialog

$
0
0

Goal


Show the node name and NOT dc:title or jcr:title in Pathfield widget of Link plugin dialog

Demo | Package Install


Product



Extension



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-classicui-rte-link-pathfield-show-nodename-not-dctitle

2) Create clientlib (type cq:ClientLibraryFolder/apps/eaem-classicui-rte-link-pathfield-show-nodename-not-dctitle/clientlib and set property categories of String type to cq.widgets and dependencies to underscore

3) Create file ( type nt:file ) /apps/eaem-classicui-rte-link-pathfield-show-nodename-not-dctitle/clientlib/js.txt, add the following

                         show-nodename.js

4) Create file ( type nt:file ) /apps/eaem-classicui-rte-link-pathfield-show-nodename-not-dctitle/clientlib/show-nodename.js, add the following code

(function(){
var EAEM_LINK_DIALOG = CQ.Ext.extend(CQ.form.rte.plugins.LinkDialog, {
constructor: function(config) {
config = config || {};

EAEM_LINK_DIALOG.superclass.constructor.call(this, config);

var pathField = this.findByType("pathfield")[0];

pathField.on("dialogopen", function(){
this.browseDialog.treePanel.on('load', showNodeName);
});

function showNodeName(node){
_.each(node.childNodes, function(childNode){
//set the nodename replacing jcr:title (folders) or dc:title (assets)
childNode.setText(childNode.attributes.name);
});
}
}
});

CQ.Ext.reg("rtelinkdialog", EAEM_LINK_DIALOG);
}());


AEM 62 - Classic UI extend Pathfield browse dialog to show file name (nodename) NOT dc:title or jcr:title

$
0
0

Goal


Show the node name and NOT dc:title or jcr:title in Pathfield widget Browse Dialog

For Touch UI check this post

Demo | Package Install


Product




Extension





Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/classicui-pathfield-show-nodename

2) Create clientlib (type cq:ClientLibraryFolder/apps/classicui-pathfield-show-nodename/clientlib and set property categories of String type to cq.widgets and dependencies to underscore

3) Create file ( type nt:file ) /apps/classicui-pathfield-show-nodename/clientlib/js.txt, add the following

                         show-nodename.js

4) Create file ( type nt:file ) /apps/classicui-pathfield-show-nodename/clientlib/show-nodename.js, add the following code

(function(){
var EAEM_PATH_FIELD = CQ.Ext.extend(CQ.form.PathField, {
onTriggerClick: function() {
EAEM_PATH_FIELD.superclass.onTriggerClick.call(this);

this.browseDialog.treePanel.on('load', showNodeName);

function showNodeName(node){
_.each(node.childNodes, function(childNode){
//set the nodename replacing jcr:title (folders) or dc:title (assets)
childNode.setText(childNode.attributes.name);
});
}
}
});

CQ.Ext.reg("pathfield", EAEM_PATH_FIELD);
}());

AEM 62 - Touch UI Composite Image Multifield

$
0
0

Goal


Create  a Touch UI Composite Multifield configuration supporting Images, widgets of type granite/ui/components/foundation/form/fileupload

For Classic UI Image Multifield check this post

For AEM 61 Touch UI Image Multifieldcheck this post

Demo | Package Install


Component Rendering




Image Multifield Structure




Nodes in CRX




Dialog




Dialog XML

<?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="62 Touch UI Image Multi Field"
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/tabs"
type="nav"/>
<items jcr:primaryType="nt:unstructured">
<company
jcr:primaryType="nt:unstructured"
jcr:title="Company"
sling:resourceType="granite/ui/components/foundation/section">
<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="Products"
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">
<company
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldDescription="Enter Company Name"
fieldLabel="Company"
name="./company"/>
<product
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/multifield"
class="full-width"
eaem-nested=""
fieldDescription="Click '+' to add a Product"
fieldLabel="Product">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/fieldset"
name="./products">
<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">
<productName
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldDescription="Enter Product Name"
fieldLabel="Product"
name="./productName"/>
<language
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/select"
class="language"
fieldLabel="Language"
name="./language">
<datasource
jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/common/datasources/languages"
addNone="{Boolean}true"/>
</language>
<show
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/checkbox"
name="./show"
text="Show"
value="yes"/>
<productImage
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/fileupload"
autoStart="{Boolean}false"
class="cq-droptarget"
fieldLabel="Product"
fileNameParameter="./productImageName"
fileReferenceParameter="./productImageRef"
mimeTypes="[image]"
multiple="{Boolean}false"
name="./productImage"
title="Upload Image"
uploadUrl="${suffix.path}"
useHTML5="{Boolean}true"/>
</items>
</column>
</items>
</field>
</product>
</items>
</column>
</items>
</fieldset>
</items>
</column>
</items>
</company>
</items>
</content>
</jcr:root>


Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-touchui-image-multifield

2) Create clientlib (type cq:ClientLibraryFolder/apps/eaem-touchui-image-multifield/clientlib and set a property categories of String type to cq.authoring.dialogdependencies of type String[] with value underscore

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

                         image-multifield.js

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

(function () {
var DATA_EAEM_NESTED = "data-eaem-nested",
CFFW = ".coral-Form-fieldwrapper",
THUMBNAIL_IMG_CLASS = "cq-FileUpload-thumbnail-img",
SEP_SUFFIX = "-",
SEL_FILE_UPLOAD = ".coral-FileUpload",
SEL_FILE_REFERENCE = ".cq-FileUpload-filereference",
SEL_FILE_NAME = ".cq-FileUpload-filename",
SEL_FILE_MOVEFROM = ".cq-FileUpload-filemovefrom";

function getStringBeforeAtSign(str){
if(_.isEmpty(str)){
return str;
}

if(str.indexOf("@") != -1){
str = str.substring(0, str.indexOf("@"));
}

return str;
}

function getStringAfterAtSign(str){
if(_.isEmpty(str)){
return str;
}

return (str.indexOf("@") != -1) ? str.substring(str.indexOf("@")) : "";
}

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

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

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

return str.substr(0, str.lastIndexOf("/"));
}

function removeFirstDot(str){
if(str.indexOf(".") != 0){
return str;
}

return str.substr(1);
}

function modifyJcrContent(url){
return url.replace(new RegExp("^" + Granite.HTTP.getContextPath()), "")
.replace("_jcr_content", "jcr:content");
}

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 setWidgetValue($field, value) {
if (_.isEmpty($field)) {
return;
}

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

/**
* Removes multifield number suffix and returns just the fileRefName
* Input: paintingRef-1, Output: paintingRef
*
* @param fileRefName
* @returns {*}
*/
function getJustName(fileRefName){
if(!fileRefName || (fileRefName.indexOf(SEP_SUFFIX) == -1)){
return fileRefName;
}

var value = fileRefName.substring(0, fileRefName.lastIndexOf(SEP_SUFFIX));

if(fileRefName.lastIndexOf(SEP_SUFFIX) + SEP_SUFFIX.length + 1 == fileRefName.length){
return value;
}

return value + fileRefName.substring(fileRefName.lastIndexOf(SEP_SUFFIX) + SEP_SUFFIX.length + 1);
}

function getMultiFieldNames($multifields){
var mNames = {}, mName;

$multifields.each(function (i, multifield) {
mName = $(multifield).children("[name$='@Delete']").attr("name");
mName = mName.substring(0, mName.indexOf("@"));
mName = mName.substring(2);
mNames[mName] = $(multifield);
});

return mNames;
}

function buildMultiField(data, $multifield, mName){
if(_.isEmpty(mName) || _.isEmpty(data)){
return;
}

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

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

_.each(value, function(fValue, fKey){
if(fKey == "jcr:primaryType" || _.isObject(fValue)){
return;
}

var $field = $multifield.find("[name='./" + fKey + "']").last();

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

setWidgetValue($field, fValue);
});
});
}

function buildImageField($multifield, mName){
$multifield.find(".coral-FileUpload:last").each(function () {
var $element = $(this), widget = $element.data("fileUpload"),
resourceURL = $element.parents("form.cq-dialog").attr("action"),
counter = $multifield.find(SEL_FILE_UPLOAD).length;

if (!widget) {
return;
}

var fuf = new Granite.FileUploadField(widget, resourceURL);

addThumbnail(fuf, mName, counter);
});
}

function addThumbnail(imageField, mName, counter){
var $element = imageField.widget.$element,
$thumbnail = $element.find("." + THUMBNAIL_IMG_CLASS),
thumbnailDom;

$thumbnail.empty();

$.ajax({
url: imageField.resourceURL + ".2.json",
cache: false
}).done(handler);

function handler(data){
var fName = getJustName(getStringAfterLastSlash(imageField.fieldNames.fileName)),
fRef = getJustName(getStringAfterLastSlash(imageField.fieldNames.fileReference));

if(isFileNotFilled(data, counter, fRef)){
return;
}

var fileName = data[mName][counter][fName],
fileRef = data[mName][counter][fRef];

if (!fileRef) {
return;
}

if (imageField._hasImageMimeType()) {
imageField._appendThumbnail(fileRef, $thumbnail);
}

var $fileName = $element.find("[name=\"" + imageField.fieldNames.fileName + "\"]"),
$fileRef = $element.find("[name=\"" + imageField.fieldNames.fileReference + "\"]");

$fileRef.val(fileRef);
$fileName.val(fileName);
}

function isFileNotFilled(data, counter, fRef){
return _.isEmpty(data[mName])
|| _.isEmpty(data[mName][counter])
|| _.isEmpty(data[mName][counter][fRef])
}
}

//reads multifield data from server, creates the nested composite multifields and fills them
function addDataInFields() {
$(document).on("dialog-ready", function() {
var $multifields = $("[" + DATA_EAEM_NESTED + "]");

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

workaroundFileInputPositioning($multifields);

var mNames = getMultiFieldNames($multifields),
$form = $(".cq-dialog"),
actionUrl = $form.attr("action") + ".infinity.json";

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

function postProcess(data){
_.each(mNames, function($multifield, mName){
$multifield.on("click", ".js-coral-Multifield-add", function () {
buildImageField($multifield, mName);
});

buildMultiField(data[mName], $multifield, mName);
});
}
});
}

function workaroundFileInputPositioning($multifields){
//to workaround the .coral-FileUpload-input positioning issue
$multifields.find(".js-coral-Multifield-add")
.css("position" ,"relative");
}

function collectImageFields($form, $fieldSet, counter){
var $fields = $fieldSet.children().children(CFFW).not(function(index, ele){
return $(ele).find(SEL_FILE_UPLOAD).length == 0;
});

$fields.each(function (j, field) {
var $field = $(field),
$widget = $field.find(SEL_FILE_UPLOAD).data("fileUpload");

if(!$widget){
return;
}

var prefix = $fieldSet.data("name") + "/" + (counter + 1) + "/",

$fileRef = $widget.$element.find(SEL_FILE_REFERENCE),
refPath = prefix + getJustName($fileRef.attr("name")),

$fileName = $widget.$element.find(SEL_FILE_NAME),
namePath = prefix + getJustName($fileName.attr("name")),

$fileMoveRef = $widget.$element.find(SEL_FILE_MOVEFROM),
moveSuffix = $widget.inputElement.attr("name") + "/" + new Date().getTime()
+ SEP_SUFFIX + $fileName.val(),
moveFromPath = moveSuffix + "@MoveFrom";

$('<input />').attr('type', 'hidden').attr('name', refPath)
.attr('value', $fileRef.val() || ($form.attr("action") + removeFirstDot(moveSuffix)))
.appendTo($form);

$('<input />').attr('type', 'hidden').attr('name', namePath)
.attr('value', $fileName.val()).appendTo($form);

$('<input />').attr('type', 'hidden').attr('name', moveFromPath)
.attr('value', modifyJcrContent($fileMoveRef.val())).appendTo($form);

$field.remove();
});
}

function collectNonImageFields($form, $fieldSet, counter){
var $fields = $fieldSet.children().children(CFFW).not(function(index, ele){
return $(ele).find(SEL_FILE_UPLOAD).length > 0;
});

$fields.each(function (j, field) {
fillValue($form, $fieldSet.data("name"), $(field).find("[name]"), (counter + 1));
});
}

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

if (!name) {
return;
}

//strip ./
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);
}

//collect data from widgets in multifield and POST them to CRX
function collectDataFromFields(){
$(document).on("click", ".cq-dialog-submit", function () {
var $multifields = $("[" + DATA_EAEM_NESTED + "]");

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

var $form = $(this).closest("form.foundation-form"),
$fieldSets;

$multifields.each(function(i, multifield){
$fieldSets = $(multifield).find("[class='coral-Form-fieldset']");

$fieldSets.each(function (counter, fieldSet) {
collectNonImageFields($form, $(fieldSet), counter);

collectImageFields($form, $(fieldSet), counter);
});
});
});
}

function overrideGranite_refreshThumbnail(){
var prototype = Granite.FileUploadField.prototype,
ootbFunc = prototype._refreshThumbnail;

prototype._refreshThumbnail = function() {
var $imageMulti = this.widget.$element.closest("[" + DATA_EAEM_NESTED + "]");

if (!_.isEmpty($imageMulti)) {
return;
}

return ootbFunc.call(this);
}
}

function overrideGranite_computeFieldNames(){
var prototype = Granite.FileUploadField.prototype,
ootbFunc = prototype._computeFieldNames;

prototype._computeFieldNames = function(){
ootbFunc.call(this);

var $imageMulti = this.widget.$element.closest("[" + DATA_EAEM_NESTED + "]");

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

var fieldNames = {},
fileFieldName = $imageMulti.find("input[type=file]").attr("name"),
counter = $imageMulti.find(SEL_FILE_UPLOAD).length;

_.each(this.fieldNames, function(value, key){
if(value.indexOf("./jcr:") == 0){
fieldNames[key] = value;
}else if(key == "tempFileName" || key == "tempFileDelete"){
value = value.substring(0, value.indexOf(".sftmp")) + getStringAfterAtSign(value);
fieldNames[key] = fileFieldName + removeFirstDot(getStringBeforeAtSign(value))
+ SEP_SUFFIX + counter + ".sftmp" + getStringAfterAtSign(value);
}else{
fieldNames[key] = getStringBeforeAtSign(value) + SEP_SUFFIX
+ counter + getStringAfterAtSign(value);
}
});

this.fieldNames = fieldNames;

this._tempFilePath = getStringBeforeLastSlash(this._tempFilePath);
this._tempFilePath = getStringBeforeLastSlash(this._tempFilePath) + removeFirstDot(fieldNames.tempFileName);
}
}

function performOverrides(){
overrideGranite_computeFieldNames();
overrideGranite_refreshThumbnail();
}

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

performOverrides();
})();

AEM 62 - Touch UI Disable Upload of File in Granite Fileupload Widget

$
0
0

Goal


Disable Browse and Upload file in FileUpload widget - /libs/granite/ui/components/foundation/form/fileupload of Image component - /libs/foundation/components/image, in Touch UI

Demo | Package Install




Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/eaem-fileupload-widget-disable-upload

2) Create node /apps/eaem-fileupload-widget-disable-upload/clientlib of type cq:ClientLibraryFolder and add a String property categories with value cq.authoring.dialog and dependencies to underscore

3) Create file (nt:file) /apps/eaem-fileupload-widget-disable-upload/clientlib/js.txt and add

                       disable-upload.js

4) Create file (nt:file) /apps/eaem-fileupload-widget-disable-upload/clientlib/disable-upload.js and add the following code

(function ($, $document, gAuthor) {
var COMPONENT = "foundation/components/image",
FILE_UPLOAD = ".coral-FileUpload",
FILE_UPLOAD_BROWSE = ".cq-FileUpload-browse",
DATA_ATTR_FILE_UPLOAD = "fileUpload";

if(!gAuthor){
return;
}

$document.on('dialog-ready', disableUpload);

function disableUpload(){
var editable = gAuthor.DialogFrame.currentDialog.editable;

//if not an image component dialog, return
if((editable.type !== COMPONENT)){
return;
}

var $fileUploads = $(FILE_UPLOAD), cuiFileUpload, $uploadBrowse;

$fileUploads.each(function(index, fileUpload){
cuiFileUpload = $(fileUpload).data(DATA_ATTR_FILE_UPLOAD);

$uploadBrowse = cuiFileUpload.$element.find(FILE_UPLOAD_BROWSE);

$uploadBrowse.off().on("click tap", function(){
showErrorAlert("Upload Disabled");
});

cuiFileUpload.$element.find("input[type='file']").remove();
});
}

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);
}
}(jQuery, jQuery(document), Granite.author));

AEM 62 - Touch UI Show Sling Resource Type of Dialog Fields

$
0
0

Goal


Sample code to get the sling resource type of dialog fields and show in message box

Demo | Package Install


Bug Fixes

Page Properties dialog fields resource types - Demo | Package Install


Resource Type in CRX



Resource Type in Dialog



Page Properties Dialog Fields




Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/eaem-find-sling-res-type-of-dialog-field

2) Create node /apps/eaem-find-sling-res-type-of-dialog-field/clientlib of type cq:ClientLibraryFolder and add a String property categories with value cq.authoring.dialog and dependencies to underscore

3) Create file (nt:file) /apps/eaem-find-sling-res-type-of-dialog-field/clientlib/js.txt and add

                       find-resource-type.js

4) Create file (nt:file) /apps/eaem-find-sling-res-type-of-dialog-field/clientlib/find-resource-type.js and add the following code

(function ($, $document, gAuthor) {
if(!gAuthor){
return;
}

$document.on('dialog-ready', showSlingResourceType);

function showSlingResourceType(){
var currentDialog = gAuthor.DialogFrame.currentDialog, dialogPath ;

if(currentDialog instanceof gAuthor.actions.PagePropertiesDialog){
var dialogSrc = currentDialog.getConfig().src;
dialogPath = dialogSrc.substring(0, dialogSrc.indexOf(".html"));
}else{
var editable = gAuthor.DialogFrame.currentDialog.editable;

if(!editable){
console.log("EAEM - editable not available");
return;
}

dialogPath = editable.config.dialog;
}

$.ajax(dialogPath + ".infinity.json").done(handler);

function handler(data) {
var resourceTypes = {}, propertyName = "name", message = "";

fillFieldsResourceType(data, propertyName, resourceTypes);

_.each(resourceTypes, function(value, key){
message = message + key + " - " + getStringBeforeLastSlash(value)
+ "/<b>" + getStringAfterLastSlash(value) + "</b>";
});

showMessageBox(message, "Field Resource Types");
}
}

function fillFieldsResourceType(obj, propName, resourceTypes){
if(!_.isObject(obj) || _.isEmpty(obj) || _.isEmpty(propName)){
return resourceTypes;
}

_.each(obj, function(value, key){
if(_.isObject(value) && !_.isEmpty(value)){
resourceTypes = fillFieldsResourceType(value, propName, resourceTypes);
}else{
if( key == propName){
resourceTypes[value] = obj["sling:resourceType"];
}
}
});

return resourceTypes;
}

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

message = message || "Message";
title = title || "Title";

fui.prompt(title, message, "notice", options);
}

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

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

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

return str.substr(0, str.lastIndexOf("/"));
}
}(jQuery, jQuery(document), Granite.author));



AEM 62 - TouchUI Open Page Properties Dialog in Fullscreen

$
0
0

Goal


Sample code to open the Page Properties dialog (not component dialog) in Full screen by default

Demo | Package Install

Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/eaem-open-page-props-dialog-full-screen

2) Create node /apps/eaem-open-page-props-dialog-full-screen/clientlib of type cq:ClientLibraryFolder and add a String property categories with value cq.authoring.dialog and dependencies to underscore

3) Create file (nt:file) /apps/eaem-open-page-props-dialog-full-screen/clientlib/js.txt and add

                       open-fullscreen.js

4) Create file (nt:file) /apps/eaem-open-page-props-dialog-full-screen/clientlib/open-fullscreen.js and add the following code

(function ($, $document, gAuthor) {
if(!gAuthor){
return;
}

$document.on('dialog-ready', openPagePropertiesFullScreen);

function openPagePropertiesFullScreen(){
var currentDialog = gAuthor.DialogFrame.currentDialog;

//if the current dialog is page properties
if(currentDialog instanceof gAuthor.actions.PagePropertiesDialog){
$('form.cq-dialog').find(".cq-dialog-layouttoggle").click();
}
}
}(jQuery, jQuery(document), Granite.author));



Viewing all 525 articles
Browse latest View live