
- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on ‎11-11-2021 05:43 PM
A massive frustration of mine, and of many others it seems (see This Idea) is that sharing Reports on a dashboard is a major hassle.
I've built a UI action, from the Dashboard Properties page, that will force a sync between the sharing permissions for the dashboard with all reports on that dashboard.
It is additive only, so you will not break the sharing permissions of reports that are on multiple dashboards or shared separately.
Attached is the UI Action XML, so you can just import this into your instance and you're good to go, or you can continue reading the code below and explanation so you can edit it to suit your purpose.
How to use
Open up a dashboard, and go to Dashboard Properties
Then the UI Action is a link in the related links section
How it works
Dashboards can be shared with 3 different things, Users, Groups, and Roles.
Reports can be shared with either Users or Groups, or they can be shared with Everyone and restricted by role. They can't be both.
As a workaround for the Role limitation, the code instead looks up all individuals with a particular role, and gives them User access permission to the report. The net result is the same, all users who can see the dashboard, can also see the reports on the dashboard.
Recommended changes for your instance
If you have very large groups/role inheritance (e.g. hundreds/thousands of users) it's probably a good idea to wrap this UI Action script into a Script Action, and trigger an event with the UI Action for the system to process later. However you wont get the instant feedback on what you were sharing
If you don't care about getting told what was shared and with who, then you can comment out or change the gs.addInfoMessage lines.
var dashboardTabM2M = new GlideRecord('pa_m2m_dashboard_tabs');
dashboardTabM2M.addQuery("dashboard", current.sys_id);
dashboardTabM2M.query();
var pages = [];
while (dashboardTabM2M.next()) {
pages.push(dashboardTabM2M.tab.page.sys_id + "");
}
var reportIds = [];
pages.forEach(function(pageID) {
var portals = new GlideRecord("sys_portal");
portals.addQuery("page", pageID);
portals.query();
while (portals.next()) {
var pageProp = new GlideRecord("sys_portal_preferences");
pageProp.addQuery("portal_section", portals.getUniqueValue());
pageProp.addQuery("name", "sys_id");
pageProp.query();
if (pageProp.next()) {
reportIds.push(pageProp.getValue("value"));
}
}
});
var shareUsers = [];
var shareRoles = [];
var shareGroup = [];
var dashboardPerms = new GlideRecord("pa_dashboards_permissions");
dashboardPerms.addQuery("dashboard", current.getUniqueValue());
dashboardPerms.query();
while (dashboardPerms.next()) {
if (dashboardPerms.type == "2")
shareGroup.push(dashboardPerms.getValue("group"));
if (dashboardPerms.type == "1")
shareRoles.push(dashboardPerms.getValue("role"));
if (dashboardPerms.type == "3")
shareUsers.push(dashboardPerms.getValue("user"));
}
var reportGR = new GlideRecord("sys_report");
reportGR.addQuery("sys_id", "IN", reportIds.join(","));
reportGR.query();
while (reportGR.next()) {
reportGR.setValue("user", "group");
reportGR.update();
}
reportIds.forEach(function(reportId) {
var validCheck = new GlideRecord('sys_report');
if(!validCheck.get(reportId))
return; // not a real report id
shareGroup.forEach(function(groupId) {
var reportPerm = new GlideRecord("sys_report_users_groups");
reportPerm.addQuery("report_id", reportId);
reportPerm.addQuery("group_id", groupId);
reportPerm.query();
if (reportPerm.next()) {
gs.addInfoMessage(gs.getMessage("Report {0} is already shared with group {1}", [reportPerm.getDisplayValue("report_id"), reportPerm.getDisplayValue("group_id")]));
} else {
reportPerm.newRecord();
reportPerm.setValue("report_id", reportId);
reportPerm.setValue("group_id", groupId);
reportPerm.insert();
gs.addInfoMessage(gs.getMessage("Report {0} shared with group {1}", [reportPerm.getDisplayValue("report_id"), reportPerm.getDisplayValue("group_id")]));
}
});
shareUsers.forEach(function(userId) {
var reportPerm = new GlideRecord("sys_report_users_groups");
reportPerm.addQuery("report_id", reportId);
reportPerm.addQuery("user_id", userId);
reportPerm.query();
if (reportPerm.next()) {
gs.addInfoMessage(gs.getMessage("Report {0} is already shared with user {1}", [reportPerm.getDisplayValue("report_id"), reportPerm.getDisplayValue("user_id")]));
} else {
reportPerm.newRecord();
reportPerm.setValue("report_id", reportId);
reportPerm.setValue("user_id", userId);
reportPerm.insert();
gs.addInfoMessage(gs.getMessage("Report {0} shared with user {1}", [reportPerm.getDisplayValue("report_id"), reportPerm.getDisplayValue("user_id")]));
}
});
shareRoles.forEach(function(roleId) {
var userIds = [];
var userRoleGR = new GlideRecord("sys_user_has_role");
userRoleGR.addQuery("user.active", true);
userRoleGR.addQuery("role", roleId);
userRoleGR.query();
var roleName;
while (userRoleGR.next()) {
if(!roleName){
roleName = userRoleGR.getDisplayValue("role");//for info msg
}
userIds.push(userRoleGR.getValue("user"));
}
var au = new global.ArrayUtil();
userIds = au.unique(userIds);
var reportName;
userIds.forEach(function(userId) {
var reportPerm = new GlideRecord("sys_report_users_groups");
reportPerm.addQuery("report_id", reportId);
reportPerm.addQuery("user_id", userId);
reportPerm.query();
if (reportPerm.next()) {
// gs.addInfoMessage(gs.getMessage("Report {0} is already shared with user {1}", [reportPerm.getDisplayValue("report_id"), reportPerm.getDisplayValue("user_id")]));
} else {
reportPerm.newRecord();
reportPerm.setValue("report_id", reportId);
reportPerm.setValue("user_id", userId);
reportPerm.insert();
// gs.addInfoMessage(gs.getMessage("Report {0} shared with user {1}", [reportPerm.getDisplayValue("report_id"), reportPerm.getDisplayValue("user_id")]));
}
if(!reportName){
reportName = reportPerm.getDisplayValue("report_id");
}
});
gs.addInfoMessage(gs.getMessage("Report {0} shared with {1} users who have the {2} role",[reportName, userIds.length + "", roleName]));
});
});
action.setRedirectURL(current);
- 8,936 Views

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
It looks very handy. Thanks for the share !
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thanks for this code. It helped very much.
However, on our instance it has been the case that the field 'page' has been empty at the dashboard tabs, but instead I had to use the field 'canvas_page' to find the related reportIDs. Then the rest of the script worked fine.
As this might help someone else as well, I will just paste the code here. You need to replace the part before the line
var shareUsers = [];
with it:
var dashboardTabM2M = new GlideRecord('pa_m2m_dashboard_tabs');
dashboardTabM2M.addQuery("dashboard", current.sys_id);
dashboardTabM2M.query();
var canvas_pages = [];
while (dashboardTabM2M.next()) {
canvas_pages.push(dashboardTabM2M.tab.canvas_page.sys_id + "");
}
var portal_widgets = [];
canvas_pages.forEach(function(canvas_pageID) {
var grid_canvas_pane = new GlideRecord("sys_grid_canvas_pane");
grid_canvas_pane.addQuery("grid_canvas", canvas_pageID);
grid_canvas_pane.query();
while (portals.next()) {
portal_widgets.push(grid_canvas_pane.portal_widget + '');
}
});
var reportIds = [];
portal_widgets.forEach(function(portal_widgetIDs) {
var portals = new GlideRecord("sys_portal");
portals.addQuery("sys_id", portal_widgetIDs);
portals.query();
while (portals.next()) {
var pageProp = new GlideRecord("sys_portal_preferences");
pageProp.addQuery("portal_section", portals.getUniqueValue());
pageProp.addQuery("name", "sys_id");
pageProp.query();
if (pageProp.next()) {
reportIds.push(pageProp.getValue("value"));
}
}
});
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@J_r_my POTTIER and @Luke Van Epen .. first.. love this option.. however, is this considered a customization? Any impacts or conflicts with future ServiceNow rollouts?

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@Duane van Gesse you're not modifying anything baseline so it wont impact future upgrades.
If you're worried about 'good' vs 'bad' customisation, I suggest you read this whitepaper on the topic:
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
I just wanted to tell you thank you so much for this!!!! With no ability to publish this at least gives us options if we can't share to roles in reports!
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
This is worth its weight in gold! Appreciate it.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Absolute game changer, thanks @Luke Van Epen
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
This solution is great. Thank you for sharing.
It does not work with deleting a user/group/role. When we delete a user/group/role from the dashboard, we want to click the "Sync sharing permissions" UI link to sync and delete the user/group/role from all the reports in the Dashboard.
How to implement it? Thank you.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thank you for sharing.
It does not work with deleting a user/group/role. If I delete a user/group from the dashboard permission and use sync, it does not remove the user/group. Can this being improved?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Very helpful and time saving! Thanks for the effort and sharing @Luke Van Epen

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@Karin Duijnker1 @Yingbo Wang As I mentioned in the article, this use case only covers additions, not deletions. This prevents cases where multiple dashboards contain the same report, where the user needs to keep permissions on one dashboard but not the other. If you also delete permissions using the sync, the user would have permissions to the underlying reports removed in all places. I do not want that, but you are free to modify this to suit. I would personally be handling that scenario in an after-delete business rule for the affected tables, rather than in the sync button.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Excellent work. Thanks a million !
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thanks @Luke Van Epen, very useful!
Q-I appreciate this was acknowledged in the original post, but has anyone since managed to amend the script to allow the role to cascade down from dashboard to reports rather than adding the members to the report who have that role?