Sunday 11 December 2016

Google Maps Integration with AEM Page

Ever wondered how does entering the pin code or address on any mobile application gives out the google map of the location !!
Here you go... It's a simple integration with Google Maps API. Yes!! this can be done on any platform easily. Take a look in the below screenshot how does it appear to the end user.


Here's a example to it using the pin code as the user data and give him the location on the map.

Step 1:
Create a component and add the following code which generates a form on the page with a submit button.
Fields on the dialog can be the title you want to give for the map which is a simple text widget.

<div class="container">
   <h2>Search stores</h2>
<label for="Enterzipcode">Enter store zipcode</label>
        <input id="zipCode" type="text">
        <input type="hidden" id="miles10" name="miles" value="${properties.miles}" />
        <button class="btn btn-info" id="submitZip" onClick=getLocation() >Submit</button>

    <div id="map-canvas">
    </div>
</div>


Step 2:
Create a project specific clientLib structure. And a folder to add all the js files required.
Create a file named geomap.js and add the below code.

<script>
function getLocation(){
    var zipcode = $('#zipCode').val();
    $.ajax({
       url : "http://maps.googleapis.com/maps/api/geocode/json?components=postal_code:"+zipcode+"&sensor=false",
       method: "POST",
       success:function(data){
           latitude = data.results[0].geometry.location.lat;
           longitude= data.results[0].geometry.location.lng;
   initMap(latitude,longitude)
       }
    });

};
   
function initMap(latitude,longitude) {

        var city = {lat: latitude, lng: longitude};
        var map = new google.maps.Map(document.getElementById('map-canvas'), {
                      zoom: 10,
                      mapMaker: true,
                      rotateControl: true,
                      center: city
                  });
        var marker = new google.maps.Marker({
                    position: city,
                    map: map
                 });
      }
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyChxoSa2XZNTPZHs1UUuA24vfEAZhm3JRc">
</script>

Make sure the above google api key is generated and added in the js file.


Step 3:
Can add the custom css to the map section which fits on the site page. Below is the sample css added to the map.

<style>
       #map-canvas {
               height: 400px;
               width: 100%;
       }
</style>

Output:

On the initial page load , a form is seen with a input field for pin code and a submit button.


On entering the pin code and click on " submit ", an ajax call will bring in the map of the entered pin code as below.

The map comes with controls like Zoom in and Zoom out.

Wednesday 8 June 2016

AEM Tagging Framework

Tags : cq:Tag Node Type

The declaration of a tag is captured in the repository in a node of type cq:Tag.
A tag can be a simple word (e.g. sky) or represent a hierarchical taxonomy (e.g. fruit/apple, meaning both the generic fruit and the more specific apple).
Tags are identified by a unique TagID.

Tag Characteristics

  • node type is cq:Tag
  • node name is a component of the TagId
  • the TagId always includes a namespace
  • optional jcr:title property (the Title to display in the UI)
  • optional jcr:description property
  • when containing child nodes, is referred to as a container tag
  • is stored in the repository below a base path called the taxonomy root node.

TagID

A TagID identifies a path which resolves to a tag node in the repository.  
Typically, the TagID is a shorthand TagID starting with the namespace or it can be an absolute TagID starting from the taxonomy root node.
The TagID consists of a namespace followed by the local TagID.  Container tags have sub-tags that represent a hierarchical order in the taxonomy. Sub-tags can be used to reference tags same as  any local TagID.   For example tagging content with "fruit" is allowed, even if it is a container tag with sub-tags, such as "fruit/apple" and "fruit/banana".
The taxonomy root node is the base path for all tags in the repository.  The taxonomy root node must not be a node of type cq:Tag

Resolving TagIDs

The following table shows some sample TagIDs, their elements, and how the TagID resolves to an absolute path in the repository :
The following table shows some sample TagIDs, their elements, and how the TagID resolves to an absolute path in the repository :
The following table shows some sample TagIDs, their elements, and how the TagID resolves to an absolute path in the repository :

TagIDNamespaceLocal IDContainer tag(s)Leaf tagRepository
Absolute tag path
dam:fruit/apple/braeburndamfruit/apple/braeburnfruit, applebraeburn/etc/tags/dam/fruit/apple/braeburn
color/reddefaultcolor/redcolorred/etc/tags/default/color/red
skydefaultsky(none)sky/etc/tags/default/sky
dam:dam(none)(none)(none, the namespace)/etc/tags/dam
/etc/tags/category/carcategorycarcarcar/etc/tags/category/car

Sunday 5 June 2016

Run Modes


Run modes allow you to tune your AEM instance for a specific purpose; for example author or publish, test, development, intranet or others.

Installation Run Modes

Installation (or fixed) run modes are used at installation time and then fixed for the entire lifetime of the instance, they cannot be changed.

Installation run modes are provided out-of-the-box:
  • author
  • publish
  • samplecontent
  • nosamplecontent
These are two pairs of mutually exclusive run modes; for example, you can:
  • define either author or publish, not both at the same time.
  • combine author with either samplecontent or nosamplecontent (but not both).

Customized Run Modes

You can also create your own, customized, run modes. These can be combined to cover scenarios such as:
  • author + development.
  • publish + test.
  • publish + test + golive.
  • publish + intranet.
There are two mechanisms for setting standard and environment specific run mode for your instance:

To set up standard run mode Use the naming convention:
  • cq-<run-mode>-<port-number>. For example, set a standard run mode by naming the jar file cq-author-4502 or cq-publish-4503.
To set up environment specific run mode there are two methods:
  • Open <cq-installation-dir>/crx-quickstart/launchpad/sling.properties.
  • Add the following properties (following example is for author, test, uk):
  • sling.jcrinstall.folder.name.regexp=.*/(install|config)? 
  • sling.run.modes=author,test,uk .
  • In above case config.author.test.uk will get picked up (Or whatever with maximum match).
Configuration values for the run modes are saved in the repository. You can store all configurations in one repository as the run mode is indicated by a suffix on the folder name; for example:
  • config, applicable for all run modes.
  • config.author, used in author run mode.
  • config.publish, used in publish run mode.
  • config.<standard-run-mode>.<environment-specific-mode>, used in the applicable run mode.

Starting CQ with a specific run mode

If you have defined configurations for multiple run modes then you need to define which is to be used upon startup. There are several methods for specifying which run mode to use; the order of resolution is:
  • sling.properties file.(Edit <cq-installation-dir>/crx-quickstart/conf/sling.properties)
  • -r option.(start the jar with java -jar cq-56-p4545.jar -r dev)
  • system properties (-D).(A system property in the start script can be used to specify the run mode. -Dsling.run.modes=publish,prod,us)
  • Filename detection (cq5-<run-mode>-p<port-number>).
To check the runmode:

http://adobeaem-learneasycodeeasy.blogspot.in/search/label/runmode.

Saturday 4 June 2016

Sling Adapters


Sling AdaptTo :


Sling offers an Adapter pattern to conveniently translate objects that implement the Adaptable interface. This interface provides a generic adaptTo() method that will translate the object to the class type being passed as the argument.

For example to translate a Resource object to the corresponding Node object, you can simply do:

Example : Node node = resource.adaptTo(Node.class);

Use Cases :

There are the following use cases:


  • Get implementation-specific objects.For example, a JCR-based implementation of the generic Resource interface provides access to the underlying JCR Node.
  • Shortcut creation of objects that require internal context objects to be passed.For example, the JCR-based ResourceResolver holds a reference to the request's JCR Session, which in turn is needed for many objects that will work based on that request session, such as the PageManager or UserManager.
  • Shortcut to services.A rare case - sling.getService() is simple as well.

How it works :

There are various ways that Adaptable.adaptTo() can be implemented:
  • By the object itself; implementing the method itself and mapping to certain objects.
  • By an AdapterFactory, which can map arbitrary objects.
  • The objects must still implement the Adaptable interface and must extend SlingAdaptable (which passes the adaptTo call to a central adapter manager).
  • This allows hooks into the adaptTo mechanism for existing classes, such as Resource.

Reference : Sling Adapters.


Sling Cheatsheet


This tutorial is to learn how Apache Sling Resource Resolution is done in AEM.

URL Decomposition :


URL – http://localhost:4502/cf#/content/aemTutorials/sightlyPage.test.html/a/b?x=12

Protocol: http
Host: localhost:4502
Content path : content/aemTutorials/sightlyPage
Selector(s) : .test
Extension : html
Suffix : a/b
Param : x=12


Sling Resource Resolution – Mapping URL to Respective JCR Node :



Step-1: Double click on a page in siteadmin , to open respective page in browser.

Step-2-3-4-5:  Analyse the URL /contents/aemTutorials/sightlyPage it is known as content path.

  • The /content path refers to the path of content folder in crx de.
  • /aemTutorials refers to the package under contents folder.
  • /sightlyPage refers to our page that we have created under siteadmin. jcr:content of sightlyPage contains a property sling:ResourceType. Which tells sling where our component is located. As shown in above figure it is at training/components/myComponent , means sling needs to check for myComponent under /apps folder.
Step-6: Analyse selector and extension  test.html.
  • Sling appends the selector and extension after the component name i.e myComponent.test.html If it found this component then it render it else it drops the extension and search again.
Note:- Sling decides rendering of script on the basis of selector + extension . First priority goes to selector, if no selector is available then priority goes to extension.

Now if we double click our page http://localhost:4502/cf#/content/sample/testing.html  then by default myComponent.jsp script will be called because here .html is an extension not selector .

Between jsp and html first priority goes to jsp as by default a component is linked to jsp at the time of creation.

So the final priority order will be myComponent.html.jsp–>html.jsp–>myComponent.jsp –>myComponent.html

Tuesday 12 April 2016

How to get Service Resource Resolver ?

Goal:

How to get Service Resource resolver ?

Explanation:

One of the Important change in AEM 6.1 securities is related to Admin Resourceresolver. In AEM 6.1 getAdministrativeResourceResolver has been deprecated other way to get this resourceresolver is using serviceResourceResolver.

Steps:

Go to /crx/explorer and click on “User Administration” link. On the popup, click on “Create System User” button, and enter the userid (anything you like).


Goto Useradmin console and provide required permissions for the user.

After creating the user and assigning appropriate permission, you need to add an entry in the “Apache Sling Service User Mapper Service”. This entry is to allow your bundle to access the permissions of the system user.

Go to Configuration Manager in the system console and search for “User Mapper Service”

<bundle symbolic name>:<any name of service>=<system user id>

*You need to add all the bundles that access the permissions of the system user. It can also be the bundle shipped with AEM.

If you see an error in the error log, which cannot access service for the specified package or bundle, it means you need to an entry for that bundle here.


Get the Service Resource Resolver in code

Map<String, Object> param = new HashMap<String, Object>();

// writeService is the service name you gave in the configuration of user mapper service

param.put(ResourceResolverFactory.SUBSERVICE, “writeService”);

ResourceResolver resolver = resolverFactory.getServiceResourceResolver(param);


Now this resourceresolver is used as per the permissions given for the user.

Saturday 9 April 2016

QR Code Generator component


Goal:

To Develop a component for generating QR code in AEM.

How To ??

Create a AEM component with the below structure.


Jsp:

<img width="<%=properties.get(WIDTH,"")%>" src="<%= request.getContextPath() %>/libs/wcm/mobile/qrcode.png?url=<%=properties.get(CODE,"")%>"/>

Mobile QR Code Generatorcom.day.cq.wcm.cq-wcm-mobile-qrcode will be used to generate the QR code.
/libs/wcm/mobile/qrcode.png


First Touch UI Component


Goal :

To create our first Touch UI component.

Steps:

  1. Create a component name touchui.
  2. Create a node with primary type nt:unstructured name cq:dialog.
    1. Property sling:resourceType and value cq/gui/components/authoring/dialog.
  3. Create a node with primary type nt:unstructured name content under cq:dialog node.
    1. Property sling:resourceType and value granite/ui/components/foundation/container.
  4. Create a node with primary type nt:unstructured name layout under content node.
    1. Property sling:resourceType and value granite/ui/components/foundation/layouts/tabs.
    2. Property type value as nav.
  5. Create a node with primary type nt:unstructured name items under content node.
  6. Create a node with primary type nt:unstructured name section under items node.
    1. Property sling:resourceType and value granite/ui/components/foundation/section.
  7. Create a node with primary type nt:unstructured name layout under section node.
    1. Property sling:resourceType and value granite/ui/components/foundation/layouts/fixedcolumns.
  8. Create a node with primary type nt:unstructured name items under section node.
  9. Create a node with primary type nt:unstructured name column under items node.
    1. Property sling:resourceType and value granite/ui/components/foundation/container
  10. Create a node with primary type nt:unstructured name items under column node.
  11. Create a node with primary type nt:unstructured name textfield under items node.
    1. Property fieldLabel and value TextField
    2. Property name and value ./name.
    3. Property sling:resourceType and value granite/ui/components/foundation/form/textfield.

Note :

To develop all types of widgets in touch UI download below dialog implementation


Sunday 20 March 2016

Error Page Handler Using ACS Commons

Goal:

Provide an author-able means for defining, creating and managing custom Error pages per content tree/site.

How to use:

  • Create the proxy overlays for Sling errorhandler scripts (404.jsp and default.jsp) which include the acs-commons counterparts.
  • Create the overlay for 404.jsp
    • /apps/sling/servlet/errorhandler/404.jsp
    • <%@page session="false"%><%%><%@include file="/apps/acs-commons/components/utilities/errorpagehandler/default.jsp" %>
  • In your base page implementation, add the following cq:Widget to the Page Properties dialog
    • <errorpages jcr:primaryType="cq:Widget" path="/apps/acs-commons/components/utilities/errorpagehandler/dialog/errorpages.infinity.json" xtype="cqinclude"/>
  • Create a sling:OsgiConfig node to enable the Error Page Handler
    • /apps/myapp/config/com.adobe.acs.commons.errorpagehandler.impl.ErrorPageHandlerImpl.xml

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="sling:OsgiConfig"
enabled="{Boolean}true"
cache.serve-authenticated="{Boolean}true"
cache.ttl="{Long}300"/>

  • Create a CQ Page that will act as the default Error page, and also contain all custom variations of error pages. Each error page’s “name” (Node name) should correspond to the HTTP Response Status code it should respond to.
    • 500: Internal Server Error
    • 404: Not Found
    • 403: Forbidden
  • Create the above pages under root page of the project structure e.g., /content/projectroot/errors/500
  • Finally, Edit the Page Properties of the site’s root node, and in the new “Error Pages” dialog input
More Links:

Friday 18 March 2016

Getting values to selection dropdown using servlet and listener


Goal:

To get the values of the page into dropdown which is selected in the pathfield.

Dialog Structure:




Create a dialog with one path field and selection field as shown in the image and listeners to the path field which is used to call the servlet and get the page values.

Listener:

Name of the listener is dialogclose :

function(){ 
var dialog = this.findParentByType('dialog');
var selectBox = dialog.findByType('selection')[0];
        $.getJSON('/libs/dropdown?Path=' + this.value, 
            function(jsonData){
selectBox.setOptions(jsonData);
                        });
 }

The above listerner is triggered when the pathfield is selected and when the browse dialog is closed.


Servlet:

package com.geometrixx.sightly.servlets;

import java.io.IOException;

import javax.jcr.Node;
import javax.servlet.ServletException;

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.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.commons.json.JSONArray;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.JSONObject;

import com.day.cq.wcm.api.Page;


@Component(immediate = true, description="DropDown Servlet")
@Service(value = javax.servlet.Servlet.class)
@Properties(value={
@Property(name="sling.servlet.extensions",value={"html","json"}),
@Property(name="sling.servlet.methods",value={"GET","POST"}),
@Property(name="sling.servlet.paths",value={"/libs/dropdown"})
})
public class DropDown extends SlingAllMethodsServlet{

/**
*/
private static final long serialVersionUID = 1L;
protected void doGet(final SlingHttpServletRequest req,
            final SlingHttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
protected void doPost(final SlingHttpServletRequest req,
            final SlingHttpServletResponse resp) throws ServletException, IOException {
JSONArray jsonArray = new JSONArray();
JSONObject jsonObject = new JSONObject();
try {
String pagePath = req.getParameter("Path");
Page selectedPage = req.getResourceResolver().getResource(pagePath).adaptTo(Page.class);
String pageTitle = selectedPage.getPageTitle();
String pageName = selectedPage.getName();
String pageNavigationTitle = selectedPage.getNavigationTitle();
jsonObject.put("text", "PageTitle");
jsonObject.put("value", pageTitle);
jsonArray.put(jsonObject);
jsonObject = new JSONObject();
jsonObject.put("text", "PageName");
jsonObject.put("value", pageName);
jsonArray.put(jsonObject);
jsonObject = new JSONObject();
jsonObject.put("text", "NavigationTitle");
jsonObject.put("value", pageNavigationTitle);
jsonArray.put(jsonObject);
resp.getWriter().println(jsonArray.toString());
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

Above servlet is used to generate the JSON response to get the options into dropdown as TEXT and VALUE pair.




Monday 7 March 2016

AEM Important Links

How to find the runmodes of a AEM instance

In your start-script of your CQ5 instance you can extend the runmodes of your CQ5 instance like this:

::* runmode(s)
set CQ_RUNMODE=author,localhost,europe

What I never could find is the option to check whether cq really picked up this change.

Well here is how to do it:

Go to your Felix console (/system/console).
Click on “Configuration Status” (/system/config)

Use these arrows to navigate through all the tabs, until you see ‘Sling Settings’.

In this tab the runmodes are displayed.

In CQ 5.6.1 there is an easier way to find the runmodes, just go to this url:

http://server:port/system/console/status-slingsettings

Thursday 3 March 2016

Customize Left Navigation for Projects.html

Goal:

Add one more menu for the http://localhost:4502/projects.html



New in AEM(CQ) 6.1 is Sling Resource MergerWith resource merger you overlay the path in /apps and add only new nav items (in this case, Users)

1) Create the following folders in CRXDE Lite (http://localhost:4502/crx/de) or use the overlay creator servlet discussed here

            /apps/cq - nt:folder
            /apps/cq/core - nt:folder
            /apps/cq/core/content - sling:OrderedFolder
            /apps/cq/core/content/nav - nt:unstructured

  The structure above is an overlay of /libs/cq/core/content/nav

2) Create node /apps/cq/core/content/nav/onemkhomepage of type nt:unstructured with the following properties

             id - onemk-homepage-link
             href - /content/geometrixx-sightly/English.html
             jcr:description - Open Home Page
             jcr:title - OneMk HomePage

     All properties are self-explanatory. Property id is to uniquely identify the nav item.




Tuesday 1 March 2016

How to Configure Stemming for AEM search

Stemming:


Decide the stemming strategy based on your use case. For mor info on strategies, please see this article.

It is highly recommend you use Porter Stemming because of its simplicity and flexibility. It usually is sufficient to yield the majority of stemming based search results.

Porter is a transforming algorithm for the English language that reduces any of the forms of a word such as "walks, walking, walked" or "teach, teaching" to their elemental roots "walk" and "teach".

Porter is based on rules and does not need a dictionary.

Go to the /oak:index/ntHierarchyNode/analyzers/default/filters path in the repository.

Create a node with these properties:
  • Name: PorterStem
  • Type: nt:unstructured
Finally, test stemming functionality by:

Typing the keyword "teach". You will get results for the term "teaching" as well.

For more info refer :



CQ User Creation Using UserManager API

Introduction:

Users:

Users will log in to AEM with their account. Each user account is unique and holds the basic account details, together with the privileges assigned.
Users are often members of Groups, which simplify the allocation of these permissions and/or privileges.

Groups:

Groups are collections of users and/or other groups; these are all called Members of a group.
Their primary purpose is to simplify the maintenance process by reducing the number of entities to be updated, as a change made to a group is applied to all members of the group. Groups often reflect:
  • a role within the application; such as someone who is allowed to surf the content, or someone who is allowed to contribute content.
  • your own organization; you may want to extend the roles to differentiate between contributors from different departments when they are restricted to different branches in the content tree.
Therefore groups tend to remain stable, whereas users come and go more frequently.
With planning and a clean structure, the use of groups can reflect your structure, giving you a clear overview and an efficient mechanism for updates.


User Creation Servlet:

Below Servlet creates user under specified paths and add them to specified group when you hit the servlet as 

http://localhost:4502/libs/cqusercreation?email=mani@gmail.com&firstName=firstName&lastName=lastName&givenName=givenName&displayName=displayName

with different parameters you can use these servlet in any async call to make a call from the component.

Servlet

package com.geometrixx.sightly.servlets;

import java.io.IOException;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;

import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.servlet.ServletException;

import org.apache.commons.lang.StringUtils;
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.JackrabbitSession;
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.jackrabbit.value.StringValue;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



@Component(immediate = true, description="User Creation")
@Service(value = javax.servlet.Servlet.class)
@Properties(value={
@Property(name="sling.servlet.extensions",value={"html","json"}),
@Property(name="sling.servlet.methods",value={"GET","POST"}),
@Property(name="sling.servlet.paths",value={"/libs/cqusercreation"})
})
public class CQUserCreationServlet extends SlingAllMethodsServlet {
private static final Logger Log = LoggerFactory
.getLogger(CQUserCreationServlet.class);
protected void doGet(final SlingHttpServletRequest req,
            final SlingHttpServletResponse resp) throws ServletException, IOException {
Log.debug("doGet");
        doPost(req, resp);
    }
protected void doPost(final SlingHttpServletRequest req,
            final SlingHttpServletResponse resp) throws ServletException, IOException {
Log.debug("doPost");
createNewCqUser(req,resp);
}
public void createNewCqUser(SlingHttpServletRequest req,
SlingHttpServletResponse resp) {
// TODO Auto-generated method stub
Log.debug("createNewCqUser");
Map<String, String> userProperties = new HashMap<String, String>();
String userEmailId = req.getParameter("email");
final String loginId= userEmailId;
String firstName = req.getParameter("firstName");
String lastName = req.getParameter("lastName");
String givenName = req.getParameter("givenName");
String displayName = req.getParameter("displayName");
userProperties.put("profile/email", userEmailId);
userProperties.put("profile/firstName", firstName);
userProperties.put("profile/lastName", lastName);
userProperties.put("profile/givenName", givenName);
userProperties.put("profile/displayName", displayName);
Log.debug("Map {}",userProperties );
Session session = req.getResourceResolver().adaptTo(Session.class);
Group userGroup;
Principal principal;
User newUser;
try {
UserManager userManager = ((JackrabbitSession) session)
.getUserManager();
Log.debug("userManager {}",userManager );
userGroup = (Group) userManager
.getAuthorizableByPath("/home/groups/e/everyone");
principal = new Principal() {
public String getName() {
return loginId;
}
};
newUser = userManager.createUser(loginId, "123456", principal,
"/home/users/geometrixx");
Log.debug("newUser {}",newUser );
if (null != newUser) {
updateAndInsertUserDetails(newUser, userProperties);
userGroup.addMember(newUser);
}
session.save();
}catch(Exception e){
}
}
public void updateAndInsertUserDetails(User user, Map<String, String> map)
throws RepositoryException {
for (Map.Entry<String, String> entry : map.entrySet()) {
setUserStringProperty(user, entry.getKey(), entry.getValue());
}
}
public void setUserStringProperty(User user, String property, String value) {
try {
if (isNotBlank(value)) {
user.setProperty(property, new StringValue(value));
}
} catch (RepositoryException e) {
}
}
public boolean isNotBlank(String value) {
return StringUtils.isNotBlank(value);
}
}


UserManager API Link: