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…
Name: getMyGroupsAdvanced
Client callable: True
Script:
//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;
}
How do I make this work?
javascript:getMyGroupsAdvanced(5).GetGroupFilter(‘database,network’)
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.
Great script!
thank you for all your work!
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.
Excellent, thank you very much, Mark! Works great!
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!
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??
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.
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.
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.
Sounds like maybe the issue is with your instance then. Can you reproduce this in the ServiceNow demo instance?
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.
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
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.
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!
I think you could simplify this quite a bit. Try putting this within the ‘getMyDivision’ function.
return gs.getUser().getRecord().getValue(‘u_department’);
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.
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.
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!
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?
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.
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?
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.
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?
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.
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
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.
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.
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?
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.
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?
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!
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?