TUESDAY, FEBRUARY 07, 2012

Advanced ‘getMyGroups’ Function

I

f you’ve taken a look at the ‘My Groups Work’ module (or maybe a particular security rule or report) you may have noticed that the condition or filter for that record makes a function call to return a list of values to compare against the value in the given field. In the case of the ‘My Groups Work’ module under the ‘Service Desk’ application there is a function called ‘getMyGroups’ that is used to identify task records where the assignment group value is one of the groups for which the current user is a member.

The ‘getMyGroups’ function simply returns an array of group sys_id values for the groups that a user belongs to. I saw a forum posting recently that pointed out (correctly) that the ‘getMyGroups’ function only returns group membership, but doesn’t return groups where the user is listed as the manager. The function also doesn’t attempt to return child groups of the groups where a person is a manager or group member. So, if I am the Director of Operations and I want to see a list of all tasks for the Operations group (which I am a manager of) as well as the sub-groups of that group, I would have to be added specifically to each sub-group to have all of the groups that I am interested in tracking be displayed in the list.

With some help from John Andersen, I’ve created the ‘Advanced getMyGroups’ function. This function is designed to give users a better way to display group membership information. It is used in the same way that you would use the ‘getMyGroups’ function, but also includes an optional parameter that allows you to return groups managed by an individual and/or sub-groups of a given group where I am a member or manager. The ‘maxDepth’ parameter returns the values as shown below…

  • No maxDepth returns groups where user is a group member (Same as current ‘getMyGroups’ function)
  • maxDepth of 0 returns groups where user is a group member or a manager
  • maxDepth greater than 0 returns groups where user is a group member or a manager PLUS all child groups up to ‘maxDepth’ levels deep

The most common usage of this function is probably in a filter on a module or a report. So, if I were creating a module to show all tickets where I am a member of the group OR where I am the group manager, PLUS all of the sub-groups up to 5 levels deep I could use the following in my filter…

‘javascript:getMyGroupsAdvanced(5)’

In order to use the function, you need to set up a new business rule on the ‘Global’ table. The business rule must also be marked as ‘Client callable’ so that you can call the function from filter/condition builders. The settings for the business rule are shown here…

GetMyGroupsAdvanced Business Rule
Name: getMyGroupsAdvanced
Table: Global
Client callable: True
Script:

//Maximum number of levels to search for groups.
//No maxDepth returns groups where user is a group member (Same as 'getMyGroups' function)
//maxDepth of 0 returns groups where user is a group member or a manager
//maxDepth greater than 0 returns groups where user is a group member or a manager PLUS all child groups up to 'maxDepth' levels deep
var maxDepth;

var x;
var groupArr = new Array();
var finalArr = new Array();

function getMyGroupsAdvanced(inputDepth){
   //Set maxDepth to the given depth
   maxDepth = inputDepth;
   //Get the sys_id of the current user
   var uID = gs.getUserID();

   //Get all active groups where user is a member
   var grmember = new GlideRecord('sys_user_grmember');
   grmember.addQuery('user', uID);
   grmember.addQuery('group.active', true);
   grmember.query();
   while(grmember.next()){
      //Push the group sys_id values into the group array
      groupArr.push(grmember.group.toString());
   }

   //If a maxDepth value is given then include groups where user is a manager
   if(maxDepth >= 0){
      //Get all active groups where user is a manager
      var grman = new GlideRecord('sys_user_group');
      grman.addQuery('manager', uID);
      grman.addQuery('active', true);
      grman.query();
      while(grman.next()){
         //Push the group sys_id values into the group array
         groupArr.push(grman.sys_id.toString());
      }
   }

   //Remove any duplicates from group string
   groupArr = checkDuplicates(groupArr);

   //If maxDepth > 0 then check for child groups
   if(maxDepth > 0){
      //Iterate through all of the groups and return all children for each group
      for(x in groupArr){
         //Only process if group sys_id is not already in the returned array
         if(finalArr.length == 0 || finalArr.join().indexOf(groupArr[x]) == -1){
            finalArr.push(groupArr[x]);
         }
         recursChildGroups(groupArr[x], 0);
      }
   }

   //If we didn't check for child groups then just return the group array
   else{
      finalArr = groupArr;
   }

   //Return the array of group IDs
   return finalArr;
}

function recursChildGroups(group, depth){
   //Increase the current depth
   depth++;
   //If we have gone more than the allowed depth then abort for the given group
   if(depth > maxDepth){
      //('Possible recursive group loop with group ' + group);
      return null;
   }
   //Make sure that we have a valid group ID
   if(group){
      if(group.toString().length == 0){
         return null;
      }
      //Query for the active child groups of this group
      var rec = new GlideRecord('sys_user_group');
      rec.addQuery('parent', group);
      rec.addQuery('active', true);
      rec.query();
         while(rec.next()){
            //If the group has already been added then do not add again
            if(finalArr.join().indexOf(rec.sys_id.toString()) > -1){
               continue;
            }
            //Add the group to the final array
            finalArr.push(rec.sys_id.toString());
            //Find the child groups of this group
            recursChildGroups(rec.sys_id.toString(),depth);
         }
   }
   return null;
}

function checkDuplicates(a){
   //Check all values in the incoming array and eliminate any duplicates
   var r = new Array();
   o:for(var i = 0, n = a.length; i < n; i++){
      for(var x = 0, y = r.length; x < y; x++){
         if(r[x]==a[i]){
            continue o;
         }
      }
      r[r.length] = a[i];
   }
   return r;
}
Tags:  

Comments

Posted On
May 04, 2010
Posted By
Bill Collins

How do I make this work?

javascript:getMyGroupsAdvanced(5).GetGroupFilter(‘database,network’)

Posted On
May 04, 2010
Posted By
Mark Stanger

I don’t think that you do. As far as I’ve seen, the ‘GetGroupFilter’ function is used for reference qualifiers but wouldn’t be applied in this way for a module or report filter.

Posted On
May 04, 2010
Posted By
Xavier Brumioul

Great script!

thank you for all your work!

Posted On
May 10, 2010
Posted By
garrett.griffin

Thanks for this, greatly appreciated! Perfect timing too, I started looking for a way to do exactly this on the same day you posted it.

Posted On
Jun 14, 2010
Posted By
Tim H

Excellent, thank you very much, Mark! Works great!

Posted On
Jul 22, 2010
Posted By
Marc G

I’ve also noticed that getMyGroups returns groups from above, i.e. parents of any groups you are a member of.

I am a member of Problem, which has a parent of SLM

when running getMyGroups I also get tickets assigned to SLM, I’m only interested in Problem group tickets.

Oddly, the filter also displays the parent group as though I’m only filtering on that.

Tasks-ASSIGNMENTGROUPIS SLM

Weird!

Posted On
Feb 06, 2012
Posted By
Andrew

This is great, thanks for this. Kind of weird there is no option in ServiceNow to do this automatically.
Any ideas on how we would get this code to work in the report section. i.e. if we give users the ability to create reports for their groups, out of the box they can create reports for their groups and any of the associated parent groups of the child groups they belong to. We need this to be the other way around, i.e. if you are a member of a parent group and you click on the visible to: Group button you should see that parent group and also the parent s children??

Posted On
Feb 06, 2012
Posted By
Mark Stanger

The report page is back-end XML so there’s no way to directly manipulate the behavior of that page. You might be able to use a UI script to manipulate the functions there but that would be a pretty significant hack that would probably end up breaking during an upgrade. It’s probably best to request this as an enhancement with ServiceNow support.

Leave a Reply


Notify me of followup comments via e-mail. You can also subscribe without commenting.

Latest Comments

  • Mark Stanger: This linkage all happens for you if you use the task survey plugin. You can look on the wiki for more...
  • Vineeth: I want a way in which if a survey is filled in by the user the response are stored in the survey response...
  • Mark Stanger: This functionality doesn’t connect to an FTP server. See this line in the post above…...
  • Mark Stanger: The report page is back-end XML so there’s no way to directly manipulate the behavior of that...