Skip to content

Commit 3a5b0c1

Browse files
authored
Merge branch 'ServiceNowDevProgram:main' into Track_Tag_Removal_Using_Delete_BusinessRule
2 parents 991faa6 + 2fca83b commit 3a5b0c1

File tree

7 files changed

+228
-0
lines changed

7 files changed

+228
-0
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
3+
<g:ui_form>
4+
<div class="modal-body">
5+
<h1 class="text-center">Important Message</h1>
6+
<p>Please read and acknowledge this important message before proceeding.</p>
7+
<p>
8+
Your access will be revoked if you don't log in for 30 days!
9+
</p>
10+
<div class="text-center" style="margin-top: 20px;">
11+
<button id="acknowledge_btn" class="btn btn-primary" onclick="return closeDialog();">Acknowledge</button>
12+
</div>
13+
</div>
14+
</g:ui_form>
15+
<script>
16+
(function() {
17+
function closeDialogAndRedirect() {
18+
try {
19+
GlideDialogWindow.get().destroy();
20+
} catch (e) {}
21+
22+
// Set user preference
23+
if (typeof setPreference === 'function') {
24+
setPreference('login.consent1', 'true');
25+
}
26+
// Redirect to home.do
27+
window.top.location.href = 'home.do';
28+
}
29+
document.getElementById('acknowledge_btn').addEventListener('click', closeDialogAndRedirect);
30+
})();
31+
</script>
32+
33+
</j:jelly>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
addLoadEvent(function() {
2+
try {
3+
// Skip contexts where GlideDialogWindow isn't available (e.g., Service Portal)
4+
if (typeof GlideDialogWindow === 'undefined' || (window.NOW && NOW.sp))
5+
return;
6+
var prefName = 'login.consent1';
7+
// Only show the dialog when the pref is explicitly 'false'
8+
var val = (typeof getPreference === 'function') ? getPreference(prefName) : null;
9+
var shouldShow = String(val || '').toLowerCase() === 'false';
10+
//alert("val"+" "+val+" "+"shouldShow"+" "+shouldShow);
11+
if (!shouldShow)
12+
return;
13+
var dialog = new GlideDialogWindow('acknowledgement_dialog'); // UI Page name
14+
dialog.setTitle('Acknowledge Message');
15+
dialog.setSize(500, 300);
16+
dialog.render();
17+
} catch (e) {
18+
if (console && console.warn) console.warn('ack loader error', e);
19+
}
20+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
**Create a user preference as follows:**
2+
<img width="1675" height="420" alt="image" src="https://github.com/user-attachments/assets/efcd19dd-f1ad-440a-ae59-10cc63832cad" />
3+
**Create a UI script:**
4+
<img width="1646" height="808" alt="image" src="https://github.com/user-attachments/assets/207f9dfa-4c6c-4686-84ac-3ff5294a0771" />
5+
This script runs during login and checks the user preference.
6+
If the preference is set to false, it displays the acknowledgement popup by calling UI page
7+
8+
**UI Page details:**
9+
<img width="1511" height="897" alt="image" src="https://github.com/user-attachments/assets/74d4be0f-9401-4733-81df-fca8f52b644e" />
10+
Set the user preference to true so that the popup will not appear for every login.
11+
12+
**Output**:
13+
**On user login:**
14+
<img width="1896" height="748" alt="image" src="https://github.com/user-attachments/assets/1af1b7ec-4647-4bb4-a786-8070817b21f8" />
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The purpose of this code is to conditionally apply a specific label (label_entry) to an Incident when the person updating the record is the same as the caller. If the update is made by someone else, the label is removed.
2+
This mechanism helps fulfillers quickly identify caller driven updates, enabling faster and more targeted responses. Additionally, it can be leveraged in reporting to track caller engagement.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//Business Rule: After update on the incident table
2+
//Condition: Additional comments changes
3+
//Create on global Tag record ex: Comments added
4+
5+
(function executeRule(current, previous /*null when async*/ ) {
6+
7+
var caller = current.caller_id.user_name;
8+
// Add tag to the incident record if the comments is updated by the caller
9+
if (current.sys_updated_by == caller) {
10+
var add_tag_entry = new GlideRecord('label_entry');
11+
add_tag_entry.initialize();
12+
add_tag_entry.label = '<sys_id of the Tag>';
13+
add_tag_entry.table = 'incident';
14+
add_tag_entry.table_key = current.sys_id;
15+
add_tag_entry.insert();
16+
} else {
17+
// Remove tag from the incident record if the agent responds back to the caller
18+
var remove_tag_entry = new GlideRecord('label_entry');
19+
remove_tag_entry.addEncodedQuery("label=<sys_id of the Tag>^table_key=" + current.sys_id);
20+
remove_tag_entry.query();
21+
if (remove_tag_entry.next()) {
22+
remove_tag_entry.deleteRecord();
23+
}
24+
}
25+
26+
})(current, previous);
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/**
2+
* This utility script include provides methods for managing and querying user-group memberships
3+
* in the sys_user_grmember table.
4+
* Accessible from both server-side and client-side (for AJAX-compatible methods).
5+
*/
6+
var GroupMembershipUtils = Class.create();
7+
GroupMembershipUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
8+
9+
/**
10+
* Returns a comma-separated list of user sys_ids who are members of the specified group.
11+
* Can be called from both server and client sides.
12+
*
13+
* @param {string} [groupSysID] - (Optional) sys_id of the group. If not provided, expects 'group_sys_id' parameter (used in client-side call).
14+
* @returns {string} Comma-separated sys_ids of users in the group.
15+
*/
16+
getGroupMembers: function(groupSysID) {
17+
var group = groupSysID ? groupSysID : this.getParameter('group_sys_id');
18+
if (!group) return;
19+
var users = [];
20+
21+
var grGroupMembers = new GlideRecord('sys_user_grmember');
22+
grGroupMembers.addQuery('group', group);
23+
grGroupMembers.query();
24+
while (grGroupMembers.next()) {
25+
users.push(grGroupMembers.getValue('user'));
26+
}
27+
28+
return users.join();
29+
},
30+
31+
/**
32+
* Returns a comma-separated list of group sys_ids that the specified user is a member of.
33+
* Can be called from both server and client sides.
34+
*
35+
* @param {string} [userSysId] - (Optional) sys_id of the user. If not provided, expects 'user_sys_id' parameter (used in client-side call).
36+
* @returns {string} Comma-separated sys_ids of groups the user belongs to.
37+
*/
38+
getUserGroups: function(userSysId) {
39+
var user = userSysId ? userSysId : this.getParameter('user_sys_id');
40+
if (!user) return;
41+
var groups = [];
42+
43+
var grGroupMembers = new GlideRecord('sys_user_grmember');
44+
grGroupMembers.addQuery('user', user);
45+
grGroupMembers.query();
46+
while (grGroupMembers.next()) {
47+
groups.push(grGroupMembers.getValue('group'));
48+
}
49+
50+
return groups.join();
51+
},
52+
53+
/**
54+
* Adds multiple users to a specified group.
55+
* Prevents unique key violation error by checking if the user is already a member.
56+
*
57+
* **Server-side only.**
58+
*
59+
* @param {string} groupSysID - sys_id of the group.
60+
* @param {Array<string>} userSysIDs - Array of user sys_ids to be added to the group.
61+
* @returns {number} The count of successfully added group memberships.
62+
*/
63+
addGroupMembers: function(groupSysID, userSysIDs) {
64+
if (!groupSysID || !userSysIDs) return 0;
65+
66+
var count = 0;
67+
68+
for (var i = 0; i < userSysIDs.length; i++) {
69+
var grGroupMembers = new GlideRecord('sys_user_grmember');
70+
grGroupMembers.addQuery('group', groupSysID);
71+
grGroupMembers.addQuery('user', userSysIDs[i]);
72+
grGroupMembers.query();
73+
74+
// Only insert if membership does not already exist
75+
if (!grGroupMembers.next()) {
76+
grGroupMembers.initialize();
77+
grGroupMembers.setValue('group', groupSysID);
78+
grGroupMembers.setValue('user', userSysIDs[i]);
79+
80+
if (grGroupMembers.insert()) {
81+
count++;
82+
}
83+
}
84+
}
85+
86+
return count;
87+
},
88+
89+
/**
90+
* Removes multiple users from a specified group.
91+
* Only removes if a membership exists.
92+
*
93+
* **Server-side only.**
94+
*
95+
* @param {string} groupSysID - sys_id of the group.
96+
* @param {Array<string>} userSysIDs - Array of user sys_ids to be removed from the group.
97+
* @returns {number} The count of successfully removed group memberships.
98+
*/
99+
removeGroupMembers: function(groupSysID, userSysIDs) {
100+
if (!groupSysID || !userSysIDs) return 0;
101+
102+
var count = 0;
103+
104+
for (var i = 0; i < userSysIDs.length; i++) {
105+
var grGroupMembers = new GlideRecord('sys_user_grmember');
106+
grGroupMembers.addQuery('group', groupSysID);
107+
grGroupMembers.addQuery('user', userSysIDs[i]);
108+
grGroupMembers.query();
109+
110+
// Only delete if membership exists
111+
if (grGroupMembers.next()) {
112+
if (grGroupMembers.deleteRecord()) {
113+
count++;
114+
}
115+
}
116+
}
117+
118+
return count;
119+
},
120+
121+
type: 'GroupMembershipUtils'
122+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Utility Script Include for managing user-group relationships in ServiceNow (sys_user_grmember table).
2+
3+
It provides methods to:
4+
Retrieve users in a group (getGroupMembers)
5+
Retrieve groups a user belongs to (getUserGroups)
6+
Add users to a group (addGroupMembers)
7+
Remove users from a group (removeGroupMembers)
8+
9+
Supports both client and server-side operations (where applicable), ensures no duplicate group memberships, and simplifies bulk updates.
10+
11+
Ideal for use in server scripts, GlideAjax calls, reference qualifiers, etc to streamline group membership management.

0 commit comments

Comments
 (0)