Saturday 5 September 2015

Render JSON Data Using Sightly & Custom MultiFieldPanel


Hi Guys,

I got the task to render the JSON Data which store in JCR via MultiFieldPanel (a custom xtype). Just sharing the solution with you. I usually get the values from the hierarchical nodes & then create the Map and render over the JSP using Custom Tags. Here I am going to demonstrate If your data is store in form of JSON format and nested in form of multiple entries, then how you can fetch and render that data using Sightly.

Below is the snapshot of dialog and how the data is getting store in JCR.



And Below is how the content store in JCR, Here it create the array of multiple JSON entries :





Here we can choose 2 ways to render the data over the html. First one is Java-Use api Second is Javascipt-Use api. Here I am demonstrating using Javascript API

1) Javascript-Use API :

Sightly use the server side Javascript in order to execute the logic in form of USE classes.
Below is the HTML code that render the markup over to browser.

<sly data-sly-test="${wcmmode.edit}">Country Information</sly>
<sly data-sly-use.countryInfo="${'practice/components/clientlibs/convertToJson.js' @ fieldName = 'countryinfo',fieldValue = ''}">
    <ul>
        <sly data-sly-list.countryDetails="${countryInfo.listJson}">
            <li> ${countryDetails.country}
                <ul>
                    <sly data-sly-use.stateInfo="${'/apps/sdsingtel/components/clientlibs/convertToJson.js' @ fieldName = '',fieldValue = countryDetails.states}">
                        <sly data-sly-list.innerlistJson="${stateInfo.listJson}">
                            <li> ${innerlistJson.state} </li>
                            <sly data-sly-use.cityInfo="${'/apps/sdsingtel/components/clientlibs/convertToJson.js' @ fieldName = '',fieldValue = innerlistJson.cities}">
                                <ul>
                                    <sly data-sly-list.citylistJson="${cityInfo.listJson}">
                                        <li>${citylistJson.city}</li>
                                    </sly>
                                </ul>
                            </sly>
                        </sly>
                </ul>
            </li>
            </sly>
    </ul>
    </sly>

Here we are invoking convertToJson file ans passing two parameter : fieldName and fieldValue. 

Here fieldName stands for name of the widget which I assume is the root from where out custom multi-field starts.

Here fieldValue stands for of the nested widget which is the root of next level from where out custom multi-field starts.

As the JSON we are rendering is nested so for one level i passed fieldName but after that its fieldValue.

Below is the JS code which takes the resource and return its corresponding JSON.


"use strict";

use(function () {
    var readJson = null;
    var listArr = [];
    if (this.fieldValue != '') {
        readJson = this.fieldValue;
        var count = 0;
        if (readJson != null && readJson != "") {
            readJson.forEach(function (entry) {
                var itemJson = JSON.parse(entry);
                listArr[count++] = itemJson;
            });
        }
    } else if (this.fieldName != '') {
        readJson = granite.resource.properties[this.fieldName];
        if (typeof readJson != "undefined" && readJson != null && readJson != "") {
            if (readJson.length > 0) {
                var count = 0;
                readJson.forEach(function (entry) {
                    var itemJson = JSON.parse(entry);
                    //    out.println(JSON.stringify(itemJson))
                    listArr[count++] = itemJson;
                });
            } else {
                listArr[0] = JSON.parse(readJson);
            }
        }
    }
    return {
        listJson: listArr
    }
});

Here is the snapshot how data is rendering via this approach.




Hope this will help.





Tuesday 26 May 2015

How to register Sling Servlet on the basis of Paths property



Hi,

We often create a Sling Servlet  and register that Servlet as per path property as below :



And we get the response 403 with error message after invoking the Servlet as like http://localhost:4502/aemcorner/demoservlet :


It generally happens when you Servlet path is not register under sling execution paths. OSGi provide the configuration Apache Sling Servlet/Script Resolver and Error Handler via Felix console to register Servlet path under Sling Execution paths.

Add the execution path as show below, After that CQSE will identify the Servlet Execution for that specific path.


            

Hope this will help and consider when you will register the Servlet for specific path.

Thanks

Sunday 29 March 2015

Dispatcher Setup on Apache2/Ubuntu14.04 in AEM6 / CQ5

Hi, 

In this blog i would like to demonstrate how to set up Dispatcher Module in Apache2 and Ubuntu14.04. We use the dispatcher for Load Balancing and to Maintain the Cache. I will strict this blog only for How to install Dispatcher module and enable the cache. There are lots of settings as per the use-case we may need to apply on the dispatcher. This blog will help you to create a road map for those.

Use-Case : Set up dispatcher in Ubuntu14.04 and Enable the Cache.

Step 1 : Download the dispatcher from Package Share as per OS you are using. In my case it is :


Step 2 : Ubuntu14.04 has the apache already installed. You can check via hitting http://localhost/. You will get the welcome screen of apache. If it doesn't show, try to troubleshoot or re-install it. By Default Apache install under /etc directory with name of apache2.

Step 3 : Extract the dispatcher module downloaded in Step 1. You will get one folder with the name of conf and one file with .so extension which is the dispatcher module. Set the full permission on them.

Step 4 : Move you dispatcher-apache2.4-4.1.9.so file to /usr/lib/apache2/modules location. Create folder conf under /etc/apache2 and move the file dispatcher.any to /etc/apache2/conf folder.

Step 5 : Now configure your apache2.conf file under /etc/apache2 directory.

a) At line 69 you will find #ServerRoot "/etc/apache2" change it to  

 ServerRoot "/etc/apache2"
 ServerName localhost:80 


b) At the end after line # vim: syntax=apache ts=4 sw=4 sts=4 sr noet add the below snippet :

##### Dispatcher Configuration ######

LoadModule dispatcher_module /usr/lib/apache2/modules/dispatcher-apache2.4-4.1.9.so

<IfModule disp_apache2.c>
    # location of the configuration file. eg: 'conf/dispatcher.any'
    DispatcherConfig conf/dispatcher.any
    # location of the dispatcher log file. eg: 'logs/dispatcher.log'
    DispatcherLog /var/log/apache2/dispatcher.log
    DispatcherLogLevel 3
    DispatcherNoServerHeader 0
    DispatcherDeclineRoot 0
    DispatcherUseProcessedURL 1
    #DispatcherPassError 0
    DispatcherPassError 1   
    SetHandler dispatcher-handler
</IfModule>
SetOutputFilter INCLUDES


c) Restart the apache service using sudo service apache2 restart command. Apache2 server will restart without warning/error means configuration is [OK].

Step 6 : Now configure the dispatcher.any file that you placed under conf folder as in step 4.

a) Change the configuration of rend01 as below :
 /rend01
        {
        # Hostname or IP of the render
        /hostname "localhost"
        # Port of the render
        /port "4503"
        # Connect timeout in milliseconds, 0 to wait indefinitely
        # /timeout "0"
        }


b) Change the stafilelevel, look for  #/statfileslevel "0" and change it to 

          /statfileslevel "1"


Statfile Level tells, dispatcher will invalidate the cache from which level of cached pages. You can look over the adobe docs for more information.

c) Last thing to make sure that you have directory /opt/communique/dispatcher/cache with full permissions. This is the location where cache file will be maintain. You can change it via /docroot "/opt/communique/dispatcher/cache"  element in dispatcher.any.

Step 7 : Start your Publish instance via port 4503. And configure the dispatcher flush agent.

Step 8 : Hit  the URL http://localhost/content/geometrixx-outdoors/en.html. If you are able to see the geometrixx page . It means your publish successfully connected with dispatcher.

Step 9 : Check the directory /opt/communique/dispatcher/cache , it must contains two folder /content and /etc. If these are not there check out the permission for this folder & give full permission.

Two more things to add :
sudo a2enmod include (to enable SSI)
Uncomment  /allowAuthorized "1" from dispatcher.any file

Step 10 : Enjoy Caching :)

Thanks !!!!


Saturday 28 March 2015

Newsletters-Email Campaigns in AEM6/CQ5


Email Marketing is one of the important aspect which enable the organizations to interact with their audience frequently for latest update, news etc. There are lot of Email Marketing softwares are available in the market. AEM also ships with the such functionality where AEM can send the the emails such as newsletters to list of users that exists in JCR.


Use-Case : Send the NewsLetters to the Subscribed Customers.


Here are the steps : 

1) First configure the Day CQ Mail Service via OSGi configuration.
2) Create Brand ---> Create Campaign(under brand) ----> Create Newsletter(under Campaign)


3) Create a Lead through Campaign Console, just mention the Mail and save.


4) Open the Newsletter and change the settings, Just fill From Name, From Address, Subject for now & OK.



5) Now to test the newsletter and to check whether email service properly configured or not. Press Ctrl+Alt+C and select the profile (the lead you created in step 3). Reload the Page.

6) Click on the Test button display on the editbar of newsletter.

7) A dialog will open, where you can add the recipient address and test. If successful, you will get the message as below.


 In case if issue comes then try to publish/replicate your newsletter and lead, also look for the mail configuration.

8) Newsletters are mean to send the list of users subscribed, So create a List from Campaign Console of AEM. This List is same as group in repository.

9) Click on the settings from step 6. And in bottom of dialog add the List (Default Recipient List) that you created in step 8. Ty to keep List name should name meaningful such as newsletters-xxxxxx.


10) Now create a custom component say : Subscribe me. This component will add the subscribed user under the group/list you created. Below snippet of code of jsp :


 <%@include file="../../../global.jsp" %>  
 <%@page session="false" %>  
 <div id="response">  
   <pre style="color: #ff0000"></pre>  
 </div>  
 <form id="subscribe">  
   <input type="text" name="subscribe_email"/></br></br>  
   <button type="submit">Subscribe Now</button>  
 </form>  
 <script>  
   (function ($) {  
     function processForm(e) {  
       $.ajax({  
         url: '/bin/subscriber',  
         dataType: 'text',  
         type: 'post',  
         data: $(this).serialize(),  
         success: function (data) {  
           console.log(data);  
           $('#response pre').html(data);  
         },  
         error: function (jqXhr, textStatus, errorThrown) {  
           console.log(errorThrown);  
         }  
       });  
       e.preventDefault();  
     }  
     $('#subscribe').submit(processForm);  
   })(jQuery);  
 </script>  

11) Create servlet to handle the request for subsciber :


 package com.aem.servlets;  
 import org.apache.commons.lang.RandomStringUtils;  
 import org.apache.felix.scr.annotations.Component;  
 import org.apache.felix.scr.annotations.Properties;  
 import org.apache.felix.scr.annotations.Property;  
 import org.apache.felix.scr.annotations.Service;  
 import org.apache.jackrabbit.api.security.user.Authorizable;  
 import org.apache.jackrabbit.api.security.user.Group;  
 import org.apache.jackrabbit.api.security.user.User;  
 import org.apache.jackrabbit.api.security.user.UserManager;  
 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.ResourceResolver;  
 import org.apache.sling.api.servlets.SlingAllMethodsServlet;  
 import org.slf4j.Logger;  
 import org.slf4j.LoggerFactory;  
 import javax.jcr.Session;  
 import javax.jcr.ValueFactory;  
 import java.util.Iterator;  
 import java.util.Map;  
 @Component(immediate = true, metatype = false, label = "Create Subscriber")  
 @Service  
 @Properties(value = {  
     @Property(name = "sling.servlet.methods", value = "POST"),  
     @Property(name = "sling.servlet.paths", value = "/bin/subscriber")  
 })  
 public class GenerateSubscriberServlet extends SlingAllMethodsServlet {  
   private static final Logger logger = LoggerFactory.getLogger(GenerateSubscriberServlet.class);  
   @Override  
   protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) {  
     ResourceResolver resourceResolver = request.getResourceResolver();  
     boolean flag = true;  
     String msgToPrint = "";  
     try {  
       Map<String, String> parameterMap = request.getParameterMap();  
       String email_id = "";  
       if (parameterMap.containsKey("subscribe_email") && resourceResolver != null) {  
         email_id = request.getParameter("subscribe_email");  
         UserManager userManager = resourceResolver.adaptTo(UserManager.class);  
         if (userManager != null) {  
 // Change Group Name as you given in step 9.  
           Group group = resourceResolver.resolve("/home/groups/n/newsletters").adaptTo(Group.class);  
           Iterator<Authorizable> authorizableIterator = group.getMembers();  
           while (authorizableIterator.hasNext()) {  
             if (authorizableIterator.next().getPrincipal().getName().equals(email_id)) {  
               msgToPrint = "User already Registered";  
               flag = false;  
               break;  
             }  
           }  
           if (flag) {  
             Session session = resourceResolver.adaptTo(Session.class);  
             ValueFactory valueFactory = session.getValueFactory();  
             User subscriberUser = userManager.createUser(email_id, RandomStringUtils.randomAlphanumeric(20).toUpperCase());  
             subscriberUser.setProperty("cq:authorizableCategory", valueFactory.createValue("mcm"));  
             subscriberUser.setProperty("profile/email", valueFactory.createValue(email_id));  
             group.addMember(subscriberUser);  
             session.save();  
             if (subscriberUser != null) {  
               msgToPrint = "User Registered Successfully";  
             }  
           }  
         }  
       }  
       response.getWriter().write(msgToPrint);  
     } catch (Exception ex) {  
       logger.error(ex.getMessage());  
     }  
   }  
 }  

12) Now build the codebase, Drop the component "Subscribe Me" from Side-Kick and subscribe the user as below :


 You will notify that you are registered. In case existing subscribed user come to re-register again, it will prompt :
Email Validation not so far done in this code, So Enter the valid email address.
13) You can view the member of List/Group created in step 9 via Campaign console & then List (in servlet i have hard-coaded the group name, you can use the OSGi configuration for that also).


 14) Now final steps are to send the email, Open the newsletter created.
15) Click on the send button, list of users populate as per the group/list. Complete the wizard.


 16) You will have newsletter in your Inbox.


Thanks !!!