Skip to content

Commit b8e431c

Browse files
Merge branch 'ServiceNowDevProgram:main' into Check-Incident-having-VIP-Caller
2 parents da5390a + f1e32cd commit b8e431c

File tree

84 files changed

+3082
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+3082
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
var GetRecentRequestValues = Class.create();
2+
GetRecentRequestValues.prototype = Object.extendsObject(AbstractAjaxProcessor, {
3+
getValues: function() {
4+
var userID = this.getParameter('sysparm_user');
5+
var itemID = this.getParameter('sysparm_item');
6+
var result = { found: false, values: {} };
7+
8+
var gr = new GlideRecord('sc_req_item');
9+
gr.addQuery('requested_for', userID);
10+
gr.addQuery('cat_item', itemID);
11+
gr.orderByDesc('sys_created_on');
12+
gr.setLimit(1);
13+
gr.query();
14+
15+
if (gr.next()) {
16+
result.found = true;
17+
18+
19+
var vars = gr.variables;
20+
result.values = {
21+
'requested_for': vars.requested_for + '',
22+
'location': vars.location + '',
23+
'department': vars.department + ''
24+
};
25+
}
26+
27+
return JSON.stringify(result);
28+
}
29+
});
30+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
function onLoad() {
2+
var user = g_user.userID;
3+
var itemID = g_form.getUniqueValue();
4+
5+
var ga = new GlideAjax('GetRecentRequestValues');
6+
ga.addParam('sysparm_name', 'getValues');
7+
ga.addParam('sysparm_user', user);
8+
ga.addParam('sysparm_item', itemID);
9+
ga.getXMLAnswer(function(response) {
10+
var data = JSON.parse(response);
11+
if (data && data.found) {
12+
var confirmFill = confirm("We found a similar request. Do you want to autofill fields?");
13+
if (confirmFill) {
14+
for (var field in data.values) {
15+
if (g_form.getControl(field)) {
16+
g_form.setValue(field, data.values[field]);
17+
console.log("Set " + field + " to " + data.values[field]);
18+
} else {
19+
console.log("Field not found: " + field);
20+
}
21+
}
22+
}
23+
} else {
24+
console.log("No previous request found.");
25+
}
26+
});
27+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Recent Request Autofill for ServiceNow Catalog.it automatically offers to fill in fields based on the user's most recent similar request.
2+
Features
3+
- Detects previous requests for the same catalog item
4+
- Prompts user to reuse values from their last submission
5+
- Autofills fields like location, department, and justification
6+
7+
<img width="878" height="395" alt="image" src="https://github.com/user-attachments/assets/33ceabf5-2bbc-43e3-8792-f1f9a99699d2" />
8+
9+
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
var SentimentAnalyzer = Class.create();
2+
SentimentAnalyzer.prototype = Object.extendsObject(AbstractAjaxProcessor, {
3+
getSentiment: function() {
4+
var text = (this.getParameter('sysparm_text') || '').toLowerCase();
5+
var positive = ['thanks', 'great', 'resolved', 'appreciate'];
6+
var negative = ['issue', 'error', 'not working', 'fail', 'problem'];
7+
8+
var score = 0;
9+
positive.forEach(function(word) { if (text.includes(word)) score++; });
10+
negative.forEach(function(word) { if (text.includes(word)) score--; });
11+
12+
if (score > 0) return 'Positive';
13+
if (score < 0) return 'Negative';
14+
return 'Neutral';
15+
}
16+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
function onChange(control, oldValue, newValue, isLoading) {
2+
if (isLoading || !newValue) return;
3+
4+
var ga = new GlideAjax('SentimentAnalyzer');
5+
ga.addParam('sysparm_name', 'getSentiment');
6+
ga.addParam('sysparm_text', newValue);
7+
ga.getXMLAnswer(function(sentiment) {
8+
g_form.addInfoMessage('Sentiment: ' + sentiment);
9+
g_form.setValue('u_sentiment', sentiment);
10+
});
11+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Incident Sentiment Detector (No AI, Pure JavaScript)
2+
3+
A lightweight ServiceNow utility that detects sentiment (Positive / Negative / Neutral) of an Incident’s short description or comments using simple keyword matching — no AI APIs or external libraries required.
4+
5+
Useful for support teams to auto-tag sentiment and analyze user frustration or satisfaction trends without expensive integrations.
6+
7+
🚀 Features
8+
9+
✅ Detects sentiment directly inside ServiceNow ✅ Works without external APIs or ML models ✅ Instant classification on form update ✅ Adds detected sentiment to a custom field (u_sentiment) ✅ Simple to extend — just add more positive/negative keywords
10+
11+
🧩 Architecture Overview
12+
13+
The solution consists of two main scripts:
14+
15+
Component Type Purpose SentimentAnalyzer Script Include Processes text and returns sentiment Client Script (onChange) Client Script Calls SentimentAnalyzer via GlideAjax on short description change 🧱 Setup Instructions 1️⃣ Create Custom Field
16+
17+
Create a new field on the Incident table:
18+
19+
Name: u_sentiment
20+
21+
Type: Choice
22+
23+
Choices: Positive, Neutral, Negative
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
**User Location Validator**
2+
This script restricts form submissions based on the physical location of the user. The current location is obtained using the browser’s geolocation API (latitude and longitude), and is then compared against the user's assigned business location stored in ServiceNow.
3+
4+
**How It Works**
5+
- The **server-side Script Include**(UserLocationUtils.js) fetches the assigned business location’s latitude, longitude, and name for the logged-in user.
6+
- The **client-side script**(User Location Validator.js) uses the browser API to obtain the current latitude and longitude of the user at form submission.
7+
- It calculates the distance between these two points using the **Haversine formula**, which accounts for the spherical shape of the Earth.
8+
- The key constant `earthRadiusKm = 6371` defines the Earth's radius in kilometers and is essential for accurate distance calculation.
9+
- If the user’s current location is outside the predefined radius (default 10 km), the form submission is blocked with an error message showing the distance and allowed location.
10+
- If the user is within range, a confirmation info message is displayed and the submission proceeds.
11+
12+
**Sample Output**
13+
- **Success:** "Location validated successfully within range of Headquarters."
14+
- **Failure:** "You are 15.23 km away from your registered location: Headquarters."
15+
16+
**Usage Notes**
17+
- Requires user consent for geolocation access in the browser.
18+
- The script uses descriptive variable names for clarity and maintainability.
19+
- Suitable for scenarios requiring geo-fencing compliance or location-based workflow restrictions.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
function onSubmit() {
2+
// Check if the browser supports geolocation
3+
if ("geolocation" in navigator) {
4+
// Request current user position
5+
navigator.geolocation.getCurrentPosition(function(position) {
6+
var currentLatitude = position.coords.latitude; // Current user latitude
7+
var currentLongitude = position.coords.longitude; // Current user longitude
8+
9+
// Allowed business location coordinates fetched from server
10+
var allowedLatitude = locData.latitude;
11+
var allowedLongitude = locData.longitude;
12+
var locationName = locData.name;
13+
14+
// Earth's radius in kilometers - constant used in distance calculation formula
15+
var earthRadiusKm = 6371;
16+
17+
// Convert degree differences to radians
18+
var deltaLatitude = (currentLatitude - allowedLatitude) * Math.PI / 180;
19+
var deltaLongitude = (currentLongitude - allowedLongitude) * Math.PI / 180;
20+
21+
// Haversine formula components
22+
var a = Math.sin(deltaLatitude / 2) * Math.sin(deltaLatitude / 2) +
23+
Math.cos(allowedLatitude * Math.PI / 180) *
24+
Math.cos(currentLatitude * Math.PI / 180) *
25+
Math.sin(deltaLongitude / 2) * Math.sin(deltaLongitude / 2);
26+
27+
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
28+
29+
// Calculate distance in kilometers between current and allowed locations
30+
var distanceKm = earthRadiusKm * c;
31+
32+
// Check if user's current distance exceeds tolerance (e.g., 10 km)
33+
if (distanceKm > 10) {
34+
alert("You are " + distanceKm.toFixed(2) + " km away from your registered location: " + locationName);
35+
g_form.addErrorMessage("Location validation failed: Submission outside the allowed radius.");
36+
return false; // Cancel form submission
37+
} else {
38+
g_form.addInfoMessage("Location validated successfully within range of " + locationName);
39+
return true; // Allow form submission
40+
}
41+
}, function(error) {
42+
alert("Geolocation error: " + error.message);
43+
return false; // Stop submission if geolocation fails
44+
});
45+
46+
// Prevent form submission while waiting for async geolocation result
47+
return false;
48+
} else {
49+
g_form.addErrorMessage("Geolocation is not supported by your browser.");
50+
return false; // Block if geolocation API unsupported
51+
}
52+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
var UserLocationUtils = Class.create();
2+
UserLocationUtils.prototype = {
3+
initialize: function() {
4+
5+
},
6+
getUserLocationCoords: function() {
7+
var user = gs.getUser();
8+
var loc = user.getRecord().location;
9+
if (loc) {
10+
var locGR = new GlideRecord('cmn_location');
11+
if (locGR.get(loc))
12+
return {
13+
latitude: parseFloat(locGR.latitude),
14+
longitude: parseFloat(locGR.longitude),
15+
name: locGR.name.toString()
16+
};
17+
}
18+
return null;
19+
},
20+
21+
type: 'UserLocationUtils'
22+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
var DomainCheckUtil = Class.create();
2+
DomainCheckUtil.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
3+
//get current domain of user session
4+
getCurrentDomainName: function() {
5+
var sessionDomainId = gs.getSession().getCurrentDomainID();
6+
var gr = new GlideRecord('domain');
7+
if (gr.get(sessionDomainId)){
8+
return gr.name;
9+
}
10+
//Return global domain name
11+
return 'Global';
12+
},
13+
14+
type: 'DomainCheckUtil'
15+
});

0 commit comments

Comments
 (0)