Skip to content

Commit 3cee3e2

Browse files
authored
Merge branch 'ServiceNowDevProgram:main' into main
2 parents 2e69ad4 + 4d1eee5 commit 3cee3e2

File tree

11 files changed

+275
-0
lines changed

11 files changed

+275
-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+
Description:
2+
This background script is used to automatically update the content of all published Knowledge Base articles by replacing outdated group references, ensuring consistency and relevance across documentation without the need to manually check out and edit each article
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
var old_reference = "Service desk"; // Old group name
2+
var new_reference = "Help desk"; // New group name
3+
4+
var regexPattern = new RegExp('(?is)'+ old_reference, 'gi'); // Building Regex to generate case-insensitive pattern
5+
6+
var kb_article = new GlideRecord('kb_knowledge');
7+
kb_article.addEncodedQuery('workflow_state=published');
8+
kb_article.query();
9+
while(kb_article.next()){
10+
kb_article.text = kb_article.text.replace(regexPattern,new_reference); // Replacing the old group reference with the new group
11+
kb_article.setWorkflow(false);
12+
kb_article.update();
13+
gs.info('Updated Article: ' + kb_article.number);
14+
}
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: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
(function executeRule(current, previous /*null when async*/ ) {
2+
/*
3+
Fire this BR when a tag is removed/deleted from a record.
4+
Retrieve the tag name, the user who removed the tag, and the tag removal date.
5+
Update the above information onto the tag-referenced record in this example, In this example its incident record
6+
*/
7+
var updateRecord = new GlideRecord(current.table);
8+
if (updateRecord.get(current.table_key)) {
9+
var notes = "Tag Name:" + " " + current.label.getDisplayValue() + "\n" + "Tag Removed By:" + " " + current.sys_updated_by + "\n" + "Tag Removed On:" + " " + current.sys_updated_on;
10+
//updateRecord.setValue("work_notes", notes);
11+
updateRecord.work_notes = notes;
12+
updateRecord.update();
13+
}
14+
})(current, previous);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
**Configure the following business rule on the label_entry table:**
2+
3+
1.Ensure the "Delete" operation is selected and the rule runs onBefore.
4+
5+
2.Retrieve the table name where the tag is removed.
6+
7+
3.Update the worknotes to track who removed the tag and when it was removed.
8+
<img width="952" height="460" alt="image" src="https://github.com/user-attachments/assets/020525dc-c98d-4ae8-94f9-d77cca6680e7" />
9+
<img width="1665" height="739" alt="image" src="https://github.com/user-attachments/assets/46c930eb-c031-4e1e-b1e6-31a3c64debb0" />
10+
11+
12+
**Output:**
13+
14+
<img width="1139" height="818" alt="image" src="https://github.com/user-attachments/assets/ed07f380-f279-4eca-82a5-208ae54054e7" />
15+
16+
<img width="780" height="401" alt="image" src="https://github.com/user-attachments/assets/283b40e1-1d7c-42aa-85f8-b8e9c6ccc3d0" />
17+
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+
});

0 commit comments

Comments
 (0)