Goal
Before trying out this extension, check if the AEM Core components Carousel serves your purpose - http://opensource.adobe.com/aem-core-wcm-components/library/carousel.html or using a Pathbrowser is good enough - /libs/granite/ui/components/coral/foundation/form/pathbrowser
Create a Touch UI Composite Multifield configuration supporting Images, widgets of type /libs/cq/gui/components/authoring/dialog/fileupload
For AEM 62 check this post
Demo | Package Install | Github
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="64 Image Multifield" sling:resourceType="cq/gui/components/authoring/dialog"> <content jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"> <items jcr:primaryType="nt:unstructured"> <column jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container"> <items jcr:primaryType="nt:unstructured"> <products jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/multifield" composite="{Boolean}true" fieldLabel="Products"> <field jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container" name="./products"> <items jcr:primaryType="nt:unstructured"> <column jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container"> <items jcr:primaryType="nt:unstructured"> <product jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/textfield" fieldDescription="Name of Product" fieldLabel="Product Name" name="./product"/> <path jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/pathbrowser" fieldDescription="Select Path" fieldLabel="Path" name="./path" rootPath="/content"/> <file jcr:primaryType="nt:unstructured" sling:resourceType="cq/gui/components/authoring/dialog/fileupload" allowUpload="false" autoStart="{Boolean}false" class="cq-droptarget" fileNameParameter="./fileName" fileReferenceParameter="./fileReference" mimeTypes="[image/gif,image/jpeg,image/png,image/webp,image/tiff]" multiple="{Boolean}false" name="./file" title="Upload Image Asset" uploadUrl="${suffix.path}" useHTML5="{Boolean}true"/> </items> </column> </items> </field> </products> </items> </column> </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.dialog.all, dependencies 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 ($, $document) { var COMPOSITE_MULTIFIELD_SELECTOR = "coral-multifield[data-granite-coral-multifield-composite]", FILE_REFERENCE_PARAM = "fileReference", registry = $(window).adaptTo("foundation-registry"), ALLOWED_MIME_TYPE = "image/jpeg", adapters = registry.get("foundation.adapters"); var fuAdapter = _.reject(adapters, function(adapter){ return ((adapter.type !== "foundation-field") || (adapter.selector !== "coral-fileupload.cq-FileUpload")); }); if(_.isEmpty(fuAdapter)){ return; } fuAdapter = fuAdapter[0]; var orignFn = fuAdapter.adapter; fuAdapter.adapter = function(el) { return Object.assign(orignFn.call(el), { getName: function () { return el.name; }, setName: function(name) { var prefix = name.substr(0, name.lastIndexOf(el.name)); el.name = name; $("input[type='hidden'][data-cq-fileupload-parameter]", el).each(function(i, el) { if ($(el).data("data-cq-fileupload-parameter") !== "filemovefrom") { this.setAttribute("name", prefix + this.getAttribute("name")); } }); } }); }; $document.on("foundation-contentloaded", function(e) { var composites = $(COMPOSITE_MULTIFIELD_SELECTOR, e.target); composites.each(function() { Coral.commons.ready(this, function(el) { addThumbnails(el); }); }); }); function addThumbnails(multifield){ var $multifield = $(multifield), dataPath = $multifield.closest(".cq-dialog").attr("action"), mfName = $multifield.attr("data-granite-coral-multifield-name"); dataPath = dataPath + "/" + getStringAfterLastSlash(mfName); $.ajax({ url: dataPath + ".2.json", cache: false }).done(handler); function handler(mfData){ multifield.items.getAll().forEach(function(item, i) { var $mfItem = $(item), $fileUpload = $mfItem.find("coral-fileupload"); if(_.isEmpty($fileUpload)){ return; } var itemName = getJustItemName($fileUpload.attr("name")); if(_.isEmpty(mfData[itemName]) || _.isEmpty((mfData[itemName][FILE_REFERENCE_PARAM]))){ return; } var imagePath = mfData[itemName][FILE_REFERENCE_PARAM]; $fileUpload.trigger($.Event("assetselected", { path: imagePath, group: "", mimetype: ALLOWED_MIME_TYPE, // workaround to add thumbnail param: "", thumbnail: getThumbnailHtml(imagePath) })); }); } function getThumbnailHtml(path){ return "<img class='cq-dd-image' src='" + path + "/_jcr_content/renditions/cq5dam.thumbnail.319.319.png'>"; } function getJustItemName(itemName){ itemName = itemName.substr(itemName.indexOf(mfName) + mfName.length + 1); itemName = itemName.substring(0, itemName.indexOf("/")); return itemName; } } function getStringAfterLastSlash(str){ if(!str || (str.indexOf("/") == -1)){ return ""; } return str.substr(str.lastIndexOf("/") + 1); } }(jQuery, jQuery(document)));
5) To render the composite multifield items eg. to create a Image Gallery component, create a HTL render script /apps/eaem-touchui-image-multifield/sample-image-multifield/sample-image-multifield.html
<div> <b>6420 Composite Image Multifield</b> <div data-sly-use.company="helper.js" data-sly-unwrap> <div data-sly-test="${!company.products && wcmmode.edit}"> Add products using component dialog </div> <div data-sly-test="${company.products}"> <div data-sly-list.product="${company.products}"> <div> <div>${product.name}</div> <div>${product.path}</div> <div><img src="${product.fileReference}" width="150" height="150"/></div> </div> </div> </div> </div> </div>
6) Finally a HTL use-script to read the multifield data /apps/eaem-touchui-image-multifield/sample-image-multifield/helper.js
"use strict"; use( ["/libs/wcm/foundation/components/utils/ResourceUtils.js","/libs/sightly/js/3rd-party/q.js" ], function(ResourceUtils, Q){ var prodPromise = Q.defer(), company = {}, productsPath = granite.resource.path + "/products"; company.products = undefined; ResourceUtils.getResource(productsPath) .then(function (prodParent) { return prodParent.getChildren(); }) .then(function(products) { addProduct(products, 0); }); function addProduct(products, currIndex){ if(!company.products){ company.products = []; } if (currIndex >= products.length) { prodPromise.resolve(company); return; } var productRes = products[currIndex], properties = productRes.properties; var product = { path: properties.path, name: properties.product, fileReference: properties.fileReference }; company.products.push(product); addProduct(products, (currIndex + 1)); } return prodPromise.promise; } );
Hi Sreekanth Choudry,
ReplyDeleteDo you have a similar fix for 6.4.2 version? I tried the above provided fix on 6.4.2 but as you mentioned it is working only on 6.4.4.
If you experience order problem like me, you can user below code part:
ReplyDeletesetName: function(name) {
var prefix = name.substr(0, name.lastIndexOf(el.name));
var oldRootElementName = "";
var newRootElementName = "";
if(prefix == ""){
oldRootElementName = el.name.substr(0, el.name.lastIndexOf("./icon"));
newRootElementName = name.substr(0, name.lastIndexOf("./icon"));;
}
el.name = name;
$("input[type='hidden'][data-cq-fileupload-parameter]", el).each(function(i, el) {
if ($(el).data("data-cq-fileupload-parameter") !== "filemovefrom") {
if(prefix == ""){
var currentChildName = this.getAttribute("name");
currentChildName = currentChildName.replace(oldRootElementName,newRootElementName);
this.setAttribute("name", currentChildName);
}else{
this.setAttribute("name", prefix + this.getAttribute("name"));
}
}
});
}
Hi Emrah,
DeleteI replaced the setName function to fix the reorder issue but its not working. Can you please suggest?
For AEM 6.5, the code has to be modified a bit. Please use the following code if working on AEM 6.5
ReplyDeletesetName function to be replaced by the following code. Rest all remains as given in the article above.
setName: function(name) {
var prefix = name.substr(0, name.lastIndexOf(el.name));
var oldRootElementName = "";
var newRootElementName = "";
if(prefix == ""){
oldRootElementName = el.name.substr(0, el.name.lastIndexOf("./"));
newRootElementName = name.substr(0, name.lastIndexOf("./"));
}
el.name = name;
$("input[type='hidden'][data-cq-fileupload-parameter]", el).each(function(i, el) {
if ($(el).data("data-cq-fileupload-parameter") !== "filemovefrom") {
if(prefix == ""){
var currentChildName = this.getAttribute("name");
currentChildName = currentChildName.replace(oldRootElementName,newRootElementName);
this.setAttribute("name", currentChildName);
} else{
this.setAttribute("name", prefix + this.getAttribute("name"));
}
}
});
}
Hi Sreekanth Choudry Nalabotu
ReplyDeleteCan you provide solution for Image Upload feature in multifield in AEM 6.5