I’ve written a piece before on roles in XPages, but since that dealt with using the ACL to limit access to a page and not about the programmatic use of roles, I wanted to return to the issue.
Back in old Notes, if we wanted to hide something based if the user did not have a certain role in the ACL, we could use a pretty simple formula:
!@IsMember(“[roleNameHere]”; @UserRoles)
Remarkably, it’s not that much harder in XPages, but there are some important wrinkles to be concerned about. As noted previously and by Russ Maher on his blog, you must remember to use your brackets [] and also keep in mind the result you want (true or false). Remember that the XPages formulas are ‘rendered’ formulas, meaning you want to ‘true’ to display the result and ‘false’ to hide it, so you’d use:
@IsMember(“[roleNameHere]”, context.getUser().getRoles());
Here’s the more complete source code for the rendered formula:
<xp:this.rendered><![CDATA[${javascript:@IsMember("[Testing]", context.getUser().getRoles());}]]></xp:this.rendered>
Now, I’m not sure what impact of referencing the user roles that way has on performance, but since I now I’m using them in many rendered formulas all over my application, I decided to compute it once and then reuse it many times. I put a few extra lines into a control that’s on my main application layout control to drop it into a sessionscope variable. I suppose I might shave a millisecond off if I only computed that once per session, but I didn’t go that far.
<xp:this.beforePageLoad> <![CDATA[${javascript:var roles = context.getUser().getRoles(); sessionScope.userRoles = roles;}]]> </xp:this.beforePageLoad>
Then, in order to check to see if my user has one of three roles when rendering an item, I could use this code:
<xp:this.rendered><![CDATA[${javascript:var manager = @IsMember("[InventoryMgr]", sessionScope.userRoles); var viewer = @IsMember("[InventoryViewer]", sessionScope.userRoles); var grantsManager = @IsMember("[InvMgrGrants]", sessionScope.userRoles); if ( manager || viewer || grantsManager ) { return true }; return false; }]]> </xp:this.rendered>
Now, I also found that sometimes I need to determine the user’s role in my Java code. That’s also not that hard, except that vectors are not quite arrays. If there is a single value, it’s not the same as a multiple value vector. I’m not sure if this is a Notes implementation issue or if it’s the way Java always handles vectors. That is, if it’s a single value, it puts our brackets [] around the value, but it does NOT for multiple values. So, when I was using the code written for us, it wasn’t always picking up the roles correctly. Once I simply told it to check both ways, our code worked more cleanly. (The reference to ExtLibUtil comes from the original code, so I didn’t modify it.)
public Vector getCurUserRoles() { try { curUserRoles = ExtLibUtil.getCurrentDatabase().queryAccessRoles(ExtLibUtil.getCurrentSession().getEffectiveUserName()); } catch (NotesException e) { this.debug("getCurUserRoles ERROR: " + e.getMessage(), "error"); curUserRoles = new Vector(); } return curUserRoles; } public boolean hasRole(String role, String uname) { try { Vector roles = this.getCurUserRoles(); if (roles.contains(role)) return true; if (roles.contains("["+role+"]")) return true; return false; } catch (Exception e) { this.debug("hasRole ERROR: " + e.getMessage(), "error"); return false; } }
Note that the debugging uses Mark Leusink‘s DebugToolbar, which I highly recommend to everyone.
In SSJS there is another approach you may want to add:
@IsMember(“[Role]”, database.queryAccessRoles(session.getEffectiveUserName()))
to render a component depending on that role.
Great Post!!!! Thanks for sharing! Really good content.