How to create a mobile site in CQ

Posted on Tuesday, April 2, 2013 By

Recently found some very nice detailed videos on how to create mobile sites in CQ. Thanks for the OP for the videos.

There are 4 videos in the series.


Of course the official Mobile page is located here


4:07 PM Permalink

Digital Marketing Webinar

Posted on Monday, February 11, 2013 By

Be sure to attend the Digital Marketing webinar that will discuss Adobe CQ dispatcher caching strategies. See the following link for more information:


To sign up for this webinar, click the following link:

2:46 PM Permalink

How to add custom namespace in CRX

Posted on Wednesday, November 21, 2012 By

There are times when custom namespace is needed in a system for organization and management purposes. Without registering the namespace with CRX, properties with custom namespace would not be accepted. In this blog post I will talk about two ways of registering a namespace in CRX.

To illustrate, let’s take a look at the behavior of CRX without registering any namespace. Let me go ahead and enter a property that has namespace in it:

Upon saving, I would get the following error:

Now let’s proceed with registering the namespace. There are two ways of doing this:

Register namespace via CRX Console

  1. Namespace can be added via Node Type Administration in CRX Console.
  2. In the Node Type Administration window, click on “Namespaces” which is located at far right of the toolbar.
  3.  At the bottom of the Namespaces window, click on “New”.
  4. Enter the URI and the Namespace mapping and click Ok. And you should see the namespace added:
  5. Voila! It’s that easy. And now you can add the property again with the registered namespace:


Register custom namespace via CND file

  1. Namespace can also be registered via a CND file. The CND file can be deployed with any CRX packages (install folder, or via the package manager).
  2. Once the package is installed on CRX, any namespaces in CND files found inside the package would be registered automatically.
  3. Here’s the content of the CND file:
  4. That’s it! It’s nothing more than a mapping=uri pair.
9:31 PM Permalink

How to prevent users from entering duplicate vanity URL’s

Posted on Monday, August 27, 2012 By

This is something which should come very handy at every CQ implementation. The requirement for vanity URLs is that you cant have two pages in CQ with the same vanity URL. In this blog I will try to go over an implementation which will prevent your content authors from entering duplicate vanity URL in the first place.

1. First thing you will need to do is override the OOTB page component dialog and the tab_basic node. To do this, please copy the following nodes

    • /libs/foundation/components/page/dialog
    • /libs/foundation/components/page/tab_basic

2. Paste these nodes as the child of your projects page component whose “sling:resourceSuperType” is “foundation/components/page”


3. For the dialog node, change the path to the basic tab to match the path of the “tab_basic” you pasted in your projects page component.
4. For the “tab_basic” node update the “tab_basic/items/vanity/items/vanityPath/fieldConfig” node to add the following two properties
  • vtype
  • vtypeText
5. Override  the following in your apps folder
  • /libs/cq/ui/widgets/js.txt
  • /libs/cq/ui/widgets/source/widgets
6. To your overridden widgets directory at “/apps/cq/ui/widgets/source/widgets”, add a file called duplicateVanityCheck.js
CQ.Ext.apply(CQ.Ext.form.VTypes, {
duplicateVanityCheck: function(v, f) {var dialog = f.findParentByType("dialog");
var dialogPath = dialog.path;
var cqresponse = CQ.HTTP.get("/apps/duplicateVanityCheck?vanityPath="+v+"&amp;pagePath="+dialogPath);
var json = eval(cqresponse);
var vanitypathsjson = json.responseText;
var JSONObj = JSON.parse(vanitypathsjson);
var jsonVanityPath = JSONObj.vanitypaths;
if (jsonVanityPath.length == 0) {
return true;
} else {
// check whether the path of the page where the vanity path is defined matches the dialog's path
// which means that the vanity path is legal
return false;
//alert( "Checking Duplicate" );


7. In your overridden js.txt file, add this line

  • widgets/duplicateVanityCheck.js


8. You also need to make sure that your foundation client lib has a dependency on “cq.widgets”.

9. Now, we have to create an OSGi bundle that will query the JCR for an entered vanity and return the JSON that will be used by duplicateVanityCheck.js. The code for this class will look something like below

 * @scr.component immediate="true" metatype="false"
 * @scr.service interface="javax.servlet.Servlet"
 * name="sling.servlet.methods" values.0="GET"
 * name="sling.servlet.paths" values.1="/apps/duplicateVanityCheck"
public class VanityDuplicateCheck extends SlingAllMethodsServlet{
    private static final Logger logger = LoggerFactory.getLogger(VanityDuplicateCheck.class);
    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
            Session session = request.getResourceResolver().adaptTo(Session.class);
            final String vanityPath = request.getParameter("vanityPath");
            final String pagePath = request.getParameter("pagePath");
  "vanity path parameter passed is {}", vanityPath);
  "page path parameter passed is {}", pagePath);
            try {
                QueryManager qm = session.getWorkspace().getQueryManager();
                String xpath = "//element(*)[sling:vanityPath='"+ vanityPath + "']";
      "xpath is {}", xpath);
                Query query = qm.createQuery(xpath, Query.XPATH);
      "Xpath Query Statement is {}", query.getStatement());
                QueryResult result = query.execute();
                NodeIterator nodes = result.getNodes();
      "result is ", result.getNodes().toString());
                TidyJSONWriter tidyJSONWriter = new TidyJSONWriter(response.getWriter());
                while (nodes.hasNext()) {
                    Node node = nodes.nextNode();
          "Node path is {}", node.getPath());
          "Page path is {}", pagePath);
                    if(node != null &amp;&amp; node.getPath().contains("/content"))
                        // check whether the path of the page where the vanity path is defined matches the dialog's path
                        // which means that the vanity path is legal.
                            //do not add that to the list
                  "Node path is {}", node.getPath());
                  "Page path is {}", pagePath);
                        } else {
            catch(RepositoryException re){
                logger.error( "Error in doGet", re );
        } catch (JSONException e) {
            logger.error( "Error in doGet", e );

8. At this point you should have everything you need to prevent content authors from entering duplicate vanity URL’s. If they do enter a URL that is already in use, it will fail the validation and they will see an error message similar to what you entered in the vtypeText property.


Enjoy..and as always, please leave comments/questions and I will try to answer them as soon as possible.

5:49 PM Permalink

CQ Selector driven results page

Posted on Sunday, August 19, 2012 By

In this article I would like to talk about CQ pages where content is based on the Sling selectors. Recently I have worked on a simple requirement for a customer project where a page would have a drop-down (or multiple) menu to filter certain types of content. In the demo below I have a simple set of Adobe products where I want to be able to filter by the product name and/or the product version. Let’s check out the demo first (focus on the selection and the URL changes):

Selector driven content demo

As you can see from the quick demo. Dropdown selection would trigger a URL redirection where content is filtered based on the sling selector. For example:

products.illustrator.all.html displays only Adobe Illustrator products, all versions.
products.illustrator.cs4.html displays only the Adobe Illustrator CS4 product.
products.photoshop.cs6.html displays only the Adobe Photoshop CS6 product.
…so on and so on…

And for demo purposes, we have created pages with a Product component with the following fields:

Now let’s get to see how the filtering works. Here I will provide code pertaining to the sling selector processing and query only.

Sling selector processing (Pagination also factored in):

    // page number should be request param not selector
    int pageNum = 0;
    if (request.getParameter("page") != null) {
            pageNum = new Integer(request.getParameter("page")).intValue();
        } catch(NumberFormatException e){}
    if(pageNum  0){
        // if product and version present
        if (selectors.length == 2) {
            selectedProduct = selectors[0].trim();
            selectedVersion = selectors[1].trim();
        } else if (selectors.length == 1) {
            if (selectors[0].trim().equalsIgnoreCase("cs4")) selectedVersion = "cs4";    
            else if (selectors[0].trim().equalsIgnoreCase("cs5")) selectedVersion = "cs5";    
            else if (selectors[0].trim().equalsIgnoreCase("cs6")) selectedVersion = "cs6";     
            else if (productsMap.containsKey((String) selectors[0].trim())) {
                selectedProduct = selectors[0].trim();
            } else {
                selectedProduct = "all";
                selectedVersion = "all";
        } else {
            selectedProduct = "all";
            selectedVersion = "all";

The above code would determine the product and the version selected by the user (since they are passed in the URL). And once we have the product, version determined, we can run the following query and display the results in tabular format just like in the demo:


    QueryBuilder builder = null;
    Map<String, Object> map = null;
    Query query = null;
    SearchResult result = null;
    map = new HashMap<String, Object>();
    map.put("path", "/content/demo/en/products");
    map.put("1_property.value","/apps/demo/templates/productspage"); // query only productspage
    map.put("2_property","jcr:content/par_main/product/sling:resourceType"); // query for pages with product component
    if (!selectedProduct.equals("all") && !selectedVersion.equals("all")) {
    } else if (!selectedProduct.equals("all")) {
    } else if (!selectedVersion.equals("all")) {
    builder = resource.getResourceResolver().adaptTo(QueryBuilder.class);
    query = builder.createQuery(PredicateGroup.create(map), currentNode.getSession());
    result = query.getResult();

And the above queries for pages that are using the product template, with a product component in it, and filter by either product category or the version if they are passed in.


10:32 PM Permalink