Tuesday, 24 November 2015

Personalize Content With Adobe AEM

Personalized content is a growing trend, and lately we've put a lot of effort in leveraging CQ5's built-in tools, as well as developing custom components that can consume rich marketing data from third parties. These components—out-of-the box or custom—are a real complement to an enterprise CMS when they are well used and well integrated. In addition, they can be especially useful to content authors and marketers when used to visualize and simulate different segmented experiences.
Let's say I have an online outdoor clothing store built on CQ5 and I want highlight the sunglasses section of my catalog. I have great sunglasses but I suspect this product section is too deep in the navigation, so visitors almost never find it. My plan is to create a personalized content that links to the sunglasses section on my homepage that will display if the visitor comes to the site via a specific search for sunglasses in a search engine.  

User Segmentation and Personalization in AEM

Personalization centers on providing the user with a tailor-made environment displaying dynamic content that is selected according to their specific needs; be this on the basis of predefined profiles, user selection, or interactive user behavior.

 Read More

Sunday, 22 November 2015

Customizing the Adobe Experience Manager Sidekick to improve the activate process

You can customize the AEM sidekick to add functionality tio meet your business requirements. For example, typically, once you activate an AEM page in author, in order to view it in publish, you typically copy its URL, paste it in new tab and change the host-name and port name to see the results. To simplify this use case, you can add a custom button to the AEM sidekick that performs these actions with 1 click.

The new sidekick button automatically give you the link to the page in publish instance once you activate it. That is, with a single click, you can see your current page in publish instance.

Friday, 20 November 2015

Integrate AEM with YouTube

You can use youtube API to add video in your webpages. Below is the sample code snippet for embedding youtube videos into your webpages.

Note:  In order to make sure your javascript works, add "enablejsapi=1" parameter to the src atttribute. Please check below HTML code.

HTML Code

<h1>In page youtube video player API</h1>
<br/>
<h2><span id="YTvideoLoadTime"></span> </h2>

<h3>Check console logs for player events</h3>
<iframe id='player' width="560" height="315" 
src="https://www.youtube.com/embed/axCKOu3YjmM?list=PLkBe8kbE_7-xmNihqYr19TlYGH7jUhQkY&enablejsapi=1" frameborder="5" allowfullscreen></iframe>



Javascript:

//YOUTUBE API
var player;
var timerStart, timetaken;

jQuery(document).ready(function ($) {
    console.log("ready!");
    loadPlayer();
});

function getArtistId() {
    return 'axCKOu3YjmM';
}

function loadPlayer() {
   console.log("Youtube IFRAME api");
    timerStart = Date.now();
    if (typeof (YT) == 'undefined' || typeof (YT.Player) == 'undefined') {
        var tag = document.createElement('script');        
        tag.src = "https://www.youtube.com/iframe_api";
        var firstScriptTag = document.getElementsByTagName('script')[0];
        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

        window.onYouTubePlayerAPIReady = function () {
           // console.log("YT undefined calling onYouTubePlayer ");
            onYouTubePlayer();
        };

    } else {
       // console.log("calling onYouTubePlayer ");
        onYouTubePlayer();

    }
}


function onYouTubePlayer() {
    player = new YT.Player('player', {
        // height: '490',
        //width: '880',
       // videoId: getArtistId(),
       // playlist: 'PLkBe8kbE_7-xmNihqYr19TlYGH7jUhQkY',
        playerVars: {
            controls: 1,
            showinfo: 0,
            rel: 0,
            showsearch: 0,
            iv_load_policy: 3
        },
        events: {
            'onStateChange': onPlayerStateChange,
                'onError': catchError,
                'onReady': onPlayerReady
        }
    });
}

var done = false;

function onPlayerStateChange(event) {
    console.log("onPlayerStateChange: " + event.data);
    if (event.data == YT.PlayerState.PLAYING ) {
        console.log("playing");
        done = true;
    } else if (event.data == YT.PlayerState.ENDED) {
        console.log("ended");
        location.reload();
    }
    else if (event.data == YT.PlayerState.PAUSED) {
        console.log("paused");
    }
}

function onPlayerReady(event) {
    //console.log("onPlayerReady");
    timetaken = Date.now() - timerStart;
    $('#YTvideoLoadTime').html("Youtube Video player has loaded in : " + timetaken / 1000 + " seconds");

}

function catchError(event) {
    if (event.data == 100) console.log("ERROR");
}

function stopVideo() {
    console.log("stopVideo");
    player.stopVideo();
}



Thursday, 19 November 2015

Load Brightcove player dynamically in AEM

In this JavaScript code you will perform the following tasks:
  • Define the variables.
  • Create the player code.
  • Inject it into the HTML page.
  • Create the appropriate script block and assign the appropriate source.
  • Append the dynamically create script to the page.
  • On the load of the script, which happens automatically with the append, call a function to instantiate the player and play the video.
Here is the sample code to load brightcove player dynamically when clicking on a button.

<!doctype html>
<html>

<head>
  <meta charset="UTF-8">
  <title>Load Player Dynamically</title>
  <!-- Page styles -->
  <style>
    .video-js {
      width: 640px;
      height: 360px;
    }
  </style>
</head>

<body>
  <div id="placeHolder">
    <button onclick="addPlayer()">Add Player</button>
  </div>
  <!-- custom script -->
  <script type="text/JavaScript">
    var myPlayer,
      playerHTML,
      playerData = {
        'accountId': '1752604059001',
        'playerId': '04ee9f58-9d08-43ea-803f-abda86776db2',
        'videoId': '3851380732001'
      };

    function addPlayer() {
      // dynamically build the player video element
      playerHTML = '<video id=\"myPlayer\" data-video-id=\"' + playerData.videoId + '\"  data-account=\"' + playerData.accountId + '\" data-player=\"' + playerData.playerId + '\" data-embed=\"default\" class=\"video-js\" controls></video>';
      // inject the player code into the DOM
      document.getElementById('placeHolder').innerHTML = playerHTML;
      // add and execute the player script tag
      var s = document.createElement('script');
      s.src = "//players.brightcove.net/" + playerData.accountId + "/" + playerData.playerId + "_default/index.min.js";
      document.body.appendChild(s);
      s.onload = callback;
    }

    function callback() {
      myPlayer = videojs('myPlayer');
      myPlayer.play();
    }
  </script>
</body>

</html>

Check Demo

Reading AEM OSGi Configuration Values

You can define OSGi configuration values by using CRXDE lite and dynamically read these values from within an OSGi service. By defining configuration values, you can define values used by an OSGi service and use these values while the service is running. Unlike hard-coding values in an AEM OSGi service, defining values in CRXDE lite lets you modify configuration values without re-compiling and re-deploying the OSGi bundle.



OSGi configuration values defined at /apps/system/config


As shown in the previous illustration, you can define OSGi configuration values in CRXDE lite. To define configuration values, you define a node of type sling:OsgiConfig. Then you define properties which are the configuration values. 

Configuration values that are defined as properties of a sling:OsgiConfig node


This development article guides you through creating an OSGi bundle that reads OSGi configuration values that are defined in CRXDE lite.

Creating a Sightly component that uses the AEM QueryBuilder API

You can create an Adobe Experience Manager (AEM) 6 Sightly  component that can be used within the AEM Touch UI view. Furthermore, you can use Sightly and the QueryBuilder API to develop the AEM component that displays AEM JCR data.

Sightly is an AEM  template language that can be used to replace the use of JSP when developing an AEM component. Sightly helps you to separate your design from your application logic.  In this use case, the Sightly logic is located within an HTML file and displays the result set. The Java part of the Sightly component uses the QueryBuilder API.


To read this AEM community article, click https://helpx.adobe.com/experience-manager/using/sightly_querybuilder.html.


Creating an System User in AEM

AEM System User:

Till AEM 6 we have the liberty to use any user as service user, for invoking and executing any service. But from AEM 6.1 there was a slight change on how to define the ServiceUserMapping and how the service user or system user has to be created.

 If we try to assign any arbitrary user as service user in AEM 6.1 we would face below error : org.apache.sling.api.resource.LoginException: Cannot derive user name for bundle ch.inside.cqblog-bundle [452] and sub service readService Note:- From AEM 6.1 service users can only be mapped to system users (jcr:primaryType = rep:SystemUser).


In AEM 6.1, you must create an AEM System User to successfully get a session using code such as:

Why System User ??

 Use of admin session and admin resource resolver through ResourceresolverFactory is now deprecated, that’s why from AEM 6.1 Adobe forces developers to create system users and map them to Service User Mapper in Felix Console. 

  •  Prevent excessive use of administrative JCR Sessions and ResourceResolvers. 
  •  Allow services access to ResourceResolvers and JCR Sessions without requiring to hard-code or configure passwords.
  •  Allow services to use service users and/or system user which are specially configured for service level access.





import javax.jcr.Session;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
....
@Reference
private ResourceResolverFactory factory;
private ResourceResolver resourceResolver;
private static Session adminSession;
...
...
Map<String, Object> param = new HashMap<String, Object>();        
    param.put(ResourceResolverFactory.SUBSERVICE, "writeService");
try {
  resourceResolver = factory.getServiceResourceResolver(param);            
  adminSession = resourceResolver.adaptTo(Session.class);  

  ResourceResolver resourceResolver=null;
  resourceResolver = resolverFactory.getServiceResourceResolver(param);
  Resource pageResource = resourceResolver.getResource("/etc/cloudservices/salesforce/kishore/jcr:content");
  Node configNode = pageResource.adaptTo(Node.class);
  configNode.setProperty("accesstoken", client.getAccessToken());
  adminSession.save();
...
} catch (LoginException e) {
...
}


This code will not work in AEM 6.1 using a standard user.

If we try to assign any arbitrary user as service user in AEM 6.1 we would face below error : org.apache.sling.api.resource.LoginException: Cannot derive user name for bundle com.kishore.aem-bundle [452] and sub service readService Note:- From AEM 6.1 service users can only be mapped to system users (jcr:primaryType = rep:SystemUser).


To create a system user, perform these tasks:

  1. Open http://localhost:4502/crx/explorer/index.jsp
  2. Login as admin
  3. Click User Administration
  4. Click Create System User
  5. Set the UserId
  6. Click Save

Once created, you can extend permissions like a normal user using the AEM ACL functionality. 

Sometimes we face below error, so I tried the other option to use the system user.
Error:Missing permission to create intermediate authorizable folders.

Other option for using system user to set properties to node.
Use in-built system user in OSGI configuration. In OSGI select Apache Sling Service User Mapper Service
Add new entry com.kishore.aem:writeService=oauthservice

  • com.kishore.aem is bundle name
  • writeService is sling subservice name
  • oauthservice is systemuser available in AEM



Note: If we try to set properties under /etc, read&write permission to be set for oauthservice system user else resourceResolver.getResource will return null pointer exception.





Click here to see how to create a system user in AEM using runmode config

Using the Java Query Object Model within Adobe Experience Manager

You can create an AEM application that searches the CQ repository for JCR data and displays results to the end user. For example, you can search CQ pages under a specific repository node (for example, nodes under /content) and look for a specific search term. All content that satisfy the search criteria are included in the search results. To search the Adobe CQ repository, you use the JCR Query Object Model (JQOM) API. For information about the API, see Interface QueryObjectModel.

JQOM is an AEM query language that is like ‘prepared statements’ and SQL2 is like ‘statements’ in JDBC queries.  For example, a use case may require to retrieve all JCR nodes from ‘Geometrixx’ which has the property pageTitle.

QueryObjectModelFactory qf = currentNode.getSession().getWorkspace().getQueryManager().getQOMFactory();
Selector selector = qf.selector("cq:PageContent", "s"); 
Constraint constriant = qf.descendantNode("s", "/content/geometrixx");
constriant = qf.and(constriant, qf.propertyExistence("s", "pageTitle"));
QueryObjectModel qm = qf.createQuery(selector, constriant, null, null);

QueryObjectModelFactory gets the instance of the JCR Object Model.  The Selector is used to set the type of node that the query needs to look at. Constraint is used to add all the constraints which is like where condition into the query model. Finally a query is created with the selector and constraint that captures the response as aQueryObjectModel.


To read this development article, click https://helpx.adobe.com/experience-manager/using/jqom.html.

Wednesday, 18 November 2015

AEM Touch UI issues

When working with AEM touch ui we often come across some issues. Below are some of the common issues which we face when using touch ui.

<cq:include path=’text’ resourceType=’foundation/components/text’/> 
In Sightly, you can do a similar one-liner:

<div data-sly-resource=${‘text’ @resourceType=’foundation/components/text’}”></div>
However, there are a few things to be aware of when embedding components, especially when using Touch UI.
I will highlight three issues that I have come across when embedding components using Sightly and Touch UI, and explain the symptom, the cause, and give a solution for each.
ISSUE 1: Edit config does not refresh page after component edit
The Symptom 
A component set up to refresh the page after edit does not refresh the page in Touch UI. In the web console there is a JavaScript error: Uncaught TypeError: Cannot read property ‘componentConfig’ of undefined.
The Cause
The component was in the .hidden component group. Touch UI has a list of editables, or components. This list does not include components in the .hidden group. When the component is edited, JavaScript runs that looks for the component in the list of editables. Because it is not in the list, the JavaScript throws an error and the editconfig afteredit action does not happen.
Uncaught TypeError: Cannot read property 'componentConfig' of undefined
EditConfigCause
The Solution
Put the component in a group other than .hidden. This will make the component available to be added to the list of components that can be dropped into a parsys, and it would be up to the content team to not include these components in the design configuration. To help keep this clear you could put the components into a group named Hidden.
Note: I am continuing to look for a good solution to handle components in the .hidden group, since the parsys component is in that group and in my experience is the most common component to be embedded. So far I have not yet found a way to deal with this. It appears in Touch UI there is an update method that runs after closing the dialog, and that supersedes any custom afteredit event handlers attached to the component.
ISSUE 2: Dialog does not open in Touch UI
The Symptom
Embedded component’s dialog box doesn’t open in Touch UI. In the web console there is a 400 (Bad Request) error.
The Cause
Embedded components do not create their content node until after the dialog has sent a POST to the component’s content path. The problem here is that in Touch UI the component looks for the dialog to open based on the content path. Since the content path doesn’t exist the dialog fails to open with an error.
DialogDoesNotOpenCause
The Solution
Make sure the content node for the component exists. There are a couple ways of doing this.
  1. Add the component to the jcr:content of the template used to create the page.
  2. Add a script to run just before the include of the component that creates the node if it doesn’t already exist. (Below is some example code For solution 2)
JS Use API
In a file named EmbeddedComponent.js
"use strict";
/**
 * Creates a component node with name, if node does not yet exist.
 */
use([], function () {
    var newResourceName = this.name;
    var newResourceType = this.type;
    var resourceResolver = resource.getResourceResolver();
    var newNodePath = resource.path + "/" + newResourceName;
    var existingComponentResource = resourceResolver.getResource(newNodePath);
    if(existingComponentResource == null){
        var properties = {"jcr:primaryType":"nt:unstructured",
            "sling:resourceType":newResourceType};
        resourceResolver.create(resource, newResourceName, properties);
        resourceResolver.commit();
    }
});
Sightly script


<div data-sly-use="${EmbeddedComponent.js' @ name='primary', type='foundation/components/parsys'/> 
Java Use API

package com.aempodcast.example.components.utils;
import com.adobe.cq.sightly.WCMUse;
import java.util.HashMap;
import java.util.Map;
import org.apache.sling.api.resource.Resource;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.ResourceResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EmbedComponent extends WCMUse {
    private static final Logger log = LoggerFactory.getLogger(EmbedComponent.class);
    private ResourceResolver resourceResolver = null;
    private String resourceType = null;
    private String resourceName = null;
    private Resource resource = null;
    private Resource child = null;
    @Override
    public void activate() throws Exception {
        // Set resource Name and Type
        resourceName = get("name", String.class);
        resourceType = get("type", String.class);
        resource = getResource();
        child = resource.getChild(resourceName);
        resourceResolver = resource.getResourceResolver();
        
        if (child != null && !child.isResourceType(resourceType)) {
            updateResourceType();
        } else if(child == null) {
            createResource();
        }
    }
    private void createResource() {
        try {
            Map<String, Object> properties = new HashMap<>();
            properties.put("sling:resourceType", resourceType);
            resourceResolver.create(resource, resourceName, properties);
            commitChanges();
        } catch (PersistenceException ex) {
            log.error("Failed to create resource", ex);
        }
    }
    private void updateResourceType() {
        try {
            Node childNode = child.adaptTo(Node.class);
            childNode.setProperty("sling:resourceType", resourceType);
            commitChanges();
        } catch (RepositoryException ex) {
            log.error("Failed to update resource type", ex);
        }
    }
    private void commitChanges() {
        try {
            resourceResolver.commit();
        } catch (PersistenceException ex) {
            log.error("Failed to save changes", ex);
        }
    }
}
Sightly Script

<div data-sly-use="${com.aempodcast.example.components.utils.EmbedComponent' 
@ name=’primary’, type='foundation/components/parsys'}" data-sly-resource="primary"></div>
ISSUE 3: Component select highlight goes beyond the component area
The Symptom
Embedded component’s authoring highlight in Touch UI is bigger than the component itself, or an embedded component below is un-editable.
HighlightGoesBeyondSymptomEach paragraph is a copy of a component. The top most is embedded while the other four are dropped into parsys. The embedded component has data-sly-unwrap set on it. The blue border shown is the selected top most component. The other four components cannot be selected.
The Cause
The wrapping element of the component had been removed with data-sly-unwrap. Adobe Experience Manager puts a cq element, which holds helpful metadata for the authoring interface. One of the things it is used for is to set which element to use as the component boundary. When the component is selected, the boundary gets highlighted with a blue border. When the default wrapping element is removed, the next parent element is then used as the component boundary, which can cause the highlight to go beyond the component, and interfere with the ability to edit other components.
The Solution
Do not use data-sly-unwrap on elements including data-sly-resource.
Conclusion
In transitioning from CQ5 JSPs and the Classic UI to Adobe Experience Manager 6.0 Sightly and Touch UI, there are things that need to be adjusted in how they are done. Knowing the possible issues–and how to solve them–will make Sightly and Touch UI easier to use for developers and content authors.

LDAP in AEM 6.x

Starting in Adobe Experience Manager 6.0, there has been a significant change in the way LDAP Authentication is managed. Rather than using the jaas.conf file of earlier releases, AEM 6.0 comes with Jackrabbit Oak, which has its own LDAP integration. Although the Apache Jackrabbit developershave provided documentation for configuring the system, we felt that it would be helpful if we could provide an example of this configuration.
There are three things we need to configure in Adobe Experience Manager: the LdapIdentityProvider, a DefaultSyncHandler, and an ExternalLoginModule. While in these examples we will be using the Felix Console, we at Axis41 always recommend that all Production OSGi configurations be saved in your source code repository, and become a regular part of your deployment process.
You should take the time to understand all the options in each configuration; however, we will be focusing on the options that you most likely will need to configure.

Apache Jackrabbit Oak LDAP Identity Provider (LdapIdentityProvider)

LdapIdentityProvider
provider.nameThe “idp.name” we will provide to the ExternalLoginModule
host.nameThe host where your LDAP server is running
bind.dnThe Distinguished Name (DN) to bind to LDAP with
bind.passwordThe username to bind to LDAP with
user.baseDNThe LDAP DN that is the base for user accounts that will be allowed to authenticate with AEM
user.objectclassThe LDAP objectclass for user objects that will be allowed to authenticate with AEM
user.idAttributeThe LDAP attribute that will determine the AEM username
group.baseDNThe LDAP DN that is the base for group objects that will be searched by AEM

Apache Jackrabbit Oak Default Sync Handler (DefaultSyncHandler)

DefaultSyncHandler
handler.nameThe “sync.handlerName” we will provide to the ExternalLoginModule
user.expirationTimeHow long after synchronization a user object should be considered valid
user.autoMembershipSynchronized users will automatically be added to this group.
user.propertyMappingA String[] of “key=value” pairs; keys will be the AEM property name, values the LDAP property which will be copied.
user.pathPrefixLocation where synced users will be stored; the value will be appended to /home/users.
group.pathPrefixLocation where synced groups will be stored; the value will be appended to /home/groups.

Apache Jackrabbit Oak External Login Module (ExternalLoginModule)

ExternalLoginModule
idp.nameprovider.name from the LdapIdentityProvider
sync.handlerNamehandler.name from the DefaultSyncHandler

What it looks like after it syncs

One of the things we found to be a surprise was the name Adobe Experience Manager chooses for the node as it syncs the user from LDAP. Here’s a CRXDE Lite view of a set of objects created using the OSGi configs I shared above. You can see the machine-generated name of both the user and group nodes after the sync took place.
crxde