Goal
This post is about extending the CQ Combo Box (CQ.Ext.form.ComboBox) widget to provide simple hierarchical (2-level) content. It also has a sample Sling Servlet to retrieve users and groups from CRX as json. Source code and Demo
Prerequisites
If you are new to CQ
1) Read this post on how to create a sample page component
2) Read this post on how to setup your IDE and create an OSGI component
Create Servlet
1) Create and deploy servlet to read users and groups from CRX. Here the servlet RepoGroupsUsers ( deployed as OSGI component ) returns the groups and users in json response.
package com.mycomponents.groupsusers; import org.apache.felix.scr.annotations.sling.SlingServlet; import org.apache.jackrabbit.api.security.user.*; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.servlets.SlingAllMethodsServlet; import org.apache.sling.commons.json.io.JSONWriter; import org.apache.sling.jcr.base.util.AccessControlUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jcr.Session; import javax.servlet.ServletException; import java.io.IOException; import java.util.Iterator; @SlingServlet( paths="/bin/mycomponents/groupsusers", methods = "GET", metatype = true, label = "Groups and Users Servlet" ) public class RepoGroupsUsers extends SlingAllMethodsServlet { private static final long serialVersionUID = 1L; private static final Logger LOG = LoggerFactory.getLogger(RepoGroupsUsers.class); @Override protected void doGet(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); response.setCharacterEncoding("utf-8"); try{ ResourceResolver resolver = request.getResourceResolver(); Session session = resolver.adaptTo(Session.class); UserManager um = AccessControlUtil.getUserManager(session); JSONWriter jw = new JSONWriter(response.getWriter()); Group group = null; User user = null; Object obj = null;String id = null; Iterator<Authorizable> users, groups = um.findAuthorizables(new Query() { public void build(QueryBuilder builder) { builder.setSelector(Group.class); } }); jw.object(); jw.key("data").array(); while(groups.hasNext()){ group = (Group)groups.next(); jw.object(); jw.key("id").value(group.getID()); jw.key("text").value(group.getPrincipal().getName()); jw.key("group").value("y"); jw.endObject(); users = group.getMembers(); while(users.hasNext()){ obj = users.next(); if(!(obj instanceof User)){ continue; } user = (User)obj; id = user.getID(); if(id.contains("@")){ id = id.substring(0, id.indexOf("@")); } jw.object(); jw.key("id").value(id); jw.key("text").value(user.getPrincipal().getName()); jw.endObject(); } } jw.endArray(); jw.endObject(); }catch(Exception e){ LOG.error("Error getting groups and users",e); throw new ServletException(e); } } }
2) Install ( this post explains how-to ) the servlet; you should see folder /apps/groupsuserscombo/install created in CRXDE Lite (http://localhost:4502/crx/de) with the bundle jar
3) Access the servlet in browser with url http://localhost:4502/bin/mycomponents/groupsusers; groups and users in CRX repository ( under /home ) are returned in json response
Create Component
1) Create the component (of type cq:Component) /apps/groupsuserscombo/groupsuserscombo
2) Create clientlib /apps/groupsuserscombo/groupsuserscombo/clientlib of type cq:ClientLibraryFolder with categories cq.widgets
3) Create file /apps/groupsuserscombo/groupsuserscombo/clientlib/combo.js and add the following code. Here we are creating a combo extension and registering it as xtype stepcombo
var MyClientLib = MyClientLib || {}; MyClientLib.StepCombo = CQ.Ext.extend(CQ.Ext.form.ComboBox, { constructor: function(config){ config = config || {}; config.store = new CQ.Ext.data.Store({ proxy: new CQ.Ext.data.HttpProxy({ "autoLoad":false, url: "/bin/mycomponents/groupsusers", method: 'GET' }), reader: new CQ.Ext.data.JsonReader({ root: 'data', fields: [ {name: 'id', mapping: 'id'}, {name: 'text', mapping: 'text'}, {name: 'group', mapping: 'group'} ] }) }); config.mode = "remote"; config.triggerAction = "all"; config.valueField = 'id'; config.displayField = 'text'; config.tpl ='<tpl for=".">' + '<tpl if="!group">' + '<div class="x-combo-list-item" style="margin-left: 20px">{text}</div>' + '</tpl>' + '<tpl if="group == \'y\'">' + '<div class="x-combo-list-item" ><b>{text}</b></div>' + '</tpl>' + '</tpl>'; MyClientLib.StepCombo.superclass.constructor.call(this, config); }, initComponent: function () { MyClientLib.StepCombo.superclass.initComponent.call(this); var resizeFn = function(combo){ var size = combo.getSize(); size.width = 200; combo.setSize(size); }; this.on('loadcontent', resizeFn); } }); CQ.Ext.reg("stepcombo", MyClientLib.StepCombo);
combo.js
5) Create dialog /apps/groupsuserscombo/groupsuserscombo/dialog (of type cq:Dialog) with following properties
The xtype of /apps/groupsuserscombo/groupsuserscombo/dialog/items/items/tab1/items/user is set to stepcombo, we've created and registered above
<?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" title="Groups Users" xtype="dialog"> <items jcr:primaryType="cq:Widget" xtype="tabpanel"> <items jcr:primaryType="cq:WidgetCollection"> <tab1 jcr:primaryType="cq:Panel" layout="form" title="Add" xtype="panel"> <items jcr:primaryType="cq:WidgetCollection"> <user jcr:primaryType="cq:Widget" fieldLabel="Select User" name="./user" xtype="stepcombo"/> </items> </tab1> </items> </items> </jcr:root>
6) Add the following code in /apps/groupsuserscombo/groupsuserscombo/groupsuserscombo.jsp
<%@include file="/libs/foundation/global.jsp" %> <%@page session="false" %> Selected User : <%= ( properties.get("user") == null ) ? "None selected" : properties.get("user") %>
Nice post, good example - thanks!
ReplyDeleteThank you Nick
ReplyDeleteVery Good Post....
ReplyDeletethank you Chandra
Deleteautocomplete not working
ReplyDeletei add config.typeAhead but returns the first value
DeleteGood post, was really instructive, thanks
ReplyDelete