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

AEM CQ 56 - Adding Images in MultiField

$
0
0

Goal


This post is on adding multiple images in a component dialog. Here we extend and use CQ.form.MultiField to add images of type CQ.html5.form.SmartImage. Source Code, Package Install and Demo Video are available for download





Prerequisities


If you are new to CQ visit this blog post; it explains page component basics and setting up your IDE

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>

8) Line 18, create an instance of widget type imagemultifield ( ImageMultiField.MultiField extending CQ.form.MultiField) explained in the next section

9) Line 25, 38, when a user clicks on Add Item of imagemultifield, create a panel of type imagemultifieldpanel (ImageMultiField.Panel extending CQ.Ext.Panel); in the panel created, add image widget imagemultifieldsmartimage ( ImageMultiField.SmartImage extending CQ.html5.form.SmartImage)

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

4) In the above step we are adding necessary js logic to create a multifield of images; each item of multifield is a panel holding one smart image.So, when user clicks on Add Item a panel is created and added to multifield, smart image added to the panel. Call me if you have trouble understanding it, i have to do a lot of typing to explain the code :)

Content Structure in CRX


1) When you add images using the above imagemultifield with default configuration, the following content structure is created in CRX (/content/multi-image/jcr:content/par/imagemultifield)



2) The below node at level /content/multi-image/jcr:content/par/imagemultifield/images stores image order for rendering images in the order they were added or reordered



3)  Each image is added as a separate node under imagemultifield/images. For example here is node /content/multi-image/jcr:content/par/imagemultifield/images/image-2



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()){
%>

Configure 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%>'/>
<%
}
}
%>


2) Jsp renders, for example, the following image source paths

<imgsrc='/content/multi-image/_jcr_content/par/imagemultifield.img.png/image-1'/>
<imgsrc='/content/multi-image/_jcr_content/par/imagemultifield.img.png/image-2'/>
<imgsrc='/content/multi-image/_jcr_content/par/imagemultifield.img.png/image-3'/>

3) Add the following code in /apps/imagemultifield/imagemultifield/img.GET.java to get image binary

package apps.imagemultifield.imagemultifield;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;

import javax.jcr.RepositoryException;
import javax.jcr.Property;
import javax.servlet.http.HttpServletResponse;

import com.day.cq.wcm.foundation.Image;
import com.day.cq.wcm.commons.AbstractImageServlet;
import com.day.image.Layer;
import org.apache.commons.io.IOUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;

public class img_GET extends AbstractImageServlet {

protected Layer createLayer(ImageContext c)
throws RepositoryException, IOException {
return null;
}

protected void writeLayer(SlingHttpServletRequest req, SlingHttpServletResponse resp, ImageContext c, Layer layer)
throws IOException, RepositoryException {
Iterator<Resource> children = c.resource.listChildren();

if(!children.hasNext()){
return;
}

String rUri = req.getRequestURI();
String selImage = rUri.substring(rUri.lastIndexOf("/"));
Resource resource = req.getResourceResolver().getResource(children.next().getPath() + selImage);

if(resource == null){
return;
}

Image image = new Image(resource);
image.setItemName(Image.NN_FILE, "image");
image.setItemName(Image.PN_REFERENCE, "imageReference");

if (!image.hasContent()) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}

image.set(Image.PN_MIN_WIDTH, c.properties.get("minWidth", ""));
image.set(Image.PN_MIN_HEIGHT, c.properties.get("minHeight", ""));
image.set(Image.PN_MAX_WIDTH, c.properties.get("maxWidth", ""));
image.set(Image.PN_MAX_HEIGHT, c.properties.get("maxHeight", ""));

layer = image.getLayer(false, false, false);

boolean modified = image.crop(layer) != null;

modified |= image.resize(layer) != null;

modified |= image.rotate(layer) != null;

if (modified) {
resp.setContentType(c.requestImageType);
layer.write(c.requestImageType, 1.0, resp.getOutputStream());
} else {
Property data = image.getData();
InputStream in = data.getStream();
resp.setContentLength((int) data.getLength());
String contentType = image.getMimeType();

if (contentType.equals("application/octet-stream")) {
contentType=c.requestImageType;
}

resp.setContentType(contentType);
IOUtils.copy(in, resp.getOutputStream());

in.close();
}

resp.flushBuffer();
}
}

4) Finally, here is the component structure in CRX (http://localhost:4502/crx)






Viewing all articles
Browse latest Browse all 525

Trending Articles