02 Template Users

The Question

Vinny Khosla asked on the JASIG-PORTAL list:

How do i setup a user layout template?
Subsequently, how do I go about creating subsequent user accounts who would inherit the layout template?

Could you please tell me what tables, columns and xml files are involved
here?

Do the new users automatically inherit the layout template?
or Is there a way to force a new user to inherit a certain custom layout
template ?

Answer (credit due to Susan Bramhall for initially posting reponse on JASIG-PORTAL):

The easiest way to create a template is to use ant md5passwd to create the account then login and use the GUI to set up the layout you want.

To map a user to use the template use the PersonDirs.xml to map the some appropriate attribute to uPortalTemplateUserName. The login process for the user will select the layout that matches the value of the uPortalTemplateUserName person attribute.

For example:

       <uidquery>select first_name||' '||Last_name first_last,
       decode(role,'UNDERGRADUATE','student',
                   'GRAD_STUDENT','student',
                   'MP', 'staff',
                   'CT', 'staff',
                   'CAS', 'staff',
                   'SM', 'staff',
                   'FAC_LADDER','faculty',
                   'FAC_NON-LADDER','faculty',
                   'VF','faculty',
                   'FEL','faculty',
                   'PDA','faculty',
                   'PDF','faculty',
                   'proto-user') template,
       from portal_person_directory where netid=?</uidquery>
with attribute template mapped like this:
           <attribute>
               <name>template</name>
               <alias>uPortalTemplateUserName</alias>
           </attribute>

This would cause the value of "role" from the sql query to govern the choice of template. This is a snippet from our actual configuration.

Behind the scenes

The IUserIdentityStore interface is the mechanism for retrieving the unique identifier for the current user in the portal. There is one implementation of this interfact: RDBMUserIdentityStore. Two properties as well as the value of person attributes for the user affect how this class makes the choice of template.

  • The property autoCreateUsers turns template behavior on or off. As distributed the property is set:
    org.jasig.portal.services.Authentication.autoCreateUsers=true
    When set to true, RDBMUserIdentityStore will automatically create all necessary data for a new portal user based on a template user. If set to false users data will not be automatically created. So far there is no other mechanism for creating user data so this property is always set to true.
  • The template user value is determined either by the property defaultTemplateUserName or by mapping a directory attribute to uPortalTemplateUserName. When a user logs in for the first time, RDBMUserIdentityStore first looks for a person attribute named uPortalTemplateUserName. The value of that attribute is expected to be the username of an existing user. The existing user is then the template for the new user. If no attribute is of that name is mapped the template user name comes from the property org.jasig.portal.services.Authentication.defaultTemplateUserName. In the distribution this is set to the user demo:
    org.jasig.portal.services.Authentication.defaultTemplateUserName=demo
    This means that if you make no changes and map no attribute to uPortalTemplateUserName then all new users will be cloned from the user demo.

Templates and layout managers

Simple layout manager (SLM) derives power and flexability from using templates to deliver different content to users based on the values in the person object. However, once a user changes this or her layout a copy is stored and the template is no longer used. So any changes to the template after the user has personalized his or her layout do not appear unless the user resets to the default. Likewise, any change of mapping from user to template user (i.e., cahnges the the value of the template user person attribute for a given person) has no effect once the user has his or her very own copy of layout by having personalized. Since both aggregated layout manager (ALM) and distributed layout manager (DLM) have better mechanisms for pushing content to users, templates can be minimized or eliminated entirely when simple layout manager is not used.

When the template mapping changes

Any given user is either 1) linked to a template user and without a layout of his own, or 2) unlinked from the template and using her very own layout. Users start out linked to their template users. They become unlinked by means of making a layout personalization, e.g. adding, moving, or deleting a channel. On personalization, uPortal copies the template layout content into a new personal layout for this user, and forevermore this user is "unlinked" from the template (until and unless the user resets layout.)
If a user is unlinked from the template layout, then neither changes to the content of the template layout nor changes to the user attribute that determined to which template the user is mapped have any effect.

If a user is linked to a template layout, then when this user's linked template user's layout changes, those changes are reflected in the perceived layout of the user. Since the user didn't have a layout of her own, she is using the template user's layout, and so experiences updates to that template on next login.

But what happens when the user's template attribute itself changes, for example the value changes from "student" to "faculty"? If the user is linked to a template layout, the portal will display the new template user layout (in this case, faculty), delete this user from all the modifiable groups in which she presently resides, and place this user in the new groups associated with the faculty template user. However, if the user is unlinked from the template, the change to template attribute will have no effect (until and unless the user resets her layout).

To restate, providing the user does not customize his or her layout, when the user's portal template attribute values changes, the portal will present the new layout and pull the user out of all past groups that it can (cannot modify read-only groups such as PAGS groups) and place this user into the new groups affiliated with the new template user, whereas in the event that a user modifies his or her layout and thereby breaks association with the template user, then even if the portal template attribute changes, the user will not receive a new layout nor will she change group membership.

Source code snippets implementing the described behavior

Only users without their own layouts are updated
 boolean hasSavedLayout = userHasSavedLayout(portalUser.getUserId());
 if (!hasSavedLayout) {
                       
     TemplateUser templateUser = getTemplateUser(templateName);                       
     if (portalUser.getDefaultUserId() != templateUser.getUserId()) {
            //Update user data with new template user's data
            updateUser(portalUser.getUserId(), person, templateUser);
         }
     }
Changing template affiliation changes group memberships
 protected void updateUser(int userId, IPerson person, TemplateUser templateUser) throws Exception {
      // Remove my existing group memberships
      IGroupMember me = GroupService.getGroupMember(person.getEntityIdentifier()); 
      Iterator myExistingGroups = me.getContainingGroups();
      while (myExistingGroups.hasNext()) {
          IEntityGroup eg = (IEntityGroup)myExistingGroups.next();
          ILockableEntityGroup leg = GroupService.findLockableGroup(eg.getKey(), me.getKey());
          removePersonFromGroup(person, me, leg);
      }
      
      // Copy template user's groups memberships
      IGroupMember template = GroupService.getEntity(templateUser.getUserName(), Class.forName("org.jasig.portal.security.IPerson"));
      Iterator templateGroups = template.getContainingGroups();
      while (templateGroups.hasNext()) {
          IEntityGroup eg = (IEntityGroup)templateGroups.next();
          ILockableEntityGroup leg = GroupService.findLockableGroup(eg.getKey(), me.getKey());
          addPersonToGroup(person, me, leg);     
      }