0

In dotCMS, user accounts serve one of two functions: to control access to the front end, or the back end. User interaction with the back end is fairly straight forward, given roles. Set up a role, tie it to the tabs a user needs, and poof. On the front end though, if you want to leverage user accounts, you’ll need a little extra mojo.

From the login/user registration side of things, all you need to do is throw the loginForm() macro in a piece of content. If you need to set up user registration, there are two .vtl files ready made you can edit to your needs:

  • {DOTCMS_ROOT}/dotCMS/WEB-INF/velocity/static/registration/registration.vtl
  • {DOTCMS_ROOT}/dotCMS/WEB-INF/velocity/static/registration/confirmation.vtl
Example of the /cms401page Login Page

Example of the /cms401page Login Page

First off, make sure you actually have a login page to go to on the front end. This is where users will end up if they try to access a folder or page that does not have the CMS Anonymous view permissions on it (for the techy folks, this is where the 401 redirect ends up). The page should be:

  • /application/login/login.dot, or…
  • Create your login page wherever you like, and create a virtual link between it and the path /cms401page (similar to how you did /cmsHomePage when you set up your site)

On that page, add a widget that contains the loginForm() macro. You can also add links to a registration page containing the aforementioned .vtl files using parse() (or dotParse() if you have modified either .vtl and saved it to your dotCMS Website Browser) to embed them in a contentlet or widget. That should cover everything you need for logins and registrations. Once you’ve grown comfortable with this, you’ll find you can also embed the login form in popups or modal windows as well, to provide logins anywhere on the site, without forcing people through the login page.

So, your users are all logged in. Now what? Let’s assume you’ve made a special user role for front end users (like Website User), and maybe you’ve granted that role the ability to see some special, protected pages, maybe a special news feed or premium content. That’s easy. To use that user’s account, you need to get familiar with the CMSUserWebAPI viewtool. You access it by calling the $cmsuser class. Specifically, there are two methods that will give you the user object information:

  • $cmsuser.getLoggedInUser($request)*
  • $cmsuser.getUserProxy($user)

These will each return a different object. The former allows you to set up a user object that is equivalent to $user (*in that way it’s not actually needed, because you can always use $user when there is a logged in user. It is the same as the object reference returned by getLoggedInUser()). The latter is somewhat more useful, as it returns an object based on the UserProxy model, which has a serialized form that gives you easy access to all the user account information. Even cooler, you can set it up just like working with a piece of content. Observe:

## SEE IF A USER IS LOGGED IN
#if($user)
    #set($thisUser = $cmsuser.getUserProxy($user).getMap())
<ul>
    <li>First Name: $!{thisUser.firstName}</li>
    <li>Last Name: $!{thisUser.lastName}</li>
    <li>Full Name: $!{thisUser.fullName}</li>
    <li>Email Address: <a href="mailto:$!{thisUser.emailAddress}">$!{thisUser.emailAddress}</a></li>
    <li>Company: $!{thisUser.company}</li>
</ul>
#else
<p>Oh crap, there's no user set right now!</p>
#end

I knew about the getMap() method from the UserProxy API. This takes all the fields from the user profile and produces a hashmap out of them. Now you can easily create custom welcome messages, user directories, and other tools based on user data. Obviously there can be everything from the obvious stuff above to user IDs and the custom fields you may have chosen to add. You can dig through them all just by dropping the $thisUser variable into the HTML and let it print out the hash map.

If you think outside the box, you can create clever custom content on pages, for instance, all users with a certain email address could be shown a particular ad on the page:

#if($user)
    #set($thisUser = $cmsuser.getUserProxy($user).getMap())
    #if($thisUser.emailAddress.substring($thisUser.emailAddress.indexOf('@')) == '@gmail.com')
        ...then do stuff for Gmail users...
    #else
        ...do other stuff for everyone else...
    #end
#end

Similarly, if you are using multiple front end roles for things like subscription levels, you can control what a user sees on a page (you can also do this to an extent with individual containers and contentlets by permissioning them appropriately, but this method would be for mixing different things in a single item, whereas permissions only work on whole items). In the CMSUserWebAPI, there is a method called isUserRole() that will check if a user has a particular role and return a boolean (true or false) based on it. You can build custom navigation based on the results, show or hide ads, or enable/disable advanced features. You’re in control.

#if($user)
    #if($cmsuser.isUserRole($user, "Level 1 Subscriber"))
        ...then do stuff for those users...
    #elseif($cmsuser.isUserRole($user, "Level 2 Subscriber"))
        ...do other stuff for people who are more important...
    #else
        ...treat other people like garbage...
    #end
#end

You can also use the CMSUserWebAPI to find users based on methods like getUserByEmail() or getUserByUserId(). This can be very helpful for building tools like a member directory search:

<form method="post" action="$!{VTLSERVLET_URI}">
    <input type="text" name="email" id="email" value="$!{request.getParameter('email')}" /> <input type="submit" id="submit" value="search" />
</form>
## SEE IF A SEARCH HAS BEEN DONE
#if($request.getParameter('email'))
    #set($email = $request.getParameter('email'))
    ## TRY TO GET THE USER ACCOUNT
    #set($foundUser = $cmsuser.getUserByEmail($email))
    #if($foundUser)
        ## CREATE A HASHMAP FOR THE FOUND USER
        #set($foundUserMap = $cmsuser.getUserProxy($foundUser).getMap())
<p>Found user: $!{foundUserMap.fullName} ($!{email})</p>
    #else
<p>No user found with that email address.</p>
    #end
#else
<p>Enter an email address above and click search.</p>
#end

Something else you can do that is not immediately related to the user’s account, but is related to the visit, is getting access to the clickstream. You can use this as a way of seeing if someone has already gone somewhere on your site, or to offer assistance if they have hit a lot of pages without taking a certain action. The clickstream is part of the session, so we get it from the session object with getAttribute():

## SET UP CLICKSTREAM DATA
#set($clickstream  = $session.getAttribute("clickstream"))
#set($clickstreamRequests = $clickstream.clickstreamRequests)

## DEFAULT THE VARIABLE
#set($visitedProductX = false)

## LOOP THROUGH CLICKSTREAM
#foreach($click in $clickstreamRequests)
	#if($click.requestURI.indexOf("/products/product-X.dot") > -1)
		 #set($visitedProductX = true)
	#end
#end

## IF THEY WERE AT THE PRODUCT, OFFER AN INCENTIVE
#if($visitedProductX)
<p>We see you were checking out Product X. If you buy it now, you get 98.7% off the price!</p>
#end

## IF THEY'VE BEEN TO A LOT OF PAGES, OFFER ASSISTANCE
#if($clickstreamRequests.size() > 10)
<p>We hope you're finding what you need. If not, <a href="help-me.dot">click here to get help from one of us</a>.</p>
#end

And just for fun, you can reach right into the cookie jar and pull out everywhere the visitor has been that session, allowing you to check for trends, repeat visits to a page, and other things:

#set($clickstream  = $session.getAttribute("clickstream"))
#set($clickstreamRequests = $clickstream.clickstreamRequests)
<table>
	<tr>
		<td>Click</td>
		<td>Page</td>
		<td>Time on Page</td>
	</tr>
#foreach($click in $clickstreamRequests)
	#set($diff = 0)
	#if($math.add($math.toInteger($velocityCount),1) < $clickstreamRequests.size())
		#set($next = $clickstreamRequests.get($velocityCount))
		#set($diff = $next.timestamp.getTime() - $click.timestamp.getTime() )
		#set($diff = $math.toInteger($math.div($diff, 1000)))
		#if($diff >60)
			#set($diff = "${math.toInteger($math.div($diff, 60))}m ${math.toInteger($math.mod($diff, 60))}s")
		#else
			#set($diff = "${math.toInteger($math.add($diff, 1))}s")
		#end
	#end
	#if($click.queryString.length() > 0)
		#set($queryVars = "?$!{click.queryString}")
	#end
	<tr>
		<td>${math.add($math.toInteger($velocityCount),1)}</td>
		<td><a href="$!{click.requestURI}$!{queryVars}" target="_blank">$!{click.requestURI}$!{queryVars}</a></td>
		<td align="center">$!diff</td>
	</tr>
#end
#if(!$clickstreamRequests || $clickstreamRequests.size() == 0)
	<tr>
		<td colspan="3">No Data</td>
	</tr>
#end
</table>

This is far from all the CMSUserWebAPI can do for you with user accounts, but hopefully this gives you an idea of just how much customization you can do for users based on whether or not they are logged in. Be sure to review the API Documentation, and also check out the new 1.9 demos showing some of this in action.


Photo credit: Attribution Some rights reserved by Marc_Smith


No comments yet.

Leave a Reply