Skip to content

Commit 02b9a72

Browse files
authored
Merge branch 'ServiceNowDevProgram:main' into main
2 parents 6579f1b + 7c55c08 commit 02b9a72

93 files changed

Lines changed: 3457 additions & 1 deletion

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
function onChange(control, oldValue, newValue, isLoading) {
2+
if (isLoading) return;
3+
4+
if (newValue === 'hardware') {
5+
g_form.setMandatory('asset_tag', true);
6+
g_form.setDisplay('asset_tag', true);
7+
g_form.setValue('assignment_group', 'Hardware Support Group');
8+
} else if (newValue === 'software') {
9+
g_form.setMandatory('asset_tag', false);
10+
g_form.setDisplay('asset_tag', false);
11+
g_form.setValue('assignment_group', 'Software Support Group');
12+
} else {
13+
g_form.setMandatory('asset_tag', false);
14+
g_form.setDisplay('asset_tag', true);
15+
}
16+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
If an Incident Category = Hardware, make Asset Tag mandatory and automatically assign to Hardware Support Group.
2+
If Software, assign to Software Support Group and hide Asset Tag.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
var GenericEmailUtility = Class.create();
2+
GenericEmailUtility.prototype = {
3+
initialize: function() {},
4+
5+
// Generate an Outlook (mailto) link with watermark tracking
6+
get_Outlook_link: function() {
7+
try {
8+
const email_payload = JSON.stringify({
9+
"REQUESTOR_ID": "",
10+
"TITLE": "",
11+
"BODY": "",
12+
"REQUEST_ID": "",
13+
"TABLE_ID": ""
14+
});
15+
16+
var mailtoLink = false;
17+
const raw_data = this.getParameter("sysparm_email_body") || email_payload;
18+
19+
if (global.JSUtil.notNil(raw_data)) {
20+
var email_data = JSON.parse(raw_data);
21+
22+
const to = this.getEmail(email_data.REQUESTOR_ID);
23+
const cc = gs.getProperty("instanceEmailAddress"); // instance default CC
24+
const subject = email_data.TITLE || '';
25+
const body = email_data.BODY || '';
26+
27+
const watermark = this.getWatermark(email_data.REQUEST_ID, email_data.TABLE_ID);
28+
29+
// Construct mailto link
30+
mailtoLink = 'mailto:' + to + '?cc=' + cc;
31+
32+
if (subject)
33+
mailtoLink += '&subject=' + encodeURIComponent(subject);
34+
35+
if (body)
36+
mailtoLink += '&body=' + encodeURIComponent(body);
37+
38+
if (watermark)
39+
mailtoLink += encodeURIComponent("\n\nRef: " + watermark);
40+
}
41+
42+
return mailtoLink;
43+
44+
} catch (ex) {
45+
gs.error("Error in get_Outlook_link(): " + ex.message);
46+
return false;
47+
}
48+
},
49+
50+
// Fetch watermark ID (creates one if missing)
51+
getWatermark: function(record_id, table_name) {
52+
var wm = new GlideRecord('sys_watermark');
53+
wm.addQuery('source_id', record_id);
54+
wm.orderByDesc('sys_created_on');
55+
wm.query();
56+
57+
if (wm.next()) {
58+
return wm.getValue('number');
59+
}
60+
61+
wm.initialize();
62+
wm.source_id = record_id;
63+
wm.source_table = table_name;
64+
wm.insert();
65+
66+
return wm.getValue('number');
67+
},
68+
69+
// Retrieve user’s email address
70+
getEmail: function(user_id) {
71+
if (global.JSUtil.notNil(user_id)) {
72+
var user = new GlideRecordSecure('sys_user');
73+
if (user.get(user_id))
74+
return user.email.toString();
75+
}
76+
return '';
77+
},
78+
79+
type: 'GenericEmailUtility'
80+
};
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Outlook Email Watermark Utility for ServiceNow
2+
3+
# Overview
4+
This reusable utility allows users to send emails **outside ServiceNow** (e.g., using Outlook or any default mail client) while still maintaining the conversation within ServiceNow.
5+
By embedding a unique watermark reference, any replies to the email will automatically append to the original record's activity feed.
6+
7+
This helps teams collaborate externally without losing internal record visibility — ideal for customers or vendors who communicate via Outlook.
8+
9+
---
10+
11+
# Objective
12+
- Enable ServiceNow users to send Outlook emails directly from a record.
13+
- Maintain conversation history in ServiceNow using watermark tracking.
14+
- Make the solution **generic**, reusable across tables (Incident, Change, Request, etc.).
15+
- Prevent dependency on outbound mail scripts or custom integrations.
16+
17+
# Components
18+
19+
## 1. Script Include: GenericEmailUtility
20+
Handles the logic for:
21+
- Constructing the mailto: link.
22+
- Fetching recipient and instance email addresses.
23+
- Generating or retrieving the watermark ID.
24+
- Returning a formatted Outlook link to the client script.
25+
26+
## Key Methods
27+
1. get_Outlook_link() - Builds the full Outlook mail link with subject, body, and watermark.
28+
2. getWatermark(record_id, table_name) - Ensures a watermark exists for the record.
29+
3. getEmail(user_id) - Fetches the email address for the target user.
30+
31+
## 2. UI Action (Client Script)
32+
Executes on the record form when the button/link is clicked.
33+
It gathers record data, constructs a payload, calls the Script Include using GlideAjax, and opens Outlook.
34+
35+
## Key Steps
36+
1. Collect field data like requestor, short description, and description.
37+
2. Pass record details to the Script Include (GenericEmailUtility).
38+
3. Receive a ready-to-use Outlook link.
39+
4. Open the mail client with prefilled details and watermark reference.
40+
41+
## How It Works
42+
1. User clicks "Send Outlook Email" UI Action on a record.
43+
2. Script gathers record data and passes it to GenericEmailUtility.
44+
3. The utility builds a 'mailto:' link including the watermark.
45+
4. Outlook (or default mail client) opens with pre-filled To, CC, Subject, and Body fields.
46+
5. When the recipient replies, ServiceNow uses the watermark to append comments to the correct record.
47+
48+
## Example Usage
49+
**User clicks “Send Outlook Email”** on a Request record:
50+
Outlook opens prefilled like this:
51+
52+
<img width="288" height="65" alt="image" src="https://github.com/user-attachments/assets/b58c5e0a-d80a-40ca-9ab5-f188a1203169" />
53+
54+
55+
<img width="710" height="496" alt="image" src="https://github.com/user-attachments/assets/5cbc7645-4233-4826-99f7-e2948bb5ab78" />
56+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
function onClick(g_form) {
2+
var separator = "\n--------------------------------\n";
3+
var email_body = "Record URL:\n" + g_form.getDisplayValue('number') + separator;
4+
email_body += "Short Description:\n" + g_form.getValue('short_description') + separator;
5+
email_body += "Description:\n" + g_form.getValue('description') + separator;
6+
7+
var email_data = {};
8+
email_data.REQUESTOR_ID = g_form.getValue('caller_id') || g_form.getValue('opened_by') || g_form.getValue('requested_for');
9+
email_data.TITLE = g_form.getValue('short_description') || 'ServiceNow Communication';
10+
email_data.BODY = email_body;
11+
email_data.REQUEST_ID = g_form.getUniqueValue();
12+
email_data.TABLE_ID = g_form.getTableName();
13+
14+
var ga = new GlideAjax('GenericEmailUtility');
15+
ga.addParam('sysparm_name', 'get_Outlook_link');
16+
ga.addParam('sysparm_email_body', JSON.stringify(email_data));
17+
ga.getXMLAnswer(function(response) {
18+
var mailto_link = response;
19+
if (mailto_link && mailto_link != 'false') {
20+
window.open(mailto_link);
21+
} else {
22+
g_form.addErrorMessage('Unable to generate Outlook link.');
23+
}
24+
});
25+
}
80.1 KB
Loading
105 KB
Loading
67.7 KB
Loading
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
## Field Review of User Record when on form using action button
2+
3+
Displays informational messages suggesting improvements to field formatting on the User Table (**sys_user**) form when the **Fields Check** button is clicked.
4+
5+
- Helps maintain consistency in user data by checking capitalization of names and titles, validating email format, ensuring phone numbers contain only digits, and preventing duplicate phone entries.
6+
- Also suggests users not to leave the **user_name** field empty.
7+
- Shows Info messages below each field highlighting fields that may need attention.
8+
- Simple Prerequisite is that: when form loads give Info message to check **Field Check** button to bring user's attention
9+
- Uses a Client-side UI Action (**Fields Check**) that to review entered data and display friendly suggestions
10+
- Name: Fields Check
11+
- Table: User (sys_user)
12+
- Client: true
13+
- Form button: true
14+
- Onclick: onClickCheckDetails()
15+
16+
---
17+
18+
### Grab user's attention on Field Check Button using Info message at top
19+
20+
![Field Review on User Table_1](Field_Review_userTable_1.png)
21+
22+
---
23+
24+
### After clicking Field Check Button where suggestions are displayed below fields
25+
26+
![Field Review on User Table_2](Field_Review_userTable_2.png)
27+
28+
---
29+
30+
### When user fixes the suggested issues and click the **Fields Check** button again, a message confirms that all fields are correctly formatted
31+
32+
![Field Review on User Table_3](Field_Review_userTable_3.png)
33+
34+
---
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
function onClickCheckDetails() {
2+
// Friendly helper for field normalization guidance
3+
g_form.hideAllFieldMsgs();
4+
g_form.clearMessages();
5+
6+
// --- Get Field values ---
7+
var firstName = g_form.getValue('first_name');
8+
var lastName = g_form.getValue('last_name');
9+
var title = g_form.getValue('title');
10+
var userId = g_form.getValue('user_name');
11+
var email = g_form.getValue('email');
12+
var businessPhone = g_form.getValue('phone');
13+
var mobilePhone = g_form.getValue('mobile_phone');
14+
15+
// --- Regex patterns ---
16+
var capitalRegex = /^[A-Z][a-zA-Z\s]*$/; // Names & titles start with a capital
17+
var emailRegex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
18+
var phoneRegex = /^\d+$/;
19+
20+
var suggestions = [];
21+
22+
if (firstName && !capitalRegex.test(firstName)) {
23+
g_form.showFieldMsg('first_name', 'Suggestion: Start the name with a capital letter.', 'info');
24+
suggestions.push('First Name');
25+
}
26+
27+
if (lastName && !capitalRegex.test(lastName)) {
28+
g_form.showFieldMsg('last_name', 'Suggestion: Start the name with a capital letter.', 'info');
29+
suggestions.push('Last Name');
30+
}
31+
32+
if (title && !capitalRegex.test(title)) {
33+
g_form.showFieldMsg('title', 'Suggestion: Titles usually start with a capital letter.', 'info');
34+
suggestions.push('Title');
35+
}
36+
37+
if (!userId) {
38+
g_form.showFieldMsg('user_name', 'Suggestion: Do not keep the User ID empty.', 'info');
39+
suggestions.push('User ID');
40+
}
41+
42+
if (email && !emailRegex.test(email)) {
43+
g_form.showFieldMsg('email', 'Suggestion: Please use a valid email format like name@example.com.', 'info');
44+
suggestions.push('Email');
45+
}
46+
47+
if (businessPhone && !phoneRegex.test(businessPhone)) {
48+
g_form.showFieldMsg('phone', 'Suggestion: Use digits only avoid letters.', 'info');
49+
suggestions.push('Business Phone');
50+
}
51+
52+
if (mobilePhone && !phoneRegex.test(mobilePhone)) {
53+
g_form.showFieldMsg('mobile_phone', 'Suggestion: Use digits only avoid letters.', 'info');
54+
suggestions.push('Mobile Phone');
55+
}
56+
57+
/
58+
if (businessPhone && mobilePhone && businessPhone === mobilePhone) {
59+
g_form.showFieldMsg('phone', 'Work and mobile numbers appear identical, use different Numbers!', 'info');
60+
suggestions.push('Phone Numbers');
61+
}
62+
63+
if (suggestions.length > 0) {
64+
g_form.addInfoMessage('Quick review complete! Please check: ' + suggestions.join(', ') + '.');
65+
} else {
66+
g_form.addInfoMessage('looks good! Nicely formatted data.');
67+
}
68+
}

0 commit comments

Comments
 (0)