Tuesday, 27 September 2016

Integrate AEM With DOJO

Using DOJO as AEM Front end

There are some cases where AEM needs to be integrated with DOJO.

What is DOJO
DOJO is a Javascript framework used to develop interactive, responsive websites, and mainly used as a UI tool.

DOJO can be integrated with AEM through an interaction Layer. AEM exposes many of the methods to integrate with any third-party applications. This enables applications to work seamlessly when integrated. A JSON layer is used to integrate AEM with DOJO in this analysis.

DOJO with AEM - Arch view
The architectural diagram is shown below.



OSGi (Open Service Gateway Initiative) is the Java framework which runs on AEM and generates the JSON Layer. The generated JSON layer is used to interact with DOJO application.

Once the component is saved, OSGI runs in AEM which helps to generate a JSON. A Java service used to generate OSGI Bundles needs to be created to generate JSON from the authored component.
Any format of JSON can be generated as per DOJO requirement here. A sample JSON response is given below.

http://localhost:4502/content/home/_jcr_content/parsys/myothercomponent.data.json

Steps to integrate DOJO with AEM:


We need to move all DOJO Libraries to AEM.
DOJO related components and templates to be moved to respective folder of AEM.
The page level items are split into templates and components. Same DOJO structure needs to be replicated in AEM, where in page will be divided into header, body, footer components. Content section can hold any levels of other components. The page structure in AEM is shown below.


Any impact on AEM Project Maintenance with respect to DOJO
New versions of AEM are available so often. Upgrades can be done without much challenges since the front end is separated from backend.


--------------Similar Posts:----------
-------------------------------------------

Sunday, 21 December 2014

CQ/AEM node operations- add,delete,Recursively fetching the nodes



Delete a node

//Below code delete 'textRenderer' node if present under /content/projectname

private static final String TEXT_RENDERER_NODE_URL = "/content/projectname";
rendererNode = currentSession.getNode(TEXT_RENDERER_NODE_URL);
                                   
                                   
                                      if(rendererNode.hasNode("textRenderer"))
                                      {
                                          Node delNode = currentSession.getNode(TEXT_RENDERER_NODE_URL + "/textRenderer");
                                          delNode.remove();  
                                          currentSession.save();
                                      }
                               
Ensure to save the session after any node modification.

--------------Similar Posts:----------
-------------------------------------------

Add a node
//Below code adds 'textRenderer' node of type 'cq:Page' under /content/projectname

private static final String TEXT_RENDERER_NODE_URL = "/content/projectname";

Node fileNode = rendererNode.addNode("textRenderer", "cq:Page");
fileNode.addMixin("rep:AccessControllable");
currentSession.save();
                                   
                                   
                                    =======================================
                                   
Recursively fetching the nodes under CQ node

Below code recursively iterates over a root node and populate the node attributes.

// Node- the root node to be iterated
// Session - current session
 public static void visitRecursively(Node node, Session currentSession) {
                       
                                       
                    try{
                           
                       
                    // get all child nodes
                          NodeIterator list = node.getNodes();
                                
                          while(list.hasNext())   {
                          
                  // get child node
                            
                          Node childNode = list.nextNode();
                          // Verify child node for cqPage type
                          if((childNode.hasProperty("jcr:primaryType")) && (childNode.getProperty("jcr:primaryType").getValue().getString()).equals("cq:Page") ){
                           
                            Node jcrNode = childNode.getNode("jcr:content");
                             
                            // Iterate some of the page properties
                            String articleTitle="";String jcrDesc="";String jcrTitle="";String keywords="";
                            if(jcrNode.hasProperty("articleTitle")){
                               
                                articleTitle = jcrNode.getProperty("articleTitle").getString();
                                log.info("articleTitle--->"+articleTitle);
                            }
                            if(jcrNode.hasProperty("jcr:description")){
                               
                                jcrDesc = jcrNode.getProperty("jcr:description").getString();
                                log.info("jcr:description--->"+jcrDesc);
                            }
                            if(jcrNode.hasProperty("jcr:title")){
                               
                                jcrTitle = jcrNode.getProperty("jcr:title").getString();
                                log.info("jcr:title--->"+jcrTitle);
                            }
                            if(jcrNode.hasProperty("keywords")){
                               
                                keywords = jcrNode.getProperty("keywords").getString();
                                log.info("keywords--->"+keywords);
                            }
                            
                            String pagePropertiesString = "articleTitle--->"+articleTitle + "jcr:description--->"+jcrDesc+"jcr:title--->"+jcrTitle + "keywords--->"+keywords ;
                       
                       
                          log.info("Page Properties :---> Node "+ childNode.getName()+ "Properties : " + pagePropertiesString );
                         
                       
                          }
                            
                          visitRecursively(childNode,currentSession);
                          }
                          }
                          catch (RepositoryException rpe){
                              log.info("Exception in recursive listing:");
                          }
                                
                }

Creating file in CQ/AEM

Create a new file under jcr node

Some cases we need to create a text or xml file under jcr nodes in CQ. In below code we are creating a text file with current jcr node name under specified location

-------Similar Posts---------------
Mapping of requests in AEM
Interact With AEM
Run Modes
AEM
Apache Sling in AEM
-------------------------------------

private static final String TEXT_RENDERER_NODE_URL = "/content/projectname";
 try {

                                // writing to file in temp location
                                FileOutputStream fop = null;
                                File file = null;
                                File testFile = null;
                                StreamResult result = new StreamResult(new StringWriter());
                                try {

                                    Path filePath = Files.createTempFile("Test" , ".txt");
                                    file = filePath.toFile();
                                    fop = new FileOutputStream(file);
                                    // get the content in bytes
                                   
                                    byte[] contentInBytes = pagePropertiesString.getBytes();
                                    fop.write(contentInBytes);
                                    fop.flush();
                                    fop.close();
                                    testFile = file;
                                } catch (IOException ioExc) {
                                    log.error(
                                            "TestFile :: IOException: ",
                                            ioExc);
                                } finally {
                                    try {
                                        if (fop != null) {
                                            fop.close();
                                        }
                                    } catch (IOException ioExc) {
                                        log.error(
                                                "TestFile :: IOException while closing output stream :",
                                                ioExc);
                                    }
                                }
                                }catch (Exception exc) {
                                    log.error("TestFile Creation Failed :: Exception: " , exc);
                                }
                               
                               
                         //Create file under jcr node
                       
                          try{
                              FileInputStream fileInputStream = new FileInputStream(testFile);
                           
                            ValueFactory valueFactory = currentSession.getValueFactory();            
                            Binary contentValue;
                            contentValue = valueFactory.createBinary(fileInputStream);
                            Node textRendererNode = currentSession.getNode(TEXT_RENDERER_NODE_URL + "/textRenderer");
                            //We are creating a new text file based on current jcr node name after converting to lower case, '' to _
                            Node fileNode = textRendererNode.addNode(jcrTitle.replace(' ', '-').toLowerCase()+".txt", "nt:file");
                            Node actualNode = (childNode.getParent()).addNode(jcrTitle+".txt", "nt:file");
                            actualNode.addMixin("mix:referenceable");
                            fileNode.addMixin("mix:referenceable");
                            Node resNode = fileNode.addNode("jcr:content", "nt:resource");
                            resNode.setProperty("jcr:data", contentValue);
                            Calendar lastModified = Calendar.getInstance();
                            lastModified.setTimeInMillis(lastModified.getTimeInMillis());
                            resNode.setProperty("jcr:lastModified", lastModified);
                            currentSession.save();
                          }catch(RepositoryException rpe){
                            log.error("Exception in Text Renderer :"+rpe.getMessage());
                        } catch (FileNotFoundException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }  catch (Exception exc) {
                            // TODO Auto-generated catch block
                            exc.printStackTrace();
                        }

AEM/CQ Node change observer


Node Change Observer Code:
Some times we may have to observe changes in nodes.

Below code helps to observe any change in jcr nodes(Changes can be addition, deletion or modification). This helps us to observe and node for modification and process some tasks.

/** Class which observes an event at /jcr:system implementation
    *
    *
    */
  
    public class NodeObserver implements EventListener{
      
    private Logger log = LoggerFactory.getLogger(getClass());
      
        @Reference
        private SlingRepository repository;
      
        private Session session;
        private ObservationManager observationManager;
             
      
        protected void activate(ComponentContext context)  throws Exception {
            session = repository.loginAdministrative(null);
           // Listen for changes to our orders
            if (repository.getDescriptor(Repository.OPTION_OBSERVATION_SUPPORTED).equals("true")) {
                observationManager = session.getWorkspace().getObservationManager();
                //We are putting types of nodes to be observed under below string array: sample in next line
               // final String[] types = { "nt:unstructured","rep:system","rep:versionStorage","nt:frozenNode" };
               //I am just observing nt:unstructured, bcz that is my requirement
                final String[] types = { "nt:unstructured" };
                //I am observing changes under /jcr:system nodes
                final String path = "/jcr:system";
                observationManager.addEventListener(this, Event.NODE_ADDED, path, true, null, types, false);
                log.error("Observing node changes to {} nodes under {}", Arrays.asList(types), path);
            }
          
        }

        protected void deactivate(ComponentContext componentContext) throws RepositoryException {
  
            if (observationManager != null) {
                observationManager.removeEventListener(this);
            }
            if (session != null) {
                session.logout();
                session = null;
  
            }
        }
      

        public void onEvent(EventIterator itr) {
                      
            try {
                while (itr.hasNext()){
                  Node versionNode = null;
                
                  String eventPath = (itr.nextEvent()).getPath();
                  log.info("something has been added : {}", eventPath);
                
                  Node jcrContentNode = session.getNode(eventPath);
                
                  Node jcrParentNode =jcrContentNode.getParent();
                  //process nodes after checking conditions
                  if(eventPath.endsWith("jcr:content") && (null!= jcrParentNode) ){
                                  
                
                  String metaDataNodePath = eventPath + "/metadata";
                  Node metaDataNode = session.getNode(metaDataNodePath);
                  log.info("metaDataNode Created" + metaDataNode);
                
                  }
                //  break;

                }
               } catch(RepositoryException e){
               log.error("Error while processing events",e);
              }
          
        }
-------Similar Posts---------------
Mapping of requests in AEM
Interact With AEM
Run Modes
AEM
Apache Sling in AEM
-------------------------------------



Saturday, 27 September 2014

CQ Upgrade issues:

CQ Upgrade issues:

Some time the upgrade of one AEM version to other goes smooth. some times it may be quite complex. Even though we might have followed all upgrade pre-procedures like clearing custom code, stopping workflows etc, still people face some minor issues.


  • JS will not be loaded so that the comments, or similar buttons gets non clickable.
To get it work we need to refresh Java Script(JS) folders by clearing CQ level cache.

  • Custom usage of deprecated code may stop us from proceeding..
We might have used some deprecated APIs in latest CQ versions like event related(For E.g ProcessJob) or String vs StringArray. We need to re write those classes to get upgrade work with all existing features.

  • Launcher related workflows having issues.
Even though all workflows disabled before upgrade, we may have to redo our workflows to get it work in case of any custom launcher workflows.

  • Personalization not loading after CQ upgrade.

After upgrade some times the personalization does not load. One reason may be Java script does not initialized properly.


To get it work, go to your custom user store,

/apps/ourproject/clientcontext/components/userstore/dialog/items/items/tab1/items/properties/fieldConfig :
property optionsProvider: < refresh this item to get personalization loading(Using a java script refresh or alert)

Personalization Segments folder gets deleted after upgrade. So all created segments may go off after upgrade. So always take backup of segments before upgrade.

In some cases, Client context may go to default with all options enabled, like geo location, browser details, users location request enabled etc. Once done with upgrade you can go and revert it to its previous settings.

-------Similar Posts---------------
Mapping of requests in AEM
Interact With AEM
Run Modes
AEM
Apache Sling in AEM
-------------------------------------
  • Product limitations.
There are cases the behavior of AEM for particular feature changes. In such cases we may have to contact Adobe to get a proper resolution. If this is a product issue, Adobe gives patch to help licensed users.


Friday, 29 August 2014

Customize the client context for cq personalization

Customization of the Client Context:

Customized client context is used to re use the personalization according to our requirements. Usually people extend the 'Generic store property' to customize the personalization.

Once the customization is done the new folder structure will look as shown below in crx-de. Here 'my user store' is customized client context.

The Apps>projects folder



Here we will be personnalizing content based on information retrieved from an external CRM system.

Steps to personalise with external CRM:

    Session Store object: We need to retrieve and keep the data from external application to make it available for our extended client context and is done by method Session Store object. This is a JavaScript object created on the client side. It uses a back-end part to retrieve data from the CRM system which can be a servlet. This is done both on the author and publish instances.

     Context Store component: This is the way we display data in the Client Context.


1)  Session Store Creation:

The Session Store is a javascript object library available both on the author and publish instances. We dont use client context in publish so , we create two javascript libraries with the following categories(The kernel is used on both author and publish instances but the ui only on the author instance.)

kernel   - > personalization.stores.kernel
ui          - > personalization.stores.ui


Add below code in myuserstore.JS under kernel
// Create the session store called "myuserstore"
if (!CQ_Analytics.CustomStoreMgr ) {

    // Create the session store as a JSONStore
    CQ_Analytics.CustomStoreMgr = CQ_Analytics.JSONStore.registerNewInstance("myuserstore");

    CQ_Analytics.CustomStoreMgr.currentId = "";

    // Function to load the data for the current user
    CQ_Analytics.CustomStoreMgr.loadData = function() {

        var authorizableId = CQ_Analytics.ProfileDataMgr.getProperty("authorizableId");
     //invoke your servlet using below code
        var url = "/apps/myuserstore/components/loader.json";

        if ( (authorizableId !== CQ_Analytics.CustomStoreMgr.currentId) & CQ_Analytics.CustomStoreMgr.initialized ) {

            console.info("Loading CustomStoreMgr data");

            url = CQ_Analytics.Utils.addParameter(url, "authorizableId", authorizableId);

            try {

                var object = CQ.shared.HTTP.eval(url);
                if (object) { this.data = object; }

            } catch(error) {
                console.log("Error", error);
            }

            CQ_Analytics.CustomStoreMgr.currentId = authorizableId;

        }

    };

    CQ_Analytics.CCM.addListener("configloaded", function() {

        CQ_Analytics.ProfileDataMgr.addListener("update", function() {
            this.loadData();
            this.fireEvent("update");
        }, CQ_Analytics.CustomStoreMgr);

    }, CQ_Analytics.CustomStoreMgr);

    CQ_Analytics.CustomStoreMgr.addListener("initialize", function() {
        this.loadData();
    });

    CQ_Analytics.CustomStoreMgr.initialized = false;

}


Now create a java servlet which is used to generate a JSON file during run time, which gets invoked from url /apps/myuserstore/components/

And below code should create a JSON and place it in/bin/project/updateclientcontext.json


public class UpdateClientContextClass extends SlingSafeMethodsServlet {

private static class JSONWriter {
//use tidy kind of JSON creato if required

response.setContentType("application/json; charset=UTF-8");
response.getWriter().write(writer.toString());
}
}

Note: in above case we can use some webservice/DB as an external url and iterate its data to populate the JSON for specific user.


2) Context Store component Creation: Create a new CQ component in /apps/myuserstore/components called myuserstore with the properties :

    sling:resourceSuperType = cq/personalization/components/contextstores/genericstoreproperties
    componentGroup = Client Context

Add below code in myuserstore.js in the ui library

if (CQ_Analytics.CustomStoreMgr ) {

    // HTML template
    CQ_Analytics.CustomStoreMgr.template =
        "<input class='customstore-input' type='checkbox' id='customstore-input-%key%' name='%key%' value='%key%' %checked%>" +
        "<label for='customstore-input-%key%' class='%checkedClass%'>" +
        "<div class='toggle'><div class='green'></div><div class='red'></div></div>" +
        "%label%</label>";

    CQ_Analytics.CustomStoreMgr.templateRenderer = function(key, label, value) {

         var checkedString = ""; var checkedClass = "";
         if (value==="true") {
             checkedString = "checked='checked'";
             checkedClass  = "checked";
         }
         var template = CQ_Analytics.CustomStoreMgr.template;
         return template.replace(/%label%/g, label)
             .replace(/%key%/g, key)
             .replace(/%checked%/g, checkedString)
             .replace(/%checkedClass%/g, checkedClass);
     }

    CQ_Analytics.CustomStoreMgr.renderer = function(store, divId) {

        // first load data
        // CQ_Analytics.CustomStoreMgr.loadData();

        $CQ("#" + divId).children().remove();

        var name = CQ_Analytics.ProfileDataMgr.getProperty("formattedName");
        var templateRenderer = CQ_Analytics.CustomStoreMgr.templateRenderer;

        // Set title
        $CQ("#" + divId).addClass("cq-cc-customstore");
        var div = $CQ("<div>").html(name + " services");
        $CQ("#" + divId).append(div);

        var data = this.getJSON();

        if (data) {
            for (var i in data) {
                if (typeof data[i] === 'object') {
                    $CQ("#" + divId).append(templateRenderer(data[i].key,data[i].label,data[i].value));
                }
            }
        }

    }

    CQ_Analytics.CustomStoreMgr.setTraitValue = function(trait, newValue) {

        var data = CQ_Analytics.CustomStoreMgr.data;
        if (data) {
            data[trait + '/value'] = newValue;
        }
    };

}

Now when the user reloads the page, the client context with corresponding data will be populated


Here the kernel ui java script invokes our servlet using the servlet url to create the json file. This created JSON data will be getting populated to the client context. Client context will have its own .css files which can be created referring any of the Geometrix .css personalized items.

Use Cases: This customized application can be used to check user data once user logs in, and a personalized teaser can be displayed to relevant users.

-------Similar Posts---------------
Mapping of requests in AEM
Interact With AEM
Run Modes
AEM
Apache Sling in AEM
-------------------------------------

External References:


Sitecatalyst integration for personalization:




Create personalization components in AEM CQ

Personalization: Personalization is a technique to serve personalized content to each user. It can be based on user logged in by city, country
Here are the steps involved in implementing personalization.

Step1: Create the Segments.(Segments hold the rule to be checked while displaying a teaser on any page)

Go to Tootls > Segmentation

                                  


Double click on created segment and configure the test condition to be evaluated. For eg: We are going to display the teaser for ‘Male’ users.

For this, drag the ‘User properties’ from side kick.

                      



Edit the property to check the condition ‘gender=male’
                      


Now the segment will look as below.

                  



Step2:  Create Brand, Campaign Container (Brand holds campaigns, campaign holds teasers)


Once segment is created, Go to > Campaigns (http://localhost:4505/mcmadmin#/content/campaigns)

Create Brand selecting brand template,

           
          

Now create Campaigns by selecting campaign template

                 
   


Step3:  Create Teasers


Once campaign is created, go to lists view and create teasers.

                   

Add some text and image to teaser by editing it. Sample is shown below.


Configure the touch point properties of teaser by selecting the segment in ‘Advanced section’as shown below.
This is the linking point between segment and teaser.

               


Now the campaign page looks as below.



Step4:  Add Teasers to any page
Now go to the page where you want to display the teaser, drag and drop teaser
                      



Enter the configuration selecting campaign to be run for the page as shown below.

                  




Step5:  Test the teaser by setting client context. (Client Context is used to check/modify any user attributes during testing the personalization)



Now go to client context (CTRl+Alt+C) and select a ‘male’ user. You can see the teaser is displayed as shown below.
                 



-------Similar Posts---------------
Mapping of requests in AEM
Interact With AEM
Run Modes
AEM
Apache Sling in AEM
-------------------------------------