Skip to content

Commit e0e64e5

Browse files
authored
Merge branch 'ServiceNowDevProgram:main' into feature/user-profile-field-validation
2 parents ccb0a36 + 2724cfe commit e0e64e5

File tree

17 files changed

+532
-0
lines changed

17 files changed

+532
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Background Script provides the list of installed plugins, version installed and version available for the upgrade in the instance
2+
3+
Note: We need to set the basic auth credential in order for the script to work on the instance where we are running it.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//Get the instance
2+
var instance_name = gs.getProperty("instance_name");
3+
4+
//build the endpoint
5+
var endPoint = 'https://'+instance_name+'.service-now.com/api/sn_appclient/appmanager/apps?tab_context=installed';
6+
7+
//initialize the RestMessageV2 API.
8+
var request = new sn_ws.RESTMessageV2();
9+
request.setEndpoint(endPoint);
10+
request.setHttpMethod('POST');
11+
12+
//Eg. UserName="admin", Password="admin" for this code sample.
13+
var user = 'admin';
14+
var password = '****';
15+
16+
//set the authentication
17+
request.setBasicAuth(user,password);
18+
19+
//set the request header
20+
request.setRequestHeader("Accept","application/json");
21+
22+
//invoke the API
23+
var response = request.execute();
24+
25+
//Parse the response
26+
var jsonResponse = JSON.parse(response.getBody());
27+
28+
var appsList = jsonResponse.result.apps;
29+
30+
//Print the Header for the response
31+
gs.info("Application name"+" | "+ "Assigned version"+" | " + "Latest version | " + "Hasupdate");
32+
appsList.forEach(function(app){
33+
//Print the plugin details
34+
var hasUpdate = app.update_available == 1 ? "true" : "false";
35+
gs.info(app.name+" | "+ app.assigned_version+" | " + app.latest_version+" | " + hasUpdate);
36+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
var updatedSysIds = [];
2+
var notUpdatedSysIds = [];
3+
4+
var gr = new GlideRecord('< table_name >');
5+
gr.addQuery('< query condition >');
6+
gr.query();
7+
8+
while (gr.next()) {
9+
10+
var relCi = new GlideRecord('cmdb_rel_ci');
11+
relCi.addQuery('child', gr.sys_id);
12+
relCi.addQuery('parent.sys_class_name', '< backend name of the table to which the reference field is referred to >');
13+
relCi.query();
14+
15+
if (relCi.next()) {
16+
// Update the reference field with the referenced table's sys_id
17+
gr.< reference field backend name > = relCi.parent.sys_id;
18+
gr.setWorkflow(false);
19+
gr.update();
20+
updatedSysIds.push(gr.sys_id.toString()); // Add to updated list
21+
}
22+
else {
23+
notUpdatedSysIds.push(gr.sys_id.toString()); // Add to not updated list
24+
}
25+
}
26+
27+
// Print the sys_ids of the records updated and not updated
28+
gs.print("Updated records sys_ids: " + updatedSysIds.join(', '));
29+
gs.print("Not updated records sys_ids: " + notUpdatedSysIds.join(', '));

Background Scripts/readME.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
In this piece of code, we are querying a table for some records and then updating a particular reference field's value of those records to the value of the specific parent class to which it has a cmdb_rel_ci relationship.
2+
We are also printing the sys_ids pf the records which would be updated and the ones that would be skipped.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
function onChange(control, oldValue, newValue, isLoading) {
2+
if (isLoading) return;
3+
4+
var passportNumber = g_form.getValue('passport_number');
5+
var dateOfIssue = g_form.getValue('date_of_issue');
6+
var age = parseInt(g_form.getValue('age'), 10);
7+
var dateOfExpiry = g_form.getValue('date_of_expiry');
8+
9+
// Passport Number Validation
10+
var passportPattern = /^[A-Z][1-9][0-9][A-Z0-9]{5}$/;
11+
if (passportNumber && !passportPattern.test(passportNumber)) {
12+
g_form.showFieldMsg('passport_number', "The entered number is invalid passport number format. It must be 8 characters long, start with an uppercase letter, followed by a number between 1-9, then 0-9, and the rest alphanumeric.", "error");
13+
g_form.clearValue('passport_number');
14+
} else {
15+
g_form.hideFieldMsg('passport_number');
16+
}
17+
18+
// Date of Expiry Calculation based on date of issue
19+
if (dateOfIssue && age) {
20+
var issueDate = new GlideDate();
21+
issueDate.setValue(dateOfIssue);
22+
var expiryDate = new GlideDate();
23+
expiryDate.setValue(issueDate);
24+
25+
if (age >= 18) {
26+
expiryDate.addYears(5); // Adult - add 5 years
27+
} else {
28+
expiryDate.addYears(10); // Under 18 - add 10 years
29+
}
30+
31+
g_form.setValue('date_of_expiry', expiryDate.getByFormat('yyyy-MM-dd')); // Set expiry date in correct format
32+
g_form.hideFieldMsg('date_of_expiry');
33+
} else if (!dateOfIssue) {
34+
g_form.showFieldMsg('date_of_issue', "Please enter the Date of Issue first.", "info");
35+
} else if (!age) {
36+
g_form.showFieldMsg('age', "Please enter your age first.", "info");
37+
}
38+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
This OnChange Catalog Client Script is for validating passport number, date of issue, and date of expiry.
2+
3+
It follows the specified rules(As per indian passport):-
4+
- The passport number should be 8 characters long, with the first character as an uppercase letter, the second and third characters as numbers (1-9 for the first digit, 0-9 for the second digit).
5+
- The date of expiry should be calculated based on the date of issue:
6+
- 1. If the user is an adult (18 years or older), the expiry date should be exactly 5 years from the date of issue.
7+
2. If the user is under 18, the expiry date should be exactly 10 years from the date of issue.
8+
9+
Passport Number Validation:
10+
The passportPattern uses a regular expression:
11+
^[A-Z] – The first character must be an uppercase letter.
12+
[1-9][0-9] – The second and third characters are numbers; the first is between 1-9, and the second between 0-9.
13+
[A-Z0-9]{5}$ – The last five characters are alphanumeric.
14+
If the passport number does not match this pattern, the script displays an error message and clears the field.
15+
16+
Date of Expiry Calculation:
17+
- After the date of issue and age are provided, the script calculates the expiry date by adding 5 years for adults (18 or older) or 10 years for minors (under 18).
18+
- The calculated expiry date is automatically set in the date_of_expiry field in the yyyy-MM-dd format.
19+
- Prompts are displayed if necessary fields like date_of_issue or age are missing before attempting the expiry date calculation.
20+
21+
This Client Script will ensure that the entered passport information and expiry date meet the requirements, providing a seamless and guided experience for the user.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Adding Placeholder Text in Resolution Notes
2+
3+
To maintain consistency and ensure specific information is captured in resolution notes, process owners may require fulfillers to follow a predefined format when resolving tickets.
4+
5+
By adding **placeholder** text in the resolution notes, fulfillers are reminded of the required information(e.g., Root cause, Steps taken, Resolution provided), reducing the risk of missing important details. The placeholder disappears as soon as the fulfiller begins entering their notes, ensuring it doesn't interfere with their input.
6+
7+
## How It Works
8+
9+
### When?
10+
- The placeholder text is automatically added when the state of the ticket changes to Resolved (6).
11+
12+
### What Happens?
13+
- A placeholder text appears in the resolution notes field to guide the fulfiller.
14+
- As soon as the fulfiller starts typing, the placeholder disappears.
15+
- This ensures consistency and alignment with the process requirements.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
2+
if (isLoading || newValue === '') {
3+
return;
4+
}
5+
var res = g_form.getElement('close_notes');
6+
if (newValue == 6)
7+
res.placeholder = "1. Root Cause" + "\n" + "2. Steps taken" + "\n" + "3. Resolution provided"; //Placeholder text as required
8+
else
9+
res.placeholder = "";
10+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Redact Sensitive Information
2+
3+
When users create an incident or HR case via the self-service portal, they may occasionally enter sensitive information (e.g., personal identifiers, account numbers).
4+
To prevent misuse of such data, **fulfillers** can redact sensitive information from the short description or description fields.
5+
6+
This ensures that confidential information is safeguarded and not accessible for unauthorized use or distribution.
7+
8+
## Prerequisites
9+
10+
1. Custom Field:
11+
Add a custom field to the form to hold the redacted text.
12+
Example: u_redact (Redact).
13+
14+
2. OnSubmit Client Script:
15+
Create an onsubmit client script to redact sensitive information.
16+
This script will update the **short description** and **description** field with custom value as required.
17+
18+
**Note**: Data that has been redacted cannot be recovered.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
function onSubmit() {
2+
var redact = g_form.getValue("u_redact"); //custom field on the form to redact information
3+
if (redact == true) {
4+
var answer = confirm(getMessage('Do you want to redact sensitive information')); //Confirm the user who wants to redact information
5+
if (answer) {
6+
g_form.setValue('short_description', 'Short Description is redacted as it contained sensitive information'); //Custom short_description post redacting
7+
g_form.setValue('description', 'Description is redacted as it contained sensitive information'); //Custom description post redacting
8+
g_form.setValue('work_notes', 'The Description and Short Description has been redacted.'); //Adding work notes to track who redacted the short_description and description
9+
g_form.setReadOnly('short_description', true);
10+
g_form.setReadOnly('description', true);
11+
g_form.setReadOnly('u_redact', true)
12+
} else {
13+
g_form.setValue('u_redact', false);
14+
return false;
15+
}
16+
}
17+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Suppose you want to gather data about incident resolution in your system.
2+
Specifically, you need to find the total number of incidents, the average time to resolution (in hours), and the number of incidents per assignment group.
3+
This information can help analyze the efficiency of different groups and improve incident handling.
4+
5+
Below are the added Aggregations:
6+
7+
inc.addAggregate('COUNT') gets the total count of resolved incidents.
8+
inc.addAggregate('AVG', 'calendar_duration') calculates the average calendar duration for incident resolution (measured in hours).
9+
inc.addAggregate('COUNT', 'assignment_group') counts incidents per assignment group, and ga.groupBy('assignment_group') groups the result by assignment group to produce totals per group.
10+
11+
Result:
12+
- It fetches total counts, average resolution time, and group-specific counts.
13+
- Logs the total number of resolved incidents and the average resolution time.
14+
- For each assignment group, it logs the group’s sys_id and the corresponding incident count.
15+
16+
Benefits of Using GlideAggregate:
17+
- Reduces the number of queries and records you need to process, as it performs calculations at the database level.
18+
- Works well with large datasets, making it suitable for summary reports and dashboards.
19+
- Allows grouping and multiple aggregations (e.g., AVG, COUNT, MIN, MAX) on various fields in a single query.
20+
21+
This GlideAggregate example provides a consolidated view of incident resolution statistics, which can aid in optimizing group efficiency and improving response times.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Create a GlideAggregate instance for the Incident table
2+
var inc = new GlideAggregate('incident');
3+
4+
// Filter for resolved incidents only
5+
inc.addQuery('state', 6);
6+
7+
// Add aggregations
8+
inc.addAggregate('COUNT'); // Total number of incidents
9+
inc.addAggregate('AVG', 'calendar_duration'); // Average resolution time in hours
10+
inc.addAggregate('COUNT', 'assignment_group'); // Count of incidents per assignment group
11+
inc.groupBy('assignment_group'); // Group by assignment group to get the count per group
12+
inc.query();
13+
14+
var totalIncidents = 0;
15+
var averageResolution = 0;
16+
var results = [];
17+
18+
while (inc.next()) {
19+
totalIncidents = inc.getAggregate('COUNT');
20+
averageResolution = inc.getAggregate('AVG', 'calendar_duration');
21+
22+
// Get assignment group and incident count per group
23+
var groupSysId = inc.assignment_group.toString();
24+
var groupIncidentCount = inc.getAggregate('COUNT', 'assignment_group');
25+
26+
results.push({
27+
groupSysId: groupSysId,
28+
groupIncidentCount: groupIncidentCount
29+
});
30+
}
31+
32+
// Display results in logs
33+
gs.info("Total Resolved Incidents are: " + totalIncidents);
34+
gs.info("Average Resolution Time (hours) is: " + averageResolution);
35+
36+
results.forEach(function(result) {
37+
gs.info("Assignment Group: " + result.groupSysId + " | Incident Count: " + result.groupIncidentCount);
38+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
This is a UI Action script that adds a button to the Incident form. When clicked, it will check if the incident has been unassigned for more than 5 days.
2+
If this condition is met, the button will trigger a notification to the manager of the incident's assignment group, informing them that the incident is still unassigned.
3+
4+
Below are the conditions when UI action will be created:
5+
Table: Incident
6+
Show Insert: False
7+
Show Update: True
8+
Form Button: Checked (to add the button on the form)
9+
Condition: current.assigned_to.nil() && current.assignment_group
10+
11+
The code contains below vaidations:
12+
- The script checks if the incident has been unassigned for more than 5 days by comparing the current date with the sys_created_on date using gs.daysAgo().
13+
- It verifies that the incident has an assignment group. If it does not, it displays an error message.
14+
- Queries the sys_user_group table to get the assignment group’s manager. If a manager is found, it sets up a notification to send an email directly to the manager.
15+
- Provides feedback to the user if the notification was sent or if there were issues (like missing assignment group or manager).
16+
- Redirects the user back to the current incident form after the UI Action runs.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Check if the incident has been unassigned for more than 5 days
2+
var unassignedDuration = gs.daysAgo(current.sys_created_on);
3+
if (unassignedDuration < 5) {
4+
gs.addErrorMessage("The incident has been unassigned for less than 5 days.");
5+
action.setRedirectURL(current);
6+
return;
7+
}
8+
9+
// Check if the incident has an assignment group
10+
if (current.assignment_group.nil()) {
11+
gs.addErrorMessage("No assignment group is set for this incident.");
12+
action.setRedirectURL(current);
13+
return;
14+
}
15+
16+
// Get the assignment group's manager
17+
var assignmentGroup = new GlideRecord('sys_user_group');
18+
if (assignmentGroup.get(current.assignment_group)) {
19+
var manager = assignmentGroup.getValue('manager');
20+
21+
if (manager) {
22+
// Create a notification
23+
var notification = new GlideEmailOutbound();
24+
notification.setFrom('[email protected]');
25+
notification.setSubject("Alert! Incident " + current.number + " is still unassigned");
26+
notification.setBody("The incident " + current.number + " has been unassigned for more than 5 days. Please assign it promptly.");
27+
notification.setTo(manager);
28+
29+
// Send the email
30+
notification.send();
31+
32+
gs.addInfoMessage("Notification sent to the assignment group's manager.");
33+
} else {
34+
gs.addErrorMessage("The assignment group has no manager defined.");
35+
}
36+
} else {
37+
gs.addErrorMessage("Could not find the assignment group.");
38+
}
39+
40+
action.setRedirectURL(current);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* function to fetch the configuration item details like serial No, name , class , install status */
2+
function searchCI() {
3+
var ciNameorId = document.getElementById('ci_input').value;
4+
if (!ciNameorId) {
5+
alert('Please Enter the Configuration item Name or ID');
6+
} else {
7+
var ga = new GlideAjax('ci_lifecycle_management_script_include');
8+
ga.addParam('sysparm_name', 'get_ci_info');
9+
ga.addParam('sysparm_ci_name_or_id', ciNameorId);
10+
ga.getXMLAnswer(function(response) {
11+
var record = JSON.parse(response);
12+
document.getElementById("ci-info").style.display = "block";
13+
document.getElementById('ci_name').innerText = record.name;
14+
document.getElementById('serial_number').innerText = record.serial_number;
15+
document.getElementById('ci_class').innerText = record.ci_class;
16+
document.getElementById('ci_install_status').innerText = record.ci_install_status;
17+
var operations = ['Stolen', 'Retired','In Stock','In Maintenance'];
18+
operations.forEach((operation) => {
19+
var hidden_ele_id = operation.replace(/\s+/g, "");
20+
var elementId = "ci_"+hidden_ele_id;
21+
var ele = document.getElementById(elementId);
22+
if(operation == record.ci_install_status){
23+
ele.style.display = "none";
24+
}
25+
});
26+
});
27+
}
28+
}
29+
/* end of searchCI() */
30+
31+
/* function to update the status of Configuration item */
32+
function UpdateCI(status) {
33+
var ciNameorId = document.getElementById('ci_input').value;
34+
if (ciNameorId) {
35+
var updateci = new GlideAjax('ci_lifecycle_management_script_include');
36+
updateci.addParam('sysparm_name', 'update_ci_info');
37+
updateci.addParam('sysparm_ci_id_or_name', ciNameorId);
38+
updateci.addParam('sysparm_ci_status', status);
39+
updateci.getXMLAnswer(function(response) {
40+
var result = JSON.parse(response);
41+
if(result.updated == true){
42+
alert("Record Updated Successfully");
43+
}
44+
45+
});
46+
47+
48+
} else {
49+
alert('Facing issues to Update the CI');
50+
}
51+
52+
}
53+
/* function to update the status of Configuration item */

0 commit comments

Comments
 (0)