This website uses cookies to ensure you get the best experience. Learn More.
Extending Liferay DXP - User Registration (Part 2)
This is the second part of the "Extending Liferay DXP - User Registration" blog. In this blog we explore the ways of implementing registration process for a portal with multiple sites.
Let’s presume we have a portal with the following sites configured:
Each of the sites has its own description which we want to display to the user:
The main steps of the user registration process that we are going to implement here are:
For the implementation of this process we use SmartForms.
Here we show only essential screenshots of the User Registration Form, the entire form definition can be downloaded (see link at the end of this blog). Once it is imported you can use Visual Designer to see how the business rules are implemented.
1. User is asked to enter an email, SmartForms connects to portal webservices to check if such email is already registered (the source code for all webservices is at the end of the blog)
2. If user already has an account, then 'must log in' message is displayed
3. If user is already signed in, the form is automatically populated with user details (user data is automatically brought by SmartForms from Liferay environment).
4. On the next page the user is asked to select a site from the site list obtained via webservice. When user selects a site the description of the site is displayed (webservice again). You can put your own definition of Terms & Conditions together with 'I agree' checkbox.
.
5. If the site the user selected is of 'restricted' type, the user is asked to provide additional information.
SmartForms will automatically handle this rule once you add the following state condition to 'Company/Organisation Details' page. Visual Designer screenshot:
6. The membership request summary is displayed on a final page, allowing the user to save membership request details as PDF file and Submit the form when ready.
7. Processing the submitted form. There are two form submission handlers :
That's it. That is the user flow, which is probably longer than the implementation notes listed below.
Below is the generic data flow where the Form Controller could be implemented inside or outside of the portal.
In our implementation we are using SmartForms to build and run User Registration Form. SmartForms Cloud is acting as Form Controller.
User registration handler implements:
Here we provide source code for essential functions, link to get the full source code is at the end of the blog.
@WebMethod (action=namespace + "getMemberStatusByEmail" )
@WebMethod
(action=namespace +
"getMemberStatusByEmail"
)
public String getMemberStatusByEmail(
public
String getMemberStatusByEmail(
@WebParam (name= "fieldName" )
@WebParam
(name=
"fieldName"
String fieldname,
@WebParam (name= "fields" )
"fields"
Field[] fieldList) {
try {
try
{
Map<String, Field> fieldMap = fieldArrayToMap(fieldList);
Field emailAddressVO = fieldMap.get(FIELDNAME_USER_EMAIL);
if (emailAddressVO == null ) {
if
(emailAddressVO ==
null
) {
logger.warn( "Call to getMemberStatusByEmail() is misconfigured, cannot find field '" + FIELDNAME_USER_EMAIL + "'" );
logger.warn(
"Call to getMemberStatusByEmail() is misconfigured, cannot find field '"
+ FIELDNAME_USER_EMAIL +
"'"
);
return "false" ;
return
"false"
;
}
String emailAddress = emailAddressVO.getValue();
if (emailAddress.trim().length() == 0 ) {
(emailAddress.trim().length() ==
0
UserLocalServiceUtil.getUserByEmailAddress( this .getCompanyId(fieldMap), emailAddress);
UserLocalServiceUtil.getUserByEmailAddress(
this
.getCompanyId(fieldMap), emailAddress);
// no exception, user exists
return "true" ;
"true"
} catch (Exception e) {}
catch
(Exception e) {}
// user is not registered
} catch (Throwable e) {
(Throwable e) {
logger.error( "System error " , e);
logger.error(
"System error "
, e);
@WebMethod (action=namespace + "getSites" )
"getSites"
public Option[] getSites(
Option[] getSites(
Option[] blankOptions = new Option[ 0 ];
Option[] blankOptions =
new
Option[
];
long companyId = this .getCompanyId(fieldMap);
long
companyId =
.getCompanyId(fieldMap);
User user = null ;
User user =
Field userScreennameField = fieldMap.get(FIELDNAME_USER_SCREENNAME);
if (userScreennameField != null ) {
(userScreennameField !=
if (userScreennameField.getValue().trim().length() > 0 ) {
(userScreennameField.getValue().trim().length() >
user = UserLocalServiceUtil.getUserByScreenName(companyId, userScreennameField.getValue());
List<Option> validGroups = new ArrayList<Option>();
List<Option> validGroups =
ArrayList<Option>();
LinkedHashMap<String, Object> params = new LinkedHashMap<String, Object>();
LinkedHashMap<String, Object> params =
LinkedHashMap<String, Object>();
// limit selection by sites only
params.put( "site" , new Boolean( true ));
params.put(
"site"
,
Boolean(
true
));
// limit selection by active groups only
params.put( "active" , new Boolean( true ));
"active"
List<Group> allGroupsList = GroupLocalServiceUtil.search(companyId, params , QueryUtil.ALL_POS, QueryUtil.ALL_POS);
Iterator<Group> allActiveGroups = allGroupsList.iterator();
while (allActiveGroups.hasNext()) {
while
(allActiveGroups.hasNext()) {
Group group = allActiveGroups.next();
boolean isAlreadyAMember = false ;
boolean
isAlreadyAMember =
false
// check if user is already a member of it
if (user != null ) {
(user !=
if (group.isGuest()) {
(group.isGuest()) {
// is a member anyway
isAlreadyAMember = true ;
} else {
else
isAlreadyAMember = UserLocalServiceUtil.hasGroupUser(group.getGroupId(), user.getUserId());
// add the site to the selection list if this is a regular community site and the user is not already a member of it
if (group.isRegularSite() && !group.isUser() && !isAlreadyAMember && !group.isGuest()) {
(group.isRegularSite() && !group.isUser() && !isAlreadyAMember && !group.isGuest()) {
// include Open and Restricted sites only
if (group.getType() == 1 || group.getType() == 2 ) {
(group.getType() ==
1
|| group.getType() ==
2
validGroups.add( new Option( group.getName(group.getDefaultLanguageId()), String.valueOf(group.getGroupId()) ) );
validGroups.add(
Option( group.getName(group.getDefaultLanguageId()), String.valueOf(group.getGroupId()) ) );
return validGroups.toArray( new Option[validGroups.size()]);
validGroups.toArray(
Option[validGroups.size()]);
return blankOptions;
blankOptions;
@WebMethod (action=namespace + "getSiteOwnerEmails" )
"getSiteOwnerEmails"
public String getSiteOwnerEmails(
String getSiteOwnerEmails(
Group group = this .getSelectedGroup(fieldMap);
Group group =
.getSelectedGroup(fieldMap);
if (group == null ) {
(group ==
// no group selected yet
return "" ;
""
} else if (group.getType() != 2 ) {
(group.getType() !=
// this is not a restricted site
// check if Terms and Conditions acknowledge is checked, otherwise no point of fetching email addresses
Field termsAndConditionsField = fieldMap.get(FIELDNAME_TnC_CHECKBOX);
if (termsAndConditionsField == null ) {
(termsAndConditionsField ==
logger.warn( "Call to getSiteOwnerEmails() is misconfigured, cannot find field '" + FIELDNAME_TnC_CHECKBOX + "'" );
"Call to getSiteOwnerEmails() is misconfigured, cannot find field '"
+ FIELDNAME_TnC_CHECKBOX +
if (termsAndConditionsField.getValue().length() == 0 ) {
(termsAndConditionsField.getValue().length() ==
// not checked
// make a list of email addresses of site owners for a restricted site
// this will be used to send 'site membership request' email
StringBuilder response = new StringBuilder();
StringBuilder response =
StringBuilder();
Role siteOwnerRole;
siteOwnerRole = RoleLocalServiceUtil.getRole(group.getCompanyId(), RoleConstants.SITE_OWNER);
} catch (PortalException e) {
(PortalException e) {
logger.error( "Unexpected error" , e);
"Unexpected error"
List<User> groupUsers = UserLocalServiceUtil.getGroupUsers(group.getGroupId());
for ( int i = 0 ; i < groupUsers.size(); i++) {
for
(
int
i =
; i < groupUsers.size(); i++) {
User user = groupUsers.get(i);
if (UserGroupRoleLocalServiceUtil.hasUserGroupRole(user.getUserId(), group.getGroupId(), siteOwnerRole.getRoleId())) {
(UserGroupRoleLocalServiceUtil.hasUserGroupRole(user.getUserId(), group.getGroupId(), siteOwnerRole.getRoleId())) {
if (response.length() > 0 ) {
(response.length() >
response.append( ';' );
response.append(
';'
response.append(user.getEmailAddress());
logger.info( "compiled site admin emails " + response.toString());
logger.info(
"compiled site admin emails "
+ response.toString());
return response.toString();
response.toString();
// method to process JSON webhook call
@POST
@Path ( "/user-registration" )
@Path
"/user-registration"
public void newFormSubmittedAsJsonFormat(String input) {
void
newFormSubmittedAsJsonFormat(String input) {
logger.info( "In /webhook/user-registration" );
"In /webhook/user-registration"
/* check authorization */
String handShakeKey = request.getHeader( "X-SmartForms-Handshake-Key" );
String handShakeKey = request.getHeader(
"X-SmartForms-Handshake-Key"
if (handShakeKey == null || !handShakeKey.equals(SMARTFORMS_HADSHAKE_KEY) ) {
(handShakeKey ==
|| !handShakeKey.equals(SMARTFORMS_HADSHAKE_KEY) ) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
throw
WebApplicationException(Response.Status.UNAUTHORIZED);
JSONObject data;
data = JSONFactoryUtil.createJSONObject(input);
Map<String, String> fields = this .jsonFormDataToFields(data);
Map<String, String> fields =
.jsonFormDataToFields(data);
logger.info( "Have received fields " + fields.size());
"Have received fields "
+ fields.size());
ServiceContext serviceContext = new ServiceContext();
ServiceContext serviceContext =
ServiceContext();
long groupId = Long.parseLong(fields.get(FIELD_SITE_ID));
groupId = Long.parseLong(fields.get(FIELD_SITE_ID));
long companyId = Long.parseLong(fields.get(FIELD_COMPANY_ID));
companyId = Long.parseLong(fields.get(FIELD_COMPANY_ID));
if (fields.get(FIELD_USER_ID).length() > 0 ) {
(fields.get(FIELD_USER_ID).length() >
logger.info( "User is already registered" );
"User is already registered"
user = UserLocalServiceUtil.getUser(Long.parseLong(fields.get(FIELD_USER_ID)));
} catch (Exception e) {
(Exception e) {
logger.error( "Unable to fetch user" , e);
"Unable to fetch user"
throw new WebApplicationException(Response.Status.NOT_FOUND);
WebApplicationException(Response.Status.NOT_FOUND);
// create user
String firstName = fields.get(FIELD_USER_FIRST_NAME);
String lastName = fields.get(FIELD_USER_LAST_NAME);
String email = fields.get(FIELD_USER_EMAIL);
logger.info( "Creating user " + firstName + " " + lastName + " " + email);
"Creating user "
+ firstName +
" "
+ lastName +
+ email);
// the following data could come from the form, but we just provide some hard-coded value
long groups[] = new long [ 0 ];
groups[] =
[
if (!fields.get(FIELD_SITE_TYPE).equals( "restricted" )) {
(!fields.get(FIELD_SITE_TYPE).equals(
"restricted"
)) {
// this is an open group, add it to the list
groups = new long [ 1 ];
groups =
groups[ 0 ] = groupId;
groups[
] = groupId;
long blanks[] = new long [ 0 ];
blanks[] =
boolean sendEmail = false ;
sendEmail =
Locale locale = PortalUtil.getSiteDefaultLocale(groupId);
boolean male = true ;
male =
String jobTitle = "" ;
String jobTitle =
long suffixId = 0 ;
suffixId =
long prefixId = 0 ;
prefixId =
String openId = null ;
String openId =
long facebookId = 0 ;
facebookId =
String screenName = null ;
String screenName =
boolean autoScreenName = true ;
autoScreenName =
boolean autoPassword = true ;
autoPassword =
long creatorUserId = 0 ;
creatorUserId =
user = UserLocalServiceUtil.addUser(
creatorUserId, companyId,
autoPassword, null , null ,
autoPassword,
autoScreenName, screenName , email,
facebookId, openId, locale,
firstName, "" , lastName,
firstName,
, lastName,
prefixId, suffixId, male,
1 , 1 , 2000 ,
2000
jobTitle,
groups, blanks, blanks, blanks,
sendEmail ,
serviceContext);
logger.error( "Unable to create user" , e);
"Unable to create user"
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
if (fields.get(FIELD_SITE_TYPE).equals( "restricted" )) {
(fields.get(FIELD_SITE_TYPE).equals(
MembershipRequestLocalServiceUtil.addMembershipRequest(user.getUserId(), groupId,
"User has requested membership via User Registration Form, you should have received an email" , serviceContext);
"User has requested membership via User Registration Form, you should have received an email"
, serviceContext);
logger.error( "Unable ot add membership request" );;
"Unable ot add membership request"
);;
} catch (JSONException e) {
(JSONException e) {
logger.error( "Unable to create json object from data" );
"Unable to create json object from data"
throw new WebApplicationException(Response.Status.BAD_REQUEST);
WebApplicationException(Response.Status.BAD_REQUEST);
private Map<String, String> jsonFormDataToFields(JSONObject data) {
private
Map<String, String> jsonFormDataToFields(JSONObject data) {
Map<String, String> map = new HashMap<String, String>();
Map<String, String> map =
HashMap<String, String>();
JSONArray fields = data.getJSONArray( "fields" );
JSONArray fields = data.getJSONArray(
for ( int i = 0 ; i < fields.length(); i++) {
; i < fields.length(); i++) {
JSONObject field = fields.getJSONObject(i);
logger.info(field.toJSONString());
map.put(field.getString( "name" ), field.getString( "value" ));
map.put(field.getString(
"name"
), field.getString(
"value"
return map;
map;
Full source code for Form Handler project can be downloaded from here:
http://extras.repo.smartfor.ms/blogs/Extending-Liferay-User-Registration/userRegistration-source.zip
To make it work on your Liferay installation using SmartForms you will need to:
One more thing, in SmartForm webhook configuration please change the localhost to URL of your portal:
Feel free to ask questions if you run into problems ...