Author Archive

April 2, 2013

How to create a mobile site in CQ

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.

  1. http://www.youtube.com/watch?v=_1Q9MS7MBj0
  2. http://www.youtube.com/watch?v=Qd6zojTpYkk
  3. http://www.youtube.com/watch?v=kt50HicMIn0
  4. http://www.youtube.com/watch?v=lRuT6mDZqcw

Of course the official Mobile page is located here

Enjoy!

4:07 PM Permalink
August 27, 2012

How to prevent users from entering duplicate vanity URL’s

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+"&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"
     * @scr.property name="sling.servlet.methods" values.0="GET"
     * @scr.property name="sling.servlet.paths" values.1="/apps/duplicateVanityCheck"
     */
     
    public class VanityDuplicateCheck extends SlingAllMethodsServlet{
     
        private static final Logger logger = LoggerFactory.getLogger(VanityDuplicateCheck.class);
     
        @Override
        protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
     
            try{
                Session session = request.getResourceResolver().adaptTo(Session.class);
                final String vanityPath = request.getParameter("vanityPath");
                final String pagePath = request.getParameter("pagePath");
                logger.info("vanity path parameter passed is {}", vanityPath);
                logger.info("page path parameter passed is {}", pagePath);
                try {
                    QueryManager qm = session.getWorkspace().getQueryManager();
                    String xpath = "//element(*)[sling:vanityPath='"+ vanityPath + "']";
                    logger.info("xpath is {}", xpath);
     
                    Query query = qm.createQuery(xpath, Query.XPATH);
                    logger.info("Xpath Query Statement is {}", query.getStatement());
                    QueryResult result = query.execute();
                    NodeIterator nodes = result.getNodes();
                    logger.info("result is ", result.getNodes().toString());
     
                    TidyJSONWriter tidyJSONWriter = new TidyJSONWriter(response.getWriter());
     
                    tidyJSONWriter.object();
     
                    tidyJSONWriter.key("vanitypaths").array();
     
                    response.setContentType("text/html");
     
                    while (nodes.hasNext()) {
                        Node node = nodes.nextNode();
                        logger.info("Node path is {}", node.getPath());
                        logger.info("Page path is {}", pagePath);
                        if(node != null && 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.
                            if(node.getPath().equals(pagePath))
                            {
                                //do not add that to the list
                                logger.info("Node path is {}", node.getPath());
                                logger.info("Page path is {}", pagePath);
                            } else {
                                tidyJSONWriter.value(node.getPath());
                            }
                        }
                    }
     
                    tidyJSONWriter.endArray();
                    tidyJSONWriter.endObject();
                    response.setContentType("application/json");
                    response.setCharacterEncoding("UTF-8");
                }
                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
    June 6, 2012

    How to fix CRXDE performance issues

    Is CRXDE too slow or is not loading at all? Try these steps below to possibly fix the issue.

    Make sure the crxde:paths property doesn’t include anything that you dont want to load in CRXDE.

    • Open the Content Explorer and browse to /etc/crxde/profiles/default, on the right hand panel there will be the crxde:paths property, this defines what nodes CRXDE will try to load. Having nodes like /content will make it slow/unresponsive.

    Locate and open the CRXDE.ini file and assign more memory to CRXDE.

    • On OSX right click on CRXDE.app and select Show Package Contents.
    • Browse to Contents/MacOS
    • Open the CRXDE.ini file and change the values for Xms, Xmx and the MaxPermSize to suit your needs and what your system will support.

    Delete the .crxde folder

    • CRXDE created a hidden .crxde folder under the users home directory.
    • Delete that folder

    Start CRXDE from command line.

    • Open -a CRXDE –args -clean (This is for OSX only)

    Hope this helps. Please leave a comment/question and I will try to answer them as soon as I can.

    11:26 AM Permalink
    February 10, 2012

    How to add align options to the textimage component

    If you have ever looked at the textimage component on the geometrixx site and seen that it has a align option for the image.

    But, dont see that option when you added  the textimage component on your site.

    To add align options for the text and image component for your site, just like the geometrixx site, follow these steps.

    1. Locate the paragraph system where you will be adding the text and image component on your site, inside your site’s design folder.

    2. Inside the par node, create a node “textimage” of type “nt:unstructured”

    3. Inside the textimage node, create a node “cq:styles” of type “nt:unstructured”.

    4. Inside the cq:styles node, create a node “imagealign” of type “nt:unstructured”.

    5. Inside the imagealign node, you can create as many alignment nodes as you wish.

    • Create a node “image_left” of type “nt:unstructured” and create a property “text” of type String, with the value “Left”
    • Create a node “image_right” of type “nt:unstructured” and create a property “text” of type String, with the value “Right”

    6. You will have to handle the actual alignment of the images in your CSS file for divs “image_right” and “image_left” or whatever other values you add to the imagealign node.

     

    1:07 PM Permalink

    How to save nodes to a dynamic path when using scaffolding

    The out of the box scaffolding lets you choose a target path which where all the pages you create using the scaffolding will be stored.

    I recently ran into a use case where the client wanted to save the pages to a dynamic path based on the date on which the page was created. One can achieve the fore mentioned use case by doing something similar to what I will lay out in this blog post.

    1. Override the out of the box wcm/scaffolding path by creating the same path structure in the apps folder. The new structure should look like below.

    You can copy the contents of the folder in the apps directory from the libs directory.

    2. We will have to update the wcm/scaffolding/components/scaffolding/body.jsp file to add our custom code to change the save location.

    • Make sure the following classes are imported.
    • The code to get the current date

    • In the myForm.addButton method, update the out of the box code to change the formUrl to your dynamic value. Code below.

     

    That should be it! You are now saving pages created using a scaffolding in dynamic locations

    12:42 PM Permalink
    • Authors

    • Archives

    • Developer Resources