Wednesday, March 29, 2017

How to customize the page properties Dialog to include dynamic DropDownList in AEM/CQ5

How to customize the page properties Dialog to include dynamic DropDownList in AEM/CQ5

This post will explain the details to customize page properties Dialog in both Touch and Classic UI's, the version used for implementing this is Adobe Experience Manager(AEM) 6.1.

The basic page properties dialog will be displayed if the sling:resourceSuperType of the page rendering component is specified as foundation page component(wcm/foundation/components/page for Sightly and foundation/components/page for JSP)

Steps to customize the basic page properties and including dynamic DropDownList to the dialog.

/apps/training/components/page-content/ will be referred as the page rendering component path in the post.

Classic UI:

Copy /libs/foundation/components/page/dialog to /apps/training/components/page-content/
Rename the new node to required name e.g. custom

Copy /libs/foundation/components/page/tab_basic to /apps/training/components/page-content/
Rename /apps/training/components/page-content/tab_basic to required name e.g tab_custom

Remove all the nodes under  /apps/training/components/page-content/tab_custom/items

Change path value in the node /apps/training/components/page-content/dialog/items/tabs/items/custom to /apps/training/components/page-content/tab_custom.infinity.json

Change title property of the node /apps/training/components/page-content/tab_custom to required value e.g Custom

Save All configurations

Open the page from site Admin(e.g. http://localhost:4502/cf#/content/training-site/en.html), now the new tab(Custom) will be added to the page properties with empty panel

Adding a textfield to the custom panel:

Create a new node of type cq:Widget under /apps/training/components/page-content/tab_custom/items

Name Type Value
xtype String textfield
name String ./fieldName e.g. ./customtext
fieldLabel String Enter the field lable

Save All configurations - open the page(e.g. http://localhost:4502/cf#/content/training-site/en.html), the new textfield will be displayed under Custom tab.

Adding a dynamic DropDownList:

Create a servlet that returns the JSON data.

import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.commons.json.JSONArray;
import org.apache.sling.commons.json.JSONObject;
import org.apache.felix.scr.annotations.Properties;

@Service(value = Servlet.class)
@Component(immediate = true, metatype = true)
@Properties({
@Property(name = "sling.servlet.paths", value = "/services/getCountries"),
@Property(name = "service.description", value = "returns list of countries"),
@Property(name = "label", value = "GetCountryList") })
public class GetCountryList extends SlingSafeMethodsServlet{

private static final long serialVersionUID = 1180258251365536303L;

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

JSONArray contryJsonArray = new JSONArray();
JSONObject jsonObject = new JSONObject();
jsonObject.put("text", "US").put("value", "United States");
contryJsonArray.put(jsonObject);
jsonObject = new JSONObject();
jsonObject.put("text", "UK").put("value", "United Kingdom");
contryJsonArray.put(jsonObject);
response.getWriter().write(contryJsonArray.toString());
}catch(Exception e){
}
}
}

Create a new node of type cq:Widget under /apps/training/components/page-content/tab_custom/items

Add the following properties to the new node

Name Type Value
xtype String selection
name String ./fieldName e.g. ./customtext
fieldLabel String Enter the field label
type String select
options String Servlet path that returns JSON data (e.g. /services/getCountries)

Save All Configurations - open the page(e.g. http://localhost:4502/cf#/content/training-site/en.html), the new DropDownList will be displayed under Custom tab.


Touch UI:

Copy /libs/foundation/components/page/cq:dialog to /apps/training/components/page-content

Copy /apps/training/components/page-content/cq:dialog/content/items/tabs/items/basic and paste to /apps/training/components/page-content/cq:dialog/content/items/tabs/items

Rename the node name to required value e.g Custom

Change the jcr:title property of the node e.g Custom

Remove nodes that are not required and create/rename nodes as required(better remove all the nodes under /apps/training/components/page-content/cq:dialog/content/items/tabs/items/custom/items/column/items and create the required nodes).

Adding a Dynamic Dropdownlist:

Create a servlet that will return the DataSource

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.servlet.ServletException;
import org.apache.felix.scr.annotations.sling.SlingServlet;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.api.wrappers.ValueMapDecorator;
import com.adobe.granite.ui.components.ds.DataSource;
import com.adobe.granite.ui.components.ds.SimpleDataSource;
import com.adobe.granite.ui.components.ds.ValueMapResource;
@SlingServlet(resourceTypes = "/services/getCountryList")
public class GetCountryListServlet extends SlingSafeMethodsServlet{
private static final long serialVersionUID = 1180258251365536303L;
@Override
protected void doGet(SlingHttpServletRequest request,SlingHttpServletResponse response) throws ServletException,IOException {
try{
ResourceResolver resolver = request.getResourceResolver();
List<Resource> countryList = new ArrayList<Resource>();
ValueMap valueMap = new ValueMapDecorator(new HashMap<String, Object>()); 
valueMap.put("value","United States");
valueMap.put("text","US");
countryList.add(new ValueMapResource(resolver, new ResourceMetadata(), "nt:unstructured", valueMap));
 
valueMap = new ValueMapDecorator(new HashMap<String, Object>()); 
valueMap.put("value","United Kingdom");
valueMap.put("text","UK");
countryList.add(new ValueMapResource(resolver, new ResourceMetadata(), "nt:unstructured", valueMap));
     
DataSource dataSource = new SimpleDataSource(countryList.iterator());
    request.setAttribute(DataSource.class.getName(), dataSource);
}catch(Exception e){
e.printStackTrace();
}
}
}

The servlet is not registered with the above code in AEM 6.4, @Component is expected for the servlet.

Servelet - AEM 6.4

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.servlet.*;
import org.apache.felix.scr.annotations.sling.SlingServlet;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.api.wrappers.ValueMapDecorator;
import com.adobe.granite.ui.components.ds.DataSource;
import com.adobe.granite.ui.components.ds.SimpleDataSource;
import com.adobe.granite.ui.components.ds.ValueMapResource;
import org.osgi.service.component.annotations.Component;
import org.osgi.framework.Constants;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(service=Servlet.class,
        property={

                Constants.SERVICE_DESCRIPTION + "=Get Country List Servlet",
                "sling.servlet.methods=" + "GET",
                "sling.servlet.resourceTypes="+ "/services/getCountryList"
           })


public class GetCountryListServlet extends SlingSafeMethodsServlet{
 private static final long serialVersionUID = 1180258251365536303L;
 Logger logger = LoggerFactory.getLogger(this.getClass());
 @Override
 protected void doGet(SlingHttpServletRequest request,SlingHttpServletResponse response) throws ServletException,IOException {
 try{
 logger.error("inside...");
 ResourceResolver resolver = request.getResourceResolver();
  List<Resource> countryList = new ArrayList<Resource>();
  ValueMap valueMap = new ValueMapDecorator(new HashMap<String, Object>());
  valueMap.put("value","United States");
  valueMap.put("text","US");
  countryList.add(new ValueMapResource(resolver, new ResourceMetadata(), "nt:unstructured", valueMap));
 
  valueMap = new ValueMapDecorator(new HashMap<String, Object>());
  valueMap.put("value","United Kingdom");
  valueMap.put("text","UK");
  countryList.add(new ValueMapResource(resolver, new ResourceMetadata(), "nt:unstructured", valueMap));
     
  DataSource dataSource = new SimpleDataSource(countryList.iterator());
  logger.error("dataSource...");
  request.setAttribute(DataSource.class.getName(), dataSource);
 }catch(Exception e){
logger.error("error..."+e.getMessage());
 e.printStackTrace();
 }
 }

}

Create a node(e.g. customlist) with type nt:unstructured under /apps/training/components/page-content/cq:dialog/content/items/tabs/items/custom/items/column/items

Add the following properties to the new node

Name Type Value
emptyText String selection
name String ./fieldName e.g. ./customlist
fieldLabel String Enter the field label
sling:resourceType String granite/ui/components/foundation/form/select

Create a node with name datasource and type  nt:unstructured under the node created in the previous step

Enter the below property

Name Type Value
sling:resourceType String Servlet Path(e.g. /services/getCountryList)

 Save All Configurations - open the page(e.g. http://localhost:4502/editor.html/content/training-site/en.html), the new fileds/DropDownList will be displayed under Custom tab.


1 comment:

  1. HI,

    Thanks for the info. When we are overriding the dialog(in TOUCH UI) in our page component with only one tab, why we are seeing the OOTB components tabs as well in page properties.

    Thanks,
    Pradeep

    ReplyDelete