Skip to content

Commit 324359b

Browse files
Generic Outlook Email Watermark Utility for ServiceNow (#2514)
* Create GenericEmailUtility.js * Create Send Email UI Action * Rename Send Email UI Action to Send Email UI Action.js * Create README.md
1 parent 927a3c6 commit 324359b

File tree

3 files changed

+161
-0
lines changed

3 files changed

+161
-0
lines changed
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+
}

0 commit comments

Comments
 (0)