Create Component
1) In your CRXDE Lite http://localhost:4502/crx/de, create below folder and save changes
/apps/imagemultifield
2) Copy the component /libs/foundation/components/logo and paste it in path /apps/imagemultifield
3) Rename /apps/imagemultifield/logo to /apps/imagemultifield/imagemultifield
4) Rename /apps/imagemultifield/imagemultifield/logo.jsp to /apps/imagemultifield/imagemultifield/imagemultifield.jsp
5) Change the following properties of /apps/imagemultifield/imagemultifield
componentGroup - My Components
jcr:title - Image MultiField Component
6) Add the following to dialog (/apps/imagemultifield/imagemultifield/dialog) xml
<?xml version="1.0" encoding="UTF-8"?><jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" jcr:primaryType="cq:Dialog" activeTab="{Long}0" title="Multi Image" xtype="tabpanel"> <items jcr:primaryType="cq:WidgetCollection"> <basic jcr:primaryType="cq:Widget" title="Images" xtype="panel"> <items jcr:primaryType="cq:WidgetCollection"> <images jcr:primaryType="cq:Widget" border="false" hideLabel="true" name="./images" xtype="imagemultifield"> <fieldConfig jcr:primaryType="cq:Widget" border="false" hideLabel="true" layout="form" padding="10px 0 0 100px" xtype="imagemultifieldpanel"> <items jcr:primaryType="cq:WidgetCollection"> <image jcr:primaryType="cq:Widget" cropParameter="./imageCrop" ddGroups="[media]" fileNameParameter="./imageName" fileReferenceParameter="./imageReference" height="250" mapParameter="./imageMap" name="./image" rotateParameter="./imageRotate" sizeLimit="100" xtype="imagemultifieldsmartimage"/> </items> </fieldConfig> </images> </items> </basic> </items></jcr:root>
Add JS Logic and Register XTypes
1) Create node /apps/imagemultifield/imagemultifield/clientlib of type cq:ClientLibraryFolder and add the following properties
categories - String - cq.widgets
2) Create file (type nt:file) /apps/imagemultifield/imagemultifield/clientlib/js.txt and add the following
imagemultifield.js
3) Create file (type nt:file) /apps/imagemultifield/imagemultifield/clientlib/imagemultifield.js and add the following code
CQ.Ext.ns("ImageMultiField");ImageMultiField.Panel = CQ.Ext.extend(CQ.Ext.Panel, { initComponent: function () { ImageMultiField.Panel.superclass.initComponent.call(this); var multifield = this.findParentByType('imagemultifield'); var image = this.find('xtype', 'imagemultifieldsmartimage')[0]; var imageName = multifield.nextImageName; if(!imageName){ imageName = image.name; if(!imageName){ imageName = "demo"; }else if(imageName.indexOf("./") == 0){ imageName = imageName.substr(2); //get rid of ./ } var suffix = multifield.nextImageNum = multifield.nextImageNum + 1; imageName = this.name + "/" + imageName + "-" + suffix; } image.name = imageName; var changeParams = ["cropParameter", "fileNameParameter","fileReferenceParameter", "mapParameter","rotateParameter" ]; CQ.Ext.each(changeParams, function(cItem){ if(image[cItem]){ image[cItem] = imageName + "/" + ( image[cItem].indexOf("./") == 0 ? image[cItem].substr(2) : image[cItem]); } }); CQ.Ext.each(image.imageToolDefs, function(toolDef){ toolDef.transferFieldName = imageName + toolDef.transferFieldName.substr(1); toolDef.transferField.name = toolDef.transferFieldName; }); }, setValue: function (record) { var multifield = this.findParentByType('imagemultifield'); var image = this.find('xtype', 'imagemultifieldsmartimage')[0]; var recCopy = CQ.Util.copyObject(record); var imagePath = multifield.path + "/" + image.name; var imgRec = recCopy.get(image.name); for(var x in imgRec){ if(imgRec.hasOwnProperty(x)){ recCopy.data[x] = imgRec[x]; } } recCopy.data[this.name.substr(2)] = undefined; var fileRefParam = image.fileReferenceParameter; image.fileReferenceParameter = fileRefParam.substr(fileRefParam.lastIndexOf("/") + 1); image.processRecord(recCopy, imagePath); image.fileReferenceParameter = fileRefParam; }, validate: function(){ return true; }});CQ.Ext.reg("imagemultifieldpanel", ImageMultiField.Panel);ImageMultiField.SmartImage = CQ.Ext.extend(CQ.html5.form.SmartImage, { syncFormElements: function() { if(!this.fileNameField.getEl().dom){ return; } ImageMultiField.SmartImage.superclass.syncFormElements.call(this); } , afterRender: function() { ImageMultiField.SmartImage.superclass.afterRender.call(this); var dialog = this.findParentByType('dialog'); var target = this.dropTargets[0]; if (dialog && dialog.el && target.highlight) { var dialogZIndex = parseInt(dialog.el.getStyle("z-index"), 10); if (!isNaN(dialogZIndex)) { target.highlight.zIndex = dialogZIndex + 1; } } var multifield = this.findParentByType('multifield'); multifield.dropTargets.push(target); this.dropTargets = undefined; }});CQ.Ext.reg('imagemultifieldsmartimage', ImageMultiField.SmartImage);CQ.Ext.override(CQ.form.SmartImage.ImagePanel, { addCanvasClass: function(clazz) { var imageCanvas = CQ.Ext.get(this.imageCanvas); if(imageCanvas){ imageCanvas.addClass(clazz); } }, removeCanvasClass: function(clazz) { var imageCanvas = CQ.Ext.get(this.imageCanvas); if(imageCanvas){ imageCanvas.removeClass(clazz); } }});CQ.Ext.override(CQ.form.SmartImage.Tool, { processRecord: function(record) { var iniValue = record.get(this.transferFieldName); if(!iniValue && ( this.transferFieldName.indexOf("/") !== -1 )){ iniValue = record.get(this.transferFieldName.substr(this.transferFieldName.lastIndexOf("/") + 1)); } if (iniValue == null) { iniValue = ""; } this.initialValue = iniValue; }});CQ.Ext.override(CQ.form.MultiField.Item, { reorder: function(item) { if(item.field && item.field.xtype == "imagemultifieldpanel"){ var c = this.ownerCt; var iIndex = c.items.indexOf(item); var tIndex = c.items.indexOf(this); if(iIndex < tIndex){ //user clicked up c.insert(c.items.indexOf(item), this); this.getEl().insertBefore(item.getEl()); }else{//user clicked down c.insert(c.items.indexOf(this), item); this.getEl().insertAfter(item.getEl()); } c.doLayout(); }else{ var value = item.field.getValue(); item.field.setValue(this.field.getValue()); this.field.setValue(value); } }});ImageMultiField.MultiField = CQ.Ext.extend(CQ.form.MultiField , { Record: CQ.data.SlingRecord.create([]), nextImageNum: 0, nextImageName: undefined, initComponent: function() { ImageMultiField.MultiField.superclass.initComponent.call(this); var imagesOrder = new CQ.Ext.form.Hidden({ name: this.getName() + "/order" }); this.add(imagesOrder); var dialog = this.findParentByType('dialog'); dialog.on('beforesubmit', function(){ var imagesInOrder = this.find('xtype','imagemultifieldsmartimage'); var order = []; CQ.Ext.each(imagesInOrder , function(image){ order.push(image.name.substr(image.name.lastIndexOf("/") + 1)) }); imagesOrder.setValue(JSON.stringify(order)); },this); this.dropTargets = []; }, addItem: function(value){ if(!value){ value = new this.Record({},{}); } ImageMultiField.MultiField.superclass.addItem.call(this, value); }, processRecord: function(record, path) { if (this.fireEvent('beforeloadcontent', this, record, path) !== false) { this.items.each(function(item) { if(item.field && item.field.xtype == "imagemultifieldpanel"){ this.remove(item, true); } }, this); var images = record.get(this.getName()); this.nextImageNum = 0; if (images) { var oName = this.getName() + "/order"; var oValue = record.get(oName) ? record.get(oName) : ""; var iNames = JSON.parse(oValue); var highNum, val; CQ.Ext.each(iNames, function(iName){ val = parseInt(iName.substr(iName.indexOf("-") + 1)); if(!highNum || highNum < val){ highNum = val; } this.nextImageName = this.getName() + "/" + iName; this.addItem(record); }, this); this.nextImageNum = highNum; } this.nextImageName = undefined; this.fireEvent('loadcontent', this, record, path); } }});CQ.Ext.reg('imagemultifield', ImageMultiField.MultiField);
Rendering Images
1) Images created in the CRX using MultiImage componet are rendered using /apps/imagemultifield/imagemultifield/imagemultifield.jsp. Add the following code in jsp
<%@include file="/libs/foundation/global.jsp"%>
<%@ page import="java.util.Iterator" %>
<%@ page import="com.day.cq.wcm.foundation.Image" %>
<%@ page import="org.apache.sling.commons.json.JSONArray" %>
<%
Iterator<Resource> children = resource.listChildren();
if(!children.hasNext()){
%>
Click here to add images
<%
}else{
Resource imagesResource = children.next();
ValueMap map = imagesResource.adaptTo(ValueMap.class);
String order = map.get("order", String.class);
Image img = null; String src = null;
JSONArray array = new JSONArray(order);
for(int i = 0; i < array.length(); i++){
img = new Image(resource);
img.setItemName(Image.PN_REFERENCE, "imageReference");
img.setSuffix(String.valueOf(array.get(i)));
img.setSelector("img");
src = img.getSrc();
%>
<img src='<%=src%>'/>
<%
}
}
%>
Very helpful blog.But its a good idea to add a git repo with such code.
ReplyDeleteCould you please help us, I am getting an issue when one item is removed and then, I try to add a new one. it seems related to getEL Uncaught TypeError: Cannot read property 'getBoundingClientRect' of null
ReplyDeleteat Object.getXY (widgets.min.js:formatted:1363)
at Function.CQ.Ext.lib.Region.getRegion (widgets.min.js:formatted:7287)
at constructor.CF_storeDropTargets (widgets.min.js:formatted:86069)
at widgets.min.js:formatted:86113