Latest Posts


This is a follow up to my post : http://cq5tutorials.blogspot.in/2014/04/cq5-multifield-in-multifield.html .  Here's a plug and play solution built by chaining adobe's multifield xtype with a couple of open source widgets.

Problem : Adobe's multifiled solution doesn't work with composite values. It stores value as a String if only one value is configured and as a String[] if multiple values are configured. A common work around to overcome this is create a custom composite field that concatenates the individual fields of a composite field into a Single string with a delimiter ( http://cq.shishank.info/2011/12/19/multifield-with-custom-xtype/ and http://stackoverflow.com/questions/26290908/multifield-component-issue/26294172 ) . This workaround fails when you want to have varying number of composite fields with multifield inside the composite field. A common requirement while creating listing components such as mega menus and accordions. A lot of guys end up dynamically including parsys for every menu coulmn or accordion block. The ideal solution would be to have a multifield in multifield (can be a little confusing for the author , but can be dealt with  authoring guide and pointers in the dialog )

Solution :  Use three widgets to create the multifield in multifield effect.
1) Multicomposite -  xtype that stores  each multifield entry as a child node with the fields as properties. [Developed by Citytech inc]

2) Multifieldpanel - xtype that stores a multifield as an array of json objects where each field is a property of the json object. [Developed by ACS]

3) Multifield - the out of box multifield xtype.

The fix  :  multicomposite  ( outer multifield )
                 |-fieldConfigs
                         |- field A (lets say a textfield , for the menu's coulmn name)
                         |- field B (a pathfield to configure the link for the column name )
                         |- field C (multifield , the inner multifield to hold sub menu entries )
                                  |-fieldConfig - xtype : multifieldpanel
                                        |- field Ca (textfield to hold title)
                                        |- field Cb (pathfield for the link)

Dialog  JSON -

{
  • matchBaseNamefalse,
  • xtype"multicompositefield",
  • name"./menu",
  • hideLabeltrue,
  • jcr:primaryType"cq:Widget",
  • fieldDescription" click + to add menu column",
  • fieldConfigs:
    {
    • jcr:primaryType"cq:WidgetCollection",
    • name:
      {
      • width"500",
      • fieldLabel"Name",
      • xtype"textfield",
      • allowBalnktrue,
      • name"title",
      • fieldLabel"Link Title",
      • jcr:primaryType"cq:Widget",
      },
    • link:
      {
      • xtype"pathfield",
      • fieldLabel"Link",
      • name"link",
      • jcr:primaryType"cq:Widget"
      },
    • submenu:
      {
      • xtype"multifield",
      • title"submenu",
      • fieldLabel"Submenu",
      • name"submenu",
      • collapsibletrue,
      • jcr:primaryType"cq:Widget",
      • fieldDescription"Click + to add sub menu entry",
      • fieldConfig:
        {
        • xtype"multifieldpanel",
        • hideLabelfalse,
        • jcr:primaryType"cq:Widget",
        • items:
          {
          • jcr:primaryType"cq:WidgetCollection",
          • title:
            {
            • key"title",
            • xtype"textfield",
            • fieldLabel"Link Title",
            • jcr:primaryType"cq:Widget"
            },
          • link:
            {
            • key"link",
            • fieldLabel"Link",
            • xtype"pathfield",
            • jcr:primaryType"cq:Widget"
            }
          }
        }
      }
    }
}       

The data is stored in the following format :

{
  • jcr:primaryType"nt:unstructured",
  • menu:
    {
    • jcr:primaryType"nt:unstructured",
    • item_1:
      {
      • submenu:
        [
        • "{"title":"Link 1","link":"http://cq5tutorials.blogspot.in"}",
        • "{"title":"Link 2","link":"http://cq5tutorials.blogspot.in"}"
        ],
      • title"Main Menu Item 1",
      • jcr:primaryType"nt:unstructured",
      },
    • item_2:
      {
      • submenu:
        [
        • "{"title":"Sub menu 1","link":"http://cq5tutorials.blogspot.in"}",
        • "{"title":"Sub menu 2","link":"http://cq5tutorials.blogspot.in"}"
        ],
      • title"Main Menu item 2",
      • jcr:primaryType"nt:unstructured",
      }
    }
}

Notice that the inner multifield (submenu) is stored as an array of json strings. There is a utility in ACS code to read entries from this :  http://adobe-consulting-services.github.io/acs-aem-commons/features/widgets.html


Few Drawbacks :

1) This xtype works only when it's on the first tab (or the tab that's active on dialog load ) of the dialog . Multifield dooesn't handle all the extjs events like the rest of the widgets as a result the inner panels don't get rendered if the widget is not visible on load. Should not cause an issue as long as its on the first tab of the dialog

2) The xtypes involved do not fire all events like the basic widgets , ex : beforeloadcontent . This can be a problem when you want to write few custom listeners .  If you know your way around extjs , this can still be accomplished by leveraging other events like the beforeadd.







Finally a plugin for making our life's easier. Find more details at http://docs.adobe.com/content/docs/en/dev-tools/aem-eclipse.html haven't had the time to take it for a spin yet. Will update this post once I do
CQ5 (AEM) is a brilliant content management system . It's different from the rest of the herd because of the framework it's built on, i.e Sling and its repository system (JCR) .

Websites have a tree structure, but in traditional CMS pages are just rows in an RDBMS table and child pages are not really child pages. They just have a special entry to tell which is the parent page. Not in cq5, pages are nodes and child pages are nodes under the parent page, this makes so much sense as a developer to visualize a website. This also leads to url's that make sense, no ugly urls with post id's or content id's as parameters. All this is possible because of Sling.

Sling is a web frame work built to be used with JCR as data store. Sling is built on REST principles. It's the JCR and REST that make Sling a match made in heaven for content management systems.

So whats REST?? I won't enlist the REST principles, there are too many articles out there on REST. From a content management point it means every atomic object of data on your website is a resource, something that can be rendered on it's own. What this means is every page is a resource. Not just the page, the images in it are resources too. So is the slider, navigation and the page body. You get to control how fine the resources are, i.e the title could be a resource or it can be  a part of the page resource. The bottom line is data has to be a resource.

The url tells what resource your looking for. The url can select a page or the slider in the page. The reason you get to have such urls is because of JCR (Java content repository), unlike a traditional RDBMS there are no tables and rows. The JCR is a tree, similar to your file system. They have nodes starting from the root. So your page is node. Under it lie the child pages as child nodes. Apart from the child pages they also have a content node under which the resources of the page (the sliders, page body) are stored as nodes.[You can create a similar effect with traditional RDBMS tables too using apis like web api of asp.net with entity frame work or Jersey api in java. It isn't still as clear as JCR ]

Now that the url uniquely identifies a resource, next rule is that a GET request to the resource returns a representation of the resource (i.e GET to an image returns an image and its rendered by the browser, GET to a page node returns html to be rendered by the browser). You cannot update the values or delete it in a GET call. Now the page node only has data not the HTML. How does a GET request to page return HTML?? Actually there is no hard and fast rule telling a page should return HTML on a GET. It can return JSON or XML or anything of your choice. What happens  on GET to an URL is, the url identifies the resource then the data in the resource is processed by the system (a script renders the node) and returns the value. The GET is not returning the resource , it returning a representation of the resource. So who decides which script renders the node ?? In sling the ResourceResolver does, any GET call to a resource is taken over by it and it uses a script to render the resource. A resource can be rendered by multiple scripts, the script is a resource too. So a resource tells which resource will render  it. In sling its done by sling:resourceType property. This property identifies who will render the resource. There can be multiple scripts for a resourceType, one among them is chosen based on the rules of resource resolution.

[It is this feature that is leveraged to create templates in cq5, you tell which script renders a template. Any page created using the template gets the sling:resourceType value you defined for the template added to it on creation. It is for this reason that when you change resourceType for a template , the pages already created out of it before the change don't reflect it.]

A post to a resource updates the resource(even create one if there is no resource at the url). The parameters in the post request are stored as name value pairs on the node.  How does this happen ? Any post request is intercepted by Sling's default POST servlet and it cycles through the parameters and stores them. You will need to have appropriate rights to do a POST to a resource else the request gets denied.

[This is the backbone behind content authoring in sling, the dialogs you see in the authoring interface are forms. On hitting OK they do a post to the node instantly updating the values.(keep your firebug console ON and edit a dialog) ] So this behavior of GET and POST in Sling makes it perfect for content management.

Now you know how Sling is leveraged in CQ5 let's see how it's all put together. Every component you create is a resource. The data needed to display it is  stored as name value pairs on the node. So if you made a GET request all the way until the components node you will set it just returns the output of your component's JSP.

So why is a GET to a page returning output of all the components on it?? The answer is cq:include and Parsys. You include components using cq:include or parsys. So when a request comes along to the page the cq:include includes response from the components within the response of the page (the response of the node at the path you mentioned in the directive is retrieved and the node [resource] is rendered by the resourceType you mentioned in the directive ). Parsys is cq included too. So when you drag and drop a component on it , it actually creates a node for the component under it. when a request for the page comes along, the script of the parsys is invoked which in turn includes responses of nodes under it. A huge servlet chaining process underneath but works like a charm for content management.

Day software (creators of cq5) designed the Sling framework specially for CQ5 and then made it open source by donating it to Apache. Sling is now an incubator project of Apache.

CRXDE lite is a nice little IDE for creating UI stuff like templates and dialogs. The browser based interface cannot match up to IDE's like Eclipse. Also writing Java code (Bundles) in crxde lite  is a major pain. Adobe has a solution for this , they have defined a Maven archetype that creates a maven project independent of IDE's. With maven plugins for Apache Sling and Apache Felix writing code is a lot simpler.

The sample maven command to create a cq5 project is

mvn archetype:generate -DarchetypeRepository=http://repo.adobe.com/nexus/content/groups/public/  -DarchetypeGroupId=com.day.jcr.vault -DarchetypeArtifactId=multimodule-content-package-archetype -DarchetypeVersion=1.0.2 -DgroupId=cq5tutorials  -DartifactId=myCqMavenproject -Dversion=1.0-SNAPSHOT -Dpackage=com.blogspot.cq5tutorials  -DappsFolderName=myCqMavenproject  -DartifactName="my Cq Maven project"   -DcqVersion="5.6.1"  -DpackageGroup="com.blogspot.cq5tutorials"



Run this MVN command and import it as a maven project into eclipse.[ If you get Mojo failure exception when running the command, try deleting the jar mentioned in error message from M2 repository and re running the command]. After the command is successfully executed import the project into Eclipse. You'll see three folders viz, the main folder (same name as your artifactId), content folder and the bundle folder.

The project is divided into two modules

1) Content : This will have all the UI elements like templates, template renders, components , client libraries and so on. The apps folder of your project will be in this module. You can also include etc and other folders. The content folder maps with jcr:root of the repository hence you can use vault tool to import and export data from eclipse to your repository. Since the repository is made of nodes this is translated into xml files and folders to convert them to native file system in eclipse.

2)Bundle: Since Cq5 uses OSGI architecture all your java classes will be written as OSGI components and services , these are then packaged in a bundle which export the functionality to be used in the UI elements. All your Servlets, filters , schedulers, taglibs will be written inside this folder.

Creating nodes of a particular type creates a dialog in CQ5. How are they converted into java script?? What is the relationship between the node and the script??

CQ5 uses Extjs for its interface. So you can use most of the stuff that comes with extjs. The framework is customized a bit and instead of Ext class you have to use CQ.Ext a subset of extjs functionality.

The dialogs we see in cq5 interface are actually forms. On clicking OK the values in forms are submitted to the node using POST. Slings default post servlet stores values that have been posted if the request has required permissions.So when you create dialogs you are actually building fancy forms that looks good and appears in a pop up.

Design dialogs are a very useful feature of cq5. It allows you to store configurations that can be accessed across pages. To be more specific the data is stored at a common place for each template. Changes through a design dialog will get reflected across all pages created using the same template.

A design can be used to store CSS and lot of other stuff .To create a design go to miscadmin from site admin(click on the gear icon in the top tool bar). Under the design folder create a new page out of the design template (like u create a normal page). That is  all it takes to create a  design. To assign your site a design on the page follow these steps.



Multifield is a very important widget in CQ5. The biggest utility of it being the author can add many as he wants, even reorder with a very simple interface.This makes it an ideal choice for scenarios where there is no fixed count for how many records a component can have(no of items in a list for example). The multifield that comes out of the box is limited by the fact that it can handle a single xtype at a given time. This problem how ever is over come by creating a custom composite xtype that can hold multiple xtypes within it.The fields are converted into a string separated by a delimiter pattern. With this hack you can use the multifield that comes out of the box for a wider range of applications as it'd be still be dealing with a single xtype. You can check out these links for how a custom multifield can be created

http://cq.shishank.info/2011/12/19/multifield-with-custom-xtype/
http://forums.adobe.com/message/4332833

Something that would be even more better would be having a custom multifield in a multifield.My friend Hariprasad Marigowda came up with this brilliant solution.The approach involved in pulling off this is explained below: