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 script include. The script include must also be marked as ‘Client callable’ so that you can call the function from filter/condition builders. The settings for the script include are shown here…

getMyGroupsAdvanced Script Include
Name: getMyGroupsAdvanced
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 groupArr = [];
var finalArr = [];

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(var 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 = [];
   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;
}