Advanced ‘getMyGroups’ Function

/, System Definition/Advanced ‘getMyGroups’ Function

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 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;
}
By | 2018-07-09T15:00:10-06:00 May 4th, 2010|Categories: Script includes, System Definition|Tags: |33 Comments

About the Author:

33 Comments

  1. Bill Collins May 4, 2010 at 12:27 pm - Reply

    How do I make this work?

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

    • Mark Stanger May 4, 2010 at 1:18 pm - Reply

      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.

  2. Xavier Brumioul May 4, 2010 at 7:04 pm - Reply

    Great script!

    thank you for all your work!

  3. garrett.griffin May 10, 2010 at 4:07 am - Reply

    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.

  4. Tim H June 14, 2010 at 5:49 am - Reply

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

  5. Marc G July 22, 2010 at 2:06 am - Reply

    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!

  6. Andrew February 6, 2012 at 9:43 am - Reply

    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??

    • Mark Stanger February 6, 2012 at 10:38 am - Reply

      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.

      • Andrew February 17, 2012 at 4:55 am - Reply

        Thanks for the reply Mark, I find it strange that this is not something that is easily amended, i.e. to change the search on groups for the user. Im sure this is something that many people have come across. It doesnt make sense not to be able to see the applicable hierarchy you are in, so it really does seem like a big limitation. Thanks again for the information and advice.

  7. Aric February 17, 2012 at 4:00 pm - Reply

    This seems to be working, but for some reason along with all the managers groups, it also shows tasks without an assignment group. Anyway to stop this from happening?

    p.s. Adding a filter that says Assignment Group is not blank does not seem to work.

    • Mark Stanger February 17, 2012 at 4:02 pm - Reply

      Sounds like maybe the issue is with your instance then. Can you reproduce this in the ServiceNow demo instance?

      • Aric February 20, 2012 at 1:05 pm - Reply

        Discovered the actual problem I’m having is that when trying to return closed and open tasks, the open tasks return fine but closed tasks display without details. I haven’t yet discovered how to resolve this.

  8. Jeff Welton March 5, 2012 at 1:03 pm - Reply

    This did not work at all for me. I also tried it on the demo site and it did not work. The Assignment Group came back blank. Any ideas? Are there any other steps besides creating the function in the Global Business Rules?

    Please let me know. Thanks!
    Jeff

    • Mark Stanger March 5, 2012 at 2:13 pm - Reply

      Hey Jeff,

      I just tested this at https://demo13.service-now.com and it works fine there. I’ve modified the ‘My Groups Work’ module to use the function. You can validate that it works correctly by impersonating Don Goodliffe. Make sure to check that your business rule is set up on the ‘Global’ table and that the ‘Client callable’ checkbox is checked.

  9. Paul Davidson July 30, 2012 at 7:54 am - Reply

    I have a user defined field called Division on my sys_user table, this field is just a reference field back to cmn_department.

    I wanted to create a function similar to getMyGroups() & getMyAssignments() to use in filters.

    This is not working for me. Here is my getMyDivision business rule:
    Name = getMyDivision
    table = Global[global]
    Client callable = true
    Active = true
    Script:

    function getMyDivision(){

    var MyDivision;
    var uID = gs.getUserID();

    var user = new GlideRecord(‘sys_user’);
    user.addQuery(‘user’, uID);
    user.query();

    while(user.next()){
    MyDivision = user.u_division.toString(); //u_division is simply a reference field back to cmn_department
    }

    return MyDivision;
    }

    All the other examples (getMyGroups) seem to simply return an array of Strings, where each string is the sys_id. That is what I am basically doing here, but my reports don’t seem to work when I use the javascript:getMyDivision()

    I have also tried returning an array of Strings, a cmn_department variable, and an array of cmn_department variables nothing works

    Is something wrong with my script? I couldn’t seem even find the getMyGroups function to look at that code, but did find some reference that makes me believe that is a server side function tied to the User class.

    Any help is greatly appreciated!

    • Mark Stanger July 30, 2012 at 9:09 am - Reply

      I think you could simplify this quite a bit. Try putting this within the ‘getMyDivision’ function.

      return gs.getUser().getRecord().getValue(‘u_department’);

      • Paul Davidson July 30, 2012 at 9:30 am - Reply

        Gee thanks! much simpler and the function now works in my filters.
        So, the getValue function grabs the actual sys_id, where in my script the u_division.toString() was probably not neccessary.

  10. Ahmed Abdrabalnabi November 20, 2012 at 11:35 pm - Reply

    Anyone else having issue with this business rule after Berlin upgrade? It was working fine before upgrading. After upgrading to Berlin, it works only for users with “admin” role. It does not return any groups if the user does not have “admin” role.

  11. Jeff Welton November 27, 2012 at 7:17 am - Reply

    This still works for us, but please note, In order to get this to work initially (prior to Aspen or Berlin) I needed remove the following lines:

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

    Not sure is that will help you or not. good luck!

    • Ahmed Abdrabalnabi November 30, 2012 at 1:35 am - Reply

      Thanks Jeff.

      I figured what was causing this. The property “glide.script.use.sandbox” was enabled. When I disabled it, the business rule started working again for non-admin users. Are you having this property disabled?

      • Mark Stanger November 30, 2012 at 6:06 am - Reply

        Disabling that property probably isn’t a good idea, and it shouldn’t be necessary if you’ve got the ‘Client callable’ checkbox checked in your global business rule.

        • Ahmed Abdrabalnabi November 30, 2012 at 10:58 pm - Reply

          The property was enabled before Berlin and the business rule was running fine. After Berlin, the issue started. The ‘client callable’ checkbox was always checked.

          I don’t have High Security Settings plugin enabled. Could this be the reason?

          • Mark Stanger December 1, 2012 at 5:47 am - Reply

            It may be due to the lack of High Security, but I doubt it since this solution was in place before High Security was the norm. I just tested this again on my Berlin release (with High Security enabled) and it works fine. You could confirm by testing in a ServiceNow demo instance.

  12. Tony Nelson August 20, 2013 at 10:28 am - Reply

    This was working fine for us until we started testing Calgary and then I ran into the same issues discussed above. This only works for users who have the admin role. We currently do NOT have the High Security plugin enabled so that isn’t the issue. I tried commenting out the following line as suggested by Jeff above with no luck:

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

    Did anyone find a solution to this with Berlin?

    • Tony Nelson September 3, 2013 at 9:37 am - Reply

      another note…if I go to demo and put this into an instance with Calgary it works. It seems to be related to the upgrade with this in place.

  13. Howard Elton July 13, 2014 at 11:18 pm - Reply

    Hi Mark,
    I did this in Dublin and it spits out this warning when I save the rule”getMyGroupsAdvanced Business Rule contains code outside of a function. Code which exists outside of a function will run against every transaction; therefore, all code should be within a function and invoked as needed.”. I am a bit hesitant to turn it on now, as it seems serious.

    Any ideas?
    Regards,
    Howard Elton

    • Mark Stanger July 14, 2014 at 7:56 am - Reply

      I haven’t ever had anyone report an issue related to this. The only code outside of a function are a couple of variable declarations so there shouldn’t be a huge concern.

    • Mark Stanger July 14, 2014 at 3:31 pm - Reply

      I’ve just updated this to use a script include instead of a global business rule. That should solve the error you’re getting. Performance should really be the same, but this will follow current best-practice more closely.

  14. Tony Nelson January 12, 2015 at 9:16 am - Reply

    Mark,

    We have been successfully using this for a number of years now. We are currently on Calgary testing Eureka. In Eureka when I go to “My Groups Work”, in the assignment group field of the filter it is blank. We have indeed changed the old global Business Rule to the new Script Includes. I know we have a lot of groups that the manager is not an assignee in the group but wants that visibility which this has done. Any thoughts?

    • Tony Nelson January 12, 2015 at 9:53 am - Reply

      I should have dug through the thread better. The problem I was experience was addressed by Ahmed Abdrabalnabi above. After reading the thread I checked and sure enough the issue would not occur if the user had the admin role. So I checked the system property of “glide.script.use.sandbox” and found it was set to true…changed that to false and it now works. I will admit that it still doesn’t make complete sense because that property itself states “If enabled, only those business rules and script includes with the “Client callable” checkbox set to true are available” and as stated, the script includes here has “Client callable” checked. I did confirm that I didn’t miss that. Just a bit strange.

  15. William December 10, 2015 at 1:01 pm - Reply

    I notice this pulls from the Task table but I need to get a state from the incident table, any suggestions on how I can accomplish this?

  16. Jean-Luc March 4, 2016 at 6:05 am - Reply

    I’ve got the same problem on Geneva. If the user is non admin and glide.script.use.sandbox is true then the returned groups from the module ‘My Groups work’ is empty. What is strange is when the same function is called from a script in an application menu is works. I try to set glide.script.use.sandbox to false but I got and error message saying this operation is unsafe.

    Finaly I’ve succeeded with the following code :

    var GMGA = Class.create();
    GMGA.prototype = Object.extendsObject(AbstractAjaxProcessor, {
    type: ‘GMGA’
    });
    var maxDepth;

    var groupArr = [];
    var finalArr = [];

    GMGA.getMyGroupsAdvanced = function (inputDepth) {

    ….

    The rest of the code is untouched.
    For calling the function use : javascript:GMGA.getMyGroupsAdvanced(5)’

    For me it’s a kind of black magic!

  17. Billi L May 10, 2016 at 12:41 pm - Reply

    I tried using a similar script to add a parent group member to child groups upon insert. Do you have a business rule or SI that is similar to accomplish this?

Leave A Comment