Jump to content

Main Page

From MartiNet Wiki
 __  __            _   _ _   _      _                    _    
|  \/  | __ _ _ __| |_(_) \ | | ___| |_   ___ ___  _   _| | __
| |\/| |/ _` | '__| __| |  \| |/ _ \ __| / __/ _ \| | | | |/ /
| |  | | (_| | |  | |_| | |\  |  __/ |_ | (_| (_) | |_| |   < 
|_|  |_|\__,_|_|   \__|_|_| \_|\___|\__(_)___\___(_)__,_|_|\_\
                                                              

General Notes - for revision

[edit | edit source]

If application/plugin does not appear in Application Manager check the store. https://store.servicenow.com https://www.servicenow.com/docs

Please Note: I have now retired, so will probably not update this wiki much on future and it may even get deleted due to inactivity.

Direct all mobile users to the Service Portal

[edit | edit source]
  1. In the filter navigator, type: sys_properties.list and press Enter
  2. Find the property: glide.entry.page.script and set the value to: new SPEntryPage().getLoginURL(), save your changes
  3. Go back to the sys_properties list, if the property glide.entry.first.page.script does not exist, click the New button create a new property. Otherwise skip to step 5.
  4. For the property name, enter: glide.entry.first.page.script
  5. For the value, enter: new SPEntryPage().getFirstPageURL() and save the record
  6. Navigate to System Definition > Script Includes and open the SPEntryPage record
  7. In the script section, find the line that begins: if (user.hasRoles() && !redirectURL && !isServicePortalURL)
  8. Edit this line to: if (user.hasRoles() && !redirectURL && !isServicePortalURL && !gs.isMobile())
  9. Next, find the line: if (!redirectURL) {
  10. Edit this line to: if (!redirectURL && !gs.isMobile()) {
  11. Save the record.
[edit | edit source]

Example of how cart() api could cause request items missing or in the wrong request <syntaxhighlight lang="javascript"> var cart - new Cart(generatID()); // recommended way to create a cart with a unique ID </syntaxhighlight>

<syntaxhighlight lang="javascript"> // use a unique cart, to avoid collisions between guest users var cart_name = 'cart_' + gs.generateGUID(); var cart = new sn_sc.CartJS(cart_name); </syntaxhighlight>

Example - download pdf files from a webiste URL
-r recursive
-l levels to recurse
-N new/updated files only
-nd no parent directory
-nH do not create directory hierarchy
-A allow file types
-X exclude directory
<syntaxhighlight lang="unix"> wget -r -N -nd -nH -l1 -X bandi --random-wait -A "*.pdf" https://<domain>/<dir>/<dir>/" </syntaxhighlight>

System Properties

[edit | edit source]
glide.sm.default_mode = allow which allows access to all tables in the absence of any other security rules (ACLs)
glide.ui.activity.style.work_notes -- background-color: LightGoldenRodYellow
glide.script.block.client.globals allow jQuery and other stuff in scoped applications.
glide.sc.request_for.query filters requested for (requested_for) list of users in sc_cart at checkout in service portal catalog

Kill Transactions

[edit | edit source]

/cancel_my_transaction.do


Elevate priviledges Go to User Administration => All Active Transactions. Right click on the transaction you want to terminate. Select Kill

[edit | edit source]

You can fix the filters in breadcrumbs by using the following statement in the 'Arguments' field of the module:

&sysparm_fixed_query

Deleting or editing a bad comment or work note from a record

[edit | edit source]

See https://support.servicenow.com/kb?id=kb_article_view&sysparm_article=KB0520375 Description: A comment or work note containing sensitive information needs to be edited or deleted from a record.

Solution: Update the journal entry in the [sys_journal_field] table and the audit entry in the [sys_audit] table. Please note that attempting to view the entire [sys_audit] table can result in a large number of records and can seriously damage the instances performance during the attempted query. Please follow the instructions below to locate the individual entries:

1. Right-click on the record and select Copy URL to Clipboard to obtain the unique sys_id of the record. For example: https://<instance name>service-now.com/nav_to.do?uri=incident.do?sys_id=85befb1c4a34bb12013b216a9fd5fee8
2. Copy the sys_id. In the URL example above, the sys_id is: 85befb1c4a34bb12013b216a9fd5fee8.
3. Update the Journal Entry.
   a. Enter the journal entry URL + the sys_id. For example: https://<instance name>.service-now.com/sys_journal_field_list.do?sysparm_query=element_id=85befb1c4a34bb12013b216a9fd5fee8
   b. Locate the journal entry.
   c. Modify the record and update or delete the record.
4. Update the Audit Entry.
   a. Enter the audit entry URL + the sys_id. For example: https://<instance name>.service-now.com/sys_audit_list.do?sysparm_query=documentkey=85befb1c4a34bb12013b216a9fd5fee8
   b. Locate the journal entry.
   c. Modify the record and update or delete the record.
5. Rebuild the History Set. This is only required when you do not use direct auditing and the property glide.sys.activity_using_audit_direct 
is set to false.
   a. Locate all History Set records for the item whose history needs to be rebuilt by entering the history set URL + the sys_id. For example: 
   https://<instance name>.service-now.com/sys_history_set_list.do?sysparm_query=id=85befb1c4a34bb12013b216a9fd5fee8
   b. Click the Delete button for each History Set. This will delete the History Set, not the audit data. The History Set will be rebuilt with the corrected audit and journal information as soon as a user views the item


Note: In cases where source of sensitive information is an email (or is recorded in an outbound email), you will need to perform bellow step to remove it.

6. Update the Email Entry.
   a. Enter the email entry URL + the sys_id. For example: https://<instance name>.service-now.com/sys_email_list.do?sysparm_query=instance=85befb1c4a34bb12013b216a9fd5fee8
   b. Locate the email entry.
   c. Modify the record and update or delete the record.


<instance>/cancel_my_transaction.do

Debugging

[edit | edit source]
  • global scope - Debugging Tools Best Practices page
  • scoped app - logging.verbosity / logging.destination properties.
  • JavaScript Log and jslog()
  • Field Watcher
  • try/catch
  • Debugging tools built into web browsers (browser dependent)


Try/Catch <syntaxhighlight lang="javascript">

function onLoad() {
  // Using Try/Catch to trap runtime errors.  
  // The helloWorld() function does not exist.
 	   
	try{
		helloWorld();
	}
	catch(err){
		jslog('A JavaScript runtime error occurred: ' + err.message);
	}
}

</syntaxhighlight>

HTMLSanitizerConfig

[edit | edit source]

<syntaxhighlight lang="javascript"> HTML_WHITELIST : { globalAttributes: { attribute:[], attributeValuePattern:{} }, iframe:{ attribute:["src","width","height","border","frameborder","allow","allowfullscreen"], attributeValuePattern:{} }, object:{ attribute:["classid"], attributeValuePattern:{} }, param:{ attribute:["name","value"], attributeValuePattern:{} }, embed:{ attribute:["src","type","width","height"], attributeValuePattern:{} }, },

</syntaxhighlight>


Allow code tagging in worknotes

[edit | edit source]
glide.ui.security.allow_codetag


Example:

[code]

<strong>Title here</strong>

<pre>

//paste code here

</pre>

[code]

JavaScript Executor

[edit | edit source]

ctrl-alt-shift-j Opens a dialog box for testing JavaScript in.

Prevent Incident From Being Re-Opened

[edit | edit source]

You can have a business rule on the incident table and have it setup like this:

Name : Stop Changing From Closed State
When : onBefore
Condition :  State changesFrom Closed
Abort Action : True

Prevent Closure if Child Task is Active

[edit | edit source]

This script checks for active task records that have the current record's sys_id as its parent.

<syntaxhighlight lang="javascript">

var gr = new GlideRecord('task');
gr.addQuery('active','true');  //find active records
gr.addQuery('parent',current.sys_id);  //where parent = the current record
gr.query();
if (gr.next()) {
    current.setAbortAction(true);  /abort transaction
}

</syntaxhighlight>

Enabling the Email Client for a Specific Table

[edit | edit source]

Adding Email to the ‘More Options’ Menu for RAPs (Also applies to Tickets, Requests, Incidents etc.)

File:Email option.jpg
Image showing an example of a RAP with the option to send an email.

To enable the Email option as shown above:

  1. Open a record in the appropriate application. For example, open any incident record.
  2. Right-click the header bar and select: Configure > Dictionary
  3. Select the first record in the Dictionary Entries list. This should have the record type collection and does not have any entry for Column name.
  4. In the Attributes field, enter email_client=true. Use a comma to separate from any existing values.
  5. Click Update.

Email Client Template

[edit | edit source]

An Email Client Template needs to be created before emails will be sent. Go to System Policy -> Client Templates Click on New


Use Insert and Stay to duplicate an existing template and give it a suitable name and choose the relevant database table.

Inbound Email Script

[edit | edit source]

Create an inbound action to link emails with the relevant request/incident/ticket/RAP.

Example of a basic action script. See other Inbound Actions for further examples.

Activity Log Settings

[edit | edit source]

In order for sent emails to appear in the Activity Log, it is necessary to add it to the list of displayed fields as shown below. Click on the filter icon and choose Configure available fields.

Select Sent/Received Emails from the list on the left then click on '>' to add it to the Selected list.

Sent emails should now appear in the activity log.

Tip: Use Dictionary override to set Assignment Group.

Tip: It is possible to access extended fields in a script using a special notation: ref_<tablename>.<extended field>. So, the Membership number field could be accessed through a User reference field such as gr.ref_x_hotel_guest.membership_number. Note you only need this syntax if the reference field points to the base table.

Date/TimeHandling

[edit | edit source]

https://developer.servicenow.com/app.do#!/api_doc?v=helsinki&id=r_GDT-GlideDateTime_GDT Not well documented.

Adding Reports/Gauges to Dashboard

[edit | edit source]

From within a report click on the triangle icon next to the save button and click Add to Dashboard.

Reports can also be converted to gauges in some cases.

Client & Server Code in One UI Action

[edit | edit source]

https://www.servicenowguru.com/system-ui/ui-actions-system-ui/client-server-code-ui-action/

Workflows

[edit | edit source]
File:BluQube workflow.JPG

Don't play nicely with update sets!

In 'Properties' (hamburger menu) - check 'Requested for.Department' matches department workflow is for.

Need to checkout to edit. Don't forget to publish after editing.

Check 'If Dept = ' - should contain a department name not sysid.

Check 'Approval Group' - should specify an approval group name not sysid.

Cancel Workflow When Request Cancelled

[edit | edit source]

Create a business rule
Tick 'Advanced'
Name: Cancel request workflows
When: After
Insert: True
Update: True
Condition: current.active.changesTo(false)
Script: <syntaxhighlight lang="javascript">

//Check for and cancel any running workflows
new Workflow().cancel(current);

</syntaxhighlight>

Force Check Out a Checked Out Workflow

[edit | edit source]

If you are looking for a way to checkout the checked out version below are the steps

  1. Login as an admin
  2. Navigate to Workflow Versions
  3. Search for the workflow version record which is already checked out
  4. Open the version record
  5. Choose New Workflow view
  6. Click Show workflow
  7. Hover Ham burger icon of workflow actions and select Force Checkout

Initialise Workflow

[edit | edit source]

<syntaxhighlight lang="javascript">

var indentifier = context.name + '-' + activity.name;
gs.log('---> check: ' + workflow.scrathpad.check, identifier);

</syntaxhighlight>

or

<syntaxhighlight lang="javascript">

//note square brackets required for comma separated list in JavaScript
var indentifier = context.name + '-' + activity.name;
workflow.info('---> [{1}] Check: {0}', [workflow.scratchpad.check, identifier]);

</syntaxhighlight>

1 second timers

[edit | edit source]

prevent slow workflow step from appearing to hang the GUI. Shows progress.

Scratchpad

[edit | edit source]

Add to Workflow Context form layout via Form Layout

Locks

[edit | edit source]

Place a one second timer before the lock activity

Max activities

[edit | edit source]

Set max activities for loops to prevent excessive processing

Refactoring Code

[edit | edit source]

Consider use if multiple if statements vs. switch statement

Workflow debug script

[edit | edit source]

can be a seperate script step or it can be embedded in any activity's script window.

at ${activity.name} the scratchpad is ${workflow.scratchpad}

Update Sets

[edit | edit source]

Can export as XML but beware as doesn't work in many cases. Not good with business rules.

To move an update between update sets:

Within the update set right click on the "Update set =" link on the "Customer Updates" tab and select 'Open in a new window'. This will display the 'Update Set' column which you can change in list view.

Calculation of ‘Impact’ When Raising an Incident via Self-service

[edit | edit source]

Following a request to make the ‘Service Affected’ field non-mandatory on the ‘IT - Report a fault with an existing service’ Catalog Item all incidents raised defaulted to priority ‘5 - Low (10 days)’.

The priority of an incident was calculated within the record producer script by calling the function ‘calculatePriority’ which is in a global business rule called ‘calculatePriority - MF’. calculatePriority takes two parameters – impact and urgency.

The code is as follows: <syntaxhighlight lang="javascript">

current.priority = calculatePriority(current.impact, current.u_service_affected.busines_criticality);

</syntaxhighlight> As ‘Service Affected’ is required in order to calculate ‘impact’, this no longer worked and as a result everything defaulted to priority ‘5 - Low (10 days)’.

To address this the following changes were made:

  • ‘Service Affected’ left as non-mandatory
  • ‘Impact’ field only displayed if ‘Service Affected’ is entered
  • If both ‘Service Affected’ and ‘Impact’ are completed the Priority is calculated.
  • A new Priority ‘99 - Not yet calculated’ was created for Incidents where ‘Service Affected’ is not known.
  • ‘Priority’ is calculated when Service Desk Complete the ‘Service Affected’ field.

There are various ‘On change’ client scripts which call an ‘On load’ client script ‘Calculate Priority Function’. ‘Calculate Priority Function’ contains a function calculatePriority() which makes an Ajax call to Script Include ‘ServiceAjax’ and passes the two parameters necessary for calculating ‘impact’. ServiceAjax calls calculatePriority (in global business rule ‘calculatePriority - MF’) and returns one of the following integer values:

Impact values:

4	1 - Total Service Failure
5	2 - Partial Service Failure
6	3 - Impacting On Work
7	4 - Inconvenience
8	0 - Stopping Lecture
9	S - Security Compromise

Client Script - Calculate Priority Function

Contains function to recalculate Priority based on a change to Impact or Urgency, called from onChange client scripts on those fields. <syntaxhighlight lang="javascript">

function onLoad() {
   // do nothing
}

function calculatePriority() {
   var values = new Array();
   values.push(g_form.getValue('impact') || 0);
//   values.push(g_form.getValue('urgency') || 0);
   values.push(g_form.getValue('u_service_affected.busines_criticality') || 0);
   var ga = new GlideAjax('ServiceAjax');
   ga.addParam('sysparm_name', 'calculatePriority');
   ga.addParam('sysparm_values', values);
   ga.getXML(calcReturn0, null, 'priority');
}

</syntaxhighlight>

Script Include – ServiceAjax <syntaxhighlight lang="javascript">

var ServiceAjax = Class.create();
ServiceAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
    ajaxFunction_calculatePriority: function() {
       var values = this.getParameter('sysparm_values').split(',');
       gs.print(values[0] + "  " + values[1]);
       return calculatePriority(values[0],values[1]);
    }
});

</syntaxhighlight>

calculatePriority – MF <syntaxhighlight lang="javascript">

function calculatePriority(impact,urgency) {
  var pVal = "4";
  if (impact == 0 || urgency == 0)
    pVal = "4";
  if (impact == 4 && urgency == 1)
    pVal = "1";
  if (impact == 5 && urgency == 1)
    pVal = "1";
  if (impact == 6 && urgency == 1)
    pVal = "3";
  if (impact == 4 && urgency == 2)
    pVal = "1";
  if (impact == 5 && urgency == 2)
    pVal = "2";
  if (impact == 6 && urgency == 2)
    pVal = "3";
  if (impact == 4 && urgency == 3)
    pVal = "2";
  if (impact == 5 && urgency == 3)
    pVal = "3";
  if (impact == 6 && urgency == 3)
    pVal = "4";
  if (impact == 4 && urgency == 4)
    pVal = "3";
  if (impact == 5 && urgency == 4)
    pVal = "4";
  if (impact == 6 && urgency == 4)
    pVal = "4";
  if (impact == 7)
    pVal = "5";
  if (impact == 8)
    pVal = "6";
  if (impact == 9)
    pVal = "1";
  if(pVal== "1")
    return "1|1 - Major (4 hours)";
  if (pVal == "2")
    return "2|2 - Very High (8 hours)";
  if (pVal == "3")
    return "3|3 - High (2 days)";
  if (pVal == "4")
    return "4|4 - Medium (4 days)";
  if (pVal == "5")
    return "5|5 - Low (10 days)";
  if (pVal == "6")
    return "0|0 - Urgent (1 hour)";
}

</syntaxhighlight>

Making a Homepage/Dashboard Visible to others

[edit | edit source]
  1. Go to Dashboards => Admin => Homepages (portal pages)
  2. Open the dashboard for editing
  3. Use the Read roles and/or Write roles options to specify who can access the dashboard
  4. Check the Selectable box

NOTE: the User field overrides the Roles fields so must be empty otherwise only the user specified will be able to see the dashboard regardless of how many roles are specified!

Surveys Are Not Picked Up In Update Sets

[edit | edit source]

Use Export Assessment method. Right-click on survey title and Export Assessment will be a menu choice. In target environment - go to any list and right click on a column head. The Import XML option will appear.

Elevate Roles

[edit | edit source]

Grant the security_admin role then Elevate Roles will appear on the user menu dropdown.

Inbound Email Actions

[edit | edit source]

Update Incident example <syntaxhighlight lang="javascript">

gs.include('validators');

if (current.getTableName() == "incident") {
 current.comments = "reply from: " + email.origemail + "\n\n" + email.body_text;

   if (email.subject.toLowerCase().indexOf("please reopen") >= 0) {
      if (current.incident_state != "7"){
      current.incident_state = "2";
      current.work_notes = "The caller did not feel that this issue was resolved";
	}
       }
          
 if (email.body.assign != undefined){
   current.assigned_to = email.body.assign;
}
 if (email.body.priority != undefined && isNumeric(email.body.priority)){
   current.priority = email.body.priority;
}
 if (email.body.category != undefined){
   current.category = email.body.category;
}
 if (email.body.short_description != undefined){
   current.short_description = email.body.short_description;
}
 current.update();
}

</syntaxhighlight>

Push notifications

[edit | edit source]

Table for application registration

sys_push_notif_app_install

Definitions - start, pause, stop conditions Schedules - define working days, holidays etc. SLA repair - can be used retrospectively. Care required with imported data.

Survey Management

[edit | edit source]
  • Survey
  • Survey Trigger
  • Survey Instance
  • Survey Responses

When survey is complete:

  • Business Rule
  • Creates Event
  • Triggers Email Notification Script
  • Email body created by Email Script

Templates

[edit | edit source]

Remember they exist!

Create a Change Template From a Change Request

[edit | edit source]
  • Export the change request from Production
  • Import the change request into Development
  • Toggle the template bar so it is visible (ellipsis menu)
  • Use the + button on the template bar to save as template
  • Amend fields on template form as necessary
  • save/submit
  • Add to modules
  • Export template and module as XML
  • Import template and module into Test and Prod

Call plugin vs. Custom extension of Task

[edit | edit source]

from: https://community.servicenow.com/message/911350#911350

I think the key decision point on using the Call plugin vs. doing a custom Call-type table (that extends task), is how you want to use the table in your work process.

If you are just using it to record notes from a phone call, then based on that real-time info create an incident, request, etc... then I think the Call plugin will work for you.

However, if you are wanting a pre-incident/request landing zone that you will use for initial review/triage (ie. for incoming email requests), then I would do this on a table that extends task.

The key logic for using option 2 is that you have incoming work items (ie. email) coming into a queue that requires someone to process them at some point in the future... therefore they are tasks and should have due dates, slas, and other task-type fields applied to them for proper management and performance measurement.

Personally, I've usually wound up using the approach that extends the task table in most situations. The only situation I would likely use the new_call plugin approach would be a phone queue-only call center.

Reporting

[edit | edit source]

Example - locating the caller's department for reporting

[edit | edit source]
Task Fields
.Parent-->Task fields
..Caller [Incident]--> fields
...Department-->Department fields

Access to Business Service (cmdb_ci_service)

[edit | edit source]

portfolio_admin role required

Knowledge

[edit | edit source]

Using Google CSS in a knowledgebase article

[edit | edit source]

<syntaxhighlight lang="html4strict">

 

<link href="https://ssl.gstatic.com/docs/script/css/add-ons1.css" rel="stylesheet" />

CSS Test page using Google's CSS API

 

Titles and headers

Bold text

Normal text

Links

Current navigation selection

Form input errors

Gray text

Secondary text

Titles and headers

Bold text Normal text Links Current navigation selection Form input errors Gray text Secondary text

 

Buttons

 

<button class="action">Translate</button> <button>Close</button> <button class="create">Create</button> <button class="share">Share</button>

 

Checkboxes

<input id="checkbox1" checked="checked" type="checkbox" /> <label for="checkbox1">Checked</label>
<input id="checkbox2" type="checkbox" /> <label for="checkbox2">Unchecked</label>
<input id="checkbox3" checked="checked" disabled="disabled" type="checkbox" /> <label for="checkbox3">Checked, disabled</label>
<input id="checkbox4" disabled="disabled" type="checkbox" /> <label for="checkbox4">Unchecked, disabled</label>

 

Radio buttons

<input id="radio1" checked="checked" name="radio-a" type="radio" /> <label for="radio1">Checked</label>
<input id="radio2" name="radio-a" type="radio" /> <label for="radio2">Unchecked</label>
<input id="radio3" checked="checked" disabled="disabled" name="radio-b" type="radio" /> <label for="radio3">Checked, disabled</label>
<input id="radio4" disabled="disabled" name="radio-b" type="radio" /> <label for="radio4">Unchecked, disabled</label>

 

Select menus

<label for="select">Select</label><select id="select">

<option selected="selected">Google Docs</option> <option>Google Forms</option> <option>Google Sheets</option>

</select>
<label for="disabled-select">Disabled select</label><select id="disabled-select" disabled="disabled">

<option selected="selected">Google Docs</option> <option>Google Forms</option> <option>Google Sheets</option>

</select>

 

Text areas

<label for="sampleTextArea">Label</label> <textarea id="sampleTextArea" rows="3"></textarea>

 

Text fields

<label for="city">City</label> <input id="city" style="width: 150px;" type="text" />
<label for="state">State</label> <input id="state" style="width: 40px;" type="text" />
<label for="zip-code">Zip code</label> <input id="zip-code" style="width: 65px;" type="text" />

 

Sidebars (not entirely successful!)

<style></style>

</syntaxhighlight>

From - Mastering ServiceNow - Second Edition by Martin Wood

[edit | edit source]

see http://read.amazon.com/kp/notebook

Blue highlight | Location: 202 Timing an SLA


Yellow highlight | Location: 835 Field Labels (sys_documentation)


Yellow highlight | Location: 898 .service-now.com/sys_user.do?sys_id=5137153cc611227c000bbd1bd8cd2005


Yellow highlight | Location: 949 rather than using a BLOB field, binary data is split into 4-KB chunks and saved into the Attachment Documents (sys_attachment_doc) table. Each chunk of a file refers back to the Attachments (sys_attachment) table,


Yellow highlight | Location: 968 sys_properties.list


Yellow highlight | Location: 976 So when you create a reference field, you need to select which table it should point to, which is stored in the dictionary. And the contents of the field will be a 32-character string. Sound familiar? Yep, you will be storing a sys_id in that field.


Yellow highlight | Location: 982 You can choose which field to show by ticking the Display field in the Dictionary entry.

Note:Display field use in reference fields.

Yellow highlight | Location: 1,077 Reference qualifiers


Yellow highlight | Location: 1,090 Tip An encoded query is a field-operator-value triplet, separated by the caret (^) symbol. This string represents part of the where clause of the resulting SQL query. For example, active=true specifies all records where the active field is true, while active=true^last_name=Smith represents active being ticked and the contents of the last_name field being Smith.


Yellow highlight | Location: 1,121 Tip Derived fields can only be added via Form Layout (rather than Form Design)


Yellow highlight | Location: 1,221 System Definition > Relationships


Yellow highlight | Location: 1,318 Tags can be shared between multiple users and used for filters and in reports. There are also many ways other ways you can add tags,


Yellow highlight | Location: 1,348 Live record updates The idea of working together extends to UI16's ability to show live record updates when two users change details at the same time. This incredibly powerful capability lets users identify whether they are working together and, if not, to start a chat conversation to work things out.


Yellow highlight | Location: 1,424 In the standard interface, the application you are editing is the one you selected in System Settings. This can be changed by clicking on the cog icon next to your name, going to Developer, and then choosing it from the Application Menus selector. (It's much easier if you enable the Show application picker in header option.)


Yellow highlight | Location: 1,627 Tip Inheritance allows one object to build on the functionality of another.


Yellow highlight | Location: 1,700 Tip The Allow base table lists to include extended table fields property in UI Properties changes this for the UI. Scripts can use a special syntax when dot-walking. This is mentioned in Chapter 3, Server-side Control, and Chapter 5, Getting Things Done with Tasks .


Yellow highlight | Location: 1,959 Dive into the API docs to obtain more examples and insight: https://developer.servicenow.com/app.do#!/api_doc?v=helsinki


Yellow highlight | Location: 1,961 background scripts


Yellow highlight | Location: 1,966 Navigate to Background Scripts by going to System Definition > Scripts (Background)


Pink highlight | Location: 1,971 Tip Inefficient scripts get stuck in an infinite loop and can seriously impact performance or even cause outages or security breaches. Always test in a sandbox instance first. If you do get stuck, try going to https://<instance>.service-now.com/cancel_my_transaction.do, using the same browser session. The instance will attempt to halt execution of whatever it is doing in the other tab.


Blue highlight | Location: 2,001 var results = []; var gr = new GlideRecord('x_hotel_check_in'); gr.addQuery('guest.name', 'CONTAINS', 'Alice'); gr.setLimit(2); gr.orderByDesc('sys_created_on'); gr.query(); while(gr.next()) {   results.push(gr.sys_created_on + ); } gs.info(results.join(', '));


Yellow highlight | Location: 2,080 Tip It is possible to access extended fields in a script using a special notation: ref_<tablename>.<extended field>. So, the Membership number field could be accessed through a User reference field such as gr.ref_x_hotel_guest.membership_number. Note you only need this syntax if the reference field points to the base table.


Yellow highlight | Location: 3,143 http://andyshora.com/promises-angularjs-explained-as-cartoon.html


Yellow highlight | Location: 3,201 there is a tendency to use GlideForm as the primary method for manipulating the field layout, but this is not a good idea. If possible, use a UI Policy and then fall back to using GlideForm if you need to.


Yellow highlight | Location: 3,273 https://developer.servicenow.com/app.do#!/api_doc?v=helsinki&type=client&scoped=null&to=class__client_glideform__helsinki


Yellow highlight | Location: 3,284 Any Ajax that runs when a page loads is heretical!


Yellow highlight | Location: 3,298 Date fields are not validated on the client side.


Yellow highlight | Location: 3,342 Since GlideRecord is not available to scoped apps, GlideAjax is the only way to get data from the client. However, it is also recommended to use it in non-scoped apps due to its performance benefits.


Yellow highlight | Location: 4,507 Service Catalog items are stored in the sc_cat_item table.


Yellow highlight | Location: 4,934 Additionally, another GlideRecord variable called event is provided. It is initialized against the appropriate record stored in the Event [sysevent] table. This gives you access to the other parameters (event.param1 and event.param2) as well as who created the event, when, and more.


Yellow highlight | Location: 5,030 Tip You can also use scripts to specify the From, To, Cc, and Bcc of an e-mail. The product documentation contains more information: https://docs.servicenow.com/bundle/helsinki-servicenow-platform/page/script/server-scripting/concept/c_ScriptingForEmailNotifications.html


Yellow highlight | Location: 5,035 Send to event creator When someone comes to me and says, "Martin, I've set up the e-mail notification, but it isn't working. Do you know why?" I like to put money on the reason. I very often win, and you can too. Just answer with "Ensure Send to event creator is ticked and try again."


Yellow highlight | Location: 5,056 Tip There is also the option to associate to a push notification, giving users the choice of either e-mail or receiving a message on their mobile device using subscriptions.


Yellow highlight | Location: 5,063 In addition to adding the value of fields, variable substitutions like the following ones also make it easy to add HTML links: ${<reference field>.URI} will create an HTML link to the reference field, with the text LINK ${<reference field>.URI_REF} will create an HTML link, but with the display value of the record as the text


Yellow highlight | Location: 5,075 Tip An object called email is also available. This gives much more control over the resulting e-mail, giving you methods such as setImportance, addAddress, and setReplyTo. The product documentation has more details:https://docs.servicenow.com/bundle/helsinki-servicenow-platform/page/script/server-scripting/reference/r_MailScriptAPI.html.


Yellow highlight | Location: 5,083 Tip The attach_links e-mail script is a good alternative: it provides HTML links that will let an interested recipient download the file from the instance.


Yellow highlight | Location: 5,116 Quick messages are a way to let the sender populate the message text, similar to a record template. Navigate to System Policy > Email > Quick Messages and set some text. These are then available in a dropdown selection field at the top of the e-mail client.


Yellow highlight | Location: 5,123 Sending e-mails with additional comments and work notes The journal fields in the Task table are useful enough, allowing you to type notes that are then displayed on the activity log in a who, what, when fashion. But sending out the contents via e-mail makes them especially helpful. This lets you combine two actions in one: Documenting information against the ticket Giving an update to interested parties The Task table has two fields, Watch list and Work notes list, that let you specify who will receive the e-mails. Sending out work notes First, change the Maintenance form to include the elements we need. The Work notes field should already be in the Maintenance form. Navigate to System Definition > Tables, and select the Maintenance entry. Click on Design Form. Use the designer to include the Work notes list field, placing it somewhere appropriate, such as underneath the Assignment group field. Once done, click Save. Both Watch list and Work notest list are list fields (often referred to as Glide Lists). What is special about these lists is that although they point towards the sys_user table and store the sys_id values of user records, they also store e-mail addresses in the same database field. The e-mail notification system knows all about this. It will run through the following logic when determining how to send an e-mail: If it is a sys_id value, the user record is looked up. The e-mail address in the user record is used. If it is an e-mail address, the user record is searched for. If one is found, any notification settings they have are respected. Tip A user may turn off e-mails, for example, by setting the Notification field to Disabled in their user record. More options are mentioned later. If a user record is not found, the e-mail is sent directly to the e-mail address. Let's try this out and create a new e-mail notification: Navigate to System Notifications > Email > Notifications, click on New, fill out the following fields, then Save: Name: Work notes update Table: Maintenance [x_hotel_maintenance] Inserted: <ticked> Updated: <ticked> Conditions: Work notes - changes Users/Groups in fields: Work notes list Subject: New work notes update on ${number} Message HTML: ${number} - ${short_description} has a new work note added. ${work_notes} Tip This simple message would normally be expanded and made to fit into the corporate style guidelines-use appropriate colors and styles. By default, the last three entries in the Work notes field would be included. If this were not appropriate, the appropriate property could be updated or a mail script could use getJournalEntry(1) to grab the latest one. To test, navigate to Hotel > Maintenances, click New, add an e-mail address or a user into Work notes list, enter something into the Work notes field, and save.


Yellow highlight | Location: 8,404 Tip If the request is an update to a record, this will most likely occur through a UI action that contains the whole form as parameters.


Yellow highlight | Location: 8,413 Tip You can access several very useful reports by navigating to Reports > View / Run and then looking for reports in the Transaction Log Entry and the Client Transaction Detailed Log Entry tables.


Yellow highlight | Location: 8,956 use filters to get them in chunks.


Yellow highlight | Location: 9,106 Tip While it is possible to edit the Customer Update record and change the Update Set field manually, this is not recommended.


Yellow highlight | Location: 9,636 Applications provide a better way to package functionality. After publishing an application to the Application Repository, you can install and update applications on other instances with only a few clicks. During the build phase, you can use the source control integration with Git to allow many developers to work on the application.


Yellow highlight | Location: 9,999 Tip There is unofficial documentation written by the developers that lists all of these functions: https://github.com/service-portal/documentation/blob/master/documentation/widget_server_script_apis.md#getForm

Coding shorthand!

[edit | edit source]

<syntaxhighlight lang="javascript"> function onChange(control, oldValue, newValue, isLoading) {

   if (g_form.getValue('eligible_role') && g_form.getValue('eligible_no_computer') && g_form.getValue('eligible_no_unused_computers') == true) {
       g_form.setValue('subsidy_monitor', (newValue != ) + );
       g_form.setDisplay('subsidy_monitor', newValue != );
   } else {
       g_form.setValue('subsidy_monitor', (newValue = ) + );
       g_form.setDisplay('subsidy_monitor', newValue = );
   }

} </syntaxhighlight>

Checking desktop vs mobile runtime

[edit | edit source]

You might want to mark a client script compatible with both Desktop and Mobile but still do something different depending on the runtime use this: <syntaxhighlight lang="javascript">

 if (window === null)
   // Write your mobile compatible code here
 else
   // Write your desktop compatible code here

</syntaxhighlight> back to top


Coding Best Practices

[edit | edit source]

ServiceNow Development Best Practices

Writing to the debug log

[edit | edit source]

To write to the debug log in your client-side JavaScript, or UI policies, make a call to the global function jslog().

An example of using jslog() in JavaScript: <syntaxhighlight lang="javascript">

function logData (r ) {
    lastLogDate  = r. responseXML. documentElement. getAttribute ( "last_log_entry" ) ; var items  = r. responseXML. getElementsByTagName ( "log" 
) ;
    jslog ( "response=" + r. responseText ) ; }

</syntaxhighlight> Additionally, when client scripts run, the name of the client script and timing information is displayed. This can be useful in determining which scripts are running and whether they are impacting performance. back to top

Sorting a list by groupedBy count

[edit | edit source]

This is now possible from the UI after grouping on a column.

The first parameter will sort in Ascending order whilst the latter in Descending order.

&sysparm_group_sort=COUNT &sysparm_group_sort=COUNTDESC If the above doesn't work, try one of these 2 parameters

%26sysparm_group_sort%3DCOUNTDESC %26sysparm_group_sort%3DCOUNT Example :

https://instance.service-now.com/nav_to.do?uri=%2Fincident_list.do%3Fsysparm_query%3DGROUPBYcaller_id%26sysparm_first_row%3D1%26sysparm_view%3D%26sysparm_group_sort%3DCOUNTDESC back to top

Exporting all fields from list view

[edit | edit source]

Take the URL from the address bar and modify it by adding the following to the end:

&XLSX&sysparm_default_export_fields=all &CSV&sysparm_default_export_fields=all

ACL - Access Control Lists

[edit | edit source]

ACL script to check if authorised user

[edit | edit source]

<syntaxhighlight lang="javascript"> var logged_in_user = gs.getUserName(); var arrayUtil = new ArrayUtil(); var authorised_users = new Array('userid1','userid2','userid3','userid4','userid5'); answer = arrayUtil.contains(authorised_users, logged_in_user); </syntaxhighlight>

ACL Script to check group membership

[edit | edit source]

<syntaxhighlight lang="javascript">

gs.getUser().getRecord().getValue('u_data');
//REQ1128990 - grant write but not create access to IT - A&T groups
if (
	gs.getUser().hasRole("admin") ||
	gs.getUser().hasRole("portfolio_admin") ||
	gs.getUser().isMemberOf("IT - Servers & Storage") || 
	gs.getUser().isMemberOf("IT - Linux") || 
	gs.getUser().isMemberOf("IT - Mac Desktop") || 
	gs.getUser().isMemberOf("IT - Windows Desktop") ||
	gs.getUser().isMemberOf("IT - Applications Team") || 
	gs.getUser().isMemberOf("IT - Digital Solutions") || 
	gs.getUser().isMemberOf("IT - Windows Desktop")) 
{
    answer=true;
}

</syntaxhighlight> back to top

More complex ACL

[edit | edit source]

<syntaxhighlight lang="javascript" style="word-wrap: normal"> //e4bd68bddb5c7f00f91c8c994b9619b9 is sys_id of ES Core Counsellors group //94b0b871db9c7f00f91c8c994b961982 is sys_id of ES Volunteer 1 group //7701bcb1db9c7f00f91c8c994b961955 is sys_id of ES Volunteer 2 group //fb1702c2dbb73700f81bee71ca9619d1 is the sys_if of ES Locum Counsellors (confidential) group answer = (gs.getUser().isMemberOf("x_uno49_enabl_svc.admin")) || //current user is ES admin (gs.getUser().isMemberOf("ES Managers")) || //current user is a manager ((gs.getUser().isMemberOf("ES Core Counsellors (confidential)") || gs.getUser().isMemberOf("ES Volunteer 1 (confidential)") || gs.getUser().isMemberOf("ES Volunteer 2 (confidential)") || gs.getUser().isMemberOf("ES Locum Counsellors (confidential)")) && gs.getUserID() == current.assigned_to) || //current user is a counsellor and case is assigned to the currently logged in user ((gs.getUser().isMemberOf("ES Core Counsellors (confidential)") || gs.getUser().isMemberOf("ES Volunteer 1 (confidential)") || gs.getUser().isMemberOf("ES Volunteer 2 (confidential)") || gs.getUser().isMemberOf("ES Locum Counsellors (confidential)")) && current.assignment_group != "e4bd68bddb5c7f00f91c8c994b9619b9" && current.assignment_group != "94b0b871db9c7f00f91c8c994b961982" && current.assignment_group != "7701bcb1db9c7f00f91c8c994b961955" && current.assignment_group != "fb1702c2dbb73700f81bee71ca9619d1")|| //current user is a counsellor and case is not assigned to a counselling group (gs.getUser().isMemberOf("ES Core Counsellors (confidential)") && (current.assignment_group == "94b0b871db9c7f00f91c8c994b961982" || current.assignment_group == "7701bcb1db9c7f00f91c8c994b961955")) || //current user is a core counsellor and case is assigned to volunteer 1 or volunteer 2 (gs.getUser().hasRole("x_uno49_enabl_svc.user") && current.assignment_group != "e4bd68bddb5c7f00f91c8c994b9619b9" && current.assignment_group != "94b0b871db9c7f00f91c8c994b961982" && current.assignment_group != "7701bcb1db9c7f00f91c8c994b961955" && current.assignment_group != "fb1702c2dbb73700f81bee71ca9619d1"); //current user is a ES user and case is not assigned to a Counselling group </syntaxhighlight> back to top

Annotations

[edit | edit source]

Header text example. Uses CSS. <syntaxhighlight lang="css"> color: black; font-family: Lucida Sans,Helvetica Neue,Helvetica,Arial,sans-serif; font-weight: bold; font-size: 24pt; text-align: center; </syntaxhighlight> back to top

Array coding examples

[edit | edit source]

Also try searching for '.push'

Using cmn_location

[edit | edit source]

<syntaxhighlight lang="javascript"> var poShips = []; var poLoc = new GlideRecord('cmn_location'); poLoc.addEncodedQuery('parent=a22e9dae1bcf75107fbe6654b24bcb03'); poLoc.query(); while (poLoc._next()){

   poShips.push({'name' : poLoc.name +  , 'sysid' : poLoc.sys_id + });

} var myOutput = JSON.stringify(poShips); gs.info(myOutput);

for (var i = 0; i < poShips.length; i++) {

   gs.info(poShips[i].name + ' : ' + poShips[i].sysid);

} </syntaxhighlight>

Attributes

[edit | edit source]

Attributes

[edit | edit source]

sys_user example <syntaxhighlight lang="javascript">

ref_auto_completer=AJAXTableCompleter,ref_ac_columns=user_name;department,ref_ac_order_by=name,ref_ac_columns_search=true

</syntaxhighlight>

Scoped app example with 4 columns <syntaxhighlight lang="javascript">

ref_auto_completer=AJAXTableCompleter,ref_ac_columns=ref_x_uno49_enabl_svc_case.assignment_group;ref_x_uno49_enabl_svc_case.enquiry_type;opened_at;

</syntaxhighlight>

back to top

Background Scripts

[edit | edit source]

Calculate actual and business elapsed time between two dates.

[edit | edit source]

<syntaxhighlight lang="javascript"> // Calculate actual and business elapsed time between two dates. // Chris Martin 25 June 2021

var startDate = new GlideDateTime('2021-06-22 13:51:18'); var endDate = new GlideDateTime('2021-06-24 15:13:25'); var schedule = new GlideSchedule('06e0049f1bb43050b8c68449d34bcbd1'); //sys_id of the schedule to base business time on var duration = GlideDate.subtract(startDate, endDate); //the difference between startDate and endDate actual time var busduration = schedule.duration(startDate, endDate); //the difference between startDate and endDate business time gs.print(startDate); gs.print(endDate); gs.print(duration.getDurationValue()); gs.print(busduration.getDurationValue()); </syntaxhighlight>

Change Date On Multiple Knowledge Base Articles

[edit | edit source]

<syntaxhighlight lang="javascript"> var gr = new GlideRecord('kb_knowledge'); var count = 0; //gr.setLimit(1); gr.addQuery('valid_to','2020-12-31'); gr.query(); while (gr.next()){

 gr.valid_to = '2021-03-31';
 gr.update();
 count++ ;

} gs.info('Number of KB record expiry dates updated = ' + count); //gs.info(gr.getRowCount()); </syntaxhighlight>

CI Class Hierarchy (from ServiceNow Community)

[edit | edit source]

<syntaxhighlight lang="javascript"> var custom_tables = "false"; // Show only custom tables var custom_attributes = "false"; // Show only custom attributes var active_attributes = "false"; // Show only active attributes var descendant_attributes = "true"; // Show only descendant attributes var base_table = "cmdb_ci"; // Starting point


var output = [], line;


line = { level: String("Level"), table_label: String("Table Label"), table_name: String("Table Name"), table_extend: String("Extended From"), column_active: String("Column Active"), column_label: String("Column Label"), column_name: String("Column Name"), column_descendant: String("descendant"), column_type: String("Column Type"), column_reference: String("Reference Table"), column_mandatory: String("Mandatory"), column_readonly: String("Read Only"), column_display: String("Display") } output.push(line);


var sysDbObject = new GlideRecord("sys_db_object"); if (sysDbObject.get("name", base_table)) { var baseItem = { sysId: sysDbObject.getUniqueValue(), name: String(sysDbObject.name), label: String(sysDbObject.label), isExtendable: String(sysDbObject.is_extendable), level: 0 };

//Retrieve Attributes getAttributes(baseItem.name, baseItem.label, baseItem.level, "");

// Retrieve child tables baseItem.children = getChildren(baseItem.sysId, baseItem.level); }


var i; var body = ""; body = "\n"; for (i = 0; i < output.length; i++) {

if ((custom_tables == "true") && (output[i]['table_name'].startsWith("u_"))){ //Show only custom tables body = body + output[i]['level'] + "|" + output[i]['table_label'] + "|" + output[i]['table_name'] + "|" + output[i]['table_extend'] + "|" + output[i]['column_active'] + "|" + output[i]['column_label'] + "|" + output[i]['column_name'] + "|" + output[i]['column_descendant'] + "|" + output[i]['column_type'] + "|" + output[i]['column_reference'] + "|" + output[i]['column_mandatory'] + "|" + output[i]['column_readonly'] + "|" + output[i]['column_display'] + "\n";

}else if ((custom_tables == "true") && (output[i]['table_name'].startsWith("u_")) && (descendant_attributes == "true")){ //Show only custom tables and descendant attributes body = body + output[i]['level'] + "|" + output[i]['table_label'] + "|" + output[i]['table_name'] + "|" + output[i]['table_extend'] + "|" + output[i]['column_active'] + "|" + output[i]['column_label'] + "|" + output[i]['column_name'] + "|" + output[i]['column_descendant'] + "|" + output[i]['column_type'] + "|" + output[i]['column_reference'] + "|" + output[i]['column_mandatory'] + "|" + output[i]['column_readonly'] + "|" + output[i]['column_display'] + "\n";

}else if ((custom_tables == "true") && (output[i]['table_name'].startsWith("u_")) && (custom_attributes == "true") && (output[i]['column_name'].startsWith("u_"))){ //Show only custom tables and custom attributes body = body + output[i]['level'] + "|" + output[i]['table_label'] + "|" + output[i]['table_name'] + "|" + output[i]['table_extend'] + "|" + output[i]['column_active'] + "|" + output[i]['column_label'] + "|" + output[i]['column_name'] + "|" + output[i]['column_descendant'] + "|" + output[i]['column_type'] + "|" + output[i]['column_reference'] + "|" + output[i]['column_mandatory'] + "|" + output[i]['column_readonly'] + "|" + output[i]['column_display'] + "\n";

}else if ((custom_tables == "false") && (custom_attributes == "true") && (output[i]['column_name'].startsWith("u_"))) { //Show only custom attributes body = body + output[i]['level'] + "|" + output[i]['table_label'] + "|" + output[i]['table_name'] + "|" + output[i]['table_extend'] + "|" + output[i]['column_active'] + "|" + output[i]['column_label'] + "|" + output[i]['column_name'] + "|" + output[i]['column_descendant'] + "|" + output[i]['column_type'] + "|" + output[i]['column_reference'] + "|" + output[i]['column_mandatory'] + "|" + output[i]['column_readonly'] + "|" + output[i]['column_display'] + "\n";

}else if ((custom_tables == "false") && (custom_attributes == "false") && (descendant_attributes == "true") && (output[i]['column_descendant'] == "false")) { //Show only descendant attributes body = body + output[i]['level'] + "|" + output[i]['table_label'] + "|" + output[i]['table_name'] + "|" + output[i]['table_extend'] + "|" + output[i]['column_active'] + "|" + output[i]['column_label'] + "|" + output[i]['column_name'] + "|" + output[i]['column_descendant'] + "|" + output[i]['column_type'] + "|" + output[i]['column_reference'] + "|" + output[i]['column_mandatory'] + "|" + output[i]['column_readonly'] + "|" + output[i]['column_display'] + "\n";

}else if ((custom_tables == "false") && (custom_attributes == "false") && (descendant_attributes == "false")) { //All tables and attributes body = body + output[i]['level'] + "|" + output[i]['table_label'] + "|" + output[i]['table_name'] + "|" + output[i]['table_extend'] + "|" + output[i]['column_active'] + "|" + output[i]['column_label'] + "|" + output[i]['column_name'] + "|" + output[i]['column_descendant'] + "|" + output[i]['column_type'] + "|" + output[i]['column_reference'] + "|" + output[i]['column_mandatory'] + "|" + output[i]['column_readonly'] + "|" + output[i]['column_display'] + "\n"; } } gs.print(body);

function getAttributes(table, label, level, parentTable){ var descendant;

var gr_attr = new GlideRecord("sys_dictionary"); gr_attr.addQuery("name", table); if (active_attributes == "true") { gr_attr.addQuery("active","true"); } gr_attr.addQuery("internal_type","!=","collection"); gr_attr.order("column_label"); gr_attr.query(); while(gr_attr.next()){ var td = GlideTableDescriptor.get(table); var ed = td.getElementDescriptor(gr_attr.element); if (ed.isFirstTableName() == false){ descendant = "true"; }else{ descendant = "false"; }

line = { level: String(level), table_label: String(label), table_name: String(table), table_extend: String(parentTable), column_active: String(gr_attr.active), column_label: String(gr_attr.column_label), column_name: String(gr_attr.element), column_descendant: String(descendant), column_type: String(gr_attr.internal_type), column_reference: String(gr_attr.reference.getDisplayValue()), column_mandatory: String(gr_attr.mandatory), column_readonly: String(gr_attr.read_only), column_display: String(gr_attr.display) } output.push(line); } }

function getChildren (parentSysId, parentLevel) { var children = new GlideRecord("sys_db_object"); children.addQuery("super_class", parentSysId); children.orderBy("label"); children.query(); var items = [], item; while (children.next()) { item = { sysId: children.getUniqueValue(), name: String(children.name), label: String(children.label), isExtendable: String(children.is_extendable), parentTable : String(children.super_class.getDisplayValue()), level: parentLevel + 1 }; items.push(item);

getAttributes(item.name, item.label, item.level, item.parentTable);


if (String(children.is_extendable) === "true") { item.children = getChildren(item.sysId, item.level); }

} return items; } </syntaxhighlight>

Copy and insert a range of records

[edit | edit source]

<syntaxhighlight lang="javascript"> var gr = new GlideRecord('sys_choice'); gr.addQuery('element', 'u_case_type'); gr.addQuery('dependent_value', '1'); gr.query(); gs.log('Number of BS choices: ' + gr.getRowCount()); while (gr.next()) {

   //Copy the variable set
   copyCS();

} function copyCS() {

   //set new values
   gr.dependent_value = 'csu';
   //insert new record
   gr.insert();

} </syntaxhighlight>

Date Manipulation

[edit | edit source]

<syntaxhighlight lang="javascript"> //Enter date/time string to manipulate as dString below

var dString = '2019-08-10 13:00:00';

gs.info('Date/time string entered = ' + dString);

var gDate = new GlideDate(); gDate.setValue(dString); var gDateTime = new GlideDateTime(gDate); var gt = gDateTime.getLocalTime();


gs.info(gDate.getByFormat('dd-MM-yyyy'));

gs.info('Time: ' + gDate.getByFormat('HH:mm')); gs.info('Day: ' + gDate.getByFormat('EEEE')); gs.info('Date: ' + gDateTime.getDayOfMonthLocalTime()); gs.info('Day: ' + gDate.getByFormat('MMMM')); gs.info('Month: ' + gDateTime.getMonthLocalTime()); gs.info('Year: ' + gDateTime.getYearLocalTime()); </syntaxhighlight> <syntaxhighlight lang="text"> Output:

      • Script: 10-08-2019
      • Script: Time: 13:00
      • Script: Day: Saturday
      • Script: Date: 10
      • Script: Day: August
      • Script: Month: 8
      • Script: Year: 2019

</syntaxhighlight>

Example - GlideQuery

[edit | edit source]

<syntaxhighlight lang="javascript"> var att = new GlideRecord('sys_attachment'); att.addQuery('table_name', 'x_uno49_enabl_svc_interaction'); att.addQuery('table_sys_id', '8387556fdbd32f40f81bee71ca9619b5'); att.query();

       gs.info('Row count = ' + att.getRowCount());
       while(att.next()){
       gs.info('File name = ' + att.getValue('file_name'));
       var sysAttach = new GlideSysAttachment();
       var sysEmailAttachments = sysAttach.getAttachments('x_uno49_enabl_svc_interaction', '8387556fdbd32f40f81bee71ca9619b5');
       sysEmailAttachments.setValue('table_name','x_uno49_enabl_svc_task');
       sysEmailAttachments.setValue('table_sys_id', '4787556fdbd32f40f81bee71ca9619b7');
       sysEmailAttachments.update();

}</syntaxhighlight>

Find records with duplicate email address

[edit | edit source]

<syntaxhighlight lang="javascript"> var table = 'sys_user'; var columns = ['first_name', 'last_name', 'email']; var pivotColumn = 'email'; var duplicateFinder = new GlideAggregate(table);

for (var i = 0; i < columns.length; i++) {

   duplicateFinder.groupBy(columns[i]);

}

duplicateFinder.addAggregate('COUNT', pivotColumn);

duplicateFinder.query();

while (duplicateFinder.next()) {

   var count = parseInt(duplicateFinder.getAggregate('COUNT', pivotColumn));
   if (count > 1){
       var message = count + ' found \n\t' + gs.getProperty('glide.servlet.uri') + table + '_list.do?sysparm_query=';
       for(var i = 0; i < columns.length; i++){
           message += columns[i] + '=' + duplicateFinder[columns[i]] + '^';
       }
       gs.log(message);
   }

} </syntaxhighlight> back to top

Find Requested Items (RITMs) with no open tasks

[edit | edit source]

<syntaxhighlight lang="javascript"> var arr = []; var gr = new GlideRecord('sc_req_item'); gr.addQuery('state', 1); gr.query(); while (gr.next()) {

   var ct = new GlideRecord('sc_task');
   ct.addQuery('parent', gr.sys_id);
   ct.query();
   while (ct.next()) {
       if (ct.state == 3 || ct.state == "3") {
           if (arr.indexOf(gr.number.toString()) == -1) {
               arr.push(gr.number.toString());
           }
       } else if (arr.indexOf(gr.number.toString()) > -1) {

arr.splice(arr.indexOf(gr.number.toString())); }

   }

} gs.print(arr); </syntaxhighlight> back to top

Find records older than x days/weeks/months/years

[edit | edit source]

<syntaxhighlight lang="javascript"> var gr = new GlideRecord('alm_hardware'); gr.setLimit(5); //gr.addEncodedQuery('delivery_dateRELATIVELT@day@ago@7^model_category=81feb9c137101000deeabfc8bcbe5dc4'); //gr.addEncodedQuery('delivery_dateRELATIVELT@week@ago@7^model_category=81feb9c137101000deeabfc8bcbe5dc4'); //gr.addEncodedQuery('delivery_dateRELATIVELT@month@ago@7^model_category=81feb9c137101000deeabfc8bcbe5dc4'); gr.addEncodedQuery('delivery_dateRELATIVELT@year@ago@7^model_category=81feb9c137101000deeabfc8bcbe5dc4'); gr.query(); //gs.info(gr.getRowCount()); while (gr.next()){

 gs.info('Display Name ' + gr.display_name + ':' + gr.delivery_date);

} </syntaxhighlight> back to top

Force Autoclose Background Script

[edit | edit source]

<syntaxhighlight lang="javascript">

 var gr = new GlideRecord('sc_request'); //Initialise new GlideRecord.
 gr.addQuery('number', 'REQ1113405').addOrCondition('number', 'REQ1113406').addOrCondition('number', 'REQ1113407').addOrCondition('number', 'REQ1113408'); //Build query
    gr.query(); //run query
    while(gr.next()) {    //Loop round query

      //gs.log(gr.number + ' is automatically closed after ' + pn + ' days');

      if(gr.request_state == 'resolved') {
          gr.request_state = 'closed_resolved';
      } else if(gr.request_state == 'cancelled_converted_to_incident') {
          gr.request_state = 'Closed - Converted to Incident';
      } else if(gr.request_state == 'cancelled_user_unavailable') {
          gr.request_state = 'closed_user_unavailable';
      } else if(gr.request_state == 'cancelled_duplicate') {
          gr.request_state = 'closed_duplicate';
      } else if(gr.request_state == 'cancelled_rejected') {
          gr.request_state = 'closed_rejected';
      } else if(gr.request_state == 'cancelled_other') {
          gr.request_state = 'closed_cancelled';
      }

      gr.work_notes = 'Request (' + gr.number + ') automatically closed by test script.\n';
      gr.active = false;  //Set active field to false
      gr.update();  //update record

    }

</syntaxhighlight> back to top

Resolve a ticket without triggering scripts

[edit | edit source]

<syntaxhighlight lang="javascript"> var gr = new GlideRecord('sn_customerservice_case'); gr.addQuery('number', 'CS0441426'); gr.query(); if (gr.next()){

 gr.setWorkflow(false); //suppress running of scripts
 gr.state = '6';
 gr.update();

} </syntaxhighlight> back to top

Search for text in Business Rules

[edit | edit source]

<syntaxhighlight lang="javascript">

findit('string you are searching for');

function findit(str) { 

 var scr = "";
var gr1 = new GlideRecord('sys_script');
gr1.query();
while (gr1.next()) {
  scr = gr1.script.toString();
  if (scr.indexOf(str) > -1) {
    gs.addInfoMessage(gr1.name);
  }
}

}

</syntaxhighlight> back to top

(Deprecated) Promote an extended table field to the parent table

[edit | edit source]

<syntaxhighlight lang="javascript"> GlideDBUtil.promoteColumn('table_to_move_from', 'table_to_move_to', 'field_to_move', true); </syntaxhighlight> back to top

Move records from parent to child class

[edit | edit source]

<syntaxhighlight lang="javascript"> // Take the records from: var sourceTable = 'incident'; // Which records? var targetQuery = 'sys_idIN7b60b6dd2f2720106996b0c62799b6fb,4abcf6e02f4a201027ee57ab2799b6d7'; // Where do we move the records? var targetTable = 'u_special_incident';

var grTargets = new GlideRecord(sourceTable); grTargets.addEncodedQuery(targetQuery); grTargets.setValue('sys_class_name',targetTable); grTargets.updateMultiple(); </syntaxhighlight> back to top

Recreate fields in sys_dictionary after moving records from parent to child

[edit | edit source]

<syntaxhighlight lang="javascript"> // Where are we moving to? var targetTable = 'u_special_incident'; // What fields are we moving? var fieldQuery = 'name=incident^elementSTARTSWITHu_';


var grDic = new GlideRecord('sys_dictionary'); grDic.addEncodedQuery(fieldQuery); grDic.query();

while (grDic.next()) {

 // Create a new record:
 var grNew = new GlideRecord('sys_dictionary');
 grNew.initialize();
 
 // Get all the fields for this record:
 var fields = grDic.getFields();
 
 // Loops through all elements:
 for (var i = 0; i < fields.size(); i++) {
   var geField = fields.get(i);
   var key = geField.getName();
   // Don't copy the sys_id or table name:
   if (key != 'sys_id' && key != 'name') {
     grNew[key] = grDic.getValue(key);
   }
 }
 // Set the table:
 grNew.setValue('name',targetTable);
 
 // Remove the old record:
 grDic.deleteRecord();
 
 // Finally insert the new record:
 var newSysId = grNew.insert();

}

</syntaxhighlight> back to top

Trigger a flow to run on existing RITM

[edit | edit source]

<syntaxhighlight lang="javascript"> (function() { var grScReqItem = new GlideRecord('sc_req_item'); grScReqItem.addEncodedQuery("cat_item.nameSTARTSWITHmiro^number=RITM0063786");// provide a Query grScReqItem.orderBy('sys_created_on'); grScReqItem.setLimit(1); grScReqItem.query(); while (grScReqItem.next()) { try { var flow = grScReqItem.cat_item.flow_designer_flow; var flowName = flow.sys_scope.scope + "." + flow.internal_name;

   var inputs = {};
   inputs['request_item'] = grScReqItem; // GlideRecord of table: sc_req_item
   inputs['table_name'] = 'sc_req_item';

var contextId = sn_fd.FlowAPI.startFlow(flowName, inputs);

 } catch (ex) {
   var message = ex.getMessage();
   gs.error(message);  
 }

} })(); </syntaxhighlight>


<syntaxhighlight lang="javascript"> var grRitm = new GlideRecord('sc_req_item'); grRitm.addQuery('number', '<RITM number>'); grRitm.query(); if(grRitm.next()){

var inputs = {};
 inputs["table_name"] = "sc_req_item";
 inputs["request_item"] = grRitm; 
 sn_fd.FlowAPI.executeFlow("global.<internal_name_of_flow>", inputs );

} </syntaxhighlight> back to top

Script to update multiple records

[edit | edit source]

<syntaxhighlight lang="javascript" line='line'>

/**
 * Script to change any status '11' asset recrds to status '1' 
 * prior to re-labelling status 11 to 'Being Configured'
 **/
var count = 0; 
 processAssetState();
function processAssetState() {
	var gr = new GlideRecord("alm_asset");
//	gr.setLimit(5);
	gr.addQuery("install_status", "11");
	gr.query()
	while (gr.next()) { 
		gr.install_status = "1";
	    count++ ;	
	gr.update(); 						}
}
gs.info('Number of asset records processed is ' + count);

</syntaxhighlight>


<syntaxhighlight lang="javascript" line='line'>

/**
* Script to change any status '11' asset recrds to status '1' 
* prior to re-labelling status 11 to 'Being Configured'
**/
var count = 0;
// var queryString = "asset_tag=31108^install_status=11";
processAssetState();
 
function processAssetState() {
	var gr = new GlideRecord("alm_asset");
// 	gr.setLimit(1);
	gr.addQuery("install_status", "11");
//	gr.addEncodedQuery(queryString);
	gr.query();
 
	while (gr.next()) { 
		gr.install_status = "1";
	    gs.log('Selected assets: ' + gr.asset_tag + ' Serial number is: ' + gr.serial_number + 'Assigned to is: ' + gr.assigned_to.getDisplayValue());
		count++ ;
 		
// 	gr.update();  job de-activated & update commented out after CHG0044150 23/05/2017 at 15:42
 						}
}
 
gs.info('Number of asset records processed is ' + count);

</syntaxhighlight> back to top

DeleteMultiple Background Script

[edit | edit source]

<syntaxhighlight lang="javascript">

var gr = new GlideRecord('incident');
gr.addQuery('active', false);
gr.deleteMultiple(); //Delete all the queried records

</syntaxhighlight> back to top

Using A Script To Create Tables/Extensions

[edit | edit source]

From: www.servicenowgems.com/2017/08/07/creating-tables-via-script/

Uses out of the box TableDescriptor script include.


Create a copy of an existing table <syntaxhighlight lang="javascript">

copyTable("incident"); //table to copy
function copyTable(tableName) {
var gr = new GlideRecord(tableName);
gr.initialize();
//Get tabledetails
var td = GlideTableDescriptor.get(tableName);
var displayName = td.getDisplayName();
var tLabel = gr.getLabel();
var tName = "u_" + tableName; // If you don't name it with u_ you won't be able to delete it
var creator = new TableDescriptor(tName, tLabel);
//check if this table is an extension
var db = new GlideRecord("sys_db_object");
db.addEncodedQuery("super_classISNOTEMPTY^name=" + tableName);
db.setLimit(1);
db.query();
if (db.next()) {
creator.setExtends(db.super_class + );
}
creator.setFields(gr);
creator.copyAttributes(td);
//copies the security to the new table
creator.setRoles(td);
//Create the table
creator.create();
//copy indexes 
creator.copyIndexes(tableName, tName);
}

Create an extension of an existing table.

createExtension("u_my_new_app", "My new application", "task");
function createExtension(tableName, tableLabel, extends) {
var creator = new TableDescriptor(tableName, tableLabel);
creator.setExtends(extends);
creator.create();
}

</syntaxhighlight> back to top

Search on sys_id

[edit | edit source]

<syntaxhighlight lang="javascript"> findSysID('your mysterious sysid here');

function findSysID(id) {

 var gr = new GlideRecord('sys_db_object');
 gr.addEncodedQuery('super_class=NULL^nameNOT LIKEts_c_^nameNOT LIKEsysx_^nameNOT LIKEv_');
 gr.query();
 var searchTable, name;
 while (gr.next()) {
   name = gr.name + ;
   searchTable = new GlideRecord(name);
   if (searchTable.isValid()) {
     searchTable.addQuery('sys_id', id);
     searchTable.queryNoDomain()
     searchTable.setLimit(1);
     searchTable.query();
     if (searchTable.hasNext()) {
       gs.print('Found on table: ' + name);
     }
   }
 }

} </syntaxhighlight> back to top

Find duplicate records

[edit | edit source]

<syntaxhighlight lang="javascript"> var dupRecords = [];

    var gaDupCheck1 = new GlideAggregate('x_uno49_enabl_svc_customer');
    //gaDupCheck1.addQuery('active','true');
    gaDupCheck1.addAggregate('COUNT', 'user');
    gaDupCheck1.groupBy('user');
    gaDupCheck1.addHaving('COUNT', '>', 1);
    gaDupCheck1.query();
    while (gaDupCheck1.next()) {
          dupRecords.push(gaDupCheck1.user.toString());
    }
    gs.print(dupRecords);

</syntaxhighlight> back to top

Find records where customer != parent record customer

[edit | edit source]

<syntaxhighlight lang="javascript"> gs.addInfoMessage('Here we go - Find records where interaction customer != folder customer'); //var notMatch = []; var gr = new GlideRecord('x_uno49_enabl_svc_interaction');

   gr.addEncodedQuery('customerISNOTEMPTY');
   gr.addEncodedQuery('customerNSAMEASparent.ref_x_uno49_enabl_svc_case.customer');
   gr.query();
   while (gr.next()) {

          gs.addInfoMessage(gr.number + ': Customer = ' + gr.customer.getDisplayValue() + ' : interaction is in folder ' + gr.parent.getDisplayValue() + ' which belongs to ' + gr.parent.customer.getDisplayValue());
          // notMatch.push(gr.number.toString());
        
   }

//gs.print('Interactions where customer does not match customer on parent folder: ' + notMatch); </syntaxhighlight> back to top

Find users that have both itil and business_stakeholder roles

[edit | edit source]

<syntaxhighlight lang="javascript"> // Query the sys_user_has_role table to find users with both itil and business_stakeholder roles

var itilUsers = []; var stakeholderUsers = [];

// Get all users with the 'itil' role var itilRole = new GlideRecord('sys_user_has_role'); itilRole.addQuery('role.name', 'itil'); itilRole.query(); while (itilRole.next()) {

   itilUsers.push(itilRole.user.toString());

}

// Get all users with the 'business_stakeholder' role var stakeholderRole = new GlideRecord('sys_user_has_role'); stakeholderRole.addQuery('role.name', 'business_stakeholder'); stakeholderRole.query(); while (stakeholderRole.next()) {

   stakeholderUsers.push(stakeholderRole.user.toString());

}

// Find users that exist in both lists (i.e., users with both roles) var commonUsers = itilUsers.filter(user => stakeholderUsers.includes(user));

// Retrieve and display user details var userCount = 0; if (commonUsers.length > 0) {

   gs.info('Users with both itil and business_stakeholder roles:');
   var userGR = new GlideRecord('sys_user');
   userGR.addQuery('sys_id', 'IN', commonUsers);
   userGR.query();
   while (userGR.next()) {
       gs.info(userGR.user_name + ' (' + userGR.name + ') : ' + userGR.getUniqueValue());

userCount += 1;

   }

} else {

   gs.info('No users found with both itil and business_stakeholder roles.');

} gs.info(userCount); </syntaxhighlight> back to top

Nested GlideQuery

[edit | edit source]

<syntaxhighlight lang="javascript"> // Set the variable 'answer' to a comma-separated list of user ids and/or group ids or an array of user/group ids to add as approvers. // // For example: // answer = []; // answer.push('id1'); // answer.push('id2'); answer = []; var gr = new GlideRecord('sys_user'); gr.initialize(); gr.addActiveQuery(); gr.addQuery('u_main_post.u_short_description', 'CONTAINS', 'Graduate School') && gr.addQuery('u_main_post.u_short_description', 'CONTAINS', 'Team Leader'); gr.query(); while(gr.next()){

 var g2 = new GlideRecord('u_employee_post');
 g2.addActiveQuery();
 g2.addQuery('u_number', gr.u_main_post.u_number);
 g2.query();
 while(g2.next()){
   answer.push(g2.u_employee);
 }

} gs.log('SSC Student Late Arrival - approvals array: ' + answer);

</syntaxhighlight> back to top

SLAs - Update pause and stop conditions

[edit | edit source]

<syntaxhighlight lang="javascript"> var gr = new GlideRecord('contract_sla'); gr.addEncodedQuery('active=true^sys_updated_by=chris.martin'); gr.query(); gs.info(gr.getRowCount()); while (gr.next()) {

   gs.info(gr.name);
   gr.pause_condition = 'state=18^ORstate=19^ORstate=6^EQ';
   gr.stop_condition = 'state=3^EQ';
   gr.update();

} </syntaxhighlight> back to top

SLAs - script to fix SLAs for a selection of records

[edit | edit source]

Comment out setValidateOnly statement to run for real.
caseQuery is an encoded query

<syntaxhighlight lang="javascript"> var caseQuery = "sys_created_on>=javascript:gs.dateGenerate('2021-09-01','00:00:00')^u_department=csu"; var slaRepair = new SLARepair(); slaRepair.setValidateOnly(true); slaRepair.repairByFilter(caseQuery, "sn_customerservice_case"); if (slaRepair.validateOnly)

  gs.log("SLARepair run in validate only mode - found " + slaRepair.taskIds.length + " Incident records to repair", "CM Text to identify in logs");

</syntaxhighlight> back to top

Knowledgebase Article - reset view count

[edit | edit source]

<syntaxhighlight lang="javascript"> var gr = new GlideRecord('kb_knowledge'); gr.initialize(); gr.addQuery('number','KB0010133'); gr.query(); if (gr.next()){

   gr.sys_view_count = 3;
   gr.update();

} </syntaxhighlight> Also clear relevant entries form kb_use

back to top

reassign tickets to a different user

[edit | edit source]

<syntaxhighlight lang="javascript"> var gr = new GlideRecord('task'); gr.addQuery('assigned_to.sys_id','<sysid of old user>'); gr.query(); gs.log(gr.getRowCount()); gr.assigned_to = '<new user name>'; gr.autoSysFields(false); // Do not update sys_updated_by, sys_updated_on, sys_mod_count, sys_created_by, and sys_created_on gr.setWorkflow(false); // Do not run any other business rules gr.updateMultiple(); </syntaxhighlight> back to top

change opened_by to a different user

[edit | edit source]

<syntaxhighlight lang="javascript"> var gr = new GlideRecord('task'); gr.addQuery('opened_by.sys_id','<sysid of old user>'); gr.query(); gs.log(gr.getRowCount()); gr.opened_by = '<new user name>'; gr.autoSysFields(false); // Do not update sys_updated_by, sys_updated_on, sys_mod_count, sys_created_by, and sys_created_on gr.setWorkflow(false); // Do not run any other business rules gr.updateMultiple(); </syntaxhighlight> back to top

Close Resolved Cases Not Updated > 2 months

[edit | edit source]

<syntaxhighlight lang="javascript"> var grCase = new GlideRecord('sn_customerservice_case'); grCase.addEncodedQuery('state=6^sys_updated_onRELATIVELT@month@ago@2'); grCase.orderBy('sys_updated_on'); grCase.setLimit(2); grCase.query(); grCase.state = '3'; grCase.autoSysFields(false); // Do not update sys_updated_by, sys_updated_on, sys_mod_count, sys_created_by, and sys_created_on grCase.setWorkflow(false); // Do not run any other business rules grCase.updateMultiple(); </syntaxhighlight> back to top

Number of logged in users

[edit | edit source]

<syntaxhighlight lang="javascript"> var total_users = 0; var diag = new Diagnostics();

while (diag.nextNode()) {

   var diagNode = diag.getNode();
   var ss = diagNode.stats.sessionsummary;
   if (ss) {
       gs.print(JSON.stringify(ss));
       total_users += parseInt(diagNode.stats.sessionsummary["@logged_in"]);
   }

}

gs.info(total_users); </syntaxhighlight> back to top

[edit | edit source]

<syntaxhighlight lang="javascript"> //enter ticket number as variable var ticketNo = 'CS0423365';

var gr = new GlideRecord('sn_customerservice_case'); gr.addQuery('number', ticketNo); gr.query(); if (gr.next()){

   gr.comments = "Further testing a script that inserts a hyperlink into additonal comments. [code]<a href='/"+gr.sys_class_name+".do?sys_id="+gr.sys_id+"'>"+gr.number+"</a>[/code]";
   gr.update();

} </syntaxhighlight> back to top

Stop SLAs for closed tickets

[edit | edit source]

<syntaxhighlight lang="javascript"> var grSLA = new GlideRecord('task_sla'); grSLA.addEncodedQuery('stage=paused^task.state=3'); grSLA.orderBy('sys_created_on'); //grSLA.setLimit('10000'); grSLA.query(); gs.info(grSLA.getRowCount()); // while (grSLA.next()){

   grSLA.end_time = grSLA.pause_time;
   grSLA.stage = 'completed';
   grSLA.updateMultiple(); 

// } </syntaxhighlight> back to top

Stop Workflows for closed tickets

[edit | edit source]

<syntaxhighlight lang="javascript"> var gr = new GlideRecord('wf_context'); //workflow contexts table gr.addQuery('state', 'executing'); //find active contexts gr.orderBy('sys_created_on'); //following line for testing on a small number of records. Comment out when satisfied with results. gr.setLimit('1000'); gr.query(); var csFound = []; //array to store processed ticket numbers while (gr.next()) {

   var csState = gr.id.getRefRecord(); //get sysid of related record. This is a document id field
   if (gr.getDisplayValue('table') == 'task_sla') { //only process task_sla related contexts
       if (csState.task.state == '3') { //filter on cases where state = '3' (Closed)

csFound.push(csState.task.number); //push case number to csFound array

           new global.Workflow().cancel(csState);
       }
   }

} gs.info('***CM*** cancelled worflow contexts for ' + csFound); </syntaxhighlight> back to top

Task SLA - create event for breached incidents without an assignee

[edit | edit source]

<syntaxhighlight lang="javascript"> var usr = []; var gr = new GlideRecord('task_sla'); gr.initialize(); gr.addEncodedQuery("active=true^task.sys_class_name=incident^task.assigned_toISEMPTY^has_breached=true^sla.type=SLA^sla.target=response^task.stateNOT IN3,4,7,8,6,106,14^business_percentage>1000^start_time<javascript:gs.dateGenerate('2024-11-01','00:00:00')"); gr.setLimit(10); gr.query(); while (gr.next()) { gs.info('Incident ' + gr.task.number); getMember(gr.task.assignment_group);

   gs.eventQueue("incident.breached.no.assignee", gr, usr[0], usr[1]);

}

function getMember(group) {

   var memGr = new GlideRecord('sys_user_grmember');
   memGr.initialize();
   memGr.addActiveQuery();
   memGr.addQuery('group', group);
   memGr.addQuery('user', '!=', null);
   memGr.query();
   if (memGr.next()) {

usr = [];

       gs.info(memGr.group.name);

usr.push(memGr.user); usr.push(memGr.user.first_name);

       gs.info(usr);
       return usr;
   }

} </syntaxhighlight> back to top

Business Rules

[edit | edit source]

GlideRecord query to find tasks and assign them to parent's assigned_to== <syntaxhighlight lang="javascript"> (function executeRule(current, previous /*null when async*/) { //script to find all task associated with a request and assign them to the same assigned_to as the parent request

  var gr = new GlideRecord('task');
  gr.addQuery('parent', current.sys_id);
  gr.query();
  while (gr.next()) {

gr.setValue('assigned_to', current.assigned_to); gr.update();

  }
  

})(current, previous); </syntaxhighlight> back to top

Business Rule to Hide Empty Records

[edit | edit source]

Create a Business Rule that runs before insert and contains the following type of script to filter out the unwanted records.

<syntaxhighlight lang="javascript">
current.addEncodedQuery('nameISNOTEMPTY^cmdb_model_categoryISNOTEMPTY'); //Encoded query
</syntaxhighlight>

back to top

Business Rule Examples

[edit | edit source]

<syntaxhighlight lang="javascript">

   (function executeRule(current, previous /*null when async*/) {

   var ci_room = current.u_room;
   var asset_id = current.asset_tag;
   var count = 0;

   var computers = new GlideRecord('alm_hardware');
   //
   // Add the asset_tag to our query to find the record we want in alm_hardware
   computers.addQuery('asset_tag', '=', asset_id );
   // 
   //
   computers.query();
   //
   //Find the matching record in alm_harware , update the CI room field
   //
   while (computers.next()) {
      computers.u_asset_room.setDisplayValue(ci_room);
      computers.update();
      count++;
   }

   })(current, previous);


   (function executeRule(current, previous /*null when async*/) {

   var asset_room = current.u_asset_room;
   var asset_id = current.asset_tag;
   var count = 0;

   var computers = new GlideRecord('cmdb_ci');
   //
   // Add the asset_tag to our query to find the record we want in cmdb_ci
   computers.addQuery('asset_tag', '=', asset_id );
   // 
   //
   computers.query();
   //
   //Find the matching record in cmdb_ci , update the CI room field
   //
   while (computers.next()) {
      computers.u_room.setDisplayValue(asset_room);
      computers.update();
      count++;
   }
   //
   //gs.log('Number of cmdb_ci records processed for room change is ' + count + '\n' + "computers.u_room = " + computers.u_room + '\n' + "computers.u_room.u_location_reference = " + computers.u_room.u_location_reference + "\n" + "Asset room = " + asset_room + "\n" + "Asset ID = " + asset_id);

   })(current, previous);

</syntaxhighlight>

Prevent Closure if Child Task is Active <syntaxhighlight lang="javascript">

(function executeRule(current, previous /*null when async*/ ) {
  //Prevents closing a task if any of the task's child tasks are still active.
  var gr = new GlideRecord('task');
  gr.addQuery('active', 'true');
  gr.addQuery('parent', current.sys_id);
  gr.query();
  if (gr.next()) {
    current.setAbortAction(true);
    gs.addInfoMessage("Unable to save as there are open tasks associated with this change request.");
  }
})(current, previous);

</syntaxhighlight>

Close parent task on child task closure <syntaxhighlight lang="javascript"> (function executeRule(current, previous /*null when async*/) { var gr = new GlideRecord('x_uno49_enabl_svc_interaction');

  gr.addQuery('active', 'true');
  gr.addQuery('number', current.parent.getDisplayValue());
  gr.query();
  if (gr.next()) {

gr.setValue('state', 3); gr.update();

     }

})(current, previous); </syntaxhighlight>

Disable Mandatory Field Check On Save

Use in a before insert or update business rule <syntaxhighlight lang="javascript">

g_form.checkMandatory = false;

</syntaxhighlight> Remove yellow background from work notes in email notification Create a 'before' business rule <syntaxhighlight lang="javascript">

(function executeRule(current, previous /*null when async*/) {

  var str=current.body;
  var newStr=str.replaceAll("background-color:LightGoldenRodYellow;", "");
  current.body=newStr;

})(current, previous);

</syntaxhighlight> back to top

Cancel Flow on RITM Closure

[edit | edit source]

<syntaxhighlight lang="javascript"> var now_GR = new GlideRecord("sys_flow_context"); now_GR.addQuery("name", "NAME OF FLOW TO CANCEL HERE"); now_GR.query();

while (now_GR.next()) { sn_fd.FlowAPI.cancel(now_GR.getUniqueValue(), 'Canceling Test Flows'); } </syntaxhighlight> back to top

Find and Close Catalog Tasks with Closed Parent RITMs

[edit | edit source]

<syntaxhighlight lang="javascript"> var arrTasks = []; var arrayUtil = new ArrayUtil();

function findTasksWithClosedRitm() {

   var grTask = new GlideRecord('sc_task');
   grTask.addEncodedQuery('state=1^parent.stateIN3,4,7');

grTask.setLimit('1');

   grTask.query();
   gs.info(grTask.getRowCount());
   while (grTask.next()) {

gs.print(grTask.number);

       grTask.state = '7';

grTask.work_notes = 'Marked as Closed Skipped as parent RITM was Closed' grTask.update();

   }

}

findTasksWithClosedRitm(); </syntaxhighlight> back to top

Find Closed RITMs with active flows and cancel the flows

[edit | edit source]

<syntaxhighlight lang="javascript"> //Lookup flows in a waiting state and push the sysids of the source record into array arrFlows function flowsWaiting() {

   var grFlow = new GlideRecord('sys_flow_context');
   grFlow.addQuery('state', 'waiting');
   grFlow.addQuery('source_table', 'sc_req_item');
   //grFlow.setLimit('1000');
   grFlow.query();
   gs.print('Found ' + grFlow.getRowCount() + ' flows in waiting state with source table sc_req_item');
   while (grFlow.next()) {
       arrFlows.push(grFlow.source_record.toString());
   }
   //gs.print(arrFlows);

}

//Lookup closed RITMs that are in the array arrFlows function closedRitmsWithActiveFlow() {

   var grRitm = new GlideRecord('sc_req_item');
   grRitm.addEncodedQuery('sys_idIN' + arrFlows + '^stateIN3,4,7');
   //grRitm.setLimit('100');
   grRitm.query();
   while (grRitm.next()) {
       arrRitm.push(grRitm.sys_id.toString());
   }
   gs.print(grRitm.getRowCount() + ' Closed RITMs with active flows');
   // gs.print(arrRitm);

}

//Lookup flow contexts and store their sysids in array arrFlows2 function getArraysToCancel() {

   var grFlow2 = new GlideRecord('sys_flow_context');
   grFlow2.addEncodedQuery('source_recordIN' + arrRitm);
   //grFlow2.setLimit('1');
   grFlow2.query();
   gs.print('Found ' + grFlow2.getRowCount() + ' flows.');
   while (grFlow2.next()) {
       arrFlows2.push(grFlow2.sys_id.toString());
   }

grFlow2 = arrayUtil.unique(grFlow2);

   //gs.print('Flows to be terminated ' + arrFlows2);

}

//Loop through the elements of arrFlows2 and cancel the flow context function cancelFlows() {

   //Loop through array of flow sysids and cancel them
   for (var i = 0; i < arrFlows2.length; i++) {
      sn_fd.FlowAPI.cancel(arrFlows2[i], 'Cancelling orphaned flows'); 
   }

}

var arrFlows = []; var arrRitm = []; var arrFlows2 = []; var arrayUtil = new ArrayUtil(); flowsWaiting(); closedRitmsWithActiveFlow(); getArraysToCancel(); //cancelFlows(); </syntaxhighlight> back to top

Copy RITM Variables and Values to Description Field

[edit | edit source]

(function executeRule(current, previous /*null when async*/ ) {

   if (current.description) {
       var desc = current.description;
   }
   var variables = current.variables.getElements();
   var str = ;
   for (var i = 0; i < variables.length; i++) {
       var question = variables[i].getQuestion();
       if (question.getDisplayValue()) {
           var variableLabel = question.getLabel();
           var variableValue = question.getDisplayValue();
           str = str + variableLabel + ' - ' + variableValue + '\n';
       }
   }
   if (desc){
       current.description = desc + '\n\n' + current.number + '\nItem orderd: ' + current.getDisplayValue('cat_item') + '\n' + str;
   } else {
       current.description = current.number + '\nItem orderd: ' + current.getDisplayValue('cat_item') + '\n' + str;
   }

})(current, previous); </syntaxhighlight> back to top

Copy RITM Description Field(s) to Request

[edit | edit source]

(function executeRule(current, previous /*null when async*/ ) {

   if (current.description) {
       var desc = current.description;
   }
   var appendDesc = ;
   var ritmGR = new GlideRecord('sc_req_item');
   ritmGR.initialize();
   ritmGR.addQuery('request', current.sys_id);
   ritmGR.query();
   while (ritmGR.next()) {
       appendDesc += ritmGR.description;
   }
   if (desc) {
       current.description = desc + appendDesc;
   } else {
       current.description = appendDesc;
   }
   current.update();

})(current, previous); </syntaxhighlight> back to top

Create a child record for each attachment

[edit | edit source]

Business rule to create a child record per attachment on the parent. BR then calls a global script include which copies a single attachment to the child record and deletes the attachment from the parent. <syntaxhighlight lang="javascript"> (function executeRule(current, previous /*null when async*/) { var att = new GlideRecord('sys_attachment'); att.addQuery('table_name', current.getTableName()); att.addQuery('table_sys_id', current.getUniqueValue()); att.query(); while(att.next()) { var task = new GlideRecord('x_uno49_enabl_svc_task'); task.initialize(); task.parent = current.getUniqueValue(); task.assignment_group = current.assignment_group; task.short_description = 'Emailed attachment - ' + att.file_name; task.insert(); var cpAtt = new global.CopySpecificAttachment(); cpAtt.CopySpecificAttachment('x_uno49_enabl_svc_interaction', current.getUniqueValue(), 'x_uno49_enabl_svc_task', task.getUniqueValue(), att.file_name); } })(current, previous); </syntaxhighlight> See also: Global script include called by this business rule.

Create Outage

[edit | edit source]

Run after insert or update.

Approval == approved AND

approval, state, planned start_date or planned end_date change.

<syntaxhighlight lang="javascript"> (function executeRule(current, previous /*null when async*/) {

// locate an existing outage var outage = new GlideRecord('cmdb_ci_outage'); outage.addQuery('type', 'planned'); outage.get('task_number', current.sys_id);

// cancelling if (current.state.changesTo(4)) { // Closed Cancelled if (outage.isValidRecord() && current.start_date > gs.daysAgo(0)) { outage.deleteRecord(); } return; }

// create an outage if one doesn't already exist if (!outage.isValidRecord()) { outage.initialize(); outage.type = 'planned'; outage.task_number = current.sys_id; outage.cmdb_ci = current.cmdb_ci; outage.begin = current.start_date; outage.end = current.end_date; outage.short_description = current.short_description; outage.u_restart_comments = current.u_business_case; outage.details = current.u_communications_plan; outage.insert(); }

// update dates if (current.start_date.changes() || current.end_date.changes()) { outage.begin = current.start_date; outage.end = current.end_date; outage.update(); }

})(current, previous); </syntaxhighlight> back to top

Set Contact Type Business Rule

[edit | edit source]

<syntaxhighlight lang="javascript">

 (function executeRule(current, previous /*null when async*/ ) {
   c_user = gs.getUser();

   if (current.contact_type != 'self-service') {
     if (gs.getUser().isMemberOf('IT - Service Desk') || gs.getUser().isMemberOf('IT - User Support Team')) {
       current.contact_type = 'phone';
     } else {
       if (c_user.getDisplayName() == "IT Ambassador") {
         current.contact_type = 'walk-in';
       } else {
         current.contact_type = 'Direct Input';
       }

     }
   }
 })(current, previous);

</syntaxhighlight> back to top

Display Business Rule to set scratchpad variables

[edit | edit source]

<syntaxhighlight lang="javascript"> (function executeRule(current, previous /*null when async*/) {

   g_scratchpad.grp_sd = gs.getUser().isMemberOf('IT - Service Desk'); 
   g_scratchpad.grp_ust = gs.getUser().isMemberOf('IT - User Support Team'); 

})(current, previous); </syntaxhighlight>

back to top

Prevent incident closure when there are open events [on update]

[edit | edit source]

<syntaxhighlight lang="javascript"> (function executeRule(current, previous /*null when async*/ ) {

   var grAlert = new GlideAggregate('em_alert');
   grAlert.addAggregate('COUNT');
   grAlert.addQuery('incident', current.sys_id);
   grAlert.addQuery('state', 'Open');
   grAlert.query();
   if (grAlert.next()) {
       if (grAlert.getAggregate('COUNT') > 0) {
           current.setAbortAction(true);
           gs.addInfoMessage("Cannot resolve/close as an alert is still open");
       }
   }

})(current, previous); </syntaxhighlight>

back to top

'On insert' Business Rule to omit the change requester from the approvers list

[edit | edit source]

<syntaxhighlight lang="javascript">

(function executeRule(current, previous /*null when async*/) {

if (current.approver.getDisplayValue() == current.sysapproval.requested_by.name) { current.setAbortAction(true);

   }
})(current, previous);

</syntaxhighlight> back to top back to top

'On insert/update' Business Rule to check lead time on change requests

[edit | edit source]

<syntaxhighlight lang="javascript"> if ((previous.approval == 'not requested')||(previous.approval == )){

if ((current.type == 'Standard') && ((current.state == '1') || (current.state == '-5') || (current.state == '2'))) {

if (!current.start_date.nil()) { var two4_ms = 86400000;//milliseconds in 24 hours

var start = current.start_date.getGlideObject().getNumericValue(); //gs.addInfoMessage('start date '+start);

var nowdt = new GlideDateTime(); nowdt.setDisplayValue(gs.nowDateTime()); var nowMs = nowdt.getNumericValue();

var now24 = nowMs + two4_ms; //gs.addInfoMessage('now24 '+now24); if ( start < now24) { gs.addInfoMessage('start must be at least 24 hours from now'); current.start_date.setError('start must be at least 24 hours from now'); current.setAbortAction(true); } } }

if ((current.type == 'Service Change') && ((current.state == '1') || (current.state == '-5') || (current.state == '2'))) {

if (!current.start_date.nil()) {

// Two weeks in milliseconds var twoWeeks_ms = 1209600000;

var start = current.start_date.getGlideObject().getNumericValue(); //gs.addInfoMessage('start date '+start);

var nowdt = new GlideDateTime(); nowdt.setDisplayValue(gs.nowDateTime()); var nowMs = nowdt.getNumericValue();

var now2Weeks = nowMs + twoWeeks_ms; //gs.addInfoMessage('now24 '+now2Weeks);

if ( start < now2Weeks ) { gs.addInfoMessage('start must be at least 2 weeks from now'); current.start_date.setError('start must be at least 2 weeks from now'); current.setAbortAction(true); }

}

}

if ((current.type == 'Minor') && ((current.state == '1') || (current.state == '-5') || (current.state == '2'))) {

if (!current.start_date.nil()) { var twoday_ms = 172800000;//milliseconds in 2 days var fiveday_ms = 432000000;//milliseconds in 5 days


var start1 = current.start_date.getGlideObject().getNumericValue(); //gs.addInfoMessage('start date '+start);

var nowdt1 = new GlideDateTime(); nowdt1.setDisplayValue(gs.nowDateTime()); var nowMs1 = nowdt1.getNumericValue();


if (current.u_is_communication_required_ == 'Not required') { var now7 = nowMs1 + twoday_ms; //gs.addInfoMessage('now7 '+now7); if ( start1 < now7) {

gs.addInfoMessage('start must be at least 2 days from now'); current.start_date.setError('start must be at least 2 days from now');

current.setAbortAction(true); } }

if ((current.u_is_communication_required_ == 'Communication from IT Service Desk') || (current.u_is_communication_required_ == 'Communication from Other')) { var now7c = nowMs1 + fiveday_ms;

//gs.addInfoMessage('now7 '+now7); if ( start1 < now7c) { gs.addInfoMessage('start must be at least 5 days from now'); current.start_date.setError('start must be at least 5 days from now');

current.setAbortAction(true); } } } }

if ((current.type == 'Intermediate' || current.type == 'Major') && ((current.state == '1') || (current.state == '-5') || (current.state == '2'))) {

if (!current.start_date.nil()) {

var oneweek_ms = 604800000;//milliseconds in 1 week

var start2 = current.start_date.getGlideObject().getNumericValue(); //gs.addInfoMessage('start date '+start);

var nowdt2 = new GlideDateTime(); nowdt2.setDisplayValue(gs.nowDateTime()); var nowMs2 = nowdt2.getNumericValue();

var now8 = nowMs2 + oneweek_ms; //gs.addInfoMessage('now7 '+now8); if ( start2 < now8) { gs.addInfoMessage('start must be at least 1 week from now'); current.start_date.setError('start must be at least 1 week from now'); current.setAbortAction(true); } } } } </syntaxhighlight> back to top

Business Rule (after Update): Make note of email attachment in activity log

[edit | edit source]

Target Table:

Created: at or after Last minute

Type is send-ready

<syntaxhighlight lang="javascript"> (function executeRule(current, previous /*null when async*/) {

appendAttachment();

function appendAttachment() { var instance = gs.getProperty('instance_name'); var targetTable = current.target_table; var gr = new GlideRecord('sys_attachment'); gr.addQuery('table_sys_id',current.sys_id); gr.query();

while(gr.next()){ gr.table_sys_id = instance; gr.table_name = targetTable; gr.update(); var task = new GlideRecord(targetTable); task.addQuery('sys_id',instance); task.query(); while (task.next()) { task.work_notes = "Attachment " + gr.file_name + " added from Email Client by " + current.sys_created_by; task.update(); } } }

})(current, previous); </syntaxhighlight> back to top

Add message to activity log for each sent email attachment

[edit | edit source]


Calls global script include LogEmailAttachment

<syntaxhighlight lang="javascript"> (function executeRule(current, previous /*null when async*/) {

var att = new global.LogEmailAttachment(); att.LogEmailAttachment(current.getUniqueValue());

})(current, previous);

</syntaxhighlight> back to top

Set expected start date of a task to date parent task was created

[edit | edit source]

Used to set retroactive start on an SLA <syntaxhighlight lang="javascript"> (function executeRule(current, previous /*null when async*/) {

// lookup date/time created from interaction 'MHWBQ Received' for this customer and set expected_start to this value. //This is for use as the retroactive start date/time for the counselling time to first appointment SLA var gr = new GlideRecord('x_uno49_enabl_svc_interaction'); gr.initialize(); gr.orderByDesc('sys_created_on'); gr.addQuery('customer', current.customer); gr.addQuery('short_description', 'MHWBQ Received'); gr.query(); if(gr.next()) { current.expected_start = gr.sys_created_on; } })(current, previous); </syntaxhighlight> back to top

Strip the tenant subject prefix and set it as a header

[edit | edit source]

<syntaxhighlight lang="javascript"> // Strip the tenant subject prefix and set it as a header // This is to work-around an issue with Exchange not exposing X-Headers through IMAP

var re = /^\|\*(Tenant|Category|Team):(.*?)\*\|\s*/i;

var i = 100;

var match; while(match = re.exec(current.subject)) { // strip the tag from the subject line current.subject = current.subject.replace(match[0], );

if (--i == 0) { gs.logWarning('Infinite loop detected in business rule sys_email.Tenant Header Rule'); break; }

var header = 'X-Service-Now-' + match[1] + ': ' + match[2].trim() + '\n'; current.headers = current.headers.replace(/\n+$/,) + '\n' + header; }

</syntaxhighlight> back to top

Prevent direct assignment of fulfiller role to user

[edit | edit source]

<syntaxhighlight lang="javascript"> (function executeRule(current, previous /*null when async*/) { function getContainedRoles(strRole, arrRoles) { var _arrRoles = arrRoles || [strRole]; var _grRoles = new GlideRecord('sys_user_role_contains');

_grRoles.addQuery('role.name', strRole); _grRoles.query();

while(_grRoles.next()){ _arrRoles.push(_grRoles.contains.name.toString());

if (_grRoles.contains != ) { getContainedRoles(_grRoles.contains.name.toString(), _arrRoles); } }

return new ArrayUtil().unique(_arrRoles); }

var _strRoleName = current.role.getDisplayValue(); var _strUserName = current.user.getDisplayValue(); var _grLicenseRole = new GlideRecord('license_role'); var _arrAllRoles = getContainedRoles(_strRoleName);

_grLicenseRole.addEncodedQuery( 'license_role_typeISNOTEMPTY^license_role_type.name!=requester^nameIN' + _arrAllRoles.join(',') ); _grLicenseRole.setLimit(1); _grLicenseRole.query();

if (_grLicenseRole.hasNext()) { gs.addErrorMessage( gs.getMessage( 'Role "{0}" or one of its contained roles require a license and ' + 'therefore cannot be assigned to user "{1}" directly. ' + 'Instead use groups for role assignments.', [_strRoleName, _strUserName] ) ); current.setAbortAction(true); }

})(current, previous); </syntaxhighlight> back to top

onLoad rule to identify empty catalog item variables

[edit | edit source]

<syntaxhighlight lang="javascript" line>

var emptyVariables = []; var tableName = current.getTableName(); var ritmSysId = ; if(tableName == 'sc_req_item')

ritmSysId = current.getUniqueValue();

if(tableName == 'sc_task')

ritmSysId = current.request_item;

var itemObj = new GlideRecord('sc_item_option_mtom'); itemObj.addQuery('request_item', ritmSysId); itemObj.addNullQuery('sc_item_option.value'); itemObj.addQuery('sc_item_option.item_option_new.type', '!=', 11); // exclude label itemObj.addQuery('sc_item_option.item_option_new.type', '!=', 19); // exclude container start itemObj.addQuery('sc_item_option.item_option_new.type', '!=', 20); // exclude container end itemObj.query();

while(itemObj.next()){ var name = itemObj.sc_item_option.item_option_new.name; emptyVariables.push(name.toString()); }

g_scratchpad.emptyVariables = emptyVariables.toString(); </syntaxhighlight>

Then use a catalog client script to access the scratchpad and hide the empty variables. Catalog Client Script: onLoad & True for "Applies on Requested Items" , "Applies on Catalog Tasks"

<syntaxhighlight lang="javascript" line> function onLoad() {

     if(g_scratchpad.emptyVariables != ){
     var emptyVars = g_scratchpad.emptyVariables.split(',');
     for(i = 0; i < emptyVars.length; i++){
        g_form.setDisplay(emptyVars[i], false);
     }
  }

} </syntaxhighlight> back to top

Catalog Client Scripts

[edit | edit source]

Check date is not in the future

[edit | edit source]

<syntaxhighlight lang="javascript"> function onChange(control, oldValue, newValue, isLoading) {

   if (isLoading || newValue == ) {
       return;
   }
   var reqDate = new Date(newValue);
   var today = new Date();
   if (reqDate >= today) {
       g_form.setValue('date_you_first_knew_of_spend_requirement', );
       g_form.showFieldMsg('date_you_first_knew_of_spend_requirement', "Date cannot be in the future.", 'error');
   }

} </syntaxhighlight> back to top

Populate Registered Asset User (owned_by) GlideAjax

[edit | edit source]

OnChange client script - calls script include. Gets user's name sysId and DisplayValue to avoid a second call to the server <syntaxhighlight lang="javascript"> function onChange(control, oldValue, newValue, isLoading) {

   if (isLoading || newValue == ) {
       return;
   }
   var ga = new GlideAjax('SotonGetRegisteredUser');
   ga.addParam('sysparm_name', 'getRegisteredUser');
   ga.addParam('user_id', newValue);
   ga.getXMLAnswer(populateUser);
   function populateUser(answer) {
       var response = JSON.parse(answer); //convert the returned JSON string to an object
       g_form.setValue('device_owner_user', response.regUserId, response.regUserName); //call setValue with both the sys_id and display value
   }

} </syntaxhighlight> Script include to find user details. Passes back sysId and DisplayValue to avoid second call to the server <syntaxhighlight lang="javascript"> var SotonGetRegisteredUser = Class.create(); SotonGetRegisteredUser.prototype = Object.extendsObject(AbstractAjaxProcessor, {

   getRegisteredUser: function() {
       var dataToReturn = {};
       dataToReturn.regUserId = ;
       dataToReturn.regUserName = ;
       var sysId = this.getParameter('user_id');
       if (sysId) {
           var gr = new GlideRecord('sys_user');
           if (gr.get(sysId)) {
               dataToReturn.regUserId = gr.getValue('name');
               dataToReturn.regUserName = gr.name.getDisplayValue();
           }
       }
       return JSON.stringify(dataToReturn);
   },
   type: 'SotonGetRegisteredUser'

}); </syntaxhighlight> back to top

Populate text field with vendor details (GlideAjax)

[edit | edit source]

Calls script include GetVendorContactInfo


<syntaxhighlight lang="javascript"> function onChange(control, oldValue, newValue, isLoading) {

   if (isLoading || newValue == ) {
       return;
   }
   var vendorDetails = ;

var ga = new GlideAjax('GetVendorContactInfo');

   ga.addParam('sysparm_name', 'getContactInfo');
   ga.addParam('sysparm_vendor_id', newValue);
   ga.getXMLAnswer(function(response) {
       var result = JSON.parse(response);
       for (i = 0; i < result.length; i++) {
           vendorDetails = vendorDetails + result[i].company + " : " + result[i].name + " : " +  result[i].email + '\n\n';
       }
       g_form.setValue('vendor_contact_name_and_email_address', vendorDetails);
   });

} </syntaxhighlight> back to top

Catalog Client Script To Make Help Text Visible by Default

[edit | edit source]

<syntaxhighlight lang="javascript">

function onLoad() {
   var myVar = g_form.getControl('caller_id');
   toggleHelp(myVar.id);
}

</syntaxhighlight> back to top

Catalog Client Script To Populate Various User Fields

[edit | edit source]

<syntaxhighlight lang="javascript">

function onChange() {
	
	var user_ref = g_form.getReference('username', setUserID);		
}

function setUserID(user_ref) {
	g_form.setValue('student_id', user_ref.user_name);
	g_form.setValue('email_address', user_ref.email);
	g_form.setValue('contact_number', user_ref.phone);
	
}

</syntaxhighlight> back to top

Catalog Client Script To Check For Empty Fields on Submission

[edit | edit source]

<syntaxhighlight lang="javascript">

function onSubmit() {
//check users have been specified beofre submitting
   var ltype = g_form.getValue('email_group_new');
   var adduser = g_form.getValue('add_email_group_members');
   var remuser = g_form.getValue('remove_email_group_members');
   if (ltype == 'Existing' && adduser == '' && remuser == '') {
       alert('Please enter users to be added and/or removed');
       return false;
   }  
}

</syntaxhighlight> back to top

Show field if date entered > 7 days ago

[edit | edit source]

<syntaxhighlight lang="javascript"> function onCondition() {

   function dateDiffInDays(startDateStr, endDateStr) {
       // Parse ServiceNow date/time fields into JS Date objects
       var startDate = new Date(startDateStr);
       var endDate = new Date(endDateStr);
       // Calculate difference in milliseconds
       var diffMs = endDate - startDate;
       // Convert milliseconds to days (rounding down)
       var diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
       return diffDays;
   }
   //Type appropriate comment here, and begin script below
   var start = g_form.getValue('date_you_first_knew_of_spend_requirement'); // field with date/time
   var end = new Date();
   if (start && end) {
       var days = dateDiffInDays(start, end);
       if (days > 7) {
           g_form.setDisplay('reason_for_delay_in_submission', true);
       } else {

g_form.setDisplay('reason_for_delay_in_submission', false); }

   }

} </syntaxhighlight> back to top

Populate a form using getReference() synchronous server call

[edit | edit source]

See https://community.servicenow.com/thread/167169

Catalog Client Script <syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading) {
userObject = g_form.getReference('bs_staff_username',setUserInfo);
}
 
function setUserInfo(userObject){
g_form.setValue('bs_contact', userObject.phone);
g_form.setValue('bs_location', userObject.u_room);
//g_form.setValue('u_whatever', userObject.field_on_sys_user); 
}

</syntaxhighlight> Can also use this format: <syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading) {
userObject = g_form.getReference('user_field'); 

g_form.setValue('u_manager_field', userObject.manager);
g_form.setValue('u_last_name', userObject.last_name);
g_form.setValue('u_whatever', userObject.field_on_sys_user); 

}

</syntaxhighlight> or the good old way: <syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading) {  
var id = g_form.getValue('u_first_field');//replace 'u_first_field' with the name of your reference field.  
var user = new GlideRecord('sys_user');  
      user.addQuery('sys_id',id);  
      user.query();  
if ( user.next() ) {  
  g_form.setValue('u_manager_field', user.manager);  
  g_form.setValue('u_last_name', user.last_name);  
  g_form.setValue('u_whatever', user.field_on_sys_user);  
}  
}  

</syntaxhighlight> back to top

Filtering a list collector

[edit | edit source]

<syntaxhighlight lang="javascript"> function onLoad() {

   //Apply a default filter to the list collector variable
   var collectorName = 'name_of_list_collector_variable';
   var filterString = 'eg. active=true';
   
   //Try Service Portal method
   try{
       var myListCollector = g_list.get(collectorName);
       myListCollector.reset();
       myListCollector.setQuery(filterString);
   }
   //Revert to Service Catalog method
   catch(e){
       //Hide the list collector until we've set the filter
       g_form.setDisplay(collectorName, false);
       setCollectorFilter();
   }
   
   function setCollectorFilter(){
       //Test if the g_filter property is defined on our list collector.
       //If it hasn't rendered yet, wait 100ms and try again.
       if(typeof(window[collectorName + 'g_filter']) == 'undefined'){
           setTimeout(setCollectorFilter, 100);
           return;
       }
       //Find and hide the filter elements (optional)
       //Simple method for items with only one list collector
       //$('ep').select('.row')[0].hide();
       //Advanced method for items with more than one list collector (more prone to upgrade failure)
       //var el = $('container_' + g_form.getControl(collectorName).id).select('div.row')[0].hide();
       
       //Reset the filter query
       window[collectorName + 'g_filter'].reset();
       window[collectorName + 'g_filter'].setQuery(filterString);
       window[collectorName + 'acRequest'](null);
       //Redisplay the list collector variable
       g_form.setDisplay(collectorName, true);
   }

} </syntaxhighlight> back to top

Resizing a slushbucket list

[edit | edit source]

<syntaxhighlight lang="javascript"> function onLoad(){

   var varName = 'idrive_name';
   var height = '100'; //Optional
   var width = '450'; //Optional
   try{
       //Get the left and right bucket input elements
       var leftBucket = $(varName + '_select_0');
       var rightBucket = $(varName + '_select_1');
       
       //If the element exists
       if(leftBucket){
           //Adjust the bucket height (default is 300px)
           if(height){
               leftBucket.style.height = height + 'px';
               rightBucket.style.height = height + 'px';
           }
           
           if(width){
               //Adjust the bucket width (default is 340px)
               leftBucket.style.width = width + 'px';
               rightBucket.style.width = width + 'px';
                               //Fix Fuji/Geneva width issue
                               leftBucket.up('.slushbucket').style.width = width*2 + 100 + 'px';
           }
           
           //Fix the expanding item preview issue
           $(varName + 'recordpreview').up('td').setAttribute('colSpan', '3');
       }
   }catch(e){}

} </syntaxhighlight> back to top

Validate IP Address Catalog Client Script

[edit | edit source]

<syntaxhighlight lang="javascript">

 function onChange(control, oldValue, newValue, isLoading) {
   if (isLoading || newValue == ) {
     return;
   }

   var regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
   var test_string = g_form.getValue('VM_IP');
   var valid = regex.test(test_string);

   if (!valid) {
     alert('Please enter a valid IP address');
     g_form.setValue('VM_IP', );
     return false;
   }
 }

</syntaxhighlight> back to top

Catalog client script to check for file attachment

[edit | edit source]

<syntaxhighlight lang="javascript"> function onSubmit() { //

var new_role = g_form.getValue('hrf_new_role');

// alert('New role = ' + new_role);

 try { //Works in non-portal ui
 var attachments = document.getElementById('header_attachment_list_label');
 if ((attachments.style.visibility == 'hidden' || attachments.style.display == 'none') && new_role == 'Yes') {
 alert('Please attach the UET approved ECF to this form before submitting.');
 return false;
 }
 } catch(e) { //For Service Portal
 var count = getSCAttachmentCount();
 if(count <= 0 && new_role == 'Yes') {
 alert('Please attach the UET approved ECF to this form before submitting.');
 return false;
 }
 }

} </syntaxhighlight>

or

<syntaxhighlight lang="javascript"> function onSubmit() {

 if (typeof $scope === 'undefined') return;

if ($scope.files.length == 0) { var set_id = 'fc604f86dbd53704f81bee71ca961903'; alert("You must attach evidence in support of your application."); jQuery('html,body').animate({ scrollTop: jQuery('#' + set_id).offset().top }, 'fast'); return false; } return true; } </syntaxhighlight>

back to top

Populate select box using a decision table

[edit | edit source]

An on change catalog client script and script include pair that uses a decision table via the API to populate a select box variable on a catalog item.

<syntaxhighlight lang="javascript"> function onChange(control, oldValue, newValue, isLoading) {

   if (isLoading || newValue == ) {
       return;
   }
   var targetChoiceField = 'model_from_decision_table'; // Set this to the name of the Selectbox variable you want to populate
   g_form.clearOptions(targetChoiceField);
   var catItem = g_form.getUniqueValue();
   var dtChoiceAjax = new GlideAjax('global.GetChoicesFromDT'); // Set this to the name of the script include with the relevant scope
   dtChoiceAjax.addParam('sysparm_name', 'getChoices');
   dtChoiceAjax.addParam('sysparm_cat_item', catItem);

dtChoiceAjax.addParam('sysparm_cat_variable', g_form.getValue('device_function'));

   /*
    * Add an other option parameter, e.g.:
    * dtChoiceAjax.addParam('sysparm_cat_variable', g_form.getValue('some_variable'));
    */
   dtChoiceAjax.getXMLAnswer(setChoices);
   function setChoices(answer) {
       if (answer) {
           var choiceArray = JSON.parse(answer);
           if (choiceArray.length == 0) {
               // Do something if the response is empty
               g_form.setReadOnly(targetChoiceField, false);
               g_form.setMandatory(targetChoiceField, false);
               g_form.setDisplay(targetChoiceField, false);
           } else {
               g_form.setDisplay(targetChoiceField, true);
               // Similarly, you might want to do something if there is only one choice, e.g. set that by default and make the field read-only. 
               var isSingleChoice = choiceArray.length == 1 ? true : false;
               if (isSingleChoice) {
                   g_form.addOption(targetChoiceField, choiceArray[0].value, choiceArray[0].label);
                   g_form.setValue(targetChoiceField, choiceArray[0].value);
                   g_form.setReadOnly(targetChoiceField, true);
               } else {
                   // And finally, if you have multiple options, decide how you want your field to behave
                   g_form.setReadOnly(targetChoiceField, false);
                   g_form.addOption(targetChoiceField, , '-- None --'); // Adding None option - this is also optional
                   for (i = 0; i < choiceArray.length; i++) {
                       g_form.addOption(targetChoiceField, choiceArray[i].value, choiceArray[i].label, i + 1);
                   }
                   g_form.setMandatory(targetChoiceField, true);
               }
           }
       } else {
           // What if there was no answer return from the script include at all?
           g_form.setReadOnly(targetChoiceField, false);
           g_form.setMandatory(targetChoiceField, false);
           g_form.setDisplay(targetChoiceField, false);
       }
   }

} </syntaxhighlight>

Client Callable Script Include

<syntaxhighlight lang="javascript"> var GetChoicesFromDT = Class.create(); GetChoicesFromDT.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {

   getChoices: function() {
       /**
        * Gets the defined choices for the passed in catalog item select box variable
        * 
        * @param {String} sysparm_cat_item
        *    The sys_id of the catalog item to get choices for - mandatory
        * @param {String} sysparm_cat_variable
        *    Value from an additional catalog variable to evaluate as part of your decision - optional
        * @return {String}
        *    A stringified array (since it goes to client script) of choices
        */


       /**
        * In addition to the above, the following variable MUST be set for the script to work:
        *
        ** decisionTableId : Sys ID of the decision table. Store in a system property and set with gs.getProperty()
        ** dtInput1, 2, etc. :  the technical names of the Decision Table inputs
        ** resultColumn : the technical name of the result column of your Decision Table that has the choices set
        */
       var catItem = gs.nil(this.getParameter('sysparm_cat_item')) ? null : this.getParameter('sysparm_cat_item'); // Mandatory parameter
       var catVar = gs.nil(this.getParameter('sysparm_cat_variable')) ? null : this.getParameter('sysparm_cat_variable'); // Optional parameter example (variable from record producer). Multiple as needed, or remove if not.
       var decisionTableId = gs.getProperty('fleet.hw.model.choices.decision.table'); //Sys ID of the decision table. Store in a system property and set with gs.getProperty()
       var dtInput1 = 'u_catalog_item'; // Make sure you set this to the technical name of the first input of your Decision Table
       var dtInput2 = 'u_catalog_variable'; // Make sure you set this to the technical name of the second input of your Decision Table, if you have one. Multiply as needed, or remove if not.
       var resultColumn = 'u_choice'; // Set this to the technical name of the result column that contains your choices
       var answerArray = [];
       var choiceArr = [];
       var iter1 = 0;
       if (!gs.nil(catItem) && !gs.nil(decisionTableId)) {
           var choiceQuery = 'var__m_sys_decision_multi_result_element_' + decisionTableId;
           var decisonTable = new sn_dt.DecisionTableAPI();
           var inputs = new Object();
           inputs[dtInput1] =  + catItem;
           // Repeat this block as necessary with additional parameters and inputs
           if (!gs.nil(catVar)) {
               inputs[dtInput2] =  + catVar;
           }

//gs.info('***CM*** ' + JSON.stringify(inputs));

           var dtResponse = decisonTable.getDecisions(decisionTableId, inputs);
           while (iter1 < dtResponse.length) {
               answerArray.push(dtResponse[iter1]['result_elements'][resultColumn].toString());
               iter1++;
           }
           // Now find the the actual choices with labels
           var choiceGr = new GlideRecord('sys_choice');
           choiceGr.addQuery('name', choiceQuery);
           choiceGr.addQuery('value', 'IN', answerArray.toString());
           choiceGr.setLimit(30); // The Choice table is huge, so I recommend setting a reasonable query limit. You should have an idea of the max # of results anyway.

choiceGr.orderBy('label');

           choiceGr.query();
           while (choiceGr.next()) {
               var choice = {};
               choice['value'] = choiceGr.getValue('value');
               choice['label'] = choiceGr.getValue('label');
               choiceArr.push(choice);
           }
           return JSON.stringify(choiceArr); // Return a stringified array to the client
       } else {
           gs.error('GetChoicesFromDT Script include did not run as the catItem mandatory variable is null: ' + catItem + ' or decision table sys_id is empty: ' + decisionTableId);
           return;
       }
   },
   type: 'GetChoicesFromDT'

});

</syntaxhighlight> back to top

Check two list collector variables for duplicate entries

[edit | edit source]

Client Script

<syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading) {

   // Skip execution on initial form load
   if (isLoading) { return; }
   // ----------------------------------------------------------------
   // 1. Read both list collector values (comma-separated sys_id strings)
   // ----------------------------------------------------------------
   var appsAdd = g_form.getValue('applications'); // e.g. "abc123,def456"
   var appsRem = g_form.getValue('applications_remove'); // e.g. "def456,ghi789"
   // Guard: if either field is empty there is nothing to compare
   if (!appsAdd || !appsRem) { return; }
   // ----------------------------------------------------------------
   // 2. Convert raw strings to arrays, filtering out any empty tokens
   // ----------------------------------------------------------------
   var ids1 = appsAdd.split(',').filter(function(id) { return id.trim() !== ; });
   var ids2 = appsRem.split(',').filter(function(id) { return id.trim() !== ; });
   // ----------------------------------------------------------------
   // 3. Build a lookup map from sys_id → display value for list 2
   //    g_form.getDisplayValue() returns the label of a field, but for
   //    list collectors we need to resolve each sys_id individually.
   //    The helper below uses GlideRecord via GlideAjax (client-safe).
   // ----------------------------------------------------------------
   /**
    * Resolves a single sys_id to its display value for a given table.
    * Uses a synchronous-style callback pattern with GlideAjax.
    *
    * @param {string[]} sysIds   - array of sys_ids to resolve
    * @param {string}   table    - table the list collector points to
    * @param {function} callback - called with an object { sys_id: display_value }
    */
   function resolveDisplayValues(sysIds, table, callback) {
       var ga = new GlideAjax('CatalogClientScriptUtils'); // See note below *
       ga.addParam('sysparm_name',   'getDisplayValues');
       ga.addParam('sysparm_table',  table);
       ga.addParam('sysparm_sysids', sysIds.join(','));
       ga.getXML(function(response) {
           var answer = response.responseXML
               .documentElement
               .getAttribute('answer');
           try {
               callback(JSON.parse(answer)); // { sys_id: "Display Name", ... }
           } catch(e) {
               callback({});
           }
       });
   }
   // ----------------------------------------------------------------
   // 4. Perform the comparison once display values are resolved
   //    Replace 'your_table_name' with the table the list collector
   //    references (e.g. 'sys_user', 'cmdb_ci', etc.)
   // ----------------------------------------------------------------
   var TABLE = 'question_choice'; // <-- UPDATE THIS
   // Resolve display values for ALL unique sys_ids across both lists
   var allIds = ids1.concat(ids2.filter(function(id) {
       return ids1.indexOf(id) === -1;
   }));
   resolveDisplayValues(allIds, TABLE, function(displayMap) {
       // Find overlapping sys_ids
       var matches = ids1.filter(function(id) {
           return ids2.indexOf(id) !== -1;
       });
       if (matches.length === 0) { return; } // No conflicts — all good
       // Build a human-readable list of conflicting display values
       var matchNames = matches.map(function(id) {
           return displayMap[id] || id; // Fallback to sys_id if unresolved
       });
       // ----------------------------------------------------------------
       // 5. Alert the user with descriptive text, not sys_ids
       // ----------------------------------------------------------------
       alert(
           'Conflict detected!\n\n' +
           'The following item(s) appear in both lists and must be unique:\n\n  • ' +
           matchNames.join('\n  • ') +
           '\n\nPlease remove the duplicate selection(s) before submitting.'
       );
       // Optionally clear the field that was just changed to force correction
       g_form.setValue(newValue === appsAdd ? 'applications' : 'applications_remove', );
   });

} </syntaxhighlight> back to top

Script Include

<syntaxhighlight lang="javascript"> var CatalogClientScriptUtils = Class.create(); CatalogClientScriptUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {

   /**
    * Returns a JSON object mapping sys_id → display value
    * for all provided sys_ids on the given table.
    *
    * Params (passed via GlideAjax):
    *   sysparm_table  - table name  (e.g. 'sys_user')
    *   sysparm_sysids - comma-separated sys_ids
    */
   getDisplayValues: function() {
       var table  = this.getParameter('sysparm_table');
       var sysIds = (this.getParameter('sysparm_sysids') || ).split(',');
       var result = {};
       if (!table || sysIds.length === 0) {
           return JSON.stringify(result);
       }
       var gr = new GlideRecord(table);
       gr.addQuery('sys_id', 'IN', sysIds.join(','));
       gr.query();
       while (gr.next()) {
           // getDisplayValue() returns the record's display field (e.g. name, number)
           result[gr.getUniqueValue()] = gr.getDisplayValue();
       }
       return JSON.stringify(result);
   },
   type: 'CatalogClientScriptUtils'

});

</syntaxhighlight> back to top

Catalog Items

[edit | edit source]

Unable to update/remove Catalog Item image

[edit | edit source]

Use the following script to set picture and/or icon to null. This should allow a new image to be uploaded.

<syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_cat_item'); gr.get('your cat item sys id'); gr.picture = ; gr.icon = ; gr.update(); </syntaxhighlight>

Catalog UI Policies

[edit | edit source]

Hide variables on RITM

[edit | edit source]

Requires container start and end variables

<syntaxhighlight lang="javascript"> function onCondition() {

   var doc = this.document ? this.document : document;
   var x = doc.getElementsByTagName("label");
   for (var i = 0; i < x.length; i++) {
       if (x[i].textContent == "Options") {
           x[i].style.display = "none";
       }
   }

} </syntaxhighlight>

Choice Lists

[edit | edit source]

Remove/Add Options From a Choice List

[edit | edit source]

<syntaxhighlight lang="javascript">

function onLoad() {
   
  if (g_form.getValue('<fieldname>') == '<value1>')
{
  g_form.removeOption('<fieldname>', '<Option 1>');
  g_form.removeOption('<fieldname>', '<Option 2>');
  g_form.removeOption('<fieldname>', '<Option 3>');
  g_form.removeOption('<fieldname>', '<Option 4>');
 
  }
  if (g_form.getValue('<fieldname>') == '<value2>')
{
  g_form.removeOption('<fieldname>', '<Option 5>');
  g_form.removeOption('<fieldname>', '<Option 6>');
  g_form.removeOption('<fieldname>', '<Option 7>');
  }
}

</syntaxhighlight> To add options:

<syntaxhighlight lang="javascript">

g_form.addOption('<name>', '<value>', '<label>');

</syntaxhighlight>

To clear list: <syntaxhighlight lang="javascript">

clearValue(fieldName)

</syntaxhighlight>

Example: restrict contact type by call type <syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
   if (isLoading || newValue === ) {
      return;
   }
 g_form.clearOptions('contact_type');
  g_form.addOption('contact_type', 'email', 'Email');
  g_form.addOption('contact_type', 'phone', 'Phone');
  g_form.addOption('contact_type', 'self-service', 'Self-service');
  g_form.addOption('contact_type', 'walk-in', 'Walk-in'); 
	
 if (g_form.getValue('call_type') == 'general_query')
 {
  g_form.removeOption('contact_type', 'walk-in');
  }  
	
 if (g_form.getValue('call_type') == 'counter_query')
 {
  g_form.removeOption('contact_type', 'email');
  g_form.removeOption('contact_type', 'phone');
  g_form.removeOption('contact_type', 'self-service'); 
  } 
 
}

</syntaxhighlight>


<syntaxhighlight lang="javascript"> Example: restrict_templates_choices

 function onChange(control, oldValue, newValue, isLoading) {
    if (isLoading || newValue == ) {
       return;
    }

    //VM Requests or Modifications - catalog client script to restrict template choice dependent on which operating system is selected
   g_form.clearOptions('VM_template');
 
   if (g_form.getValue('VM_OS') == 'RHEL 6')
  {
   g_form.addOption('VM_template', 'RHEL 6.x', 'RHEL 6.x');
   g_form.addOption('VM_template', 'RHEL 6 – Oracle 11g', 'RHEL 6 – Oracle 11g');
   g_form.addOption('VM_template', 'None', 'None');
   }  

 if (g_form.getValue('VM_OS') == 'RHEL 7')
  {
   g_form.addOption('VM_template', 'RHEL 7', 'RHEL 7');
   g_form.addOption('VM_template', 'RHEL 7 – Oracle 12c', 'RHEL 7 – Oracle 12c'); 
   g_form.addOption('VM_template', 'None', 'None');
   }

 if (g_form.getValue('VM_OS') == 'Windows Server 2012 R2')
  {
   g_form.addOption('VM_template', 'Windows 2012 R2', 'Windows 2012 R2');
   g_form.addOption('VM_template', 'Windows 2012 R2 – SQL', 'Windows 2012 R2 – SQL');
   g_form.addOption('VM_template', 'None', 'None');
   }

 if (g_form.getValue('VM_OS') == 'Windows Server 2016')
  {
   g_form.addOption('VM_template', 'Windows 2016', 'Windows 2016');
   g_form.addOption('VM_template', 'Windows 2016 - SQL', 'Windows 2016 - SQL');
   g_form.addOption('VM_template', 'None', 'None');
   }

 if (g_form.getValue('VM_OS') == 'Windows 7')
  {
     g_form.addOption('VM_template', 'None', 'None');  
   }

 if (g_form.getValue('VM_OS') == 'Other')
  {
     g_form.addOption('VM_template', 'None', 'None');
   }
 }

</syntaxhighlight> back to top

Client Scripts

[edit | edit source]

Count number of active records (rowcount)

[edit | edit source]

<syntaxhighlight lang="javascript"> var user_gr = new GlideRecord('sys_user');

user_gr.addActiveQuery(); user_gr.query(); var number_of_active_users = user_gr.getRowCount();

gs.info('Number of Active users:' + number_of_active_users) </syntaxhighlight> back to top

onChange Client Script To Populate Fields From a Task Table Lookup

[edit | edit source]

<syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
  if (isLoading || newValue === ) {
     return;
  }
  //alert(g_form.getValue('u_task_number'));
  var myLookup = new GlideRecord('task');
  myLookup.addQuery('sys_id', g_form.getValue('u_task_number'));
  myLookup.query();
  while (myLookup.next()) {

g_form.setValue('short_description', myLookup.short_description.toString()); g_form.setValue('description', myLookup.description.toString());

  }

} </syntaxhighlight> back to top

Date Handling

[edit | edit source]
<syntaxhighlight lang="javascript">
var sec=Date.parse("Thursday, July 14, 2016 11:00:43 PM");
var gdt = new GlideDateTime();
gdt.setNumericValue(sec);
gdt.getDisplayValue(); //this will give you the date in your format
</syntaxhighlight>

back to top

Subtract 3 months from a date (GlideAjax Client Script/Script Include

[edit | edit source]

Client Script

<syntaxhighlight lang="javascript">
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
   if (isLoading)  
      return;  
var ga = new GlideAjax('u_subtract_3_months'); //This argument should be the exact name of the script include.
ga.addParam('sysparm_name', 'subtract_3_months'); //sysparm_name is the name of the function in the script include to call.     
ga.addParam('sysparm_ends', g_form.getValue('ends')); //set a parameter to pass to script include
ga.getXML(myCallBack); //This is our callback function, which will process the response.
     
function myCallBack(response) { //the argument 'response' is automatically provided when the callback function is called by the system.
    var answer = response.responseXML.documentElement.getAttribute("answer"); //Dig out the 'answer' attribute, which is what our function returns. 
        g_form.setValue('renewal_date', answer); //set 'renewal_date' field to returned value.
     
 }
}
</syntaxhighlight>

Script Include

<syntaxhighlight lang="javascript">
//Script called by client script Renewal Process Start Date
//Calulates a date three months earlier than the contract end date.
//This was written in order to handle UK date format dd/MM/yyyy
//gdt.setDisplayValue(parm_data); is crucial to this working for all dates as without it day numbers of 12 or less are
//interpreted as months.
var u_subtract_3_months = Class.create();
 u_subtract_3_months.prototype = Object.extendsObject(AbstractAjaxProcessor, {
     subtract_3_months: function() {
         var parm_data = this.getParameter('sysparm_ends'); //retrieve parameter passed from client script
         var gdtDay = "";
         var gdtMonth = "";
         var gdtYear = "";
         var gdt = new GlideDateTime(parm_data);
         gdt.setDisplayValue(parm_data);
         gdt.addMonths(-3);
          
         if(gdt.getDayOfMonth().toString().length == 1) {
             gdtDay = "0" + gdt.getDayOfMonth();
         }
         else {
             gdtDay = gdt.getDayOfMonth();
         }
          
         if(gdt.getMonth().toString().length == 1) {
             gdtMonth = "0" + gdt.getMonth();
         }
         else {
             gdtMonth = gdt.getMonth();
         }
          
         gdtYear = gdt.getYear();
          
         var gdtDate = gdtDay + "/" + gdtMonth + "/" + gdtYear;
         return gdtDate;
     }

 });

</syntaxhighlight>

GlideAjax client script to populate a form field (calls script include below)

[edit | edit source]

<syntaxhighlight lang="javascript">

// Called by u_permitted_use client script.
// Auto-populates 'Permitted Use' field on the Software Installation request form.
function onChange(control, oldValue, newValue, isLoading) {
    if (isLoading)  
    return; 
    if (newValue);
//Code commented out below is the old method of poulating the Permitted Use field that 
//does not work in the Service Portal. Reatained for reference.
//	var parm_data = g_form.getReference('softins_name', popPermittedUse);
//    //var perm_use = perm_data.u_permitted_use;
//	function popPermittedUse(parm_data){
//		g_form.setValue('u_permitted_use', parm_data.u_permitted_use, parm_data.getDisplayValue('u_permitted_use'));
//	}
    var ga = new GlideAjax('u_get_permitted_use'); //This argument should be the exact name of the script include. 
    ga.addParam('sysparm_name', 'popPermittedUse'); //sysparm_name is the name of the function in the script include to call. 
    ga.addParam('sysparm_softins', g_form.getValue('softins_name')); //set a parameter to pass to script include
    ga.getXML(myCallBack); //This is our callback function, which will process the response.

    function myCallBack(response) { //the argument 'response' is automatically provided when the callback funciton is called by the system.
    var answer = response.responseXML.documentElement.getAttribute("answer"); //Dig out the 'answer' attribute, which is what our function returns. 
        g_form.setValue('u_permitted_use', answer); //set 'Permitted Use' field to returned value.
    }
}

</syntaxhighlight> back to top

Flow - Restart a flow designer flow example: RITM

[edit | edit source]

<syntaxhighlight lang="javascript"> (function() {

 var now_GR = new GlideRecord('sc_req_item'); 
 now_GR.addQuery('number', 'RITM0024611'); 
 now_GR.query(); 
 now_GR.next();
 try {
   var inputs = {};
   inputs['request_item'] = now_GR; // GlideRecord
   inputs['table_name'] = 'sc_req_item';
   var contextId = sn_fd.FlowAPI.startFlow('global.security_tooling_item_flow', inputs);	
 } catch (ex) {
   var message = ex.getMessage();
   gs.error(message);  
 }

})(); </syntaxhighlight> back to top

GlideAjax script include (used by client script above to populate a form field)

[edit | edit source]

<syntaxhighlight lang="javascript">

//Used by client script u_permitted_use to populate the 'Permitted Use' field 
//on the Sofware Installation request form.

var u_get_permitted_use = Class.create();
u_get_permitted_use.prototype = Object.extendsObject(AbstractAjaxProcessor, {
    popPermittedUse: function() {
    var parm_data = this.getParameter('sysparm_softins'); //retrieve parameter passed from client script
    var permUse = ''; //declare variable to hold the Permitted Use data 
    var gr = new GlideRecord('cmdb_software_product_model'); 
        gr.addQuery('sys_id', parm_data); //parameter passed is a sysid so find matching record
        gr.query();
        while(gr.next())
        {
         permUse = gr.u_permitted_use; //assign value of u_permitted_use to permUse 
        }
         return permUse; //return value to calling client script
        }
});

</syntaxhighlight> back to top

Glide Ajax Date Handling Example

[edit | edit source]

<syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading) {  
 
 if (isLoading || newValue == ) {  
     return;  
 }  
 
 if (g_form.getValue('var_eventStart') != )  
 {  
 var cdt = g_form.getValue('var_eventStart'); //First Date/Time field  
 var sdt = g_form.getValue('var_eventEnd'); //Second Date/Time field  
 var dttype = 'second'; //this can be day, hour, minute, second. By default it will return seconds.  
  
 var ajax = new GlideAjax('ClientDateTimeUtils');  
 ajax.addParam('sysparm_name','getDateTimeDiff');  
 ajax.addParam('sysparm_fdt', cdt);  
 ajax.addParam('sysparm_sdt', sdt);  
 ajax.addParam('sysparm_difftype', dttype);  
 ajax.getXML(function () {  
   
   
   
  
 var answer = ajax.getAnswer();  
     
 if (answer <0){  
   alert('You cannot select an End Date prior to the Start Date.');  
   g_form.setValue('var_eventEnd', );  
 }  
     
 else if (answer == 0){  
   alert('You cannot select an End Date equal to Start Date.');  
         g_form.setValue('var_eventEnd',);  
 }   
 });  
}  
else{  
 alert('Start Date cannot be empty');  
   g_form.setValue('var_eventEnd', );  
}  
 
}

</syntaxhighlight> back to top

Glide Ajax getReference equivalent

[edit | edit source]

<syntaxhighlight lang="javascript">

//Script Include  
// Name: CallerLocation  
//Client Callable: checked  
//Script:  
var CallerLocation = Class.create();  
CallerLocation.prototype = Object.extendsObject(AbstractAjaxProcessor, {  
   getLocation: function() {  
  var loc = ;  
      var callerId = this.getParameter('sysparm_user_name');  
  var gr = new GlideRecord('sys_user');  
  gr.addQuery('sys_id',callerId);  
  gr.query();  
  if(gr.next())  
  {  
  loc = gr.location;  
  }  
  return loc;  
   }  
  
});  
  
//onChange Client Script of Caller field  
  
  
function onChange(control, oldValue, newValue, isLoading, isTemplate) {  
  if (isLoading)  
  return;  
  if(newValue != ){  
  var ga = new GlideAjax('CallerLocation');  
  ga.addParam('sysparm_name','getLocation');  
  ga.addParam('sysparm_user_name',newValue);  
  ga.getXML(LocationParse);  
  
  function LocationParse(response) {  
    var answer = response.responseXML.documentElement.getAttribute("answer");  
    g_form.setValue('location',answer);  
  }  
  }  
}

</syntaxhighlight> back to top

u_get_permitted_use

[edit | edit source]

<syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading) {
     
	if (newValue);
	var parm_data = g_form.getReference('softins_name', popPermittedUse);
    //var perm_use = perm_data.u_permitted_use;
	function popPermittedUse(parm_data){
		g_form.setValue('u_permitted_use', parm_data.u_permitted_use, parm_data.getDisplayValue('u_permitted_use'));
	}
}

</syntaxhighlight> back to top

Client Script with Switch statement to show/hide various fields

[edit | edit source]

<syntaxhighlight lang="javascript"> function onChange(control, oldValue, newValue, isLoading, isTemplate) {

  if (isLoading || newValue === ) {
     return;
  }
  //Show or hide on hold expiry/reason fields depending on incident state
  var myState = g_form.getValue('incident_state');
  switch(myState){
  	   case '8': 

case '13': g_form.setMandatory('u_on_hold_expiry', true);

	      g_form.setDisplay('u_on_hold_expiry', true);

g_form.setMandatory('u_on_hold_reason', false);

	      g_form.setDisplay('u_on_hold_reason', false);

break; case '14': g_form.setMandatory('u_on_hold_expiry', true);

	      g_form.setDisplay('u_on_hold_expiry', true);
             g_form.setMandatory('u_on_hold_reason', true);
	      g_form.setDisplay('u_on_hold_reason', true);

break; default: g_form.setMandatory('u_on_hold_expiry', false);

	      g_form.setDisplay('u_on_hold_expiry', false);

g_form.setMandatory('u_on_hold_reason', false);

	      g_form.setDisplay('u_on_hold_reason', false);
             g_form.setValue('u_on_hold_expiry',);

g_form.setValue('u_on_hold_reason',); }

} </syntaxhighlight> back to top

Client Script to Check if a Check Box is Ticked

[edit | edit source]

<syntaxhighlight lang="javascript">

function onSubmit() {
  var agree = g_form.getValue('variables.u_caveat_agree');
  
  if (agree == 'false') {  
   alert('You must agree to the caveat before submitting'); 
   return false;  
  }
}

</syntaxhighlight> back to top

Auto-populate Reference Field Variable Using Default Value

[edit | edit source]

UserID in this example <syntaxhighlight lang="javascript">

javascript:gs.getUserID(); //returns sys_id of currently logged in user
javascript:gs.getUserName(); 
javascript:gs.getUserDisplayName();

</syntaxhighlight> back to top

Jobs open 'IT - Service Desk' automatically assigned to opened by

[edit | edit source]

<syntaxhighlight lang="javascript">

function onLoad() 
{
   //run this script only for itil users
   if (!g_user.hasRole("itil"))
   		return;  

   var tblName = g_form.getTableName(); 

   if (g_form.isNewRecord() && (tblName == 'sc_request' || tblName == 'incident'))
   {
      //1. Assign group_sys_is (for 'IT- Service Desk') is the same for dev, test, & live  
      var group_id = '96bc6cec8c51dc00483188886d6e3dfd';   
 
      //2. Get user_sys_id
      var usrID = g_user.userID; 

      //3. Check if the current user (opened by) id the above group member 
      var grmember = new GlideRecord('sys_user_grmember');  
      grmember.addQuery('group',group_id); 
      grmember.addQuery('user',usrID); 
      grmember.query();  
      while(grmember.next())  
      {  
         //Assign assignment group to 'IT- Service Desk'
         g_form.setValue('assignment_group',group_id); 

         //assign this user if is a member
         g_form.setValue('assigned_to',usrID); 
      }  

    }// end if 
}

</syntaxhighlight> back to top

Client Script - on load - Hide Standard Change Type

[edit | edit source]

<syntaxhighlight lang="javascript">

 function onLoad() {

   var mySysID = getParmVal('sys_id');
   var myParm = getParmVal('sysparm_template');
   var this_sysparm_record_target = getParmVal('sysparm_record_target');
   /*
   We only want to hide type 'Standard' when creating a new non-templated change request.
   The following if statement checks:
   sys_id = -1 (new templated changes also have a sys_id of -1 until they are saved)
   sysparm_template is not present
   sysparm_record_target is not present (this is present on existing change requests)
   */
   if (myParm == "noTemplate" && this_sysparm_record_target == "noTemplate" && mySysID == -1) {
     g_form.removeOption('type', 'Standard');
   }

   function getParmVal(name) {
     var url = document.URL.parseQuery();
     if (url[name]) {
       return decodeURI(url[name]);
     } else {
       return "noTemplate";
     }
   }
 }

</syntaxhighlight>

Client Script - Advanced Type - CallTypeChanged

[edit | edit source]

Condition: current.call_type.changes() && current.transferred_to.nil()

<syntaxhighlight lang="javascript">
   var ctype = current.call_type;

   //if (ctype != 'hang_up' && ctype != 'wrong_number' && ctype != 'status_call' && ctype != 'general_inquiry' && ctype != 'sc_request' && ctype != 'status_call'){
   if (ctype == 'incident' || ctype == 'change_request'){
       var gr = new GlideRecord(ctype);
       gr.short_description = current.short_description;
       gr.description = current.description.getHTMLValue();
       gr.contact_type = current.contact_type;
       gr.company = current.company;
       gr.opened_by = current.opened_by;

       // update taks work notes
       var callerName = current.caller.name;
       var taskType = current.call_type.getDisplayValue();
       var currentLink = "[code]<a href='" + current.getLink() + "'>" + current.number + "</a>[/code]";
       var journalEntry = gs.getMessage("This {0} has been chased by {1} from {2}", [taskType, callerName, currentLink]);
       gr.work_notes = journalEntry;

       if (GlidePluginManager.isRegistered('com.glide.domain'))
           gr.sys_domain = getDomain();
       
       if (ctype == 'incident'){
                   if(isServiceDeskMember(current.opened_by))
                   {
                     gr.assigned_to = current.opened_by;
                     gr.assignment_group = '96bc6cec8c51dc00483188886d6e3dfd';
                     //gs.log('Update assigned_to and group');
                   } 
           gr.caller_id = current.caller;
           gr.location = current.caller.location;
           gr.comments = current.description.getHTMLValue();
       }
       
       if (ctype == 'change_request'){
           gr.requested_by = current.caller;
       }
       
       var sysID = gr.insert();
       current.transferred_to = sysID;
       var url = ctype + '.do?sys_id=' + sysID;
       gs.addInfoMessage(current.number + gs.getMessage(" transferred to ") + ":  <a href='" + url + "'>" + current.transferred_to.getDisplayValue() + "</a>");
   }
   else if (ctype == 'status_call')
   {
       var sysID = current.u_call_status_task;
       var taskName = current.u_call_status_task.getDisplayValue();

       //gs.log('Inside Status Call - ELSE IF - sysID: ' + sysID + ' | taskName: ' + taskName);

       //assign call redirect url
       current.transferred_to = sysID;

       var tableName = ;
       if(taskName.indexOf('INC') != -1)
       {
          tableName = 'incident';
       }
       else if (taskName.indexOf('REQ') != -1)
       {
          tableName = 'sc_request';
       }
       //gs.log('tableName: ' + tableName);

       if(tableName != ) 
       {
           //gs.log('Inside If tableName : ' + tableName);

       // update taks work notes
       var callerName = current.caller.name;
       var currentLink = "[code]<a href='" + current.getLink() + "'>" + current.number + "</a>[/code]";
           var journalEntry = current.short_description + '\n' + current.description.getHTMLValue() + '\n';
       journalEntry += gs.getMessage("This {0} has been chased by {1} from {2}", [taskName, callerName, currentLink]);
       
           //gs.log('Comments: ' + journalEntry);

           var gr = new GlideRecord(tableName);
           gr.addQuery('sys_id', sysID); 
           gr.query(); 
           if(gr.next())
           { 
             //gr.work_notes = journalEntry;
             gr.comments = journalEntry;
             gr.update();

             //gs.log('GlideRecord updated ');
           }

        }
   }

   //check if open_by is a member of Service Desk
   function isServiceDeskMember(usrID)
   {
         var returnValue = false;

         //1. Assign group_sys_is (for 'IT- Service Desk') is the same for dev, test, & live  
         var group_id = '96bc6cec8c51dc00483188886d6e3dfd';   
    
         //2. Check if the current user (opened by) id the above group member 
         var grmember = new GlideRecord('sys_user_grmember');  
         grmember.addQuery('group',group_id); 
         grmember.addQuery('user',usrID); 
         grmember.query();  
         if(grmember.next())  
         {  
            //gs.log('I am a Service Desk Member: ' + usrID);
            returnValue = true; 
         } 

         return returnValue;
   }

   function getDomain(){
       // only set the domain if the caller has a domain that is not global
       if (JSUtil.notNil(current.caller) && JSUtil.notNil(current.caller.sys_domain) && current.caller.sys_domain.getDisplayValue() != 'global') 
           return current.caller.sys_domain;
       else
           return getDefaultDomain();
   }

</syntaxhighlight> back to top

Re-calculate 'Priority' when value of 'Service Affected' changes.

[edit | edit source]

Name: u_calc_priority
Table: incident
UI Type: Both
Type: onChange
Field name: Service Affected

<syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
   if (isLoading || newValue === ) {
      return;
   }
    g_form.setValue('priority',calculatePriority(g_form.getValue('impact'), g_form.getValue('u_service_affected.busines_criticality')));
   
}

</syntaxhighlight> back to top

[edit | edit source]

When to Apply: Call Type is Status Call

Execute if true <syntaxhighlight lang="javascript">

function onCondition() {
var list = $$('div[tab_caption="Tasks by Same Caller"]')[0];
if(list.hasClassName('embedded')){
   list.show();
}
}

</syntaxhighlight> Execute if false <syntaxhighlight lang="javascript">

function onCondition() {
var list = $$('div[tab_caption="Tasks by Same Caller"]')[0];
if(list.hasClassName('embedded')){
   list.hide();
}
}

</syntaxhighlight> back to top

[edit | edit source]

<syntaxhighlight lang="javascript">

g_form.hideRelatedList('table name');

</syntaxhighlight>

[edit | edit source]

<syntaxhighlight lang="javascript">

g_form.showRelatedList('table name');

</syntaxhighlight> back to top

Get next incident number

[edit | edit source]

condition: current.number.nil() <syntaxhighlight lang="javascript">

current.number = getNextObjNumberPadded();
gs.addInfoMessage(gs.getMessage("Created Incident") + " " + current.number );

</syntaxhighlight> back to top

Redirect to page

[edit | edit source]

<syntaxhighlight lang="javascript"> function onLoad() { // redirect to a KB article that explains the situation location = '?id=kb_article_view&sysparm_article=KB0061348'; } </syntaxhighlight> back to top

Set Active Tab on Form

[edit | edit source]

From: https://www.servicenowguru.com/scripting/client-scripts-scripting/changing-active-tab-selection-servicenowcom/ <syntaxhighlight lang="javascript"> //for main form g_tabs2Sections.setActive(1); //for related lists g_tabs2List.setActive(3); </syntaxhighlight> back to top

Set Knowledgebase Article Review Date When State Changes to 'Published'

[edit | edit source]

Client Script to set the Published Date

[edit | edit source]

<syntaxhighlight lang="javascript">

 //Client Script - knowledge published date
 //Script to set the published date when 'State' changes to 'Published'. 
 //Calls script include u_ClientDateTimeUtils.
 function onChange(control, oldValue, newValue, isLoading, isTemplate) {
   if (isLoading)
     return;

   var state = g_form.getValue('workflow_state');

   if (state == 'published') {
     var ajax = new GlideAjax('u_ClientDateTimeUtils');
     ajax.addParam('sysparm_name', 'getNowDate');
     ajax.getXML(function() {
       g_form.setValue('published', ajax.getAnswer());
     });

   }

 }

</syntaxhighlight>

Client Script to set the Review Date to Published + 12 Months

[edit | edit source]

<syntaxhighlight lang="javascript">

 //Client Script - knowledge review date
 //Script to calculate a review date 12 months from the published date. Calls script include u_add_12_months.
 //calls script include u_add_12_months
 function onChange(control, oldValue, newValue, isLoading, isTemplate) {
   if (isLoading)
     return;

   var state = g_form.getValue('workflow_state');
   var review = g_form.getValue('u_review_date');

   if (state == 'published') {
     var ajax = new GlideAjax('u_ClientDateTimeUtils');
     ajax.addParam('sysparm_name', 'getNowDate');
     ajax.getXML(function() {
       g_form.setValue('published', ajax.getAnswer());
     });

     if (state == 'published' && review == ) {

       var ga = new GlideAjax('u_add_12_months'); //This argument should be the exact name of the script include.
       ga.addParam('sysparm_name', 'add_12_months'); //sysparm_name is the name of the function in the script include to call. 	
       ga.addParam('sysparm_published', g_form.getValue('published')); //set a parameter to pass to script include
       ga.getXML(myCallBack); //This is our callback function, which will process the response.
     }

   }


   function myCallBack(response) { //the argument 'response' is automatically provided when the callback function is called by the system.
     var answer = response.responseXML.documentElement.getAttribute("answer"); //Dig out the 'answer' attribute, which is what our function returns. 
     g_form.setValue('u_review_date', answer); //set 'renewal_date' field to returned value.

   }

 }

</syntaxhighlight>

Script Include called to add 12 months to Published date

[edit | edit source]

<syntaxhighlight lang="javascript">

 //Script called by client script knowledge review date
 //Calulates a date 12 months after the published date of a knowledge article.
 //This was written in order to handle UK date format dd/MM/yyyy
 //gdt.setDisplayValue(parm_data); is crucial to this working for all dates as without it day numbers of 12 or less are
 //interpreted as months.
 var u_add_12_months = Class.create();
 u_add_12_months.prototype = Object.extendsObject(AbstractAjaxProcessor, {
   add_12_months: function() {
     var parm_data = this.getParameter('sysparm_published'); //retrieve parameter passed from client script
     var gdtDay = "";
     var gdtMonth = "";
     var gdtYear = "";
     var gdt = new GlideDateTime(parm_data);
     gdt.setDisplayValue(parm_data);
     gdt.addMonths(12);

     if (gdt.getDayOfMonth().toString().length == 1) {
       gdtDay = "0" + gdt.getDayOfMonth();
     } else {
       gdtDay = gdt.getDayOfMonth();
     }

     if (gdt.getMonth().toString().length == 1) {
       gdtMonth = "0" + gdt.getMonth();
     } else {
       gdtMonth = gdt.getMonth();
     }

     gdtYear = gdt.getYear();

     var gdtDate = gdtDay + "/" + gdtMonth + "/" + gdtYear;
     return gdtDate;
   }

 });

</syntaxhighlight> back to top

Client script to set contact type based on user who creates the request.

[edit | edit source]

If member of Service Desk or UST contact type = phone If IT Ambassador then contact type = walk-in For all others - contcat type = Direct Input <syntaxhighlight lang="javascript"> function onLoad() {

 var openedBy = g_form.getDisplayBox('opened_by').value;
    if (g_form.contact_type != 'self-service' || g_form.contact_type != 'chat') {

if (g_scratchpad.grp_sd == true || g_scratchpad.grp_ust == true){ g_form.setValue('contact_type','phone'); } else if(openedBy == "IT Ambassador"){ g_form.setValue('contact_type','walk-in'); } else { g_form.setValue('contact_type','Direct Input'); }

  }

} </syntaxhighlight>

back to top

Loop to set all fields non mandatory

[edit | edit source]

<syntaxhighlight lang="javascript"> for (var i = 0; i < g_form.elements.length; i++) {

   var el = g_form.elements[i];
   var fieldName = el.fieldName;
   g_form.setMandatory(fieldName, false);

} </syntaxhighlight>

back to top

On Load example using setLabelOf to change field labels

[edit | edit source]

<syntaxhighlight lang="javascript"> function onLoad() { //alert('WNAC test ===> Logged for = ' + g_form.getValue('u_logged_for'));

  if(g_form.getValue('u_logged_for') == 'wnac'){

// alert('Test passed - label = ' + g_form.getLabel('description')); g_form.setLabelOf('request_item.short_description', 'Customer');

  }

} </syntaxhighlight> back to top

Strip spaces from a telephone no. string (regex)

[edit | edit source]

<syntaxhighlight lang="javascript"> function onSubmit() {

  g_form.setValue('wnac_phone_number', g_form.getValue('wnac_phone_number').replace(/ +/g, ""));

} </syntaxhighlight> back to top

Add https:// and trailing slash to URL (regex)

[edit | edit source]

link to code in regex section

Collections

[edit | edit source]

Archive User with Transactions

[edit | edit source]

UI Page - task_archive_dialog

[edit | edit source]


HTML <syntaxhighlight lang="html4strict"> <?xml version="1.0" encoding="utf-8" ?> <j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null"> <g:ui_form> <g:evaluate var="jvar_sys_id" expression="RP.getWindowProperties().get('sys_id')" />

Include in the archive

<label><input type="checkbox" id="sysparm_include_child_tasks" name="sysparm_include_children" checked="checked"/> Child tasks</label>
<label><input type="checkbox" id="sysparm_include_attachments" name="sysparm_include_attachments" checked="checked"/> Attachments</label>
<label><input type="checkbox" id="sysparm_include_variables" name="sysparm_include_variables" checked="checked"/> Variables</label>
<label><input type="checkbox" id="sysparm_include_activities" name="sysparm_include_activities" checked="checked"/> Activities</label>
<label><input type="checkbox" id="sysparm_include_emails" name="sysparm_include_emails" checked="checked"/> Emails</label>
<label><input type="checkbox" id="sysparm_include_accesses" name="sysparm_include_accesses" checked="checked"/> Access log</label>

<g:dialog_buttons_ok_cancel ok="return taskArchive()" ok_type="button" cancel_type="button" />

</g:ui_form> </j:jelly> </syntaxhighlight> Client Script <syntaxhighlight lang="javascript"> function taskArchive() { GlideDialogWindow.get().destroy(); //Close the dialog window //g_form.addInfoMessage('Archive generation successfully requested. When the archive is complete you will be emailed a link to access it.'); var ga = new GlideAjax('global.TaskArchiveAjax'); ga.addParam('sysparm_name', 'createTaskArchive'); ga.addParam('sysparm_table_name', g_form.getTableName()); ga.addParam('sysparm_table_sys_id', g_form.getUniqueValue()); ['attachments', 'variables', 'activities', 'emails', 'accesses', 'child_tasks'].forEach(function(name) { ga.addParam('sysparm_include_' + name, gel('sysparm_include_' + name).checked ? 'true' : 'false'); }); ga.getXMLAnswer(function(sys_id) { if (!sys_id || sys_id.length != 32) { g_form.addErrorMessage('An error occurred when creating the archive: ' + sys_id); return; } window.open(location.protocol + '//' + location.host + '/taskArchiveZip.do?sysparm_sys_id=' + sys_id + '&sysparm_table=u_task_archive_bundle'); }); }

</syntaxhighlight>

Script Include - TaskArchiveAjax

[edit | edit source]

<syntaxhighlight lang="javascript"> var TaskArchiveAjax = Class.create(); TaskArchiveAjax.prototype = Object.extendsObject(global.AbstractAjaxProcessor, { createTaskArchive: function() { var self = this;

if (!gs.hasRole('itil')) { return 'Permission denied.'; }

var table = this.getParameter('sysparm_table_name'); var sys_id = this.getParameter('sysparm_table_sys_id');

var gr = new GlideRecord(table); if (!gr.get(sys_id) || !gr.canRead()) { return 'Could not find record ' + table + '.' + sys_id + '.'; }

var bundle = new GlideRecord('u_task_archive_bundle'); ['attachments', 'variables', 'activities', 'emails', 'accesses', 'child_tasks'].forEach(function(name) { bundle.setValue('u_include_' + name, self.getParameter('sysparm_include_' + name)); }); bundle.insert();

new global.TaskArchive().generateBundle(bundle, gr);

return bundle.sys_id; },

   type: 'TaskArchiveAjax'

}); </syntaxhighlight>

Script Include - TaskArchive

[edit | edit source]

<syntaxhighlight lang="javascript"> var TaskArchive = Class.create(); TaskArchive.prototype = {

   initialize: function() {

this.paper_size = 'a4'; this.headerImage = '7f26a3324fa2c700f53f36e18110c716'; this.tag_whitelist = { img: true, br: true, p: true, div: true, span: true, a: true, ul: true, ol: true, li: true, strong: true, em: true, i: true, b: true, // hr: true, sup: true, table: true, tr: true, td: true, th: true, }; this.tag_map = { p: 'br', };

   },

/* * Export a bundle as a .zip * Called from global scope which gives us access to a Java output stream object. */ process: function(g_request, g_response, g_processor) { var sysid = g_request.getParameter('sysparm_sys_id'); var table = g_request.getParameter('sysparm_table');

if (table != 'u_task_archive_bundle') { g_response.setStatus(404); return; }

var gr = new GlideRecord(table); if (!gr.get(sysid) || !gr.canRead()) { g_response.setStatus(404); return; }

var zipName = gr.u_task.number + '.zip';

g_response.setContentType('application/octet-stream'); g_response.setHeader('Pragma', 'public'); g_response.setHeader('Cache-Control', 'max-age=0'); g_response.setHeader('Content-Disposition', 'attachment;filename=' + zipName);

var zip = new Packages.java.util.zip.ZipOutputStream(g_response.getOutputStream());

// add PDFs from the bundle record var att = new GlideRecord('sys_attachment'); att.addQuery('table_name', gr.getRecordClassName()); att.addQuery('table_sys_id', gr.getUniqueValue()); att.query();

while (att.next()) { this._addAttachmentToZip(zip, att.file_name, att); }

// add related attachments if (gr.u_include_attachments) { att.initialize(); att.addQuery('table_sys_id', 'IN', [gr.u_task, gr.u_child_tasks].join(',')); att.query();

while(att.next()) { var parent = new GlideRecord(att.table_name); parent.get(att.table_sys_id);

this._addAttachmentToZip(zip, parent.getDisplayValue() + '/' + att.file_name, att); } }

// Complete the ZIP file zip.close(); },

_addAttachmentToZip: function(zip, fileName, att) {

// limit is 5MB but let's be safe and cut off at 4ish if (parseInt(att.getValue('size_bytes')) <= 4000000) { var sa = new global.GlideSysAttachment();

var binData = sa.getBytes(att);

zip.putNextEntry(new Packages.java.util.zip.ZipEntry(fileName)); zip.write(binData, 0, binData.length); zip.closeEntry(); } else { var StringUtil = new GlideStringUtil();

zip.putNextEntry(new Packages.java.util.zip.ZipEntry(fileName + '.gz'));

var att_part = new GlideRecord('sys_attachment_doc'); att_part.addQuery('sys_attachment', att.getUniqueValue()); att_part.orderBy('position'); att_part.query();

while(att_part.next()) { var binData = StringUtil.base64DecodeAsBytes(att_part.data); zip.write(binData, 0, binData.length); }

zip.closeEntry(); } },

_addBytesToZip: function(zip, fileName, binData) { zip.putNextEntry(new Packages.java.util.zip.ZipEntry(fileName)); zip.write(binData, 0, binData.length); zip.closeEntry(); },

/* * Generate a bundle record for the given task * @param GlideRecord bundle Bundle to populate * @param GlideRecord gr Record to archive */ generateBundle: function(bundle, gr) { var seen = {}; if (gr.getRecordClassName() == 'x_uno49_enabl_svc_customer') { var task = new GlideRecord('task'); task .addQuery('ref_x_uno49_enabl_svc_interaction.customer', gr.sys_id) .addOrCondition('ref_x_uno49_enabl_svc_task.customer', gr.sys_id) ; task.query(); while(task.next()) { bundle.u_task = task.getUniqueValue(); this._generateBundle(bundle, task, seen); } } else { bundle.u_task = gr.sys_id; this._generateBundle(bundle, gr, seen); }

bundle.update();

return bundle.sys_id; },

_generateBundle: function(bundle, task, seen) { var self = this;

if (seen[task.sys_id+]) { return; } seen[task.sys_id+] = true; if (!task.canRead()) { return; }

var header = this.generateHeader(bundle, task); var footer = this.generateFooter(bundle, task);

var pages = this.generateTaskHTML(bundle, task); this.generatePDF(header, footer, pages, task.number + '.pdf', bundle.getTableName(), bundle.getUniqueValue());

// find child tasks and process them if (bundle.u_include_child_tasks) { var child = new GlideRecord('task'); child .addQuery('parent', task.getUniqueValue()) .addOrCondition('ref_incident_task.incident', task.getUniqueValue()) ; child.query();

while(child.next()) { bundle.u_child_tasks = bundle.u_child_tasks.nil() ? child.sys_id+ : [bundle.u_child_tasks, child.sys_id].join(',');

// switch to the specific class, to make sure we get extended fields var child2 = new GlideRecord(child.getRecordClassName()); child2.get(child.sys_id); this._generateBundle(bundle, child2, seen); } } },

generateEmailHTML: function(email) { var self = this;

var body = ;

body += '

From: ' + email.user + '
'; body += '
To: ' + email.recipients + '
'; body += '
Subject: ' + email.subject + '

';

body += email.getValue('body');

body = body.replace(/<style.+?<\/style>/g, ); // strip all style blocks body = body.replace(/<\s*(\/)?\s*([^/>\s]+)(\s?[^>]+)?>/g, function(undefined, close, tag, attributes) { tag = tag.toLowerCase(); if (tag == 'img') { var sys_id_match = attributes.match(/sys_attachment.do.*sys_id=(\w{32})/); if (!sys_id_match) { return ; } var att = new GlideRecord('sys_attachment'); if (!att.get(sys_id_match[1])) { return ; } attributes = ' src="' + self._getAttachmentBase64(att) + '"'; } return self.tag_whitelist[tag] ? ((close ? '</' : '<') + (self.tag_map[tag] ? self.tag_map[tag] : tag) + (attributes||) + '>') : ; });

return body; },

_getAttachmentBase64: function(attachmentGR) { var base64ImageStr = GlideStringUtil.base64Encode(new GlideSysAttachment().getBytes(attachmentGR)); return "data:image/png;base64," + base64ImageStr + ""; },

/* * Generate an HTML verson of the task. */ generateTaskHTML: function(bundle, task) { var pages = [];

var jr = new global.JellyRunner();

var ui_macro = new GlideRecord('sys_ui_macro'); ui_macro.get('11ffb7a5db76cc14f81bee71ca9619b1');

pages.push(jr.run(ui_macro.xml, task, { bundle: bundle, gr: task }));

if (bundle.u_include_variables && Object.keys(task.variables).length > 0) { ui_macro.get('83e34e69db8bc450f91c8c994b96192a'); pages.push(jr.run(ui_macro.xml, task, { bundle: bundle, gr: task })); }

if (bundle.u_include_activities) { ui_macro.get('f1d74039dbb6cc14f81bee71ca961964'); pages.push(jr.run(ui_macro.xml, task, { bundle: bundle, gr: task })); }

if (bundle.u_include_accesses) { ui_macro.get('8916fc86dbfa4054f81bee71ca9619ca'); pages.push(jr.run(ui_macro.xml, task, { bundle: bundle, gr: task })); }

return pages; },

/* * Generate a PDF based on the given HTML. */ generatePDF: function(header, footer, pages, filename, table, table_sys_id) { var formAPI = new global.GeneralFormAPI (filename, table, table_sys_id);

var headerImage = null; var att = new GlideRecord('sys_attachment'); if (att.get('7f26a3324fa2c700f53f36e18110c716')) { headerImage = this._getAttachmentBase64(att); }

gs.debug('setDocument'); // headerImage, footerImage, footnote, headerPosition, footerPosition, pageSize formAPI.setDocument(headerImage, , footer, '1', '1', this.paper_size);

gs.debug('createPDF'); // GeneralFormAPI is a bit weird when it comes to pages formAPI.createPDF(, pages.map(function(page) { return {heading: page}; })); },

// unused - no way to inject this header into the PDF generateHeader: function(bundle, task) { var html = ;

html += ''; var att = new GlideRecord('sys_attachment'); if (att.get(this.headerImage)) { html += '';

}

html += ''; html += ''; html += '
<img src="' + this._getAttachmentBase64(att) + '" />CONFIDENTIAL' + task.number + '
';

html += '
';

return html; },

/* * Get generic footer information */ generateFooter: function(bundle, task) { return 'CONFIDENTIAL ' + new GlideDateTime() + ' ' + gs.getUserName(); },

   type: 'TaskArchive'

};

</syntaxhighlight>

Dictionary overrides

[edit | edit source]

Default value override example:
<syntaxhighlight lang="javascript"> javascript:var u = new GlideRecord('sys_user'); u.get(gs.getUserID()); if (u.getDisplayValue('u_primary_group').indexOf('(confidential)') == '-1') { u.getValue('u_primary_group'); } </syntaxhighlight>

Embeddable Catalog Item Stuff (Create record from external web form)

[edit | edit source]

Embeddable test UI page

[edit | edit source]

<syntaxhighlight lang="javascript"> <?xml version="1.0" encoding="utf-8" ?> <j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null"> <html> <head> <link type="text/css" href="https://cdn.southampton.ac.uk/assets/site/design/styles/uos.main.0.10.3.min.css" rel="stylesheet"/> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script> </head> <body>

<g:evaluate var="jvar_catalog_item" object="true" jelly="true"> var gr = new GlideRecord("sc_cat_item"); var sys_id = RP.getParameterValue("sysparm_sys_id") + ""; if (!sys_id) { sys_id = 'aa5417794f4acf00f53f36e18110c744'; } gr.addQuery("sys_id", sys_id); gr.query(); gr; </g:evaluate> <j:choose> <j:when test="${gr.next()}">

${jvar_catalog_item.getValue('name')} - ${jvar_catalog_item.getValue('sys_id')}

<script src="/x_uno49_embed_cata.EmbedCatalogItem.jsdbx?${gs.generateGUID()}"></script> </j:when> <j:otherwise> Catalog item ${HTML:RP.getParameterValue("sysparm_sys_id")} not found. </j:otherwise> </j:choose> <g:evaluate var="jvar_other_item" object="true" jelly="true"> var gr = new GlideRecord("sc_cat_item"); gr.addQuery('sc_catalogs', 'CONTAINS', '9e9d0ac5db9687406f3df57eaf9619e6'); gr.addQuery('active', 'true'); gr.orderBy('name'); gr.query(); gr; </g:evaluate>

Embed this form

Copy the following code fragment into the page you want the form to appear on. On SitePublisher you need to use a Embed Code (limited support).

<div id="catalog_item" data-catalog-item="${jvar_catalog_item.getValue('sys_id')}"></div>
<script src="https://sotonproduction.service-now.com/x_uno49_embed_cata.EmbedCatalogItem.jsdbx"></script>

Other Embeddable Forms

    <j:while test="${jvar_other_item.next()}">
  • <a href="?sysparm_sys_id=${jvar_other_item.getValue('sys_id')}">${jvar_other_item.getValue('name')}</a>
  • </j:while>

</body> </html> </j:jelly> </syntaxhighlight>

EmbedCatalogItemTemplate UI Page

[edit | edit source]

<syntaxhighlight lang="javascript"> <?xml version="1.0" encoding="utf-8" ?> <j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null"> <style> .catalog-item-attachments { display: table; } .catalog-item-attachment { display: table-row; } .catalog-item-attachment > div { display: table-cell; padding: 5px; } </style>

An error occurred during the request - Template:Err.
Please wait ...

<form ng-if="!(submitting || submitted)" method="POST" ng-submit="submit()" class="uos-formset">

<fieldset ng-repeat="set in data.sets" class="catalog-item-set" id="Template:Set.sys id">

<legend ng-if="variable.display_title == 'true'" class="uos-set-title">Template:Variable.label</legend>
<input type="{{variable.attributes.html5_type ? variable.attributes.html5_type[0] : 'text'}}" id="Template:Variable.name" name="Template:Variable.name" ng-model="variables[variable.name]" ng-required="variable.mandatory == 'true'"/>
<input type="email" id="Template:Variable.name" name="Template:Variable.name" ng-model="variables[variable.name]" ng-required="variable.mandatory == 'true'"/>
<input type="date" id="Template:Variable.name" name="Template:Variable.name" ng-model="variables[variable.name]" ng-required="variable.mandatory == 'true'"/>
<textarea id="Template:Variable.name" name="Template:Variable.name" ng-model="variables[variable.name]" ng-required="variable.mandatory == 'true'"/>

<input type="radio" name="Template:Variable.name" ng-model="variables[variable.name]" value="Template:Choice.value" id="Template:Variable.name_Template:Choice.value" ng-required="variable.mandatory == 'true'"><label for="Template:Variable.name_Template:Choice.value">Template:Choice.text</label></input>

<select name="Template:Variable.name" ng-model="variables[variable.name]" id="Template:Variable.name" ng-required="variable.mandatory == 'true'"> <option value="">-- None --</option> <option ng-repeat="choice in variable.choices" value="Template:Choice.value" selected="selected">Template:Choice.text</option> </select>

<input type="checkbox" id="Template:Variable.name" name="Template:Variable.name" ng-model="variables[variable.name]" ng-required="variable.mandatory == 'true'"/> <label for="Template:Variable.name">Template:Variable.label</label>

<button type="file" ngf-select="uploadFile($file, $invalidFiles)" accept="*/*" ngf-max-size="5MB" class="uos-btn">Attach new file (Max 5MB)</button>

<button ng-if="!(submitting || submitted)" ng-click="removeFile(file)" class="uos-btn">Remove</button>

Unhandled variable type 'Template:Variable.type'.

</fieldset>

<input type="submit" value="Submit" class="uos-btn"/>

</form>

0 && submitted == false">

File Attachments

<button ng-if="!(submitting || submitted)" ng-click="removeFile(file)" class="uos-btn">Remove</button>

</j:jelly> </syntaxhighlight>

EmbedCatalogItem Script Include

[edit | edit source]

<syntaxhighlight lang="javascript"> var EmbedCatalogItem = Class.create(); EmbedCatalogItem.prototype = {

   initialize: function(sys_id) {

// is in Embeddable catalog var catalog_item = new GlideRecord('sc_cat_item'); catalog_item.addQuery('sc_catalogs', 'CONTAINS', '9e9d0ac5db9687406f3df57eaf9619e6'); catalog_item.addQuery('sys_id', sys_id); catalog_item.query();

// use to test user criteria var catItem = GlideappCatalogItem.get(sys_id);

if (catalog_item.next() && catItem.canView()) { this.catalog_item = catalog_item; } else { throw new Error('Catalog item not found'); }

   },

/* * Submit a catalog item * @param object variables Key-value variables to submit * @return GlideRecord The resulting sc_req_item */ orderItem: function(variables) { // if variables is empty the cart will fail, so support empty forms // but just adding a dummy value if (Object.keys(variables).length == 0) { variables["_"] = "_"; }

var version = new GlideRecord('sys_upgrade_history'); version.addEncodedQuery('from_version!=n/a'); version.setLimit('1'); version.orderByDesc('upgrade_finished'); version.query(); version.next(); gs.debug('version=' + version.to_version); if (version.isValidRecord() && version.getValue('to_version').startsWith('glide-london')) { gs.debug('Executing London compatible catalog order.'); return this._orderItemLondon(variables); } else { gs.debug('Executing NewYork+ compatible catalog order.'); return this._orderItemNewYork(variables); } },

// INC1793904 London compatible version // see https://hi.service-now.com/kb_view.do?sysparm_article=KB0714209 // London hangs when we try to use a cart name _orderItemLondon: function(variables) { var sys_id = this.catalog_item.sys_id+;

var mutex = new global.Mutex('x_uno49_embed_cata.EmbedCatalogItem._orderItemLondon');

if (!mutex.get()) { throw new Error('Timed out getting mutex.'); }

// use a unique cart, to avoid collisions between guest users var cart_name = 'cart_' + gs.generateGUID();

var cart = new sn_sc.CartJS();

// make sure guest's cart is empty if (gs.getUserName() == 'guest') { var sc_cart_item = new GlideRecord('sc_cart_item'); sc_cart_item.addQuery('cart', cart.getCartID() || -1); sc_cart_item.query(); sc_cart_item.deleteMultiple(); }

var item = { 'sysparm_id': sys_id, 'sysparm_quantity': '1', 'sysparm_requested_for': gs.getUserID(), 'get_portal_messages': 'true', // see https://hi.service-now.com/kb_view.do?sysparm_article=KB0714209 'variables':variables };

// orderNow doesn't appear to work with a named cart var checkoutInfo = cart.orderNow(item); //var cartDetails = cart.addToCart(item); //var checkoutInfo = cart.checkoutCart();

mutex.release();

var req_item = new GlideRecord('sc_req_item'); req_item.addQuery('request', checkoutInfo.request_id || -1); req_item.addQuery('cat_item', sys_id); req_item.query();

if (!req_item.next()) { throw new Error('Cart API did not create requested item.'); }

req_item.correlation_id = cart_name; req_item.update();

return req_item; },

// New York+ compatible version _orderItemNewYork: function(variables) { var sys_id = this.catalog_item.sys_id+;

// INC1793904 use a unique cart, to avoid collisions between guest users var cart_name = 'cart_' + gs.generateGUID();

var cart = new sn_sc.CartJS(cart_name);

var item = { 'sysparm_id': sys_id, 'sysparm_quantity': '1', 'sysparm_requested_for': gs.getUserID(), 'get_portal_messages': 'true', // see https://hi.service-now.com/kb_view.do?sysparm_article=KB0714209 'sysparm_cart_name': cart_name, // see https://hi.service-now.com/kb_view.do?sysparm_article=KB0714209 'variables':variables };

// orderNow doesn't appear to work with a named cart var checkoutInfo = cart.orderNow(item); //var cartDetails = cart.addToCart(item); //var checkoutInfo = cart.checkoutCart();

var req_item = new GlideRecord('sc_req_item'); req_item.addQuery('request', checkoutInfo.request_id || -1); req_item.addQuery('cat_item', sys_id); req_item.query();

if (!req_item.next()) { throw new Error('Cart API did not create requested item.'); }

req_item.correlation_id = cart_name; req_item.update();

return req_item; },

/* * Get the Catalog Item GlideRecord */ getCatalogItem: function() { return this.catalog_item; },

/* * Get the result template, which is a variable (in a variable set) named EmbedCatalogItemResult. Provides a sane default if no template is defined. */ getResultTemplate: function() { var self = this;

var template = "

Thank you for your enquiry. In any future correspondence please quote the reference Template:Current.number.display value.

";

var sets = []; this.mapSets(function(item_set) { sets.push(item_set.sys_id + ); });

if (sets.length == 0) return template;

var item = new GlideRecord('item_option_new');

item.addQuery('name', 'EmbedCatalogItemResult'); item.addQuery('variable_set', 'IN', sets); item.query(); if (item.next()) { template = item.instructions + ; }

return template; },

/* * Iterate through the catalog item's sets. * @param f function(item_set) */ mapSets: function(f) { var self = this;

var item_set = new GlideRecord('io_set_item');

item_set.addQuery('sc_cat_item', self.catalog_item.sys_id); item_set.orderBy('order'); item_set.query();

while(item_set.next()) { f(item_set.variable_set.getRefRecord()); } },

/* * Iterate through the set of variables. * @param f function(io_set_item, item_option_new, target) {} */ mapVariables: function(f) { var self = this;

var item_set = new GlideRecord('io_set_item');

item_set.addQuery('sc_cat_item', self.catalog_item.sys_id); item_set.orderBy('order'); item_set.query();

while(item_set.next()) { var item = new GlideRecord('item_option_new'); item.addActiveQuery(); item.addQuery('variable_set', item_set.variable_set); item.addQuery('name', '!=', 'EmbedCatalogItemResult'); item.orderBy('order'); item.query();

while(item.next()) { var target;

var create_roles = item.create_roles.nil() ? [] : item.getValue('create_roles').split(',');

// if requires a role and does not contain public/snc_external if (create_roles.length > 0 && create_roles.indexOf('public') == -1 && create_roles.indexOf('snc_external') == -1) { continue; }

// disabled by variable attribute if (item.attributes.toString().match(/(^|,)embeddable=false(,|$)/i)) { continue; }

if (item.type == '15') { target = new GlideRecord('sys_ui_page'); target.get(item.ui_page); }

if (item.type == '3' || item.type == '5') { target = new GlideRecord('question_choice'); target.addQuery('question', item.sys_id); target.orderBy('order'); target.query(); }

f(item_set, item, target); } } },

/* * Iterate through the set of onSubmit scripts * @param f function(catalog_script) { ... } */ mapOnSubmit: function(f) { var self = this;

var set_ids = []; self.mapSets(function(item_set) { set_ids.push(item_set.sys_id + ); });

var qry = [ 'active=true^type=onSubmit^applies_to=item^cat_item=' + this.catalog_item.sys_id, 'active=true^type=onSubmit^applies_to=set^variable_setIN' + set_ids ].join('^NQ'); // Top level OR

var catalog_script = new GlideRecord('catalog_script_client'); catalog_script.addEncodedQuery(qry); catalog_script.query(); while(catalog_script.next()) { f(catalog_script); }

},

/* * Iterate through the set of Catalog UI policies * @param f function(ui_policy) { ... } */ mapUIPolicy: function(f) { var gr = new GlideRecord('catalog_ui_policy'); gr.addActiveQuery(); gr.addQuery('catalog_item', this.catalog_item.sys_id); gr.query(); while(gr.next()) { f(gr); } },

   type: 'EmbedCatalogItem'

}; </syntaxhighlight>

attach Scripted REST Resource

[edit | edit source]

<syntaxhighlight lang="javascript"> (function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {

var correlation_id = request.pathParams.correlation_id;

var sc_req_item = new GlideRecord("sc_req_item"); if (gs.nil(correlation_id) || !sc_req_item.get('correlation_id', correlation_id) && sc_req_item.active == true) { response.setStatus(404); return; }

var contentDisposition = request.getHeader("Content-Disposition"); if (!contentDisposition) { response.setStatus(500); return "Missing Content-Disposition header."; }

var match = contentDisposition.match(/filename="([^"]+)"/); if (!match) { response.setStatus(500); return "No filename specified in Content-Disposition."; } var filename = match[1];

var sa = new GlideSysAttachment(); sa.writeContentStream(sc_req_item, filename, request.getHeader("Content-Type"), request.body.dataStream);

return 0;

})(request, response); </syntaxhighlight>

template Scripted REST Resource

[edit | edit source]

<syntaxhighlight lang="javascript"> (function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {

   var gr = new GlideRecord("sys_ui_page");

gr.get("cca267374ff85700f53f36e18110c746");

response.setContentType("text/plain"); response.getStreamWriter().writeString(gr.getValue("html"));

})(request, response); </syntaxhighlight>

catalog_item Scripted REST Resource

[edit | edit source]

<syntaxhighlight lang="javascript"> (function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {

var data = { };

   var sys_id = request.pathParams.sys_id;

var embedCatalogItem; try { embedCatalogItem = new EmbedCatalogItem(sys_id); } catch(err) { response.setStatus(404); return err.toString(); }

data.catalog_item = sys_id;

var variables = {}; data.variables = variables;

embedCatalogItem.mapVariables(function(item_set, item, target) {

// set checkboxes to "false" if unset, otherwise we get a variable change on save if (item.type == '7' && !request.body.data[item.name]) { variables[item.name] = 'false'; } // Convert Dates to user local format, which is what the catalog API expects else if (item.type == '9') { if (gs.nil(request.body.data[item.name])) { variables[item.name] = null; } else { var gdt = new GlideDateTime(request.body.data[item.name]); variables[item.name] = gdt.getDisplayValue(); } } else if (request.body.data[item.name] !== undefined) { // stringify everything to avoid Java error variables[item.name] = request.body.data[item.name] + ; }

});

var req_item; try { req_item = embedCatalogItem.orderItem(variables); } catch(error) { gs.error(error); response.setStatus(500); return 'A problem occurred during form submission. Please try again later.'; }

data.request_item = req_item.sys_id + ; data.correlation_id = req_item.correlation_id + ;

['sys_created_on', 'due_date', 'number'].forEach(function(name) { var ele = req_item.getElement(name); data[name] = { type: ele.getED().getInternalType(), value: ele.toString(), display_value: ele.getDisplayValue() }; if (data[name].type == 'glide_date_time') { data[name].date = data[name].value.substring(0,10); } }); data.message = gs.getMessage("Your submission has been processed and has been given the reference {0}. Please quote this reference in any future correspondance.", req_item.getDisplayValue());

return data; })(request, response); </syntaxhighlight>

result_template Scripted REST Resource

[edit | edit source]

<syntaxhighlight lang="javascript"> (function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {

   var sys_id = request.pathParams.sys_id;

var embedCatalogItem; try { embedCatalogItem = new EmbedCatalogItem(sys_id); } catch(err) { response.setStatus(404); return err.toString(); }

var template = embedCatalogItem.getResultTemplate();

response.setContentType("text/plain"); response.getStreamWriter().writeString(template);

})(request, response); </syntaxhighlight>

variables Scripted REST Resource

[edit | edit source]

<syntaxhighlight lang="javascript"> (function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) { gs.include('JellyRunner');

var data = { sets: [], on_submit: [], ui_policy: [],

result_template: "

Thank you for your enquiry. In any future correspondence please quote the reference Template:Current.number.

"

};

   var sys_id = request.pathParams.sys_id;

var embedCatalogItem; try { embedCatalogItem = new EmbedCatalogItem(sys_id); } catch(err) { response.setStatus(404); return err.toString(); }

data.catalog_item = sys_id;

var catalog_item = embedCatalogItem.getCatalogItem(); data.name = catalog_item.getDisplayValue("name"); data.description = catalog_item.getDisplayValue("description");

var set = {}; embedCatalogItem.mapVariables(function(item_set, item, target) { // we have a new set if (set.sys_id != item_set.variable_set.sys_id) { set = { name: item_set.variable_set.name + , sys_id: item_set.variable_set.sys_id + , variables: [] }; data.sets.push(set); }

var type = item.type + ; var content = ; var choices = [];

// UI Page if (type == '15') { content = new global.JellyRunner().run(target.html); }

// Multiple choice if (type == '3' || type == '5') { while(target.next()) { choices.push({ text: target.getValue('text'), value: target.getValue('value') }); } }

// Magic "result" variable if (item.name == "EmbedCatalogItemResult") { data.result_template = item.instructions + ; return; }

var attributes = {}; item.attributes.toString().split(/\s*,\s*/).forEach(function(attr) { var name = attr.replace(/=.*$/, ); var value = []; if (attr.indexOf("=") != -1) { value = attr.replace(/^[^=]+=/, ).split(";"); } attributes[name] = value; });

set.variables.push({ sys_id: item.sys_id + , type: item.getDisplayValue('type') + , _type: item.type + , name: item.name + , label: item.question_text + , mandatory: item.mandatory + , instructions: item.instructions + , display_title: item.display_title + , sp_widget: item.sp_widget + , attributes: attributes, content: content, choices: choices }); });

// populate on_submit embedCatalogItem.mapOnSubmit(function(catalog_script) { data.on_submit.push(catalog_script.script + ); });

response.setContentType('text/javascript'); response.setStatus(200); var writer = response.getStreamWriter(); if (request.queryParams.callback) { writer.writeString(request.queryParams.callback + '(' + JSON.stringify(data) + ')'); } else { writer.writeString(JSON.stringify(data)); }

return; })(request, response); </syntaxhighlight>

Email Notifications

[edit | edit source]

Email Scripts

[edit | edit source]

Fetch additional comments

[edit | edit source]

<syntaxhighlight lang="javascript"> (function runMailScript(/* GlideRecord */ current, /* TemplatePrinter */ template, /* Optional EmailOutbound */ email, /* Optional GlideRecord */ email_action, /* Optional GlideRecord */ event) { var data = ; var commentGR = new GlideRecord('sys_journal_field'); commentGR.addQuery('element_id', current.sys_id); commentGR.addQuery('element','!=','work_notes'); commentGR.orderByDesc('sys_created_on'); commentGR.query(); while (commentGR.next()) { if (commentGR.value != ) { var comment = commentGR.value.replace('[code]',).replace('[/code]',); //comment = commentGR.value.replace(/[\[\]]+/g,);

data += commentGR.sys_created_on + ' - ' + commentGR.getDisplayValue('sys_created_by') + ' (Additional comments) \n\n' + comment + '
\n';

} }

template.print('
\n' + data);

})(current, template, email, email_action, event); </syntaxhighlight>

Get email attachment

[edit | edit source]

<syntaxhighlight lang="javascript"> printattachments();

function printattachments() {

   var gr = new GlideRecord('sys_attachment');
   gr.addQuery('table_sys_id', current.sys_id);
   gr.query();
   while (gr.next()) {
       template.print('Attachment: <a href="http://' + gs.getProperty("instance_name") + '.service-now.com/sys_attachment.do?sys_id='
           + gr.sys_id + '">' + gr.file_name + '</a>');
   }

} </syntaxhighlight>

Appointment reminder

[edit | edit source]

<syntaxhighlight lang="javascript"> (function runMailScript(current, template, email, email_action, event) { var wnacAssessor = ; var wnacAssessmentCentre = ; var gDate = new GlideDate(); if (current.number.indexOf('RITM') > -1){ gDate.setValue(current.variables.date_and_time_of_meeting); wnacAssessor = current.variables.wnac_assessor; wnacAssessmentCentre = current.variables.wnac_assessment_centre;

} else { gDate.setValue(current.request_item.variables.date_and_time_of_meeting); wnacAssessor = current.request_item.variables.wnac_assessor; wnacAssessmentCentre = current.request_item.variables.wnac_assessment_centre; } var gDateTime = new GlideDateTime(gDate); gt = gDateTime.getLocalTime(); template.print('This is a reminder of your WNAC meeting on ' + gDate.getByFormat('dd/MM') + ' at ' + gt.getByFormat('HH:mm') + ' with ' + wnacAssessor + ' at ' + wnacAssessmentCentre + '. If you cannot attend, please reply or call 02380 597233 asap.');

})(current, template, email, email_action, event); </syntaxhighlight>

On date at time

[edit | edit source]

<syntaxhighlight lang="javascript"> (function runMailScript(current, template, email, email_action, event) {

var gDate = new GlideDate(); if (current.number.indexOf('RITM') > -1){ gDate.setValue(current.variables.date_and_time_of_meeting); } else { gDate.setValue(current.request_item.variables.date_and_time_of_meeting); } var gDateTime = new GlideDateTime(gDate); gt = gDateTime.getLocalTime(); template.print('on ' + gDate.getByFormat('EEEE dd MMMMM yyyy') + ' at ' + gt.getByFormat('HH:mm'));

})(current, template, email, email_action, event); </syntaxhighlight>

Auto-populate Email Signatures

[edit | edit source]

Set Newlines to HTML: true <syntaxhighlight lang="javascript"> (function runMailScript(current) {

var user = new GlideRecord('sys_user'); user.get(gs.getUserID());

template.print(user.getValue('name')+'\n'); template.print('Title:'+user.getValue('title')+'\n'); template.print('Business Phone:'+user.getValue('phone')+'\n'); template.print('Department:'+user.department.getDisplayValue()+'\n');

})(current); </syntaxhighlight> back to top

Email Script to populate email body with survey results

[edit | edit source]

Used encoded query to overcome issue of selecting incorrect records.

getDisplayValue() used to return value rather than sys_id.

Instance of survey stored in - asmt_assessment_instance.

Survey results stored in - asmt_metric_result. <syntaxhighlight lang="javascript">

 var ins = current.number.getDisplayValue();
 var queryString = "metric.metric_type.evaluation_method=survey^instance.number=" + ins;
 template.print("You have received some feedback for survey " + '${URI_REF}');
 template.print("<br />");
 var gr = new GlideRecord('asmt_metric_result');  
 gr.addEncodedQuery(queryString);
 gr.orderByDesc('actual_value');
 //gr.setLimit(10);
 gr.query();
 var counter = 1;
 while(gr.next()) { 
 
    if(counter == 1){ 
  	   template.print("Submitted by " + gr.user.getDisplayValue() + "<br />");
 	   template.print("<br />");
 	   counter++;
    }
    template.print(gr.instance_question.getDisplayValue() + "<br />");
    template.print(gr.string_value + "<br />");
    template.print("<br />");
 }

</syntaxhighlight> back to top

Hide redundant closed states

[edit | edit source]

<syntaxhighlight lang="javascript">

// Hide old "Closed" request states made redundant by REQ from everyone but admin

function onLoad() { 
	if (g_user.hasRole('admin'))
		 return;
	//don't show the 'old' label 'Closed Converted to Incident so it does not cause confusion with the 'new' naming format
		//of 'Cancelled - Converted to Incident' 
	if (g_form.getValue('request_state') != 'Cancelled - Converted to Incident')
		g_form.removeOption('request_state', 'Cancelled - Converted to Incident');
		//
		if (g_form.getValue('request_state') != 'closed_incomplete')
		g_form.removeOption('request_state', 'closed_incomplete');
		//
		if (g_form.getValue('request_state') != 'closed_complete')
		g_form.removeOption('request_state', 'closed_complete');
		//
		if (g_form.getValue('request_state') != 'Pending testing')
		g_form.removeOption('request_state', 'Pending testing');
 //
	if (g_form.getValue('request_state') != 'closed_cancelled')
		g_form.removeOption('request_state', 'closed_cancelled');
 //
	if (g_form.getValue('request_state') != 'Closed - Converted to Incident')
		g_form.removeOption('request_state', 'Closed - Converted to Incident'); 
//
	if (g_form.getValue('request_state') != 'closed_duplicate')
		g_form.removeOption('request_state', 'closed_duplicate');
 //
	if (g_form.getValue('request_state') != 'closed_rejected')
		g_form.removeOption('request_state', 'closed_rejected');
 //
	if (g_form.getValue('request_state') != 'closed_resolved')
		g_form.removeOption('request_state', 'closed_resolved');
 //
if (g_form.getValue('request_state') != 'closed_user_unavailable')
	g_form.removeOption('request_state', 'closed_user_unavailable');
}

</syntaxhighlight> back to top

Email script with GlideDate examples

[edit | edit source]

<syntaxhighlight lang="javascript"> (function runMailScript(current, template, email, email_action, event) {

var gDate = new GlideDate(); gDate.setValue(current.variables.date_and_time_of_meeting);

template.print('

' + 'On: ' + gDate.getByFormat('dd/MM') + '

');
       template.print('
' );
template.print('

' + 'At: ' + gDate.getByFormat('HH/mm') + '

');

</syntaxhighlight>

Example which formats the date as for example - Thursday June 17 2019 <syntaxhighlight lang="javascript"> (function runMailScript(current, template, email, email_action, event) {

var gDate = new GlideDate(); gDate.setValue(current.variables.date_and_time_of_meeting); template.print('on ' + gDate.getByFormat('EEEE dd MMMMM yyyy') + ' at ' + gDate.getByFormat('HH:mm'));

})(current, template, email, email_action, event); </syntaxhighlight> back to top

This version get local time from the system rather than UTC. Appointment reminder emails had the incorrect time on during BST. <syntaxhighlight lang="javascript"> (function runMailScript(current, template, email, email_action, event) {

var gDate = new GlideDate(); gDate.setValue(current.variables.date_and_time_of_meeting); var gDateTime = new GlideDateTime(gDate); gt = gDateTime.getLocalTime();

template.print('

' + 'On: ' + gDate.getByFormat('EEEE dd MMMMM yyyy') + '

');

template.print('
' );

template.print('

' + 'At: ' + gt.getByFormat('HH:mm') + '

');


})(current, template, email, email_action, event); </syntaxhighlight> back to top

[edit | edit source]

<syntaxhighlight lang="javascript"> (function runMailScript(current, template, email, email_action, event) {

// generate a self-service link to Service Portal linked on the task number

var uri = gs.getProperty('glide.servlet.uri');

// get the path for Service Portal var sp = new GlideRecord('sp_portal'); sp.get('2b2fb4e2dbf783006f3df57eaf9619dc'); uri += sp.url_suffix;

// get the text to link var text = current.getDisplayValue();

// get the page the task type is used with var page; switch (current.getRecordClassName()+) { case 'sysapproval_approver': page = 'approval'; text = sp.getDisplayValue(); break; case 'sc_req_item': page = 'sc_request'; break; default: page = 'ticket'; break; }

// compose the full url uri += '?id=' + page + '&table=' + current.getTableName() + '&sys_id=' + current.getUniqueValue();

// print the link template.print('<a href="' + uri + '">' + text + '</a>');

})(current, template, email, email_action, event); </syntaxhighlight> back to top

Encoded Query

[edit | edit source]

<syntaxhighlight lang="javascript">

current.addEncodedQuery('nameISNOTEMPTY^cmdb_model_categoryISNOTEMPTY'); //Encoded query

</syntaxhighlight>

<syntaxhighlight lang="javascript">

gr.addEncodedQuery('active=true^state=2'); //Encoded query

</syntaxhighlight> back to top

Events

[edit | edit source]

Event parameters can be accessed in email notifications using the following syntax - <syntaxhighlight lang="javascript">event.parm1.toString()</syntaxhighlight>

Using an Event to Change The Status of a Task When Updated From Call Module

[edit | edit source]

Business Rule - CallTypeChanged amended to create events when status calls are created. <syntaxhighlight lang="javascript">

gs.eventQueue("call.incident.update", current, taskName, tableName);
gs.eventQueue("call.request.update", current, taskName, tableName);

</syntaxhighlight> Event Registry - call.request.update and call.incident.update created. Script Actions - Update Incident From Call and Update Request From Call created which set the status of incidents/requests to active

Update Incident From Call <syntaxhighlight lang="javascript">

//fired by event - call.incident.update.
//parameters: 1 - Incident number. 2 - table name
//Sets incident_state to active.
var incident_gr = new GlideRecord(event.parm2);
incident_gr.addQuery('number', event.parm1);
incident_gr.query();
if (incident_gr.next()) 
{
   incident_gr.incident_state = '2';
   incident_gr.update();
}

</syntaxhighlight> Update Request From Call <syntaxhighlight lang="javascript">

//fired by event - call.request.update.
//parameters: 1 - Request number. 2 - table name
//Sets request_state to active.
var request_gr = new GlideRecord(event.parm2);
request_gr.addQuery('number', event.parm1);
request_gr.query();
if (request_gr.next()) 
{
   request_gr.request_state = 'Active';
   request_gr.update();
}

</syntaxhighlight> back to top

Event creation Example

[edit | edit source]

Example of creating events for incidents. <syntaxhighlight lang="javascript">

if (current.operation() != 'insert' && current.comments.changes()) {
  gs.eventQueue("incident.commented", current, gs.getUserID(), gs.getUserName());
}

if (current.operation() == 'insert') {
 gs.eventQueue("incident.inserted", current, gs.getUserID(), gs.getUserName());
}

if (current.operation() == 'update') {
 gs.eventQueue("incident.updated", current, gs.getUserID(), gs.getUserName());
}

if (!current.assigned_to.nil() && current.assigned_to.changes()) {
  gs.eventQueue("incident.assigned", current, current.assigned_to.getDisplayValue() , previous.assigned_to.getDisplayValue());
}

if (!current.caller_id.nil() && current.caller_id.changes()) {
  gs.eventQueue("incident.caller", current, previous.caller_id.first_name , previous.caller_id);
}

if (!current.assignment_group.nil() && current.assignment_group.changes()) {
  gs.eventQueue("incident.assigned.to.group", current, current.assignment_group.getDisplayValue() , previous.assignment_group.getDisplayValue());
}

if (current.priority.changes() && current.priority == 1) {
  gs.eventQueue("incident.priority.1", current, current.priority, previous.priority);
}

if (current.priority.changes() && (current.priority == 1 || current.priority == 2)) { 
  gs.eventQueue("incident.priority.maj", current, current.priority, previous.priority);
}

if (current.severity.changes() && current.severity== 1) {
  gs.eventQueue("incident.severity.1", current, current.severity, previous.severity);
}

if (current.escalation.changes() && current.escalation > previous.escalation && previous.escalation != -1) {
  gs.eventQueue("incident.escalated", current, current.escalation , previous.escalation );
}

if(current.impact.changes() && current.impact == 9) { 
  gs.eventQueue("incident.impact.security_compromise", current, current.impact, previous.impact);
} 

if(current.active.changesTo(false)){
  gs.eventQueue("incident.inactive", current, current.incident_state, previous.incident_state);
  gs.workflowFlush(current);
}

</syntaxhighlight> back to top

Fix Scripts

[edit | edit source]

Changing the approver on a RITM

[edit | edit source]

<syntaxhighlight lang="javascript"> // //Check for special accounts!!! // var approvalSysid = <enter sysID>; //enter sysid of approval record var approverSysid = <enter sysID>; //enter sysid of new approver var approverName = <full name of new approver>; //enter Name of New approver

function changeApprover() {

   var ab = new GlideRecord('sysapproval_approver');
   ab.addEncodedQuery('sys_id=' + approvalSysid);
   ab.query();
   while (ab.next()) {
       ab.approver.setValue(approverSysid)
       ab.update();
       gs.eventQueue("approval.inserted", ab, approverSysid, approverName);
   }

}

changeApprover(); </syntaxhighlight> back to top

Flow Designer

[edit | edit source]

Script to set flow variable

[edit | edit source]

<syntaxhighlight lang="javascript"> var roleIDs = fd_data._1__get_catalog_variables.role_profile.split(','); var roleName = []; roleIDs.forEach(function(id){

    var roleGR = new GlideRecord('question_choice');
    if (roleGR.get(id))
       roleName.push(' ' + roleGR.text);

});

return roleName.toString(); </syntaxhighlight> back to top

Example script to populate sctask description

[edit | edit source]

<syntaxhighlight lang="javascript"> if (fd_data._1__get_catalog_variables.account_type == 'new_account'){

   desc = 'Addition of the following role profiles has been approved for ' + fd_data.trigger.request_item.requested_for.name + ':\n';

} else {

   desc = 'The following role profile modifications have been approved for ' + fd_data.trigger.request_item.requested_for.name + ':\n';

} if (fd_data._8__ask_for_approval.approval_state == 'approved'){

   if (fd_data._1__get_catalog_variables.account_type == 'new_account'){
   desc += '\nAdmin roles (new account): ' + fd_data.flow_var.adminroles;
   } else {
   desc += '\nAdmin roles to be added: ' + fd_data.flow_var.admin_roles_add;
   desc += '\nAdmin roles to be removed: ' + fd_data.flow_var.admin_roles_remove;
   }

} if (fd_data._10__ask_for_approval.approval_state=='approved'){

   if (fd_data._1__get_catalog_variables.account_type == 'new_account'){
       desc += '\nNon-admin roles (new account): ' + fd_data.flow_var.roles;
   } else {
       desc += '\nNon-admin roles to be added: ' + fd_data.flow_var.roles_add;
       desc += '\nNon-admin roles to be removed: ' + fd_data.flow_var.roles_remove;
   }

} return desc; </syntaxhighlight> back to top

Add comment to Solarwinds Alert

[edit | edit source]

Note: used a REST Step in the flow instead.

<syntaxhighlight lang="javascript"> (function execute(inputs, outputs) {

   try {
       var request = new sn_ws.RESTMessageV2();
       var regex = /.*mn01/gmi;
       var regex2 = /([^AAT:]*$)/;
       var link = inputs.link;
       var url = regex.exec(link).toString();
       url = url.replace('http:', 'https:');
       var aatId = parseInt(regex2.exec(link)[1]);
       gs.info('***CM*** url: ' + url +'\nAAT: ' + aatId);
       request.setHttpMethod('POST');
       request.setEndpoint(url +':17774/SolarWinds/InformationService/v3/Json/Invoke/Orion.AlertActive/AppendNote');
       gs.info('***CM*** endpoint url: ' + url +':17774/SolarWinds/InformationService/v3/Json/Invoke/Orion.AlertActive/AppendNote');
       gs.info('***CM*** getendpoint url: ' + request.getEndpoint());

request.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); request.setRequestHeader("Accept","*/*");

       request.setMIDServer(inputs.mid);

request.setBasicAuth("servicenow","n3*^Q4r79jGP");

       request.setRequestBody('{"alertObjectIDs": [aatId], "note": "Incident " + inputs.inc + " created in ServiceNow"}');
       gs.info('***CM*** RequestBody: ' + JSON.stringify({"alertObjectIDs": [aatId], "note": "Incident " + inputs.inc + " created in ServiceNow"}));
       gs.info('***CM*** GetRequestBody: ' + request.getRequestBody());
       var response = request.execute();

gs.info('***CM*** Response: ' + JSON.stringify(response.getBody()));

       var httpResponseStatus = response.getStatusCode();
       gs.info("http response status_code: " + httpResponseStatus);        
   }
   catch (ex) {
       var message = ex.getMessage();
       gs.info(message);
   }

})(inputs, outputs); </syntaxhighlight> back to top

Forms

[edit | edit source]

Finding Assignment Groups for an Assignee

[edit | edit source]

Script Include <syntaxhighlight lang="javascript"> FindAssigneeGroups.getUserGroups = function(userID) {

var FoundGroups = new GlideRecord(‘sys_user_group’);

if (!gs.nil(userID)) { var AssigneeGroups = Packages.com.glide.sys.User.getMyGroups(userID); if (AssigneeGroups.size() != 0) { FoundGroups.addQuery('sys_id’, AssigneeGroups); } else { FoundGroups.addQuery('sys_id’,'No groups found for this asignee’); } }

return FoundGroups.getEncodedQuery(); }; </syntaxhighlight> Reference Qualifier <syntaxhighlight lang="javascript"> javascript:FindAssigneeGroups.getUserGroups('assigned_to) </syntaxhighlight> back to top

GlideAjax Examples

[edit | edit source]

Lookup Substitute Hardware

[edit | edit source]

Catalog Client Script - Populate Substitutes <syntaxhighlight lang="javascript"> function onChange(control, oldValue, newValue, isLoading) {

  if (isLoading || newValue == ) {
     return;
  }
  ga = new GlideAjax('SotonSubstituteHardwareAjax');
  ga.addParam('sysparm_name', 'getSubstituteComputers');
  ga.addParam('sysparm_dispname', newValue);
  ga.getXMLAnswer(function(answer) {
     g_form.setValue('substitute_computers', answer);
  });

} </syntaxhighlight> Script Include - SotonSubstituteHardwareAjax in one step <syntaxhighlight lang="javascript"> var SotonSubstituteHardwareAjax = Class.create(); SotonSubstituteHardwareAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, { getSubstituteComputers: function() { var parm_data = this.getParameter('sysparm_dispname'); var dRec = new GlideRecord('cmdb_hardware_product_model'); dRec.get(parm_data); var dName = dRec.display_name; gs.info('dName: ' + dName);

var hw = new GlideRecord('cmdb_m2m_model_substitute'); var data = [];

       hw.addEncodedQuery('model.display_name=' + dName + '^model.certified=true');

hw.query(); while(hw.next()) { gs.info('SotonSubstituteHardwareAjax push: ' + hw.substitute); data.push(hw.substitute.toString()); } gs.info('data = ' + data.join(',')); return data.join(','); },

type: 'SotonSubstituteHardwareAjax' }); </syntaxhighlight> Script Include - SotonSubstituteHardwareAjax in two steps <syntaxhighlight lang="javascript"> var SotonSubstituteHardwareAjax = Class.create(); SotonSubstituteHardwareAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, { getSubstituteComputers: function() { var parm_data = this.getParameter('sysparm_dispname'); var dRec = new GlideRecord('cmdb_hardware_product_model'); dRec.get(parm_data); var dName = dRec.display_name; gs.info('dName: ' + dName); //var hw = new GlideRecord('cmdb_m2m_model_substitute');

var data = []; var hw = new SotonSubstituteHardware().getSubstituteComputers(dName); // hw.addEncodedQuery('model.sys_id=' + parm_data); // hw.query(); while(hw.next()) { gs.info('SotonSubstituteHardwareAjax push: ' + hw.getUniqueValue()); // data.push(hw.substitute); data.push(hw.substitute.toString()); } gs.info('data = ' + data); return data.join(','); },

type: 'SotonSubstituteHardwareAjax' }); </syntaxhighlight> Script Include - SotonSubstituteHardware <syntaxhighlight lang="javascript"> var SotonSubstituteHardware = Class.create(); SotonSubstituteHardware.prototype = {

   initialize: function() {
   },

/* * Get a GlideRecord list of substitute computers */ getSubstituteComputers: function(dName) { var hw = new GlideRecord('cmdb_m2m_model_substitute'); hw.addEncodedQuery( 'model.display_name=' + dName ); hw.query();

       return hw;

},

   type: 'SotonSubstituteHardware'

}; </syntaxhighlight> back to top

GlideSchedule Examples

[edit | edit source]

Calculate elapsed time using schedule

[edit | edit source]

<syntaxhighlight lang="javascript"> var startDate = new GlideDateTime('2014-10-16 02:00:00'); var endDate = new GlideDateTime('2014-10-18 04:00:00'); var schedule = new GlideSchedule(); schedule.load('090eecae0a0a0b260077e1dfa71da828'); // loads "8-5 weekdays excluding holidays" schedule var duration = schedule.duration(startDate, endDate); gs.info(duration.getDurationValue()); // gets the elapsed time in schedule </syntaxhighlight> back to top

Calculate historic metrics

[edit | edit source]

<syntaxhighlight lang="javascript"> /**

* Retroactively backfill case metrics for historic cases
*
*/
(function () {
   // Set this to the name of your metric definition name
   var metric_definition_name = 'Case - Open to resolved';
   /**
     * Everything below this line is business logic
     */
   // Find out the metric definition id
   var grMetricDef = new GlideRecord('metric_definition');
   if (!grMetricDef.get('name', metric_definition_name)) {
       gs.log('--->populateMetrics: Cannot find metric definition name ' +
           metric_definition_name);
       return;
   }
   // Iterate over all existing cases
   var grcase = new GlideRecord('sn_customerservice_case');
   grcase.addQuery('state', 'IN', '6,3');
   grcase.query();
   while (grcase.next()) {
           // Check to see if metric for this ticket exists
           var grMetric = new GlideRecord('metric_instance');
           grMetric.addQuery('id', grcase.getValue('sys_id'));
           grMetric.addQuery('definition.name', metric_definition_name);
           grMetric.addQuery('value', 'Resolved');
           grMetric.query();
           if (grMetric.hasNext()) {
           }
           else {
               // If not, create a metric instance
               var startDate = new GlideDateTime(grcase.getValue('sys_created_on'));
               var endDate = new GlideDateTime(grcase.getValue('resolved_at'));
               var schedule = new GlideSchedule();
               schedule.load('dc6a3325dbdf4450f5fd88805b96195c'); // loads "Business Hours - Mon-Fri (9am to 5pm) - With Holidays" schedule
               var busduration = schedule.duration(startDate, endDate);
               var duration = GlideDate.subtract(startDate, endDate); //the difference between startDate and endDate 
               gs.info(duration.getDurationValue()); // gets the elapsed time in schedule
               var instant = new GlideDuration(0);
               grMetric = new GlideRecord('metric_instance');
               grMetric.initialize();
               grMetric.setValue('table', grcase.getRecordClassName());
               grMetric.setValue('id', grcase.getValue('sys_id'));
               grMetric.setValue('definition', grMetricDef.getValue('sys_id'));
               grMetric.setValue('field', 'state');
               grMetric.setValue('value', 'Resolved');
               grMetric.setValue('duration', duration);
               grMetric.setValue('business_duration', busduration);
               grMetric.setValue('calculation_complete', true);
               grMetric.setValue('start', startDate);
               grMetric.setValue('end', endDate);
               grMetric.insert();
           }
       }
   

})(); </syntaxhighlight> back to top

Global Text Search

[edit | edit source]

Configuration

[edit | edit source]

Use Search V4. Ensure Text Index is checked on collection record of table to be included in search.

This should add a record to Text Indexes.

Check Text Index Configurations and Text Index Groups.

Under Text Index Configurations add Text Index Table Attribute Maps and Text Index Column Attribute Maps.

Under Search Experience check Search Sources and Search Applications.

AI Search Indexed Sources.

Re-generate table indexes.

Screenshot of a Search Source
Screenshot: Search Source

back to top

Inbound Email Actions

[edit | edit source]

Example: Handling email attachments

[edit | edit source]

<syntaxhighlight lang="javascript"> var sysAttach = new GlideSysAttachment(); var emailSysId = sys_email.getUniqueValue(); gs.log("Email sys_id is: " + emailSysId, "CM"); if (emailSysId) {

 var sysEmailAttachments = sysAttach.getAttachments("sys_email", emailSysId);
 gs.log("Number of attachments found: " + sysEmailAttachments.getRowCount(), "CM");
 gs.log("Updating attachments to table: " + current.getTableName() + " and sys_id: " + current.getUniqueValue(), "CM");
 while (sysEmailAttachments.next()) {
   sysEmailAttachments.setValue("table_name", current.getTableName());
   sysEmailAttachments.setValue("table_sys_id", current.getUniqueValue());
   sysEmailAttachments.update();
 }

} </syntaxhighlight> back to top

Conditional Import - don't always create customer

[edit | edit source]

<syntaxhighlight lang="javascript"> (function runAction(/*GlideRecord*/ current, /*GlideRecord*/ event, /*EmailWrapper*/ email, /*ScopedEmailLogger*/ logger, /*EmailClassifier*/ classifier) {

var myType = ; var myCust = new GlideRecord('sys_user'); myCust.initialize(); myCust.addQuery('sys_id', email.from_sys_id); myCust.query(); while(myCust.next()){ myType = myCust.getValue('u_type'); } var customer = new GlideRecord('x_uno49_enabl_svc_customer');

// Lookup customer if (email.from_sys_id) { if (!customer.get('user', email.from_sys_id)) { customer.initialize(); customer.user = email.from_sys_id; if (customer.getDisplayValue('user') != 'Guest' && myType != 'staff'){ //user is not Guest or staff customer.insert(); } } }

current.customer = customer.isValidRecord() ? customer.getUniqueValue() : ; current.from = email.origemail; current.setValue('state', -5); current.contact_type = 'email'; current.assignment_group = "NULL"; current.assigned_to = "NULL"; current.short_description = email.subject; current.email_body = email.body_text; current.insert();

})(current, event, email, logger, classifier); </syntaxhighlight> back to top

Different action depending on recipient address (uses regex)

[edit | edit source]

Condition: email.recipients.match(/\b(email1@domain.co.uk|email2@domain.co.uk|email3@domain.co.uk|email4@domain.co.uk)/i) != null <syntaxhighlight lang="javascript"> (function(current, email, sys_email) { gs.info('User = ' + gs.getUserID()); var eu = new global.EmailUtil(); var parent = current.parent.getRefRecord(); var gr = new GlideRecord(current.getTableName()); gr.initialize(); gr.parent = parent.getUniqueValue(); gr.customer = parent.customer; if(email.recipients.match(/\b(email1@domain.co.uk|email2@domain.co.uk)/i) != null) { gr.contact_type = 'email_in_1_2'; } else if (email.recipients.match(/\b(email3@domain.co.uk|email4@domain.co.uk)/i) != null) { gr.contact_type = 'email_in_3_4'; }

gr.interaction_enquiry_type = parent.ref_x_uno49_enabl_svc_case.enquiry_type; gr.short_description = email.subject; gr.work_notes = email.body_text; gr.assignment_group = parent.assignment_group; if (parent.assigned_to){ gr.assigned_to = parent.assigned_to; } else { gr.assigned_to = ""; } gr.state = 1; current.get(gr.insert());


})(current, email, sys_email);

</syntaxhighlight> back to top

JSON Parsing

[edit | edit source]

Sample JSON

[edit | edit source]

<syntaxhighlight lang="json"> {

 "ConnectionId" : "79a36b001bfe46107fbe6654b24bcbcc",
 "ImpactedEntities" : [ {
   "type" : "HOST",
   "name" : "CUK-UAT-TAS01.CarnivalUK.com",
   "entity" : "HOST-A6C8F0B53B313185"
 } ],
 "ImpactedEntity" : "Low disk space on Host CUK-UAT-TAS01.CarnivalUK.com",
 "PID" : "-7657400866275833997_1724141040000V2",
"ProblemDetailsHTML" : "

RESOLVED Problem P-2408993 in environment CUK DC Non Prod


Problem detected at: 08:07 (UTC) 20.08.2024 - 08:27 (UTC) 20.08.2024 (was open for 20 min)
1 impacted infrastructure component

Host
CUK-UAT-TAS01.CarnivalUK.com

Low disk space
The total available space on disk C:\\ is lower than 5 %


Root cause

Based on our dependency analysis all incidents are part of the same overall problem.


<a target=\"_blank\" href=\"https://izm07948.live.dynatrace.com/#problems/problemdetails;pid=-7657400866275833997_1724141040000V2\">Open in Browser</a>

",
 "ProblemDetailsJSON" : {
   "id" : "-7657400866275833997_1724141040000V2",
   "startTime" : 1724141220000,
   "endTime" : 1724142420000,
   "displayName" : "P-2408993",
   "impactLevel" : "INFRASTRUCTURE",
   "status" : "CLOSED",
   "severityLevel" : "RESOURCE_CONTENTION",
   "commentCount" : 0,
   "tagsOfAffectedEntities" : [ ],
   "rankedEvents" : [ {
     "startTime" : 1724141040000,
     "endTime" : 1724142420000,
     "entityId" : "HOST-A6C8F0B53B313185",
     "entityName" : "CUK-UAT-TAS01.CarnivalUK.com",
     "severityLevel" : "RESOURCE_CONTENTION",
     "impactLevel" : "INFRASTRUCTURE",
     "eventType" : "LOW_DISK_SPACE",
     "resourceId" : "DISK-DDD61808CEA794DC",
     "resourceName" : "C:\\",
     "status" : "CLOSED",
     "severities" : [ ],
     "isRootCause" : false,
     "annotationDescription" : "The total available space on disk C:\\ is lower than 5 %",
     "effectiveEntity" : "C:\\",
     "correlationId" : "ef54a1c309d9b3e6"
   } ],
   "rankedImpacts" : [ {
     "entityId" : "HOST-A6C8F0B53B313185",
     "entityName" : "CUK-UAT-TAS01.CarnivalUK.com",
     "severityLevel" : "RESOURCE_CONTENTION",
     "impactLevel" : "INFRASTRUCTURE",
     "eventType" : "LOW_DISK_SPACE",
     "resourceId" : "DISK-DDD61808CEA794DC",
     "resourceName" : "C:\\"
   } ],
   "affectedCounts" : {
     "INFRASTRUCTURE" : 0,
     "SERVICE" : 0,
     "APPLICATION" : 0,
     "ENVIRONMENT" : 0
   },
   "recoveredCounts" : {
     "INFRASTRUCTURE" : 1,
     "SERVICE" : 0,
     "APPLICATION" : 0,
     "ENVIRONMENT" : 0
   },
   "hasRootCause" : false
 },
 "ProblemDetailsMarkdown" : "## RESOLVED Problem P-2408993 in environment *CUK DC Non Prod*\n\nProblem detected at: 08:07 (UTC) 20.08.2024 - 08:27 (UTC) 20.08.2024 (was open for 20 min)\n\n---\n**1 impacted infrastructure component**\n\n---\n\n\nHost\n### **CUK-UAT-TAS01.CarnivalUK.com**\n> **Low disk space**\n>\n> The total available space on disk C:\\ is lower than 5 %\n\n\n---\nRoot cause\n---\n\n**Based on our dependency analysis all incidents are part of the same overall problem.**\n\n---\n\n[Open in Browser](https://izm07948.live.dynatrace.com/#problems/problemdetails;pid=-7657400866275833997_1724141040000V2)\n",
 "ProblemDetailsText" : "RESOLVED Problem P-2408993 in environment CUK DC Non Prod\nProblem detected at: 08:07 (UTC) 20.08.2024 - 08:27 (UTC) 20.08.2024 (was open for 20 min)\n\n1 impacted infrastructure component\n\nHost\nCUK-UAT-TAS01.CarnivalUK.com\n\nLow disk space\nThe total available space on disk C:\\ is lower than 5 %\n\nRoot cause\n\nBased on our dependency analysis all incidents are part of the same overall problem.\n\nhttps://izm07948.live.dynatrace.com/#problems/problemdetails;pid=-7657400866275833997_1724141040000V2",
 "ProblemID" : "P-2408993",
 "ProblemImpact" : "INFRASTRUCTURE",
 "ProblemSeverity" : "RESOURCE_CONTENTION",
 "ProblemTitle" : "Low disk space",
 "ProblemURL" : "https://izm07948.live.dynatrace.com/#problems/problemdetails;pid=-7657400866275833997_1724141040000V2",
 "State" : "RESOLVED",
 "Tags" : "",
 "correlation_id" : "HOST-A6C8F0B53B313185",
 "name" : "CUK-UAT-TAS01.CarnivalUK.com"

} </syntaxhighlight>

Example use of JSON Parser in a Flow Action

[edit | edit source]

<syntaxhighlight lang="javascript"> if (fd_data.subflow_inputs.ah_alertgr.additional_info.indexOf('Low Disk Space - C disks under 10GB') >= 0) {

   return 'a2ddbeb51b1e4510d5e20dcad34bcb01'; //sys_id of Servers and Storage

} else {

   return fd_data.subflow_inputs.ah_alertgr.cmdb_ci.support_group;

} </syntaxhighlight>

Keytool

[edit | edit source]

Command line tool within jre package. Typically found in C:\<MID Server installation directory>\agent\jre\bin Examples:

Importing a certificate.

keytool -import -alias <descriptive name> -file <path to file> -keystore C:\<MID Server directory>\agent\jre\lib\security\cacerts

List certificates in trust keystore

keytool -list -keystore C:\ServiceNow_DEV_MID_Server\agent\jre\lib\security\cacerts

Other parameters

-v verbose output -alias <alias name>

keytool -list -v -alias cuk-sou-mn01 -keystore C:\ServiceNow_DEV_MID_Server\agent\jre\lib\security\cacerts

Modules

[edit | edit source]

List users with roles assigned (example)

[edit | edit source]

<syntaxhighlight lang="javascript"> sys_user_list.do?sysparm_query=sys_id=javascript:getRoledUsers('NOT IN', 'bu_role_staff,bu_role_student') </syntaxhighlight>

OAuth 2.0

[edit | edit source]

Configuration Notes

[edit | edit source]

Create a user to be associated with the connection

[edit | edit source]
  • Identity type: machine (formerly Web Access Only)
  • Grant roles to give required access

Create Inbound Authentication Profile

[edit | edit source]
  • Ensure you are in correct scope
  • Create a new record
  • Auth Parameter: eg. Header for API Key
  • Authentication policy if required

Create API Access Policy

[edit | edit source]
  • Ensure you are in correct scope
  • Create a new record
  • REST Api: i.e Table API
  • Select tables as required

OAuth Inbound Integrations/Application Registries

[edit | edit source]
  • Ensure you are in the correct application scope
  • Create new record for the integration
  • OAuth Application User from previous step
  • Client id will be set
  • Client secret will be automatically generated
  • Default Grant type: Client Credentials
File:Inbound auth profile.png

Genrate an OAuth token

[edit | edit source]

Using Postman or similar:

  • Create a new http request
  • Type: OAuth 2.0
  • Use the Configure New Token section to generate a new token
  • Access Token URL: https://<instance>.service-now.com/oauth_token.do
  • CLient id and secret from earlier step

Test using a GET <api url> with the newly generate token

Parameters

[edit | edit source]

Access URL Parameters

[edit | edit source]

<syntaxhighlight lang="javascript">

RP.getParameterValue('sysparm_xxxx');

</syntaxhighlight>

or

<syntaxhighlight lang="javascript">

//check if it exists
gs.action.getGlideURI().toString().indexOf('String to search for')
//retrieve url parameter
gs.action.getGlideURI().getMap().get('sysparm_xxxx')

</syntaxhighlight> back to top

Record Producer Scripts

[edit | edit source]

Add all variables to the description field

[edit | edit source]

<syntaxhighlight lang="javascript" line='line'> current.short_description = 'AR Pack Request - ' + producer.dealer_name; current.assignment_group = '51af5bfadb424f007be0a3e84b9619d2'; //sys_id of comply assignment group current.u_department = '7'; //Compliance current.u_case_type = 'AR Pack Request'; current.contact_type = 'mobile app';

var arr_questionSysIds = []; for (var v in producer) {

   if (v.startsWith("IO")) { //only variables
       arr_questionSysIds.push(v.substring(2));
   }

}

var gr_questions = new GlideRecord('item_option_new'); gr_questions.addQuery('sys_id', 'IN', arr_questionSysIds.toString()); //Single call to the table with all variables we are looking for gr_questions.orderBy('order'); //Order by the order they are on the catalog item form gr_questions.query(); var description = ""; while (gr_questions.next()) {

   var question = gr_questions.getValue('question_text');
   var value = producer[gr_questions.getValue('name')].getDisplayValue();
   if (value !== "") { //only get variables with values
       description += question + ": " + value + "\n"; 
   }

}

current.description = description; current.comments = description; //added so agent can see what was submitted in the mobile app activity stream </syntaxhighlight> back to top

List Collector Array Handling

[edit | edit source]

<syntaxhighlight lang="javascript" line='line'>

//Start to build notes for the Desciption field of the generated request record.
var notes = "Permission required for the following staff: " ;

// REQ1137375 - Following loop added to retrieve userids from sys_user for each name entered in the list selector.
var listIDriveNames = producer.getValue('idrive_name'); // store contents of idrive_name variable in listIDriveNames. This will be a comma separated list of sysids
var arrayIDriveNames = listIDriveNames.split(','); // Split listIdriveNames at the commas and store in arrayIDriveNames. 
// Loop round array elements
for (var i = 0; i < arrayIDriveNames.length; i++) { // loop round a number of times equal to the number of elements in the array
	var gr = new GlideRecord('sys_user'); 
	gr.addQuery('sys_id', arrayIDriveNames[i]); //add query to search sys_user table for a record that matches the sys_id of one of the array elements.
	gr.query();  // Search sys_user table for a match on sys_id
	while(gr.next()) {
		// read values for name and user_name from the record that matches the sys_id
		notes += "\n     " + gr.name.getDisplayValue() + " (" + gr.user_name.getDisplayValue() + ")";
	}
}

</syntaxhighlight> back to top

[edit | edit source]

<syntaxhighlight lang="javascript"> current.u_name = producer.u_name_of_user; current.u_software = producer.u_software; current.u_additional_email = producer.u_additional_email; if (producer.u_additional_email != ){ var u_add_email = producer.u_additional_email;

  }

var sw_package = current.u_software.getDisplayValue(); var gr = new GlideRecord('u_software_for_workathome_use'); gr.initialize(); gr.addQuery('u_name',sw_package); gr.query(); while (gr.next()){

  if (gr.u_link_to_web_request != ){
     producer.redirect = 'u_software_link_to_web_request.do?sysparm_software=' + current.u_software.getDisplayValue();  
  }
  else if (gr.u_link_to_web_request ==  && gr.u_licence_key ==  && gr.u_name == 'Adobe Creative Cloud Enterprise'){

var gr_request = new GlideRecord('sc_request'); gr_request.initialize(); gr_request.requested_for = producer.u_name_of_user; gr_request.assignment_group.setDisplayValue = 'IT - Service Desk'; gr_request.short_description = 'Software Installation - Adobe Creative Cloud Enterprise for work at home use'; var notes = 'Request for Adobe Creative Cloud Enterprise for work at home use';

  	   notes += "\n Requested for: " + producer.u_name_of_user.getDisplayValue();

if (producer.u_additional_email != ){ notes += "\n Preferred email: " + u_add_email;

} notes += "\n Service Desk - See Work Notes for details on how to enable access to Adobe Creative Cloud Enterprise for " + producer.u_name_of_user.getDisplayValue(); gr_request.description = notes; gr_request.priority = 5;

gr_request.work_notes = '[code]

Instructions for IT - Service Desk

[/code]' +

"\n Requested for: " + producer.u_name_of_user.getDisplayValue() +

       '\n Preferred email address: ' + u_add_email +

'\n To enable staff access to Adobe Creative Cloud Enterprise see knowledge base article - [code]<a href="https://bournemouth.service-now.com/nav_to.do?uri=%2Fkb_view.do%3Fsysparm_article%3DKB0015414%26sysparm_tsqueryId%3Dc3247ff8db8a9780f9bf9ee3db961951%26sysparm_rank%3D1" target="_blank">KB0015414</a>[/code]';

gr_request.insert();

producer.redirect = 'u_adobe_creative_cloud_requested.do?sysparm_software=' + sw_package;

  }
  else if (gr.u_link_to_web_request ==  && gr.u_licence_key == ){

producer.redirect = 'u_software_install_guide.do?sysparm_software=' + sw_package;

  }
  else if (gr.u_link_to_web_request ==  && gr.u_licence_key != ){
     producer.redirect = 'u_software_licence_key.do?sysparm_software=' + sw_package;
  }
  else {
     current.setAbortAction(true);
  }

} </syntaxhighlight>

back to top

Set assignment group and write notes to activity log when room matches criteria

[edit | edit source]

<syntaxhighlight lang="javascript" line='line'>

//Set assignment group and write notes to activity log when room matches criteria
var myRoom = producer.u_inc_location.getDisplayValue();
var row = new GlideRecord('u_rooms');
var roomFound = false;
row.addQuery('u_location_reference', 'STARTSWITH', myRoom);
row.query();
while (row.next() && !roomFound) {
  if (row.u_scitech == true) {
    current.assignment_group.setDisplayValue("<assignment_group>");
    current.work_notes = "Automatically assigned to <assignment_group> as room " + myRoom + " is marked in u_rooms as a <Dept.> room";
    roomFound = true;
  }
}


 current.short_description = "Software Installation";
 if (producer.username != ){
    current.requested_for = producer.username;
 }
 var notes = "Software name: " + producer.softins_name.display_name.getDisplayValue();
 notes += "\n Software licence model: " + "( " + producer.softins_name.u_licence_model.getDisplayValue() + " )";
 notes += "\n To device: " + producer.softins_dev.getDisplayValue();
 //CJ - added below to add additional details from Software Model form - REQ1045658
 notes += "\n Deployment type: \n" + producer.softins_name.u_deployment_type;
 notes += "\n Deployment method: \n" + producer.softins_name.u_deployment_method;
 notes += "\n Install AD group: \n" + producer.softins_name.u_install_ad_group;
 notes += "\n Install instructions: \n" + producer.softins_name.u_install_instructions.getDisplayValue();
 if(producer.softins_name.u_deployment_method.getDisplayValue() == "Manual"){
    notes += "\n Installer location: \n" + producer.softins_name.u_installer_location;
 }
 notes += "\n Comments: \n" + producer.Additional_Information;
 current.description = notes;
 //CJ - populate CI field with information provided in form - REQ1056173
 current.cmdb_ci = producer.softins_dev;
 //CJ - populate service affected and record which form used to generate request - REQ1055636
 current.u_form_name = "Software Installation";
 current.u_service_affected ='66bd2aad8cfc9400483188886d6e3d4e';
 //Has the request been raised by someone in IT?
 //if (current.opened_by.department.getDisplayValue() != "IT"){
 //current.contact_type = 'self-service';}
 // above condition changed under REQ1030520
 //Has the request been raised by a member of IT Service Desk? If so contact type is phone otherwise contact type is self-service
 if (gs.getUser().isMemberOf('IT - Service Desk'))
 {
 current.contact_type = 'phone';
 }
 else {
 current.contact_type = 'self-service';}
 if (producer.softins_name != ){
    current.priority = 5;
    }
    else
    {
  current.priority = 6;
    } 
 if (producer.softins_name.display_name.getDisplayValue() == ){
 gs.addErrorMessage("We could not find the software you asked for, please add it to the 'Additional comments' field of this request");
 }
 if(producer.softins_dev.getDisplayValue() == ){
 gs.addErrorMessage("We could not find the PC number you entered, please add it to the 'Additional comments field of this request");
 }
 //CJ - send to group listed on Software Model form if software is campus licenced or has no licence cost - REQ1045658
 if (producer.softins_name.u_licence_model.getDisplayValue() == "Campus"){
    current.assignment_group = producer.softins_name.u_install_team;
 }
 if (producer.softins_name.u_licence_model.getDisplayValue() == "Chargeable"){
    current.assignment_group.setDisplayValue("IT - Supplier and Licencing");
 }
 if (producer.softins_name.u_licence_model.getDisplayValue() == "Limited"){
 current.assignment_group.setDisplayValue("IT - Supplier and Licencing");
 }
 if (producer.softins_name.u_licence_model.getDisplayValue() == "No associated licence cost"){
 current.assignment_group = producer.softins_name.u_install_team;
 }

</syntaxhighlight> back to top

Reference Qualifiers

[edit | edit source]

Example: used to restrict records displayed to those where the case number starts with ESCASE and the short description starts with the value of the current record's customer field <syntaxhighlight lang="javascript"> javascript: 'numberSTARTSWITHESCASE^ref_x_uno49_enabl_svc_case.customer='+current.customer </syntaxhighlight> Exmaple: restrict catalog item reference variable (sys_user) to display only users managed by selected user or their manager <syntaxhighlight lang="javascript"> javascript:'manager=' + new SotonGetManager().getManager(current.variables.name_of_leaver) + '^ORmanager=' + current.variables.name_of_leaver </syntaxhighlight> Calls script include below: <syntaxhighlight lang="javascript"> var SotonGetManager = Class.create(); SotonGetManager.prototype = Object.extendsObject(AbstractAjaxProcessor, {

   getManager: function(user) {
       var manager = ;
       var gr = new GlideRecord('sys_user');
       gr.addEncodedQuery('sys_id=' + user);
       gr.query();
       if (gr.next()) {
           manager = gr.manager;
       }
       return manager.toString();
   },
   type: 'SotonGetManager'

}); </syntaxhighlight> back to top

Regular Expressions (regex)

[edit | edit source]

Examples

[edit | edit source]

Currency ($) ^\$[0-9]*\.[0-9]{2}$

Currency (€) ^\€[0-9]*\.[0-9]{2}$

Email address ^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$

IBAN ^[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}$

IP address ^\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b$

Letter (A-z) ^[A-Za-z]*$

Mac address ^[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}$

Zip Code (NL) ^[1-9][0-9]{3}[\s]?[A-Za-z]{2}$

grab first string from two strings separated by a space

[edit | edit source]

<syntaxhighlight lang="javascript"> replace(/^([^\s]+)\s.*/, "$1") </syntaxhighlight> back to top

match email addresses

[edit | edit source]

<syntaxhighlight lang="javascript"> if(email.recipients.match(/\b(enable@soton.ac.uk|enable@southampton.ac.uk|enabling@soton.ac.uk|enabling@southampton.ac.uk)/i) != null) { gr.contact_type = 'email_in'; } else if (email.recipients.match(/\b(firstsupport@soton.ac.uk|firstsupport@southampton.ac.uk|firstsup@soton.ac.uk|firstsup@southampton.ac.uk)/i) != null) { gr.contact_type = 'email_in_fs'; } replace(/^([^\s]+)\s.*/, "$1") </syntaxhighlight> back to top

Add https:// and trailing slash to URL (for use in client script)

[edit | edit source]

<syntaxhighlight lang="javascript"> function onChange(control, oldValue, newValue, isLoading, isTemplate) {

if (isLoading || newValue === ) {
 return;
}

if (/^https?:\/\//.test(newValue) == false)
 g_form.setValue('base_url', 'https://' + newValue);

if (/\/$/.test(newValue) == false)
 g_form.setValue('base_url', newValue + '/');

} </syntaxhighlight> back to top

Grab email addresses from a text file (or XML, CSV, JSON etc)

[edit | edit source]

<syntaxhighlight lang="javascript"> In Notepad++

find: (\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}\b) replace: \r\n$&\r\n

mark: (\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}\b)

Remove unmarked lines

</syntaxhighlight> back to top

Validate FQDN

[edit | edit source]

<syntaxhighlight lang="javascript"> isValidFQDN: function(fqdn) {

       var fqdnPattern = /^(?=.{1,253}$)(([A-Za-z0-9]+(-[A-Za-z0-9]+)*\.)+[A-Za-z]{2,63})$/;
       return fqdnPattern.test(fqdn);
   }

</syntaxhighlight> back to top

Relationships

[edit | edit source]

Applies to table: customer Queries from table: sc_req_item

So "current" will be sc_req_item

"current" is the current query in the related list. parent is the parent form

<syntaxhighlight lang="javascript"> (function refineQuery(current, parent) { current.addQuery('u_on_behalf_of', parent.user); current.addEncodedQuery('sys_created_onRELATIVEGE@year@ago@1'); })(current, parent); </syntaxhighlight> back to top

Reports

[edit | edit source]

Tip: how to use a javascript query in a condition

[edit | edit source]

Can be inserted in a 'is one of' condition. <syntaxhighlight lang="javascript"> javascript:new groupMember().getMember("Sales - CORP - Agents - Northern Zone"); </syntaxhighlight>

back to top

Scheduled Job

[edit | edit source]

script example

[edit | edit source]

<syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item');

gr.addEncodedQuery("u_logged_for=wnac^cat_item=bbcbd724db8d33c0f81bee71ca9619d7^variables.01391172db757700f91c8c994b96199cONTomorrow@javascript:gs.beginningOfTomorrow()@javascript:gs.endOfTomorrow()"); gr.query(); while(gr.next()) { var phone = gr.variable_pool.wnac_phone_number.toString(); if (phone.substring(0,2) == '07') { var gDate = new GlideDate(); gDate.setValue(gr.variable_pool.date_and_time_of_meeting); var gDateTime = new GlideDateTime(gDate); gt = gDateTime.getLocalTime(); gs.eventQueue("x_uno49_enabl_svc.wnac_appointment", gr, gr.variable_pool.wnac_phone_number + '@sms.textapp.net', 'This is a reminder of your WNAC meeting on ' + gDate.getByFormat('dd/MM') + ' at ' + gt.getByFormat('HH:mm') + ' with ' + gr.variable_pool.wnac_assessor.toString().replace(/^([^\s]+)\s.*/, "$1") + ' at ' + gr.variable_pool.wnac_assessment_centre + '. If you cannot attend, please reply or call 02380 597233 asap.'); } } </syntaxhighlight> back to top

Script Includes

[edit | edit source]

CUK Utils

[edit | edit source]

<syntaxhighlight lang="javascript"> var CUK_Utils = Class.create(); CUK_Utils.prototype = Object.extendsObject(AbstractAjaxProcessor, {

   getManagers: function() {
       var mgrs = [];
       var arrayUtil = new ArrayUtil();
       var grManagers = new GlideRecord('sys_user');
       grManagers.addNotNullQuery('manager');
       grManagers.addActiveQuery();
       grManagers.query();
       while (grManagers.next()) {
           mgrs.push(grManagers.getValue('manager'));
       }
       mgrs = arrayUtil.unique(mgrs);
       mgrs = 'sys_idIN' + mgrs;
       return mgrs + ; //return a comma separated list of sysids to calling client script
   },
   getLocation: function() {
       var loc = ;
       var incId = this.getParameter('sysparm_inc');
       var gr = new GlideRecord('incident');
       gr.addQuery('sys_id', incId);
       gr.query();
       if (gr.next()) {
           loc = gr.caller_id.location;
       }
       return loc;
   },

getUserLocation: function() {

       var usrloc = ;
       var usrId = this.getParameter('sysparm_usr');
       var gr = new GlideRecord('sys_user');
       gr.addQuery('sys_id', usrId);
       gr.query();
       if (gr.next()) {
           usrloc = gr.location;
       }
       return usrloc;
   },

getErrMsg: function() {

       var err = ;
       var incId = this.getParameter('sysparm_inc');
       var gr = new GlideRecord('incident');
       gr.addQuery('sys_id', incId);
       gr.query();
       if (gr.next()) {
           err = gr.u_error_message;
       }
       return err;
   },
   type: 'CUK_Utils'

}); </syntaxhighlight> back to top

AP_StoredProcedures

[edit | edit source]

Get user's department

Return a list of managers

<syntaxhighlight lang="javascript">

var AP_StoredProcedures = Class.create(); AP_StoredProcedures.prototype = Object.extendsObject(AbstractAjaxProcessor, {

   getAPDepartment: function() {
       var parm_data = this.getParameter('sysparm_userid'); //retrieve parameter passed from client script
       var dept = [];
       var gr = new GlideRecord('sys_user');
       gr.addQuery('sys_id', parm_data); //parameter passed is a sysid so find matching record
       gr.query();
       if (gr.next()) {
           dept.push(gr.department + );
       }
       return JSON.stringify(dept); //return value to calling client script
   },
   getManagers: function() {
       var mgrs = [];
       var arrayUtil = new ArrayUtil();
       var gr = new GlideRecord('sys_user');
       gr.addNotNullQuery('manager');
       gr.addActiveQuery();
       gr.query();
       while (gr.next()) {
           mgrs.push(gr.getValue('manager'));
       }
       mgrs = arrayUtil.unique(mgrs);
       mgrs = 'sys_idIN' + mgrs;
       return mgrs + ; //return a comma separated list of sysids to calling client script
   },
   type: 'AP_StoredProcedures'

});


Get list of users in same groups as selected user

[edit | edit source]

<syntaxhighlight lang="javascript"> var GetMyGroupsUsers = Class.create();

GetMyGroupsUsers.prototype = {

   initialize: function() {},
   getGroupUsers: function() {
       var groupsList = "";
       var grGroups = new GlideRecord('sys_user_grmember');  //group membership table
       grGroups.addQuery('user', current.user); //lookup user in group membership table
       grGroups.query();  //returns sys_ids
       while (grGroups.next()) {  //iterate through results to get groups user is a member of
           if (grGroups.group != 'bee515a01ba5f410deac43f5e34bcbd1') {  //exclude CSM License group
               groupsList = groupsList + grGroups.group + ',';  //build list of groups User is a member of
           }
       }

//next - query group membership table matching groups found in first step

       var usersList = ;
       var grUsers = new GlideRecord('sys_user_grmember');
       grUsers.addQuery('group', 'IN', groupsList);  
       grUsers.query(); //returns sys_ids
       while (grUsers.next()) {

if (grUsers.user != current.user){ //exclude self from the list

               usersList = usersList + grUsers.user + ',';  //build list of users that are members of the groups found in the previous step

}

       }
       usersList = 'sys_idIN' + usersList;  //add the prefix sys_idIN to the string for use as a reference qualifier.
       return usersList;
   },
   type: 'GetMyGroupsUsers'

}; </syntaxhighlight> back to top

Lookup vendor details

[edit | edit source]

<syntaxhighlight lang="javascript"> var GetVendorContactInfo = Class.create(); GetVendorContactInfo.prototype = Object.extendsObject(AbstractAjaxProcessor, {

   getContactInfo: function() {
       var vendorId = this.getParameter('sysparm_vendor_id');
       var vendors = vendorId.split(',');
       var vendorGR = new GlideRecord('core_company');
       var result = [];
       for (i = 0; i < vendors.length; i++) {
           vendorGR.get(vendors[i]);
           var companyName = vendorGR.name.toString();
           var contactName = vendorGR.contact.name.toString();
           var contactEmail = vendorGR.contact.email.toString();
           result[i] = {
               company: companyName,
               name: contactName,
               email: contactEmail
           };
       }
       return JSON.stringify(result);
   }

}); </syntaxhighlight> back to top

(Global) copy attachment to child then delete

[edit | edit source]

Copy single attachment to a child record. Called from a Business Rule that created a child record for each attachment on the parent. This script include also deletes the attachment on the parent record. Called from This Business Rule <syntaxhighlight lang="javascript"> var CopySpecificAttachment = Class.create(); CopySpecificAttachment.prototype = { initialize: function() { }, CopySpecificAttachment: function(donorTable, donorID, recipientTable, recipientID, fileName) { try{ var donorAttSysID; var newAttRecord; var linkToNewRecord; var attDataRecord; var newDocRecord; var attRecord = new GlideRecord('sys_attachment'); attRecord.addQuery('table_name', donorTable); attRecord.addQuery('table_sys_id', donorID); attRecord.addQuery('file_name', fileName); attRecord.query(); while (attRecord.next()) { donorAttSysID = attRecord.getValue('sys_id'); newAttRecord = this.copyRecord(attRecord); newAttRecord.setValue('table_name', recipientTable); newAttRecord.setValue('table_sys_id', recipientID); newAttRecord.update(); linkToNewRecord = gs.getProperty('glide.servlet.uri') + newAttRecord.getLink(); attDataRecord = new GlideRecord('sys_attachment_doc'); attDataRecord.addQuery('sys_attachment', donorAttSysID); attDataRecord.query(); while (attDataRecord.next()) { newDocRecord = this.copyRecord(attDataRecord); newDocRecord.setValue('sys_attachment', newAttRecord.getValue('sys_id')); newDocRecord.update(); } try{ attRecord.deleteRecord(); } catch(err) { gs.log('====> error deleting attachment: message['+err.message+']'); } } } catch(err) { gs.log('====> error in CopySpecificAttachment: message['+err.message+']'); }

}, copyRecord: function(record) { try{ var recordElement; var recordElementName; var recordTable = record.getTableName(); var recordFields = record.getFields(); var newRecord = new GlideRecord(recordTable); newRecord.initialize(); for (var i = 0; i < recordFields.size(); i++) { recordElement = recordFields.get(i); if(recordElement.getName() != 'sys_id' && recordElement.getName() != 'number') { recordElementName = recordElement.getName(); newRecord.setValue(recordElementName, record.getValue(recordElementName)); } } var newSysId = newRecord.insert(); return newRecord; } catch(err) { gs.log('====> error in CopySpecificAttachment.copyRecord: message['+err.message+']'); } }, type: 'CopySpecificAttachment' }; </syntaxhighlight> back to top

Script called from a dictionary override reference qualifier (javascript: u_get_customer_cases()

[edit | edit source]

<syntaxhighlight lang="javascript"> // var u_get_customer_cases = Class.create(); // u_get_customer_cases.prototype = Object.extendsObject(global.AbstractAjaxProcessor, { function u_get_customer_cases() { var es_cs = ; var es_cust = current.customer; //return everything if the assigned_to value is empty if(!es_cust)

return; //x_uno49_enabl_svc_case has the user to case relationship var es_case = new GlideRecord('x_uno49_enabl_svc_case'); es_case.addQuery('sys_class_name','x_uno49_enabl_svc_case'); es_case.addQuery('customer',es_cust); es_case.query(); while(es_case.next()) { if (es_cs.length > 0) { //build a comma separated string of cases if there is more than one es_cs += (',' + es_case.customer); } else { es_cs = es_case.customer; } }

		// return cases where assigned to is in those cases we use IN for lists
		return 'sys_class_name=x_uno49_enabl_svc_case^sys_idIN' + es_cs;

// return 'sys_class_name=x_uno49_enabl_svc_case'; // } // type: 'u_get_customer_cases' //}); }

</syntaxhighlight> back to top

Utility script to support locating email templates

[edit | edit source]

Name: CustomEmailTemplate

<syntaxhighlight lang="javascript"> var CustomEmailTemplate = Class.create(); CustomEmailTemplate.prototype = { table: ["change_request","change_task","incident","incident_task","problem","problem_task","sc_request","sc_req_item","sc_task", "x_uno49_enabl_svc_interaction"],

   initialize: function() {
   },

/* * Is the current user allowed to compose an email? */ canCompose: function(current) { // Supported table (x-scope access is allowed) if (this.table.indexOf(current.getTableName()) == -1) return false;

// Current record is active if (current.active != true) return false;

// Only itil may compose emails if (!gs.hasRole('itil')) return false;

// Write access to the current record if (!current.canWrite()) return false;

// Email Client is enabled var dict = new GlideRecord('sys_dictionary'); dict.addQuery('name', current.getTableName()); dict.addQuery('internal_type', 'collection'); dict.addQuery('attributes', 'CONTAINS', 'email_client=true'); dict.query(); if (!dict.hasNext()) return false;

return true; },

/* * Get a template query that matches the current record */ listTemplates: function(current) { var template = new GlideRecord('sys_email_client_template'); template.orderBy('order'); template.orderBy('name');

var template_ids = [];

var gr = new GlideRecord('sys_email_client_template'); gr.addQuery('table', current.getTableName()); gr.query(); while(gr.next()) { if (GlideFilter.checkRecord(current, gr.condition)) { template_ids.push(gr.sys_id + ); } }

if (template_ids.length == 0) { template.addQuery('sys_id', '-1'); // no match } else { template.addQuery('sys_id', 'IN', template_ids); }

template.query();

return template; },

   type: 'CustomEmailTemplate'

}; </syntaxhighlight> back to top

Oldest 10 Records (Cases) by department

[edit | edit source]

Name: oldestOpenCases

<syntaxhighlight lang="javascript">

var oldestOpenCases = Class.create(); oldestOpenCases.prototype = Object.extendsObject(AbstractAjaxProcessor, {

   getCases: function(dept, limit) {
       var oldCases = [];
       var department = ;
       if (dept) {
           department = this._getDeptName(dept);
       }
       var listLimit = ;
       if (limit) {
           listLimit = limit;
       } else {
           listLimit = '10';
       }
       var gr = new GlideRecord('sn_customerservice_case');
       if (department) {
           gr.addQuery('u_department', department);
       }
       gr.addEncodedQuery('stateNOT IN6,3');
       gr.orderBy('sys_created_on');
       gr.setLimit(listLimit);
       gr.query();
       while (gr.next()) {
           oldCases.push(gr.sys_id.toString());
       }
       return oldCases;
   },
   _getDeptName: function(val) {
       switch (val.toLowerCase()) {
           case 'business support':
               department = '1';
               break;
           case 'it':
               department = '2';
               break;
           case 'customer support':
               department = '3';
               break;
           case 'finance':
               department = '4';
               break;
           case 'underwriting':
               department = '5';
               break;
           case 'manheim':
               department = '6';
               break;
           case 'compliance':
               department = '7';
               break;
           case 'smart uk':
               department = '16';
               break;
           case 'marketing':
               department = '17';
               break;
           case 'customer care team':
               department = '19';
               break;
           case 'csu':
               department = 'csu';
               break;
           case 'claims':
               department = 'claims';
               break;
           default:
               department = ;
               break;
       }
       return department;
   },
   type: 'oldestOpenCases'

}); </syntaxhighlight> back to top

HTMLSanitizerConfig

[edit | edit source]

<syntaxhighlight lang="javascript"> //html_whitelist attributes to allow adding embedded videos from YouTube and videos uploaded to ServiceNow. HTML_WHITELIST : { globalAttributes: { attribute:[], attributeValuePattern:{} }, iframe:{ attribute:["src","width","height","border","frameborder","allow","allowfullscreen"], attributeValuePattern:{} }, object:{ attribute:["classid"], attributeValuePattern:{} }, param:{ attribute:["name","value"], attributeValuePattern:{} }, embed:{ attribute:["src","type","width","height"], attributeValuePattern:{} }, },

</syntaxhighlight>

Global client script to check for email attachments

[edit | edit source]


Called from business rule ES log sent email attachments.

Write details of outbound email attachments to work notes.

sys_id of email interaction passed as parameter 'esintref'

Lookup email(s) for interaction in sys_email table - lookup attachments for that email - write details of attachments found to Work Notes of the interaction.


<syntaxhighlight lang="javascript"> var LogEmailAttachment = Class.create(); LogEmailAttachment.prototype = { initialize: function() { }, LogEmailAttachment: function(esintref) { //sys_id of interaction var eml = new GlideRecord('sys_email'); eml.addQuery('instance', esintref); eml.addQuery('target_table', 'x_uno49_enabl_svc_interaction'); eml.query(); while(eml.next()) { var att = new GlideRecord('sys_attachment'); att.addQuery('table_name', 'sys_email'); att.addQuery('table_sys_id', eml.getUniqueValue()); //sys_id of the email att.query(); while(att.next()) { var esint = new GlideRecord('x_uno49_enabl_svc_interaction'); esint.addQuery('sys_id', esintref); //sys_id of interaction esint.query(); while(esint.next()) { esint.work_notes = 'Email ' + '"' + eml.subject + '" sent with attachment ' + '"' + att.file_name +'"'; esint.update(); }

} } }, type: 'LogEmailAttachment' </syntaxhighlight> back to top

Get stock level from alm_hardware

[edit | edit source]

<syntaxhighlight lang="javascript"> var SotonStockManagement = Class.create(); SotonStockManagement.prototype = Object.extendsObject(AbstractAjaxProcessor, { getStockLevel: function() { var gr = new GlideRecord('alm_hardware'); gr.addQuery('install_status', '6'); // In Stock gr.addQuery('model', this.getParameter('sysparm_model_id')); gr.query(); return gr.getRowCount() + ; },

   type: 'SotonStockManagement'

}); </syntaxhighlight> back to top

Triage Processing Example

[edit | edit source]

<syntaxhighlight lang="javascript"> // // Notes: RJU 2017-04-11: Request and Request_items do not have a close_code field. // Intend to use Action_code when rework of that function is complete. Then // incident and request/item will need updating to use action code. // var iSolutions_triage_processing = Class.create(); iSolutions_triage_processing.prototype = { _inc_New: 1, _inc_Assigned: 9, _inc_In_Progress: 19, _inc_On_Hold: 17, _inc_Awaiting_Third_Party: 10, _inc_Awaiting_User_Info: 4, _inc_Resolved: 6, _inc_Closed: 7, _req_Pending: -5, _req_Open: 1, _req_Work_In_Progress: 2, _req_Closed_Complete: 3, _req_Closed_Incomplete: 4, _req_Closed_Skipped: 7, _req_Closed_Rejected: 15, _ritm_state_on_hold : -100, _ritm_hold_agreed : 'agreed_hold', _ritm_hold_awaiting_user : 'awaiting_user_info', _call_hold_type_not_required : 'not_required', _call_hold_type_on_hold : 'on_hold', _call_hold_type_awaiting_user : 'awaiting_user_info',

initialize: function() {

gs.log("@@@ DEBUG::iSolutions_triage_processing::INIT Entered tenant is ["+current.u_logged_for+"]"); // // priorities 1-4 are indexes 0-3 // P1 - Critical P2 - High P3 - Moderate P4 - Low this.priorities = [{impact:1, urgency:1},{impact:1, urgency:2},{impact:2, urgency:2},{impact:3, urgency:3}];

this.GenericRequest = 'Generic Request'; this.SignPostingRequest = 'Signposting Request'; this.CallServiceOfferingName = 'SERVICELINE'; this.CallDefaultServiceID = 'CMDB145419'; // less likely to change than the name this.CallDefaultServiceReference = this.getcmdb_ciRefFromID(this.CallDefaultServiceID);

},

// // return the Triage team for the tenantName // _getTenantTriageTeam: function(tenantName) { var result = ; try { var tenant = new IT_Tenants_Table_Utils(); result = tenant.GetTriageTeam(tenantName);

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'getTenantTriageTeam' message[" + err.message + "]"); }

return result; },

// // This (private) method allows the triage team reference to be tested for each tenant // _TenantsTeamTESTS: function() { var result = ; try { var tenant = new IT_Tenants_Table_Utils();

result = tenant.GetTriageTeam('IT'); var gr = new GlideRecord('sys_user_group'); if (gr.get(result)) { gs.log("IT triage group is [" + result + "] name["+gr.name +"]"); }

result = tenant.GetTriageTeam('hr'); if (gr.get(result)) { gs.log("HR triage group is [" + result + "] name["+gr.name +"]"); }

result = tenant.GetTriageTeam('Finance'); if (gr.get(result)) { gs.log("Finance, triage group is [" + result + "] name["+gr.name +"]"); }

// logged for 'Research and Innovation Services' maps to name 'ris' result = tenant.GetTriageTeam('Research and Innovation Services'); if (gr.get(result)) { gs.log("Research and Innovation Services, triage group is [" + result + "] name["+gr.name +"]"); } // logged for 'Student Services Centre' maps to name 'ssc' result = tenant.GetTriageTeam('Student Services Centre'); if (gr.get(result)) { gs.log("Student Services Centre, triage group is [" + result + "] name["+gr.name +"]"); } // logged for 'Wessex' maps to 'wessex' result = tenant.GetTriageTeam('Wessex'); if (gr.get(result)) { gs.log("Wessex, triage group is [" + result + "] name["+gr.name +"]"); }

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'TenantsTeamTESTS' message[" + err.message + "]"); } },

// // Tenant Processing. // This method is used to process Calls that are created for tenants other than IT. // it's sole purpose is to create the Incident record with the correct assignment group. // // this routine will be called by business rule in response to a 'raise a new issue' record producer action // anything logged for IT will land in IT's call queue. everything else should go through here. // tenantProcessing: function() { try { //gs.log("@@@ DEBUG:: Tenant Processing Entered. tenant is ["+current.u_logged_for+"]");

current.call_type='incident'; // the call-type for the call this._incident();

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'tenantProcessing' message[" + err.message + "]"); } },

// // This method is the dispatcher for submitted Call records. // This process is performed as part of an update business rule. // Processing is based on call_type. // processing: function() {

try { //gs.log("@@@ DEBUG:: Processing Entered. tenant is ["+current.u_logged_for+"]");

// // Do no re-processing of call records that have a NON BLANK transfered_to field. // this field is used to indicate the record is inactive. as there is no active flag. // var transferred = current.transferred_to+; if(transferred !=) { return; }

// // dispatch on call type // if(current.call_type=='NonITQuery_HangUp') { //gs.log("@@@ dispatched to Non IT Query / Hang_up"); this._NonITQuery_HangUp(); } else if(current.call_type=='incident_first_time_fix') { //gs.log("@@@ dispatched to incident_first_time_fix"); this._incident_first_time_fix(); } else if(current.call_type=='incident') { //gs.log("@@@ dispatched to incident"); this._incident(); } else if(current.call_type=='junk_email') { //gs.log("@@@ dispatched to Junk_email"); this._junk_email(); } else if(current.call_type=='Reopen') { //gs.log("@@@ dispatched to Reopen"); this._re_open(); } else if(current.call_type=='update') { //gs.log("@@@ dispatched to update"); this._update(); } else if(current.call_type=='chase') { //gs.log("@@@ dispatched to chase"); this._chase(); } else if(current.call_type=='request') { //gs.log("@@@ dispatched to request"); this._request(); } else if(current.call_type=='request_first_time_fulfilment') { //gs.log("@@@ dispatched to request_first_time_fix"); this._requestFirstTimeFix(); } else { gs.log("@@@ 'iSolutions_triage_processing' method 'processing' encountered an unsupported call type = {"+ current.call_type + "} not dispatched"); }

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'processing' message=" + err.message); }


},

// // // _incident: function() { try { gs.log("@@@ iSolutions_triage_processing::_incident processing."); var call_agent = current.u_call_agent+; var inc = this._createBasicIncident();

inc.u_logged_for = current.u_logged_for; inc.caller_id = current.u_called_for; inc.u_location_of_issue = current.u_location_of_issue; inc.u_room_of_issue = current.u_room_of_issue; inc.u_contact_number_of_issue = current.u_contact_number;

if(current.u_logged_for == 'IT') { inc.assignment_group = current.u_support_group;

this.assignRelatedFields(inc, current.u_service_offering); inc.u_action_code = current.u_action_code;

if(current.u_hold_type=='not_required') { // default to assigned state inc.state = this._inc_Assigned; gs.log("@@@ DEBUG:: Incident assigned."); } else { if(current.u_hold_type=='on_hold') { // place on hold until the date given. inc.due_date = current.u_hold_until; inc.state = this._inc_On_Hold; gs.log("@@@ DEBUG:: Placing Incident on hold."); } else if(current.u_hold_type=='awaiting_user_info') { // place on hold until the date given. inc.u_prompt_after = current.u_hold_until; inc.state = this._inc_Awaiting_User_Info; gs.log("@@@ DEBUG:: Placing Incident on hold."); } }

if( inc.assignment_group == this._getTenantTriageTeam(current.u_logged_for) && inc.assignment_group == this.getPrimaryGroup() ) { inc.assigned_to = call_agent; }

if(current.u_parent_incident.toString().trim() != ) { inc.parent_incident = current.u_parent_incident; } if(current.u_problem_id.toString().trim() != ) { inc.problem_id = current.u_problem_id; } this._assignPriority(inc);

} else { // default init for non-it tenant. inc.assignment_group = this._getTenantTriageTeam(current.u_logged_for); inc.state = this._inc_Assigned; gs.log("@@@ DEBUG:: Non-IT, Incident assigned."); }

var inc_sys_id = inc.insert(); this.copyAffectedCIs(inc_sys_id);

// // Copy any attachments on the Call form. // GlideSysAttachment.copy(current.getTableName(), current.sys_id, 'incident', inc_sys_id);


// update call record before it's written to the DB current.transferred_to = inc_sys_id;

if(current.u_logged_for == 'IT') { var transURL = 'incident.do?sys_id=' + inc_sys_id; gs.addInfoMessage(current.number + gs.getMessage(" transferred to ") + ": <a href='" + transURL + "'>" + current.transferred_to.getDisplayValue() + "</a>"); } } catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'incident' message=" + err.message); } },

_incident_first_time_fix: function() {

try { gs.log("@@@ iSolutions_triage_processing::incident first time fix processing.");

var call_agent = current.u_call_agent +; var inc = this._createBasicIncident(); inc.caller_id = current.u_called_for;

inc.assignment_group = this.getPrimaryGroup(); inc.assigned_to = call_agent;


this.assignRelatedFields(inc, current.u_service_offering); inc.u_action_code = current.u_action_code; inc.u_location_of_issue = current.u_location_of_issue; inc.u_room_of_issue = current.u_room_of_issue; inc.u_contact_number_of_issue = current.u_contact_number; if(current.u_parent_incident.toString().trim() != ) { inc.parent_incident = current.u_parent_incident; } if(current.u_problem_id.toString().trim() != ) { inc.problem_id = current.u_problem_id; } this._assignPriority(inc); inc.state = this._inc_Assigned;

var inc_sys_id = inc.insert(); this.copyAffectedCIs(inc_sys_id);

// // Copy any attachments on the Call form. // GlideSysAttachment.copy(current.getTableName(),current.sys_id, 'incident', inc_sys_id);

// // Having inserted the record, we need to update // the reference we have, as it is out of date // due to business rules etc. Then we can add the // resolve changes. // var incDB = new GlideRecord('incident'); incDB.addQuery("sys_id", inc_sys_id); incDB.query(); if(incDB.next()) { incDB.state = this._inc_Resolved; this.assignCloseFields(incDB); incDB.close_notes = current.u_resolution_to_customer; incDB.close_code = current.u_close_code; incDB.resolved_by = call_agent; incDB.active = false; incDB.update(); }

// update call record before it's written to the DB current.transferred_to = inc_sys_id; var transURL = 'incident.do?sys_id=' + inc_sys_id; gs.addInfoMessage(current.number + gs.getMessage(" transferred to ") + ": <a href='" + transURL + "'>" + current.transferred_to.getDisplayValue() + "</a>"); } catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'incident_first_time_fix' message=" + err.message); } },

_re_open: function() {

try { //gs.log("@@@ iSolutions_triage_processing::re-open processing."); var called_for = current.u_called_for.getRefRecord().name; var caller = current.caller.getRefRecord().name; record = current.u_ticket_reference.getRefRecord();

var intro = caller; if(called_for != caller) { intro = caller + " called on behalf of " + called_for; } record.state = this._inc_In_Progress; if(current.contact_type=='email') { record.comments = current.short_description + "\n" + current.description; } else { record.comments = intro + " - " + current.short_description + "\n" + current.description; } var notes = current.u_notes + ""; if(notes.trim()!=) record.work_notes += "\nCall Notes: " + notes.trim();

// // Copy any attachments on the Call form. // GlideSysAttachment.copy(current.getTableName(), current.sys_id, record.getTableName(), record.sys_id);

record.update(); gs.eventQueue("incident.reopened", record, gs.getUserID(), gs.getUserName() );

current.transferred_to = current.u_ticket_reference; current.u_service_offering = this.getcmdb_ciRef(this.CallServiceOfferingName); } catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 're_open' message=" + err.message); } },

_chase: function() {

try { //gs.log("@@@ iSolutions_triage_processing::chase processing.");

var called_for = current.u_called_for.getRefRecord().name; var caller = current.caller.getRefRecord().name; record = current.u_ticket_reference.getRefRecord();

var intro = caller; if(called_for != caller) { intro = caller + " called on behalf of " + called_for; }

record.comments = intro + " - " + current.short_description + "\n" + current.description;

var chased = record.u_chased; chased +=1;

record.u_chased = chased; if(chased <2) { gs.eventQueue("incident.chased", record, gs.getUserID(), gs.getUserName()); } else { gs.eventQueue("incident.chasedManager", record, gs.getUserID(), gs.getUserName()); }

var notes = current.u_notes + ""; if(notes.trim()!=) record.work_notes = "Call Notes: " + notes.trim();

// // Copy any attachments on the Call form. // GlideSysAttachment.copy(current.getTableName(), current.sys_id, record.getTableName(), record.sys_id);


record.update();

current.transferred_to = current.u_ticket_reference; current.u_service_offering = this.getcmdb_ciRef(this.CallServiceOfferingName);

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'chase' message=" + err.message); } },

_update: function() {

try { //gs.log("@@@ iSolutions_triage_processing::update processing.");

var called_for = current.u_called_for.getRefRecord().name; var caller = current.caller.getRefRecord().name; record = current.u_ticket_reference.getRefRecord();

var intro = caller; if(called_for != caller) { intro = caller + " called on behalf of " + called_for; } record.comments = intro + " - " + current.short_description + "\n" + current.description; var notes = current.u_notes + ""; if(notes.trim()!=) record.work_notes = "Call Notes: " + notes.trim();


// // Copy any attachments on the Call form. // GlideSysAttachment.copy(current.getTableName(), current.sys_id, record.getTableName(), record.sys_id);

record.update();

current.transferred_to = current.u_ticket_reference; current.u_service_offering = this.getcmdb_ciRef(this.CallServiceOfferingName);

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'update' message=" + err.message); } },

_junk_email: function() {

try { gs.log("@@@ iSolutions_triage_processing::junk_email processing.");

var inc = this._createBasicIncident(); inc.assignment_group = this.getPrimaryGroup(); inc.assigned_to = current.u_call_agent+; inc.resolved_by = current.u_call_agent+; inc.close_code = 'junk mail'; inc.close_notes = "Resolved by Call agent."; inc.impact = 3; // low inc.urgency = 3; // low inc.priority = 4; // low inc.state = this._inc_Closed; inc.cmdb_ci = this.CallDefaultServiceReference; inc.active = false; var inc_sys_id = inc.insert();

// update call record before it's written to the DB current.transferred_to = inc_sys_id; current.u_service_offering = this.CallDefaultServiceReference;

gs.log(current.number + " transferred to: " + current.transferred_to.getDisplayValue());

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'junk_mail' message=" + err.message); } },

// A.K.A request first time fulfilment _requestFirstTimeFix: function() { try{ // preserve the original short and long description. (used for email source processing) var shortDesc = current.short_description; var desc = current.description; var call_agent = current.u_call_agent+;

var req = this._createBasicRequest(); req_sysid = req.insert();

// // Having inserted the record, we need to update // the reference we have, as it is out of date // due to business rules etc. Then we can add the // resolve changes. // var reqDB = new GlideRecord('sc_request'); reqDB.addQuery("sys_id", req_sysid); reqDB.query(); if(reqDB.next()) { reqDB.stage = 'closed_complete'; reqDB.state = this._req_Closed_Complete; }

var ritm = this._createBasicRequestItem(req_sysid, this.GenericRequest);

ritm.assignment_group = this.getPrimaryGroup(); ritm.assigned_to = call_agent; this._assignPriority(ritm);

ritm.state = this._req_Work_In_Progress; ritm.cmdb_ci = current.u_service_offering; // add 2017-04-07 RJU ritm.u_contact_number_of_issue = current.u_contact_number; ritm.u_room_of_issue = current.u_room_of_issue;

this.assignRelatedFields(ritm, current.u_service_offering); ritm.u_action_code = current.u_action_code; // Hard coded value? field is not visible so not set

var ritm_sysid = ritm.insert(); this.copyAffectedCIs(ritm_sysid);

// // Copy any attachments on the Call form. // GlideSysAttachment.copy(current.getTableName(),current.sys_id, 'sc_req_item', ritm_sysid);

current.transferred_to = ritm_sysid;

var ritmDB = new GlideRecord('sc_req_item'); ritmDB.addQuery("sys_id", ritm_sysid); ritmDB.query(); if(ritmDB.next()) { ritmDB.state = this._req_Closed_Complete; //ritmDB.close_code = '###'; // RJU - needs to be action_code when re-worked ritmDB.resolved_by = call_agent;

ritmDB.work_notes = "Contact details:\nLocation: " + current.u_location_of_issue + "\nRoom: " + current.u_room_of_issue + "\nContact number: " + current.u_contact_number + "\n\n call notes:" + current.u_notes;

ritmDB.close_notes = 'Closing RITM THIS IS A PLACE HOLDER'; if(current.contact_type=='email') { current.u_notes = "Short description (email subject):" + shortDesc + "\n Description (Email content):" + desc + "\n\ncall notes:" + current.u_notes; } }

ritmDB.update();

reqDB.update();

var transURL = 'sc_req_item.do?sys_id=' + ritm_sysid; gs.addInfoMessage(current.number + gs.getMessage(" transferred to ") + ": <a href='" + transURL + "'>" + current.transferred_to.getDisplayValue() + "</a>");

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'requestFirstTimeFix' message=" + err.message); } },

_request: function() { try{ if(current.u_no_form_available==false) { //gs.log("@@@ DEBUG:: request: incident selection. nFA["+current.u_no_form_available+"]"); this._requestIncidentLike(); } else { //gs.log("@@@ DEBUG:: request: RITM selection. nFA["+current.u_no_form_available+"]"); this._requestGenericRITM(); } } catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'request' message=" + err.message); } },


_requestGenericRITM: function() { try{ // preserve the original short and long description. (used for email source processing) var shortDesc = current.short_description; var desc = current.description; var call_agent = current.u_call_agent +;

var req = this._createBasicRequest(); req_sysid = req.insert();

// // Having inserted the record, we need to update // the reference we have, as it is out of date // due to business rules etc. Then we can add the // resolve changes. // var reqDB = new GlideRecord('sc_request'); reqDB.addQuery("sys_id", req_sysid); reqDB.query(); if(reqDB.next()) { reqDB.stage = 'closed_complete'; reqDB.state = this._req_Closed_Complete; }

var ritm = this._createBasicRequestItem(req_sysid, this.GenericRequest);

ritm.assignment_group = current.u_support_group; if( ritm.assignment_group == this._getTenantTriageTeam(current.u_logged_for) && ritm.assignment_group == this.getPrimaryGroup() ) { ritm.assigned_to = call_agent; } // else don't assign

this._assignPriority(ritm);

// assign default state ritm.state = this._req_Work_In_Progress; ritm.cmdb_ci = current.u_service_offering; // add 2017-04-07 RJU ritm.u_contact_number_of_issue = current.u_contact_number; ritm.u_room_of_issue = current.u_room_of_issue;

// if hold state requested assign RITM state if(current.u_hold_type != this._call_hold_type_not_required) { //gs.log("@@@ DEBUG::iSolutions_triage_processing:: Assign on hold state. current.u_hold_type["+current.u_hold_type+"]"); ritm.state = this._ritm_state_on_hold;

if(current.u_hold_type == this._call_hold_type_on_hold) { //gs.log("@@@ DEBUG::iSolutions_triage_processing:: Assign on hold."); ritm.u_on_hold_reason = this._ritm_hold_agreed;

} else if(current.u_hold_type == this._call_hold_type_awaiting_user) { //gs.log("@@@ DEBUG::iSolutions_triage_processing:: Assign awaiting User info"); ritm.u_on_hold_reason = this._ritm_hold_awaiting_user; } else gs.log("@@@ DEBUG::iSolutions_triage_processing:: UNKNOWN Hold type. ["+current.u_hold_type+"]");

ritm.u_prompt_after = current.u_hold_until; } //else gs.log("@@@ DEBUG::iSolutions_triage_processing:: No hold required");

var ritm_sysid = ritm.insert(); this.copyAffectedCIs(ritm_sysid);

// // Copy any attachments on the Call form. // GlideSysAttachment.copy(current.getTableName(),current.sys_id, 'sc_req_item', ritm_sysid);

current.transferred_to = ritm_sysid;

var ritmDB = new GlideRecord('sc_req_item'); ritmDB.addQuery("sys_id", ritm_sysid); ritmDB.query(); if(ritmDB.next()) { ritmDB.work_notes = "Contact details:\nLocation: " + current.u_location_of_issue + "\nRoom: " + current.u_room_of_issue + "\nContact number: " + current.u_contact_number + "\n\n call notes:" + current.u_notes;

if(current.contact_type=='email') { current.u_notes = "Short description (email subject):" + shortDesc + "\n Description (Email content):" + desc + "\n\ncall notes:" + current.u_notes; } } ritmDB.update();

reqDB.update();

var transURL = 'sc_req_item.do?sys_id=' + ritm_sysid; gs.addInfoMessage(current.number + gs.getMessage(" transferred to ") + ": <a href='" + transURL + "'>" + current.transferred_to.getDisplayValue() + "</a>");

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'requestGenericRITM' message=" + err.message); } },

_requestIncidentLike: function() { try { // preserve the original short and long description. (used for email source processing) var shortDesc = current.short_description; var desc = current.description; var call_agent = current.u_call_agent +;

//gs.log("@@@ iSolutions_triage_processing::requestIncidentLike processing."); var URLBuilder = new iSolutionsCallRequestURL(); var formURL = URLBuilder.getFullURL(current.u_catalog); var formName = URLBuilder.getFormName(current.u_catalog);

var req = this._createBasicRequest(); var req_sysid = req.insert();

var ritm = this._createBasicRequestItem(req_sysid, this.SignPostingRequest); ritm.assignment_group = this.getPrimaryGroup(); ritm.assigned_to = call_agent; this._assignPriority(ritm); ritm.state = this._req_Work_In_Progress;

var notes = current.u_notes + ""; // ensure string // don't add empty notes if(notes.trim()!=) ritm.work_notes = "Call Notes: " + notes.trim();

// // preserve original email or agent typed details // //if(current.contact_type=='email') { // current.u_notes = "Short description (email subject):" + shortDesc + "\n Description (Email content):" + desc + "\n\ncall notes:" + current.u_notes; //} else { // current.u_notes = "Short description (orig.):" + shortDesc + "\n Description (orig.):" + desc + "\n\ncall notes:" + current.u_notes; //}

ritm.short_description = "Sign Posting Request - " + shortDesc; ritm.description = "Signposting Request for form: '" +formName + "' - URL: " + formURL + "\n\nContent: " + desc + "\n"; // // Having inserted the request, we need to update // the reference we have, as it is out of date // due to business rules etc. Then we can add the // resolve changes. // var reqDB = new GlideRecord('sc_request'); reqDB.addQuery("sys_id", req_sysid); reqDB.query(); if(reqDB.next()) { reqDB.stage = 'closed_complete'; reqDB.state = this._req_Closed_Complete; // reqDB.close_code = 'Signposting'; // RJU - not on the table needs to use Action_code when re-worked. reqDB.resolved_by = call_agent; }

// now insert the requested item record ritm_sysid = ritm.insert(); this.copyAffectedCIs(ritm_sysid);

// // Copy any attachments on the Call form. // GlideSysAttachment.copy(current.getTableName(),current.sys_id, 'sc_req_item', ritm_sysid);

current.transferred_to = ritm_sysid; current.u_service_offering = this.getcmdb_ciRef(this.CallServiceOfferingName);

// then update the requested item close states var ritmDB = new GlideRecord('sc_req_item'); ritmDB.addQuery("sys_id", ritm_sysid); ritmDB.query(); if(ritmDB.next()) { ritmDB.state = this._req_Closed_Complete; // ritmDB.close_code = 'Signposting'; // RJU - not on the table needs to use Action_code when re-worked. ritmDB.resolved_by = call_agent;

if(current.u_email_form_to_caller_called_for) { // send email to caller and called_for (if different) //gs.log("@@@ RESULT FORM URL is URL:[" + formURL + "] form name:["+ formName +"]"); gs.eventQueue("iSolutions call request", current, formURL, formName); ritmDB.close_notes = "Call agent sent form link via email to caller/called_for for form:" + formName;

} else if (current.u_complete_for_caller) { // Agent has completed form for caller ritmDB.close_notes = "Call agent completed form ('"+ formName + "') for caller/called_for"; } else { // error: guard very unlikely event ritmDB.close_notes = "AN ERROR occured progressing this request for form ('"+ formName + "') for caller/called_for, No tickbox option selected."; gs.log("@@@ Error in 'iSolutions_triage_processing' method 'requestIncidentLike' no tickbox option selected"); } current.short_description = ritm.short_description; current.description = ritmDB.description; ritmDB.description += "\nGenerated from: " + current.number; ritmDB.update(); }

reqDB.update();

//gs.log("@@@ requestIncidentLike::Call agent is["+current.u_call_agent+"]"); var transURL = 'sc_req_item.do?sys_id=' + ritm_sysid; gs.addInfoMessage(current.number + gs.getMessage(" transferred to ") + ": <a href='" + transURL + "'>" + current.transferred_to.getDisplayValue() + "</a>");

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'requestIncidentLike' message=" + err.message); } },

_NonITQuery_HangUp: function() {

try { //gs.log("@@@ iSolutions_triage_processing::NonITQuery_HangUp processing.");

var inc = this._createBasicIncident(); inc.assignment_group = this.getPrimaryGroup(); inc.assigned_to = current.u_call_agent+; inc.resolved_by = current.u_call_agent+; inc.close_code = 'Hang Up'; inc.impact = 3; // low inc.urgency = 3; // low inc.priority = 4; // low inc.state = this._inc_Closed; inc.cmdb_ci = this.CallDefaultServiceReference; inc.active = false; var inc_sys_id = inc.insert();

// update call record before it's written to the DB current.transferred_to = inc_sys_id; current.u_service_offering = inc.cmdb_ci = this.CallDefaultServiceReference;

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'NonITQuery_HangUp' message=" + err.message); } },

// // helper routines below here //

// // Assign the three priority related fields (Priority,impact,Urgency) based // on the selected priority. // This method will work with task or any table extended from it. // _assignPriority: function(iref) { try { // select based on Priority var index = current.u_priority-1; // index iref.impact = this.priorities[index].impact; iref.urgency = this.priorities[index].urgency; iref.priority = current.u_priority; return; } catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'assignPriority' message=" + err.message); } },

// // copyAffectedCIs // // This method creates a new task_ci table entries for any affected CI's listed on the call form. // copyAffectedCIs: function(inc_sys_id) { try {

var rec = new GlideRecord('u_m2m_calls_configuration_items'); rec.addQuery('u_call', current.sys_id); rec.query();

while(rec.next()) { //gs.log("@@@ DEBUG:: @@@ Call Number {" + rec.u_call.number +"}, Configuration Items u_number{" + rec.u_configuration_item.u_number + "}, Name {" + rec.u_configuration_item.name + "}");

var task_ci = new GlideRecord('task_ci'); task_ci.initialize(); task_ci.applied = false; task_ci.task = inc_sys_id; task_ci.ci_item = rec.u_configuration_item; task_ci.insert();

} } catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'copyAffectedCIs' message=" + err.message);

}

}, // // Complete fields based on the service selected. // This methods works with task and any table extended from it. // Only Incident currently supports the two support_group fields. // assignRelatedFields: function(record, service) { try { record.cmdb_ci = service; var csi = new GlideRecord('u_csi_master_table'); csi.addQuery('u_configuration_item', service); csi.query(); if (csi.next()) { //gs.log("@@@ DEBUG assignRelatedFields has valid record from u_csi_master_table"); record.category = csi.u_category_value; record.subcategory = csi.u_subcategory_value; record.u_item = csi.u_item_value; } if(record.getTableName()=='incident') { record.u_support_group = this.getServiceSupportGroup(service); record.u_user_support_group = this.getServiceUserSupportGroup(service); }

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'assignRelatedFields' message=" + err.message);

} },

getServiceSupportGroup: function(service) { var result = ""; try { var cca = new GlideRecord('cmdb_ci_appl'); cca.addQuery('sys_id', service); cca.query(); if (cca.next()) {

result = cca.support_group; //gs.log("@@@ DEBUG assignRelatedFields has valid record. service is ["+service+"] record["+cca.support_group+"]"); }

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'getServiceSupportGroup' message=" + err.message); } return result; },

getServiceUserSupportGroup: function(service) { var result = ""; try { var cca = new GlideRecord('cmdb_ci_appl'); cca.addQuery('sys_id', service); cca.query(); if (cca.next()) {

result = cca.u_user_support_group; //gs.log("@@@ DEBUG assignRelatedFields has valid record. service is ["+service+"] record["+cca.user_support_group+"]"); }

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'getServiceUserSupportGroup' message=" + err.message); } return result; },

assignCloseFields: function(record) { try { record.u_close_category = record.category; record.u_close_subcategory = record.subcategory; record.u_close_item = record.u_item;

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'assignCloseFields' message=" + err.message);

} }, // // // getcmdb_ciRef: function(recordname) { try { var rec = new GlideRecord('cmdb_ci'); rec.addQuery('name',recordname); rec.query(); if(rec.next()) { //gs.log("@@@ DEBUG:: found sys_id {" + rec.sys_id + "} for recordname={"+recordname+"}"); return rec.sys_id; } } catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'getcmdb_ciRef' message=" + err.message); } return ""; },

// // // getcmdb_ciRefFromID: function(CMDB_ID) { try { var rec = new GlideRecord('cmdb_ci'); rec.addQuery('u_number',CMDB_ID); rec.query(); if(rec.next()) { //gs.log("@@@ DEBUG:: found sys_id {" + rec.sys_id + "} for recordname={"+recordname+"}"); return rec.sys_id; } } catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'getcmdb_ciRefFromID' message=" + err.message); } return ""; },

// // Get the Primary Group of the current user // getPrimaryGroup: function() { var user = new GlideRecord('sys_user'); user.get(gs.getUserID()); return user.u_primary_group; },

// // // getAssignmentGroupRef: function(recordname) { try { var rec = new GlideRecord('sys_user_group'); rec.addQuery('name',recordname); rec.query(); if(rec.next()) { //gs.log("@@@ DEBUG:: found sys_id {" + rec.sys_id + "} for recordname={"+recordname+"}"); return rec.sys_id; } } catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'getAssignmentGroupRef' message=" + err.message); } return ""; },

// // Creates an incident and adds the standard data // _createBasicIncident: function() { var cbi = null; try { cbi = new GlideRecord('incident'); cbi.initialize(); cbi.opened_at = current.opened_at; cbi.short_description = current.short_description; cbi.description = current.description; cbi.u_logged_by = current.u_call_agent; cbi.caller_id = current.u_called_for; cbi.opened_by = current.caller;

cbi.contact_type = current.contact_type; var notes = current.u_notes + ""; if(notes.trim()!=) cbi.work_notes = "Call Notes: " + notes.trim();

var public_notes = current.u_public_notes+""; if(public_notes.trim()!=) cbi.comments = public_notes.trim();

if (current.caller != current.u_called_for) { cbi.watch_list = this._addToWatchList(cbi.watch_list, current.caller); }

// guest email to watchlist, if supplied. if(!current.u_guest_email.nil()) { var ge = current.u_guest_email + ""; ge = ge.trim(); if(ge!=) { gs.log("@@@ createBasicIncident:: adding guest email to watch list ge["+ge+"] guest["+current.u_guest_email+"]"); cbi.watch_list = this._addToWatchList(cbi.watch_list, ge); } } } catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'createBasicIncident' message=" + err.message); }

return cbi; },

_addToWatchList: function(wlst, user) { try {

var watch_list = wlst; if(watch_list!="") { watch_list = watch_list + "," + user; } else { watch_list = user; }

return watch_list; } catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method '_addToWatchList' message=" + err.message); }

},

// template for getting Catalog from name still TBD getCatalogGUIDFromName: function(catName){ try { //var rec = new GlideRecord(); //rec.addQuery('name',catName); //rec.query(); //if(rec.next()) { // gs.log("@@@ DEBUG:: found sys_id {" + rec.sys_id + "} for catName={"+catName+"}"); // return rec.sys_id; //}

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'getCatalogGUIDFromName' message=" + err.message); }

return ; },

// template to getting category from name still TBD getCategoryGUIDFromName: function(catalogGUID, catName){ try { //var rec = new GlideRecord(); //rec.addQuery('name',catName); //rec.query(); //if(rec.next()) { // gs.log("@@@ DEBUG:: found sys_id {" + rec.sys_id + "} for catName={"+catName+"}"); // return rec.sys_id; //}

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'getCategoryGUIDFromName' message=" + err.message); }

return ; },

// // Find the named application GUID returns empty string if not found. // getApplicationGUIDFromName: function(AppName) { try { var rec = new GlideRecord('sys_scope'); rec.addQuery('name',AppName); rec.query(); if(rec.next()) { gs.log("@@@ DEBUG:: found sys_id {" + rec.sys_id + "} for AppName={"+AppName+"}"); return rec.sys_id; }

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'getApplicationGUIDFromName' message=" + err.message); }

return ; },

// // Find the GUID for the named form. // it may be neccesary to add catalog and category filters // if the form name is also available in other catalogs. // getFormGUIDFromName: function(formName) { try { //var catalog = getCatalogGUIDFromName('IT Catalog'); //var category = getCategoryGUIDFromName(catalog, 'IT General Forms'); var rec = new GlideRecord('sc_cat_item'); rec.addQuery('name',formName); //rec.addQuery('sc_catalogs', catalog); //rec.addQuery('category', category); rec.query(); if(rec.next()) { gs.log("@@@ DEBUG:: found sys_id {" + rec.sys_id + "} for formName={"+formName+"}"); return rec.sys_id; }

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'getFormGUIDFromName' message=" + err.message); } }, // // Creates an request and adds the standard data. // THIS method does not commit to the DB. // _createBasicRequest: function() { var cbr = null; try { cbr = new GlideRecord('sc_request'); cbr.initialize(); cbr.requested_for = current.u_called_for;

} catch(err) { gs.log("@@@ Exception in 'iSolutions_triage_processing' method 'createBasicRequest' message=" + err.message); }

return cbr; },

// // Creates an request item and adds the standard data. and assigns it to the provided // request record. Does NOT commit to DB // _createBasicRequestItem: function(reqGUID, formName) { var cbri = null; try { cbri = new GlideRecord('sc_req_item'); cbri.initialize(); cbri.short_description = current.short_description; cbri.description = current.description; //cbri.requested_for = current.u_call_agent; cbri.requested_for = current.u_called_for; cbri.opened_by = current.caller; cbri.contact_type = current.contact_type;

cbri.cat_item = this.getFormGUIDFromName(formName); //cbri.application = this.getApplicationGUIDFromName('Global'); cbri.parent = reqGUID; cbri.request = reqGUID; // var notes = current.u_notes + ""; // ensure string // don't add empty notes // if(notes.trim()!=) cbri.work_notes = "Call Notes: " + notes.trim();

var public_notes = current.u_public_notes+""; if(public_notes.trim()!=) cbri.comments = public_notes.trim();

if (current.caller != current.u_called_for) { var watchlist = cbri.watch_list; if(watchlist!="") { cbri.watch_list = watchlist + "," + current.caller; } else { cbri.watch_list = current.caller; } }

// guest email to watchlist, if supplied. if(!current.u_guest_email.nil()) { var ge = current.u_guest_email + ""; ge = ge.trim(); if(ge!=) { gs.log("@@@ createBasicRequestItem:: adding guest email to watch list ge["+ge+"] guest["+current.u_guest_email+"]"); cbri.watch_list = this._addToWatchList(cbri.watch_list, ge); } }

} catch(err) { gs.log("@@@ Exception in iSolutions_triage_processing::createBasicRequestItem message[" + err.message + "]"); }

return cbri; },

type: 'iSolutions_triage_processing' }; </syntaxhighlight> back to top

Reports by Email

[edit | edit source]

<syntaxhighlight lang="javascript"> /* // Example usage var current = new GlideRecord('x_uno49_rep_email_reports_by_email'); //current.query(); //current.next(); current.get('5360bdf56fff3a00dc90c7642e3ee402'); var template = {print: function(msg) { gs.print(msg); } }; (function(current, template) { var report = current.report.getRefRecord();

gs.print(report.filter);

gs.include('x_uno49_rep_email.ReportsByEmail');

new x_uno49_rep_email.ReportsByEmail(current).print(template);

gs.print(new x_uno49_rep_email.ReportsByEmail(current).getRecipients()); })(current, template);

  • /

var ReportsByEmail = Class.create(); ReportsByEmail.prototype = {

   initialize: function(id) {

if (id.sys_id) { this.report_by_email = id; } else { var report_by_email = new GlideRecord('x_uno49_rep_email_reports_by_email'); if (!report_by_email.get(id)) { gs.error('Error getting x_uno49_rep_email_reports_by_email:' + id); } this.report_by_email = report_by_email; }

   },

/* * Called by the scheduled job to execute the report. */ execute: function() { var report_by_email = this.report_by_email;

if (report_by_email.report.nil()) { gs.error('Cannot execute report by email without a report.'); return; // should never happen }

var report = report_by_email.report.getRefRecord();

// get the report's result set var gr = new GlideRecord(report.table); gr.addEncodedQuery(report.filter); gr.query();

if (gr.getRowCount() == 0 && report_by_email.omit_if_no_records) { return; }

var email_addresses = this.getRecipients();

gs.eventQueue('x_uno49_rep_email.send_report', report_by_email, email_addresses.join(',')); },

/* * Apply a function to this report and all children */ applyToChildren: function(f) { this._applyToChildren(f, []); },

_applyToChildren: function(f, stack) { var report_by_email = this.report_by_email; stack.push(report_by_email.sys_id + );

f(report_by_email);

// find child reports var child = new GlideRecord(report_by_email.getTableName()); child.addActiveQuery(); child.addQuery('include_with', report_by_email.sys_id); child.orderBy('order'); child.query(); while(child.next()) { // protect against circular references for(var i=0; i<stack.length; ++i) { if (stack[i] == child.sys_id) { gs.error('Circular reference detected in ' + child.getTableName() + ':' + child.sys_id); return stack; } } new ReportsByEmail(child)._applyToChildren(f, stack); } },

/* * Get the list of addresses to additionally send the report to */ getRecipients: function() { var self = this;

var recipients = [];

if (!this.report_by_email.address_list.nil()) { recipients = this.report_by_email.address_list.split(/\s*,\s*/); }

var f = function(report_by_email) { if (report_by_email.cc_field.nil()) { return; }

var report = report_by_email.report.getRefRecord();

var _gr = new GlideAggregate(report.table); _gr.addEncodedQuery(report.filter); _gr.groupBy(report_by_email.cc_field); _gr.query(); while(_gr.next()) { var address = _gr.getValue(report_by_email.cc_field); if (address != ) { recipients.push(address); } } };

this.applyToChildren(f);

return recipients; },

/* * Print the report to a template object (typically an email) * @param template object Exposes the print() function * @param email object Email object passed to notification email scripts * @param email_action * @param event GlideRecord The event triggering the notification */ print: function(template, email, email_action, event) { // allow the user to override the sender as part of the report by email if (email !== undefined && !this.report_by_email.from.nil()) { email.setFrom(this.report_by_email.getValue('from')); } else if (email !== undefined) { email.setFrom(gs.getProperty('soton.email.donotreply', 'donotreply@soton.ac.uk')); }

var f = function(report_by_email) { if (report_by_email.report.nil()) { return; // should never happen }

var report = report_by_email.report.getRefRecord();

// get the report's result set var gr = new GlideRecord(report.table); gr.addEncodedQuery(report.filter); gr.query();

// if no matching records, don't print this part of the report out if (gr.getRowCount() == 0 && report_by_email.omit_if_no_records) { return; }

// get the report's columns var field_list = report.field_list.split(',');

// use a macro as it provides a better interface for rendering HTML var macro = new GlideRecord('sys_ui_macro'); macro.get('21c925356fff3a00dc90c7642e3ee4ac'); // Reports by Email var xml = macro.xml;

// execute the macro with the result set var jr = new global.JellyRunner(); var content = jr.run(xml, null, { gr: gr, field_list: field_list });

template.print(report_by_email.report_body); template.print(content); };

this.applyToChildren(f); },

   type: 'ReportsByEmail'

}; </syntaxhighlight> back to top

Service Portal

[edit | edit source]

Sort Categories in Service Portal

[edit | edit source]

<syntaxhighlight lang="javascript">

data.categories.sort(function(a, b) {var stringA = a.label.toUpperCase();var stringB = b.label.toUpperCase();return (stringA < stringB) ? -1 : (stringA > stringB) ? 1 : 0;});

</syntaxhighlight> back to top

Pop-up confirmation dialog

[edit | edit source]

<syntaxhighlight lang="javascript"> function onSubmit() {

     return confirm("Please confirm");

} </syntaxhighlight>

Styles

[edit | edit source]

Add paperclip icon in list view if there's an attachment

[edit | edit source]

Table - ES Interactions

Field name - Number

Value

javascript:hasAttachment(); function hasAttachment(){var gr= new GlideRecord('sys_attachment'); gr.addQuery('table_sys_id',current.getValue('sys_id')); gr.query();if(gr.hasNext()){gs.info(hasAttachment); return true; }}

Style

background-image: url('images/icons/attachment.gifx');
background-repeat: no-repeat;
background-position: 98% 5px;
padding-right: 30px;

System Properties

[edit | edit source]

System Property 'glide.knowman.create_incident_link' Example =

[edit | edit source]

This was used to auto-assign icidents raised via a knowledge article to 'Knowledge'

incident.do?sys_id=-1&sysparm_query=active=true^contact_type=$⁠[HTML:knowledgeRecord.u_param_knowledge]^contact_type=knowledge^comments=(Created after Knowledge search: 
$[HTML:knowledgeRecord.short_description])&sysparm_stack=knowledge_home_launcher.do

back to top

Useful for grouping records.

Can be set automatically by defining a standard tag with conditions. May need to add the 'Conditions for Labels' related list to the tag creation form. back to top

UI Actions

[edit | edit source]

Delete audit/journal entries

[edit | edit source]

(from servicenowguru.com) UI Action

Name: Delete History Line
Table: History [sys_history_line]
Action name: delete_history_line
Show insert: false
Show update: true
Client: true
Form button: true
Onclick: confirmDelete()
Condition: gs.hasRole(‘admin’)
Script:
<syntaxhighlight lang="javascript">

function confirmDelete(){
   if(confirm('Are you sure you want to permanently delete this history line and all corresponding audit history?\n\nTHIS ACTION CANNOT BE 
UNDONE!')){
      //Call the UI Action and skip the 'onclick' function
      gsftSubmit(null, g_form.getFormElement(), 'delete_history_line'); //MUST call the 'Action name' set in this UI Action
   }
   else{
      return false;
   }
} 

//Code that runs without 'onclick'
//Ensure call to server-side function with no browser errors
if(typeof window == 'undefined')
   deleteHistoryLine(); 

function deleteHistoryLine(){
   var fieldVal = current["new"];
   var fieldName = current.field;
    
   //Query for and delete the 'sys_audit' record
   var aud = new GlideRecord('sys_audit');
   aud.addQuery('documentkey', current.set.id);
   aud.addQuery('fieldname', fieldName);
   aud.addQuery('newvalue', fieldVal);
   aud.query();
   if(aud.next()){
      aud.deleteRecord();
   }
   
   //Query for and delete the 'sys_journal_field' record (if applicable)
   var je = new GlideRecord('sys_journal_field');
   je.addQuery('element_id', current.set.id);
   je.addQuery('element', fieldName);
   je.addQuery('value', fieldVal);
   je.query();
   if(je.next()){
      je.deleteRecord();
   }
   
   //Set redirect and info message for the parent record
   gs.addInfoMessage(current.label + " entry '" + fieldVal + "' deleted.");
   action.setRedirectURL(current.set.getRefRecord());
    
   //Delete the 'sys_history_line' record
   current.deleteRecord();
}

</syntaxhighlight> back to top

Add Button to Form but Display Selectively

[edit | edit source]

Example - Requests & Incidents by This Caller

UI Action Table: Call [new_call] Action: sysverb_insert_and_stay Condition: isAdvancedUI() && current.canCreate()

Script: 
action.setRedirectURL(current);
current.insert();
gs.include('ActionUtils');
var au = new ActionUtils();
au.postInsert(current);

back to top

UI Policy Short Description: Show Display Button if Status Call

When to apply: When Call Tpe is Status call

Scripts:

Execute if true <syntaxhighlight lang="javascript">

function onCondition() {
	var items = $$('BUTTON').each(function(item){
       if(item.innerHTML.indexOf('Incidents by This Caller') > -1){
           item.show();
       }
   });
}

</syntaxhighlight> Execute if false <syntaxhighlight lang="javascript">

function onCondition() {
	var items = $$('BUTTON').each(function(item){
       if(item.innerHTML.indexOf('Incidents by This Caller') > -1){
           item.hide();
       }
   });
}

</syntaxhighlight> back to top

Compose Email Button with Template Choice

[edit | edit source]

Onclick: composeEmailDialog()

Condition: (new <table_name>.CustomEmailTemplate()).canCompose(current)

<syntaxhighlight lang="javascript"> function composeEmailDialog() { // Perform an API query to determine if there is a choice // If there's more than one template, show a dialog to pick // Otherwise, open the email client

var table = g_form.getTableName(); var sys_id = g_form.getUniqueValue();

function openMultiChoice() { var dialog = new GlideDialogWindow('<application name>_email_template_picker'); dialog.setTitle("Choose Email Template");

// blows up if we just specify "table" as the preference name - some kind of bug/conflict dialog.setPreference('sysparm_table', table); dialog.setPreference('sysparm_sys_id', sys_id);

dialog.render(); }

function openSingleChoice() { var url = 'email_client.do?'; url += jQuery.param({ sysparm_table: table, sysparm_sys_id: sys_id, sysparm_target: table, sys_target: table, sys_uniqueValue: sys_id, sys_row: 0, sysparm_encoded_record: , sysparm_stack: 'no' });

popupOpenEmailClient(url); }

var url = [ '/api/<application name>/client_template/list', table, sys_id ].join('/');

jQuery.ajax({ type: 'GET', url: url, dataType: 'json', success: function(data) { if (data.result && data.result.length > 1) { openMultiChoice(); } else { openSingleChoice(); } }, data: {}, async: false });

} </syntaxhighlight> back to top

UI Scripts

[edit | edit source]

Populate Assignment Group based on Assigned To

[edit | edit source]

<syntaxhighlight lang="javascript"> jQuery(function() { if (document.location.pathname != '/sys_user_group_list.do') return; if (!document.location.search.match(/sysparm_view=sys_ref_list/)) return;

var _f = GwtTree2Node.prototype.deferredClickHandler; GwtTree2Node.prototype.deferredClickHandler = function() {

if (this.image.indexOf('user_obj') != -1) { // set the assigned-to var sys_id = this.sys_id; var display_value = this.text; top.window.opener.g_form.setValue('assigned_to', sys_id, display_value); }

_f.call(this); }; }); </syntaxhighlight> back to top

Date Validation

[edit | edit source]

Class to support date validation (uses momentjs).

This script is quite complex because ServiceNow's behaviour between platform and portal is different and - on portal - it triggers change on both the user's entered value and the computed date (i.e. 1/1 becomes 01-01-2018). For good measure Portal also emits another change of oldValue=newValue but we ignore that in the Client Script. <syntaxhighlight lang="javascript"> var SotonDateValidation = function(g_form) { this.g_form = g_form; this._formats = [ g_user_date_format.toUpperCase(), 'DDMMYYYY', 'DDMMYY', 'DD/MM/YYYY', 'DD/MM/YY', 'DD-MM-YY', 'DD-MM-YYYY' ]; };

SotonDateValidation.prototype = { parse: function(value) { var date = moment(value, this._formats); return date.format() == 'Invalid date' ?  : date; },

/* * Validate two dates that represent a range (start-end) * @param {String} start_name Name of start variable * @param {String} end_name Name of end variable * @param {String} change_name Name of the variable that is changing (to add message to) * @param {Object} params Parameters for the comparison, otherwise defaults to isBefore * @return {boolean} Whether the values are valid */ validateRange: function(start_name, end_name, change_name, params) { console.log('validateRange'); var g_form = this.g_form;

g_form.hideFieldMsg(change_name, true);

if (!params) { params = {}; }

var start_value = g_form.getValue(start_name); var end_value = g_form.getValue(end_name);

var start_date = this.parse(start_value); var end_date = this.parse(end_value);

var start_label = g_form.getLabel(start_name); var end_label = g_form.getLabel(end_name);

// need two dates to validate if (start_date == || end_date == ) { return false; }

var days = end_date.diff(start_date, 'days');

var err = ;

// start after end if (!start_date.isBefore(end_date)) { err = start_label + " must come before " + end_label; } // not enough days before end else if (params.minimum && days < params.minimum) { err = "Must be at least " + params.minimum + " days between " + start_label + " and " + end_label; } // too many days before end else if (params.maximum && days > params.maximum) { err = "Based on " + start_label + ", " + end_label + " can be no later than " + start_date.add(params.maximum, 'days').format(this._formats[0]); } if (err) { // allow portal to finish its re-valuing setTimeout(function() { g_form.setValue(change_name, ); g_form.hideFieldMsg(change_name, true); g_form.showFieldMsg(change_name, err, "error"); }, 0); return false; }

return true; },

validate: function(name, newValue) { var g_form = this.g_form;

g_form.hideFieldMsg(name, true);

var date = moment(newValue, this._formats); var user_format = date.format(g_user_date_format.toUpperCase());

if (user_format == 'Invalid date') { // allow portal to finish its re-valuing setTimeout(function() { g_form.setValue(name, ); g_form.hideFieldMsg(name, true); g_form.showFieldMsg(name, "Enter date in format " + g_user_date_format.toUpperCase(), "error"); }, 0); return false; }

// date not in default format, re-assign it if (newValue != user_format) { // allow portal to finish its re-valuing setTimeout(function() { g_form.setValue(name, user_format); }, 0); return false; }

return true; } }; </syntaxhighlight> back to top

UI Pages

[edit | edit source]

Note: UI pages/Jelly appears to work with encoded queries only.

Page to display duplicate records from a table

[edit | edit source]
Called from a module with link type URL (from parameters)
parameter: ./ui_page.do?sys_id=<sys_id>

<syntaxhighlight lang="javascript"> <?xml version="1.0" encoding="utf-8" ?> <j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">

 <body>    
Enabling Services Duplicate Customer List
   
   <g2:evaluate jelly="true">    
       var ciRec = new GlideAggregate("x_uno49_enabl_svc_customer");    
       ciRec.addAggregate('COUNT', 'user');    
       ciRec.groupBy('user');    
       ciRec.addHaving('COUNT', 'user', '>', '1');          
       ciRec.query();    
       ciRec;    
   </g2:evaluate>    
 <j2:while test="$[ciRec.next()]">    
 <j2:set var="jvar_ci_link" value="x_uno49_enabl_svc_customer.do?sys_id=$[ciRec.sys_id]"/>
 <j2:set var="jvar_ci_list_link" value="x_uno49_enabl_svc_customer.do?sysparm_query=user=$[ciRec.user]"/>
   </j2:while>    
Customer NameCount
<a href="$[jvar_ci_list_link]" class="linked" style="padding-right:10px;">$[ciRec.user.name]</a>$[ciRec.getAggregate('COUNT', 'user')]
   
 </body>    

</j:jelly> </syntaxhighlight> back to top

Page to display records where customer does not match customer on parent record

[edit | edit source]

<syntaxhighlight lang="javascript"> <?xml version="1.0" encoding="utf-8" ?> <j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">

 <body>    



Enabling Services Interaction/Folder Customer Mismatch

   
   <g2:evaluate jelly="true" object="true">

var gr = new GlideRecord('x_uno49_enabl_svc_interaction'); gr.addEncodedQuery('customerNSAMEASparent.ref_x_uno49_enabl_svc_case.customer'); gr.query(); gr; </g2:evaluate> <j2:if test="$[gr.getRowCount() == 0]"> No mismatched records found. </j2:if>

   <j2:while test="$[gr.next()]">

<j2:if test="$[gr.customer != gr.parent.customer && gr.customer != && gr.parent.customer != && gr.parent.customer != undefined]"> Interaction $[gr.getDisplayValue('number')] ($[gr.getDisplayValue('customer')]) is in folder $[gr.getDisplayValue('parent.number')] ($[gr.getDisplayValue('parent.customer')])
</j2:if>

   </j2:while>    
 </body>    

</j:jelly> </syntaxhighlight> back to top

UI Policies

[edit | edit source]

UI Policy Example

[edit | edit source]

<syntaxhighlight lang="javascript">

function onCondition() {
g_form.setValue('u_response_complete', 'true');
}

</syntaxhighlight>

UI Policy Script to place text under a field

[edit | edit source]

<syntaxhighlight lang="javascript">

function onCondition() {
  // Display a message under the Other field to explain what to put in the
  // Other field.
  g_form.showFieldMsg('u_other','Briefly explain what you need.','info');
}

</syntaxhighlight> back to top

UI Policy Script Example - Hiding/Displaying Fields

[edit | edit source]

<syntaxhighlight lang="javascript">

function onCondition() {
	g_form.setMandatory('u_on_hold_expiry', true);	
	g_form.setDisplay('u_on_hold_expiry', true);
		if(g_form.getValue('request_state') == 'on_hold_other'){
		g_form.setMandatory('u_on_hold_reason', true);
		g_form.setDisplay('u_on_hold_reason', true);
	}
}

</syntaxhighlight>


<syntaxhighlight lang="javascript">

function onCondition() {
	g_form.setDisplay('u_on_hold_expiry', true);
	g_form.setMandatory('u_on_hold_expiry', true);
	g_form.setMandatory('u_on_hold_reason', false);
	g_form.setDisplay('u_on_hold_reason', false);
}

</syntaxhighlight>

Make type read only for type Minor change requests

[edit | edit source]

Check URL for parameter sysparm_template

When to Apply: Type IS Minor

<syntaxhighlight lang="javascript">

 function onCondition() {
   var myParm = getParmVal('sysparm_template');
   if (myParm != "") {
     g_form.removeOption('type', 'Standard');
     g_form.setReadOnly(type, true);
   }

   function getParmVal(name) {
     var url = document.URL.parseQuery();
     if (url[name]) {
       return decodeURI(url[name]);
     } else {
       return "";
     }
   }
 }

</syntaxhighlight> back to top

Restrict a field to a specific group or members of Assignment Group

[edit | edit source]

<syntaxhighlight lang="javascript">

function onCondition() { //Is currently logged on user a member of Assginment Group var isMember = false;

   var usrID = g_user.userID; //Get current user ID
   var assignmentGroup = new GlideRecord('sys_user_grmember');
   assignmentGroup.addQuery('assignmentGroup', g_form.getUniqueValue('assignnment_group'));
   assignmentGroup.addQuery('user', usrID);

assignmentGroup.query(assignmentGroupMemberCallback);

function assignmentGroupMemberCallback(assignmentGroup) {

       //If user is a member of selected group
       if (assignmentGroup.next()) {
           isMember = true;

} }

   //Check to see if assigned to is a member of selected group
   var grpName = 'Security Operations';
   //var usrID = g_user.userID; //Get current user ID
   var grp = new GlideRecord('sys_user_grmember');
   grp.addQuery('group.name', grpName);
   grp.addQuery('user', usrID);
   grp.query(groupMemberCallback);
   function groupMemberCallback(grp) {
       //If user is a member of selected group
       if (grp.next()) {
           g_form.setDisplay('u_restricted', true);
           g_form.setReadOnly('u_restricted', false);
       } else {
           if (g_form.isNewRecord() || isMember) {
               g_form.setDisplay('u_restricted', true);
               g_form.setReadOnly('u_restricted', false);
           } else {
               g_form.setDisplay('u_restricted', false);
               g_form.setReadOnly('u_restricted', true);
           }
       }
   }

}

</syntaxhighlight>

back to top

UI Policy scripts to make all change request fields non-mandatory when 'state' is Draft

[edit | edit source]

Written in response to a requirement for partially completed change request forms to be saved.

If the 'state' is changed to 'Draft' then all fields are made non-mandatory so the form can be saved.

When changed back to 'Open' or 'Pending' then fields are made mandatory depending on related answers.

When to apply:

'Request state' is one of (Pending, Open, Work in Progress).

Scripts

When true: <syntaxhighlight lang="javascript"> function onCondition() {

g_form.setMandatory('requested_by', true); g_form.setMandatory('u_tested_by_requester_', true); g_form.setMandatory('assigned_to', true); g_form.setMandatory('u_tested_by_requester_', true); g_form.setMandatory('assigned_to', true); g_form.setMandatory('u_cost_effectiveness', true); g_form.setMandatory('u_hazard_rating', true); g_form.setMandatory('u_latest_time_for_backout', true); g_form.setMandatory('u_point_of_no_return', true); g_form.setMandatory('u_requested_for', true); g_form.setMandatory('u_requested_for', true); g_form.setMandatory('short_description', true); g_form.setMandatory('u_change_benefits', true); g_form.setMandatory('u_service_affected', true); g_form.setMandatory('u_backout_contact', true); g_form.setMandatory('u_delayed_live_proving', true); g_form.setMandatory('u_service_affected', true); g_form.setMandatory('category', true); g_form.setMandatory('u_delayed_live_proving', true); g_form.setMandatory('u_will_validation_take_place_w', true); g_form.setMandatory('u_is_communication_required_', true); g_form.setMandatory('u_service_owner', true); g_form.setMandatory('u_acceptable_probability', true); g_form.setMandatory('u_responsible_for_backout_acti', true); g_form.setMandatory('u_service_owner', true); g_form.setMandatory('u_steps_to_prove_successful_ch', true); g_form.setMandatory('u_responsible_for_backout_acti', true); g_form.setMandatory('impact', true); g_form.setMandatory('description', true); g_form.setMandatory('u_proof_change_has_not_affecte', true); g_form.setMandatory('u_testing_of_proof_for_backout', true); g_form.setMandatory('u_proof_change_has_not_affecte', true);

//Schedule tab g_form.setMandatory('start_date', true); g_form.setMandatory('end_date', true); g_form.setDisplay('u_will_implementation_of_this_', true); g_form.setMandatory('u_will_implementation_of_this_', true);

if(g_form.getValue('u_will_implementation_of_this_') != 'Yes'){ g_form.setMandatory('u_timing_justification', false); g_form.setDisplay('u_timing_justification', false); } else { g_form.setMandatory('u_timing_justification', true); g_form.setDisplay('u_timing_justification', true);

}

//Change and Test Plan tab g_form.setMandatory('implementation_plan', true); g_form.setDisplay('implementation_plan', true);

g_form.setMandatory('u_tested_by_requester_', true); var testedState = g_form.getValue('u_tested_by_requester_'); switch(testedState){ case 'Yes': g_form.setDisplay('test_plan', true); g_form.setMandatory('test_plan', true); break; case 'No - tested by other': g_form.setDisplay('u_person_responsible_for_testi', true); g_form.setMandatory('u_person_responsible_for_testi', true); break; case 'Not tested': g_form.setDisplay('u_justification_for_no_testing', true); g_form.setMandatory('u_justification_for_no_testing', true); break; } //end switch

g_form.setMandatory('u_steps_to_prove_successful_ch', true);

g_form.setMandatory('u_proving_carried_out_by_assig', true); if(g_form.getValue('u_proving_carried_out_by_assig') == 'No'){ g_form.setMandatory('u_person_undertaking_the_provi', true); }

g_form.setMandatory('u_provide_proof_that_changes_h', true);

g_form.setMandatory('u_validation_carried_out_by_as', true); if(g_form.getValue('u_validation_carried_out_by_as') == 'No'){ g_form.setMandatory('u_validator', true); }

g_form.setMandatory('u_period_of_monitoring', true);


//Impact Assessment tab g_form.setMandatory('u_similar_change_new', true); g_form.setMandatory('u_resilience_dr_comp_new', true); g_form.setMandatory('u_risks_of_impementing_this_ch', true);

//Any permanent impact to any of the below? g_form.setMandatory('u_will_there_be_any_permanent_', true); if ('u_will_there_be_any_permanent_' == 'Yes'){ g_form.setMandatory('u_impact___performance__availa', true); g_form.setMandatory('u_detail_performance__availabi', true); g_form.setMandatory('u_impact___security', true); g_form.setMandatory('u_detail_security_impact', true); g_form.setMandatory('u_impact___capacity', true); g_form.setMandatory('u_detail_capacity_impact', true); g_form.setMandatory('u_impact___backup__restore', true); g_form.setMandatory('u_detail_backup__restore_impac', true); g_form.setMandatory('u_impact___other', true); g_form.setMandatory('u_detail_any_impact_from_this_', true); }

//Will alerts be placed into maintenance mode? g_form.setMandatory('u_will_alerts_be_placed_into_m', true); if ('u_will_alerts_be_placed_into_m' == 'No'){ g_form.setMandatory('u_automated_alerts_expected', true); g_form.setMandatory('u_detail_expected_alerts', true); }

//Are changes required to any of the below docs? g_form.setMandatory('u_documentation_updates', true); if ('u_documentation_updates' != 'No'){ g_form.setMandatory('u_moniroting_changes', true); if('u_moniroting_changes' == 'Yes'){ g_form.setMandatory('u_changes_to_monitoring_system', true); } g_form.setMandatory('u_cmdb_changes', true); if('u_cmdb_changes' == 'Yes'){ g_form.setMandatory('u_detail_any_changes_to_cmdb', true); } g_form.setMandatory('u_service_map_changes_', true); if('u_service_map_changes_' == 'Yes'){ g_form.setMandatory('u_service_map_change_detail', true); } g_form.setMandatory('u_service_catalogue_changes_', true); if('u_service_catalogue_changes_' == 'Yes'){ g_form.setMandatory('u_catalogue_change_detail', true); } g_form.setMandatory('u_are_changes_required_to_reco', true); if('u_are_changes_required_to_reco' == 'Yes'){ g_form.setMandatory('u_detail_any_changes_to_recove', true); } } else { g_form.setMandatory('u_moniroting_changes', false); g_form.setMandatory('u_cmdb_changes', false); g_form.setMandatory('u_service_map_changes_', false); g_form.setMandatory('u_service_catalogue_changes_', false); g_form.setMandatory('u_are_changes_required_to_reco', false); g_form.setDisplay('u_moniroting_changes', true); g_form.setDisplay('u_cmdb_changes', true); g_form.setDisplay('u_service_map_changes_', true); g_form.setDisplay('u_service_catalogue_changes_', true); g_form.setDisplay('u_are_changes_required_to_reco', true);

}

//Communications Plan Tab

g_form.setMandatory('u_is_communication_required_', true); var commsChoice = g_form.getValue('u_is_communication_required_'); switch(commsChoice){ case 'Communications managed by M&C': g_form.setMandatory('u_which_customers_need_to_be_i', true); g_form.setMandatory('u_communication_to_customers', true); g_form.setMandatory('u_impact_on_users', true); g_form.setMandatory('u_when_impacted', true); g_form.setMandatory('u_user_action', true); g_form.setMandatory('u_date_for_comms', true); break; case 'Communication from Other': g_form.setMandatory('u_person_responsible_for_execu', true); g_form.setMandatory('u_which_customers_need_to_be_i', true); g_form.setMandatory('u_communication_to_customers', true); g_form.setMandatory('u_impact_on_users', true); g_form.setMandatory('u_when_impacted', true); g_form.setMandatory('u_user_action', true); g_form.setMandatory('u_date_for_comms', true); g_form.setDisplay('u_person_responsible_for_testi', true); g_form.setMandatory('u_person_responsible_for_testi', true); break; case 'Not required': g_form.setDisplay('u_justifcation_for_no_communic', true); g_form.setMandatory('u_justifcation_for_no_communic', true); break; } //end switch


//Backout Plans Tab g_form.setMandatory('backout_plan', true); g_form.setMandatory('u_risks_with_backout', true); g_form.setMandatory('u_proof_for_successful_backout', true);

} </syntaxhighlight>


When false:

<syntaxhighlight lang="javascript"> function onCondition() {

g_form.setMandatory('requested_by', false); g_form.setMandatory('u_tested_by_requester_', false); g_form.setMandatory('assigned_to', false); g_form.setMandatory('u_tested_by_requester_', false); g_form.setMandatory('assigned_to', false); g_form.setMandatory('u_cost_effectiveness', false); g_form.setMandatory('u_hazard_rating', false); g_form.setMandatory('u_latest_time_for_backout', false); g_form.setMandatory('u_point_of_no_return', false); g_form.setMandatory('u_requested_for', false); g_form.setMandatory('u_requested_for', false); g_form.setMandatory('short_description', false); g_form.setMandatory('u_change_benefits', false); g_form.setMandatory('u_service_affected', false); g_form.setMandatory('u_backout_contact', false); g_form.setMandatory('u_delayed_live_proving', false); g_form.setMandatory('u_service_affected', false); g_form.setMandatory('category', false); g_form.setMandatory('u_delayed_live_proving', false); g_form.setMandatory('u_will_validation_take_place_w', false); g_form.setMandatory('u_is_communication_required_', false); g_form.setMandatory('u_service_owner', false); g_form.setMandatory('u_acceptable_probability', false); g_form.setMandatory('u_responsible_for_backout_acti', false); g_form.setMandatory('u_service_owner', false); g_form.setMandatory('u_steps_to_prove_successful_ch', false); g_form.setMandatory('u_responsible_for_backout_acti', false); g_form.setMandatory('impact', false); g_form.setMandatory('description', false); g_form.setMandatory('u_proof_change_has_not_affecte', false); g_form.setMandatory('u_testing_of_proof_for_backout', false); g_form.setMandatory('u_proof_change_has_not_affecte', false);

//Schedule tab g_form.setMandatory('start_date', false); g_form.setMandatory('end_date', false); g_form.setMandatory('u_will_implementation_of_this_', false); //g_form.setDisplay('u_timing_justification', true); g_form.setMandatory('u_timing_justification', false); //g_form.setDisplay('u_timing_justification', false); g_form.setMandatory('Implementation Steps', false); g_form.setMandatory('Validation carried out by assignee', false);

//Change and Test Plan tab g_form.setMandatory('test_plan', false); g_form.setMandatory('implementation_plan', false); g_form.setMandatory('u_person_responsible_for_testi', false); g_form.setMandatory('u_justification_for_no_testing', false); g_form.setMandatory('u_steps_to_prove_successful_ch', false); g_form.setMandatory('u_proving_carried_out_by_assig', false); g_form.setMandatory('u_person_undertaking_the_provi', false); g_form.setMandatory('u_provide_proof_that_changes_h', false); g_form.setMandatory('u_period_of_monitoring', false); g_form.setMandatory('u_validation_carried_out_by_as', false); g_form.setMandatory('u_validator', false);


//Impact Assessment tab

g_form.setMandatory('u_impact_assesment', false); g_form.setMandatory('u_similar_change_new', false); g_form.setMandatory('u_resilience_dr_comp_new', false); g_form.setMandatory('u_risks_of_impementing_this_ch', false); g_form.setMandatory('u_will_there_be_any_permanent_', false); g_form.setMandatory('u_impact___performance__availa', false); g_form.setMandatory('u_detail_performance__availabi', false); g_form.setMandatory('u_impact___security', false); g_form.setMandatory('u_detail_security_impact', false); g_form.setMandatory('u_impact___capacity', false); g_form.setMandatory('u_detail_capacity_impact', false); g_form.setMandatory('u_impact___backup__restore', false); g_form.setMandatory('u_detail_backup__restore_impac', false); g_form.setMandatory('u_impact___other', false); g_form.setMandatory('u_detail_any_impact_from_this_', false); g_form.setMandatory('u_will_alerts_be_placed_into_m', false); g_form.setMandatory('u_automated_alerts_expected', false); g_form.setMandatory('u_detail_expected_alerts', false); g_form.setMandatory('u_documentation_updates', false); g_form.setMandatory('u_moniroting_changes', false); g_form.setMandatory('u_changes_to_monitoring_system', false); g_form.setMandatory('u_cmdb_changes', false); g_form.setMandatory('u_detail_any_changes_to_cmdb', false); g_form.setMandatory('u_service_map_changes_', false); g_form.setMandatory('u_service_map_change_detail', false); g_form.setMandatory('u_service_catalogue_changes_', false); g_form.setMandatory('u_catalogue_change_detail', false); g_form.setMandatory('u_are_changes_required_to_reco', false); g_form.setMandatory('u_detail_any_changes_to_recove', false);

//Communications Plan Tab g_form.setMandatory('u_is_communication_required_', false); g_form.setMandatory('u_person_responsible_for_execu', false); g_form.setMandatory('u_which_customers_need_to_be_i', false); g_form.setMandatory('u_communication_to_customers', false); g_form.setMandatory('u_impact_on_users', false); g_form.setMandatory('u_when_impacted', false); g_form.setMandatory('u_user_action', false); g_form.setMandatory('u_date_for_comms', false); g_form.setDisplay('u_person_responsible_for_testi', false); g_form.setMandatory('u_person_responsible_for_testi', false); g_form.setMandatory('u_justifcation_for_no_communic', false); g_form.setDisplay('u_justifcation_for_no_communic', true); //Backout Plans Tab g_form.setMandatory('backout_plan', false); g_form.setMandatory('u_risks_with_backout', false); g_form.setMandatory('u_proof_for_successful_backout', false); } </syntaxhighlight> back to top

Redirect to catalog item URL

[edit | edit source]

<syntaxhighlight lang="javascript"> function onCondition() {

   var url = top.window.location.href; //identifies the URL that is displaying the catalog item
   if (url.indexOf('/sp') != -1) {
       g_form.modified = false;

g_navigation.open('https://autoprotectdev.service-now.com/sp?id=sc_cat_item&sys_id=3053c4251b3c9550b8c68449d34bcbc1&sysparm_category=52310e2e1b2b0c1091fadc66bd4bcb1c&catalog_id=e0d08b13c3330100c8b837659bba8fb4');

       //top.window.location.href = 'https://autoprotectdev.service-now.com/sp?id=sc_cat_item&sys_id=3053c4251b3c9550b8c68449d34bcbc1&sysparm_category=52310e2e1b2b0c1091fadc66bd4bcb1c&catalog_id=e0d08b13c3330100c8b837659bba8fb4';
   } else {
       g_form.modified = false;

g_navigation.open('https://autoprotectdev.service-now.com/mesp?id=sc_cat_item&sys_id=3053c4251b3c9550b8c68449d34bcbc1&sysparm_category=52310e2e1b2b0c1091fadc66bd4bcb1c&catalog_id=e0d08b13c3330100c8b837659bba8fb4');

       //top.window.location.href = 'https://autoprotectdev.service-now.com/mesp?id=sc_cat_item&sys_id=3053c4251b3c9550b8c68449d34bcbc1&sysparm_category=52310e2e1b2b0c1091fadc66bd4bcb1c&catalog_id=e0d08b13c3330100c8b837659bba8fb4';
   }

} </syntaxhighlight> back to top

Variable attributes

[edit | edit source]

Used to control which columns are displayed and searchable when selecting from a reference field <syntaxhighlight lang="javascript"> ref_auto_completer=AJAXTableCompleter,ref_ac_columns=first_name;last_name;user_name;department;u_type,ref_ac_columns_search=true,ref_ac_display_value=false </syntaxhighlight>

Variables

[edit | edit source]

Variables can be declared in forms and workflows.

Example use of a variable to populate the subject of an email. <syntaxhighlight lang="javascript"> javascript:current.variables.wnac_phone_number + '@sms.textapp.net' </syntaxhighlight>

Example: Variables used in an email script: <syntaxhighlight lang="javascript"> (function runMailScript(current, template, email, email_action, event) {

var gDate = new GlideDate(); gDate.setValue(current.variables.date_and_time_of_meeting); template.print('This is a reminder of your WNAC meeting on ' + gDate.getByFormat('dd/MM') + ' at ' + gDate.getByFormat('HH:mm') + ' with ' + current.variables.wnac_assessor + ' at ' + current.variables.wnac_assessment_centre + '. If you cannot attend, please reply or call 02380 597233 asap.');

})(current, template, email, email_action, event); </syntaxhighlight> back to top

Variable Sets

[edit | edit source]

Attachments Required

[edit | edit source]

Variable set to check file is attached before allowing submission.

Add an onLoad script (or onChange's) that sets the value of "minimum_attachments".

By default at least one attachment is required.

This Variable Set will enforce that number of attachments are present before submission.

Variables:

attachments_minimum Minimum Number of Attachments Single Line Text
attachments_current Current number of attachments Single Line Text
attachments_message Attachments Error Message Multi Line Text
attachments_current_macro CurrentAttachments_macro Macro

Catalog UI Policy: Hide Visible Variables
attachments_minimum
attachments_current
attachments_message

Catalog Client Scripts: Require Attachments (Portal) <syntaxhighlight lang="javascript"> function onSubmit() { console.log("catalog_script_client.a11b5b624f91d740f53f36e18110c75c Checking for attachments"); var msg = g_form.getValue("soton_attachments_message"); var require = g_form.getValue("soton_attachments_minimum"); var have = g_form.getValue("soton_attachments_current");

if (have < require) { alert(msg); return false; }

// INT4022356: workaround for REST error when a number is submitted, all values must be stringified ["soton_attachments_message", "soton_attachments_minimum", "soton_attachments_current"].forEach(function(name) { g_form.setValue(name, g_form.getValue(name) + ""); });

return true; } </syntaxhighlight> Require Attachments (Platform) <syntaxhighlight lang="javascript"> function onSubmit() { var msg = g_form.getValue("soton_attachments_message"); var require = g_form.getValue("soton_attachments_minimum"); var ele = document.getElementsByClassName("attachment_list_items"); var have = ele.length - 1; // ignore "Manage Attachments" label

if (have < require) { alert(msg); return false; }

return true; } </syntaxhighlight> Macro: Widget: Variable - Attachments
Populate variable current_attachments with the current number of attachments.

Client Controller <syntaxhighlight lang="javascript"> function($scope, $timeout) { /* widget controller */ var c = this; var g_form = $scope.page.g_form; var $cat_scope = angular.element("#sc_cat_item").scope(); if (!$cat_scope) { // not on the catalog item view return; }

$cat_scope.$watch("attachments", function(attachments) { if (!attachments) return; // not initialised yet g_form.setValue("soton_attachments_current", attachments.length+); }); } </syntaxhighlight> back to top

Widgets

[edit | edit source]

Price as Variable

[edit | edit source]

Server script

[edit | edit source]

default empty function (no code)

Client Controller

[edit | edit source]

<syntaxhighlight lang="javascript"> function($scope, $timeout) {

 /* widget controller */
 var c = this;

var g_form = $scope.page.g_form; var $cat_scope = angular.element("#sc_cat_item").scope();

$cat_scope.$watch("data.sc_cat_item.price", function(price) { g_form.setValue('catalog_item_price', price); }); } </syntaxhighlight> Data table Instance [sp_instance]

back to top

Who Am I

[edit | edit source]

UI Page to display node info.

<syntaxhighlight lang="javascript"> <?xml version="1.0" encoding="utf-8" ?> <j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null"> <style> .timingDiv {

   	display: none;  
  	} 

.node_list { width:100%; border: none; cell-spacing: 10px; cell-padding: 10px; vertical-align: top; } .node_list tr{border-bottom: 1px solid #00008B; }

</style> <g:evaluate var="jvar_user_dets" object="true" jelly="true">

 var myid = gs.getUserID();
 var me = new GlideRecord('sys_user');
 me.get(myid);
 var encq = "sys_created_onONToday@javascript:gs.daysAgoStart(0)@javascript:gs.daysAgoEnd(0)^sys_created_by="+me.user_name+"^urlSTARTSWITH/";
 var ns = new GlideRecord('syslog_transaction');
 ns.addEncodedQuery(encq);
 ns.orderByDesc('sys_created_on');
 ns.query();
 ns.next();
 var obj = {userid: me.user_name, username: me.name, node: ns.system_id };	
 obj;

</g:evaluate>

<g:ui_form id="who_am_i">

Current User Summary

<g:evaluate var="jvar_user_id" jelly="true"> var user_id = jelly.jvar_user_dets.userid; user_id; </g:evaluate> <g:evaluate var="jvar_user_name" jelly="true"> var name = jelly.jvar_user_dets.username; name; </g:evaluate> <g:evaluate var="jvar_node_id" jelly="true"> var node_id = jelly.jvar_user_dets.node; node_id; </g:evaluate>
User ID User Name Node
${jvar_user_id} ${jvar_user_name} ${jvar_node_id}
</g:ui_form>

</j:jelly> </syntaxhighlight>

Workflows

[edit | edit source]

Script to set short description and set description to contain RITM and variable details

[edit | edit source]

<syntaxhighlight lang="javascript"> //set a generic short description to help identify the type of request //cycle through sc_req_item and add to description for use on approval requests and purchase order task current.description = 'Items ordered:\n'; //current.requested_for = current.opened_by; var s = ; var item = new GlideRecord('sc_req_item'); item.addQuery('request', current.sys_id); item.query(); while (item.next()) {

   var set = new GlideappVariablePoolQuestionSet();
   set.setRequestID(item.sys_id); // requested item sys_id
   set.load();
   var vs = set.getFlatQuestions();
   for (var i = 0; i < vs.size(); i++) {
       if (vs.get(i).getLabel() !=  && JSUtil.notNil(vs.get(i).getDisplayValue()) && vs.get(i).getDisplayValue() != 'false') {
           var gr = new GlideRecord('item_option_new');
           gr.addQuery('question_text', vs.get(i).getLabel());
           gr.query();
           if (gr.next()) {
               var grPrice = gr.price_if_checked;
               var grPriceWithPrefix = '£' + gr.price_if_checked;
               if (grPrice == '0') {
                   grPriceWithPrefix = ;
               } else {
                   if (/\.\d$/(grPrice)) {
                       grPriceWithPrefix = '£' + gr.price_if_checked + '0';
                   }
               }
               gs.info('Add users to groups grPriceWithPrefix ' + gr.PriceWithPrefix);
               s += '     ' + vs.get(i).getLabel() + ": " + vs.get(i).getDisplayValue() + "  " + grPriceWithPrefix + "\n";
           }
       }
   }

current.description += '\n ' + item.cat_item.getDisplayValue() + ' - ' + item.cat_item.short_description.getDisplayValue();

   if (item.cat_item.price.getDisplayValue() != '0') {
       current.description += ' : ' + item.cat_item.price.getDisplayValue() + '\n ';
   } else {
       current.description += '\n ';
   }
   if (s) {
       current.description += 'Options:' + '\n' + s;
   }

}

   if (current.price.getDisplayValue() != '0') {
       current.description += '\nTotal: ' + current.price.getDisplayValue();
   }
   current.short_description = 'Service catalog request (' + item.cat_item.getDisplayValue() + ') for ' + current.requested_for.getDisplayValue();
   item.requested_for = current.requested_for;
   item.update();

</syntaxhighlight> back to top

Script to find a manager who is a member of senior management for approvals

[edit | edit source]

<syntaxhighlight lang="javascript"> var answer = []; //variable to store the approver to be returned //create an array of exco members var excomembers = []; var grgm = new GlideRecord('sys_user_grmember'); grgm.addQuery('group', '94637df41bcf4910b8c68449d34bcb36'); //exco grgm.query(); while (grgm.next()) {

   excomembers.push(grgm.user.toString());

}

//lookup requested_for user var grmgr = new GlideRecord('sys_user'); grmgr.get(current.requested_for);

var mgr = grmgr.manager; var found = 'false';

//check if manager is a member of exco if (excomembers.indexOf(mgr.toString()) > -1) {

   found = 'true';
   answer.push(grmgr.manager);

} //if not, iterate round loop looking up manager's manager until an exco member is found while (found != 'true') {

   grmgr.get(mgr);
   mgr = grmgr.manager;
   if (excomembers.indexOf(mgr.toString()) > -1) {
       found = 'true';
       answer.push(grmgr.manager);
   }

} </syntaxhighlight> back to top

Script to cancel muliple workflows

[edit | edit source]

https://community.servicenow.com/community?id=community_blog&sys_id=11fca2a5dbd0dbc01dcaf3231f9619f1

Embed font uploaded to sys_attachment by sysid

[edit | edit source]
 <div> //may be needed to trick browser
 @font face {
 font-family: 'Bitter-Regular';
 src: url('/sys_attachment.do?sys_id=287c4c224f133200a83444b18110c7c2');
 }
 </div>

This worked in a template!

<head>
<link href="https://fonts.googleapis.com/css?family=Bitter" rel="stylesheet">
</head>

</syntaxhighlight> back to top

Workflow Update Script

[edit | edit source]

<syntaxhighlight lang="javascript"> var wf = new Workflow(); var ri = new GlideRecord("sc_req_item"); if (ri.get(current.request_item)) {

      wf.runFlows(ri, 'update');   

} </syntaxhighlight> back to top

Start Workflow on Specified Task Record

[edit | edit source]

<syntaxhighlight lang="javascript"> var current = GlideRecord('sc_req_item'); //Get the record you want as a GlideRecord object. current.get('<ritm-sys_id>') //replace this with a sys_id of one of the RITMS you want to use.

//First, we cancel any running workflows. var workflow = new Workflow(); workflow.cancel(current);

var flowID = '592e5b661bd20010917ca9b4bd4bcb86' //This is the sys_id of the workflow you want to attach from the wf_workflow table

//Now we attach the new workflow startWorkflow(flowID, current);


//Helper functions function startWorkflow(id, current) {

  var w = new Workflow();
  var context = w.startFlow(id, current, current.operation(), getVars(current));
  if (context != null) 
  current.context = context.sys_id;

}

//Get the variables to pass to the workflow function getVars(current) {

  var vars = {};
  for (var n in current.variables) 
     vars[n] = current.variables[n];
  
  return vars;

} </syntaxhighlight> back to top

Workflow Script To Determine Departmental Email Address

[edit | edit source]

<syntaxhighlight lang="javascript">

// Set the variable 'answer' to a comma-separated list of group ids or an array of group ids to add as approvers.
//
// For example:
//       var answer = [];
//       answer.push('id1');
//       answer.push('id2');
//
//
// To add a new department add the following clause before the final else clause:
//
// else if (dept.id == '<insert department id>') {
//     group.get('name', '<insert approval group name>');
// }
//

var group = new GlideRecord('sys_user_group'),
    dept = new GlideRecord('cmn_department');

dept.get(current.u_requested_for_department);

if(dept.id == 'IT') {
    group.get('name', 'Idea Request Approval - IT');
} else if (dept.id == 'OVC' || dept.id == 'Office of Vice Chancellor') {
    group.get('name', 'Idea Request Approval - OVC');
} else if (dept.id == 'Estates') {
    group.get('name', 'Idea Request Approval - Estates');
} else if (dept.id == 'F&P' || dept.id == 'FAP' || dept.id == 'FandP') {
    group.get('name', 'Idea Request Approval - Finance');
} else if (dept.id == 'SU' || dept.id == 'SUBU') {
    group.get('name', 'Idea Request Approval - SUBU');
} else if (dept.id == 'Academic Services' || dept.id == 'AS') {
    group.get('name', 'Idea Request Approval - AS');
} else if (dept.id == 'PRIME') {
    group.get('name', 'Idea Request Approval - Prime');
} else if (dept.id == 'PMO') {
    group.get('name', 'Idea Request Approval - PMO');
} else if (dept.id == 'GEHUB') {
    group.get('name', 'Idea Request Approval - GEHUB');
} else if (dept.id == 'HR') {
    group.get('name', 'Idea Request Approval - HR');
} else if (dept.id == 'LS') {
    group.get('name', 'Idea Request Approval - LS');
} else if (dept.id == 'R&KEO' || dept.id == 'RKEO') {
    group.get('name', 'Idea Request Approval - R&KEO');
} else if (dept.id == 'HSC' || dept.id == 'HSS') {
    group.get('name', 'Idea Request Approval - Health & Social Sciences');
} else if (dept.id == 'M&C' || dept.id == 'MAC') {
    group.get('name', 'Idea Request Approval - M&C');
} else if (dept.id == 'CEL') {
    group.get('name', 'Idea Request Approval - CEL');
} else if (dept.id == 'MS' || dept.id == 'Media School' || dept.id == 'FMC') {
    group.get('name', 'Idea Request Approval - MS');
} else if (dept.id == 'Graduate School') {
    group.get('name', 'Idea Request Approval - Graduate School');
} else if (dept.id == 'BS' || dept.id == 'School of Tourism') {
    group.get('name', 'Idea Request Approval - Faculty of Management');
} else if (dept.id == 'Kaplan') {
    group.get('name', 'Idea Request Approval - Kaplan');
} else if (dept.id == 'H&S' || dept.id == 'HandS' || dept.id == 'Health and Safety') {
    group.get('name', 'Idea Request Approval - H&S');
} else if (dept.id == 'Alumni' || dept.id == 'Fundraising') {
    group.get('name', 'Idea Request Approval - Alumni & Fundraising');
} else if (dept.id == 'SSS' || dept.id == 'Student Support Services') {
    group.get('name', 'Idea Request Approval - SSS');
} else if (dept.id == 'SciTech' || dept.id == 'DEC' || dept.id == 'ApSci') {
    group.get('name', 'Idea Request Approval - SciTech');
} else if (dept.id == 'TEST') {
    group.get('name', 'Idea Request Approval - TEST');
} else if (dept.id == 'FM') {
    group.get('name', 'Idea Request Approval - Faculty of Management'); //added AA for INCINC0125618
} else {
    current.comments += 'Approval has not been requested.\n\nThe department this has been requested for does not exist in the workflow 
activity. \n\nPlease contact a Service Now administrator.';
    var workflow = new Workflow();
    workflow.cancelContext(current);  
}

var answer = [];
answer.push(group.sys_id);

</syntaxhighlight> back to top

Approval - user Script

[edit | edit source]

<syntaxhighlight lang="javascript">

var answer = []; //Array to store list of approvers excluding the change requester
var approvalGroups = []; //Array to store list of approval groups 
/****************************************************
Add approval groups to the approvalGroups array below
*****************************************************/
approvalGroups = ['CAB - Digital Solutions', 'CAB - Buisness Information Systems', 'CAB - Windows Desktop', 'CAB - Information Security', 'CAB - Systems Team', 'CAB 

- Communications Team', 'CAB - Service Operations', 'CAB - Governance'];

for (var i = 0; i < approvalGroups.length; i++) { // loop round a number of times equal to the number of elements in the array
  var approvers = new GlideRecord('sys_user_grmember');
  approvers.addQuery('group.name', approvalGroups[i]); //Matches assignment group on change with the group membership table
  approvers.addQuery('user.name', '!=', current.requested_by.getDisplayValue()); //exclude 'requested_by'user 
  approvers.query();
  while (approvers.next()) {
    answer.push(approvers.user.sys_id);
  }
}

</syntaxhighlight> back to top

Approval - lookup approver from cmn_department (recursive)

[edit | edit source]

<syntaxhighlight lang="javascript"> answer = findApproverHHH(current.variables.department);

function findApproverHHH(dept) {

   var rec = new GlideRecord('cmn_department');
   a = [];
   rec.initialize();
   rec.get(dept);

while (!rec.dept_head){ rec.get(rec.parent); } a.push(rec.dept_head + "");

   gs.log("@@@ DEBUG::W/F Visitor HHH approver [" + a[0] + "]");
   return a;

} </syntaxhighlight>

Was previously nested else statements:

<syntaxhighlight lang="javascript"> answer = findApproverHHH(current.variables.department);

function findApproverHHH(dept) {

   var rec = new GlideRecord('cmn_department');
   a = [];
   var p = ;
   rec.initialize();
   rec.get(dept);
   if (rec.dept_head) { //if Head of Department has a value
       a.push(rec.dept_head + "");
   } else { //if Head of Department is not populated get parent record
       p = rec.parent + "";
       rec.initialize();
       rec.get(p);
       if (rec.dept_head) { //if Head of Department has a value
           a.push(rec.dept_head + "");
       } else { //if Head of Department is not populated get parent record
           p = rec.parent + "";
           rec.initialize();
           rec.get(p);
           if (rec.dept_head) { //if Head of Department has a value
               a.push(rec.dept_head + "");
           } else { //if Head of Department is not populated get parent record
               p = rec.parent + "";
               rec.initialize();
               rec.get(p);
               if (rec.dept_head) { //if Head of Department has a value
                   a.push(rec.dept_head + "");
               }
           }
       }
   }
   gs.log("@@@ DEBUG::W/F Visitor HHH approver [" + a[0] + "]");
   return a;

} </syntaxhighlight> back to top

Allocate Stock Workflow Script

[edit | edit source]

<syntaxhighlight lang="javascript"> (function runScript(current) { var computer = new GlideRecord('alm_asset'); if (!computer.get(current.variables.computer_serial_number)) { activity.fault_description = "Invalid or unknown computer."; activity.result = "error"; return; }

// reserve all the monitors var hw = new GlideRecord('alm_asset'); hw.addQuery('sys_id', 'IN', [ current.variables.computer_monitor_1_serial_number, current.variables.computer_monitor_2_serial_number ]); hw.query();

hw.install_status = 6; hw.substatus = 'reserved'; hw.reserved_for = current.request.requested_for; hw.request_line = current.sys_id;

hw.updateMultiple();


// reserve the computer computer.install_status = 6; computer.substatus = 'reserved'; computer.reserved_for = current.request.requested_for; computer.request_line = current.sys_id;

computer.update();


// consume all the consumables var products = []; if (current.variables.standard_wired_keyboard_mouse == 'Required') { products.push('1ea1fccfdb0af340d5c86055ca96198f'); // Keyboard products.push('6281b8cfdb0af340d5c86055ca96199c'); // Mouse } var ca = current.variables.vs_computer_consumables.computer_consumable; for(var i=0; i<ca.length; ++i) { products.push(ca[i]); }

var cs = new GlideRecord('alm_consumable'); var cs2 = new GlideRecord('alm_consumable');

products.forEach(function(id) { cs.initialize(); cs.addQuery('model', id); cs.addQuery('install_status', '6'); // In Stock cs.addQuery('substatus', 'available'); cs.query(); if (cs.next()) { cs2.initialize(); cs2.get(new Consumables().split(cs.sys_id, 1, '10', , computer.sys_id, , , current.request.requested_for)); cs2.request_line = current.sys_id; cs2.update(); } }); })(current); </syntaxhighlight>

Copy attachment to email notification

[edit | edit source]

<syntaxhighlight lang="javascript"> printattachments();

function printattachments() {

   var gr = new GlideRecord('sys_attachment');
   gr.addQuery('table_sys_id', current.sys_id);
   gr.query();
   while (gr.next()) {
       template.print('Attachment: <a href="http://' + gs.getProperty("instance_name") + '.service-now.com/sys_attachment.do?sys_id='
           + gr.sys_id + '">' + gr.file_name + '</a>');
   }

} </syntaxhighlight>

Retrieve variables from a requested item (RITM)

[edit | edit source]

<syntaxhighlight lang="javascript"> current.short_description = 'Service catalog request for ' + current.requested_for.getDisplayValue(); current.description = 'Items ordered:\n'; var s = ; var item = new GlideRecord('sc_req_item'); item.addQuery('request', current.sys_id); item.query(); while (item.next()) { var set = new GlideappVariablePoolQuestionSet();

   set.setRequestID(item.sys_id); // requested item sys_id
   set.load();
   var vs = set.getFlatQuestions();
   for (var i = 0; i < vs.size(); i++) {
       if (vs.get(i).getLabel() !=  && JSUtil.notNil(vs.get(i).getDisplayValue()) && vs.get(i).getDisplayValue() != 'false') {
           var gr = new GlideRecord('item_option_new');
           gr.addQuery('question_text', vs.get(i).getLabel());
           gr.query();
           if (gr.next()) {
               var grPrice = gr.price_if_checked;
           }
           s += '     ' + vs.get(i).getLabel() + ": " + '£'+grPrice + "\n";
       }
   }
   current.description += '\n ' + item.cat_item.getDisplayValue() + ' - ' + item.cat_item.short_description.getDisplayValue() + ' : ' + item.cat_item.price.getDisplayValue() + '\n\n ';
   if (s) {
       current.description += 'Options:' + '\n' + s;
   }
   current.description += '\nTotal: ' + current.price.getDisplayValue();

} </syntaxhighlight></text>

     <sha1>748x067nc20dx9e9oll8z0enarru5as</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
 </page>
 <page>
   <title>Code Examples</title>
   <ns>0</ns>
   <id>31</id>
   <redirect title="MartiNet:Code Examples" />
   <revision>
     <id>128</id>
     <timestamp>2017-06-13T15:51:57Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <comment>CMPadmin moved page Code Examples to CMNotes:Code Examples</comment>
     <text xml:space="preserve" bytes="35">#REDIRECT CMNotes:Code Examples</text>
     <sha1>feskfukrtr0d1f03l76gd2mgk6utunu</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
 </page>
 <page>
   <title>ServiceNow mkii</title>
   <ns>0</ns>
   <id>32</id>
   <revision>
     <id>132</id>
     <timestamp>2017-06-13T15:57:42Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <comment>Created page with "Code Examples"</comment>
     <text xml:space="preserve" bytes="17">Code Examples</text>
     <sha1>gg331o63tcksvjaa2zmgosz0lzfzib2</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
   <revision>
     <id>134</id>
     <parentid>132</parentid>
     <timestamp>2017-06-13T16:02:33Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <text xml:space="preserve" bytes="132">Code Examples

<inputbox>

type=create
width=100
break=no
buttonlabel=Create new article
default=(Article title)
</inputbox></text>
     <sha1>pefzdj7uj50z94p1ooh1xpt4s3nvr5w</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
 </page>
 <page>
   <title>Test Article</title>
   <ns>0</ns>
   <id>33</id>
   <revision>
     <id>135</id>
     <timestamp>2017-06-13T16:02:59Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <comment>Created page with "Test article"</comment>
     <text xml:space="preserve" bytes="12">Test article</text>
     <sha1>jdgcd365cjag9dymxk1dvxdonopfzbt</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
 </page>
 <page>
   <title>JavaScript</title>
   <ns>0</ns>
   <id>34</id>
   <revision>
     <id>187</id>
     <timestamp>2017-07-28T10:10:20Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <comment>Created page with "https://johnresig.com/blog/simple-class-instantiation/"</comment>
     <text xml:space="preserve" bytes="54">https://johnresig.com/blog/simple-class-instantiation/</text>
     <sha1>jkg10v6kmkl3nfcx30o6es8i8dubdk0</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
   <revision>
     <id>193</id>
     <parentid>187</parentid>
     <timestamp>2017-08-01T09:23:46Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <text xml:space="preserve" bytes="718">https://johnresig.com/blog/simple-class-instantiation/

S-Now Background Script to Play With Dates

[edit | edit source]
var myDate = "04/01/2018";
var myDate2 = myDate + " 12:00:00";
gs.info("myDate = " + myDate);
gs.info("myDate2 = " + myDate2);
var gdt = new GlideDateTime(myDate2);
gdt.setDisplayValue(myDate2, "dd/MM/yyyy 12:00:00");
gs.info("Date = " + gdt.getDate());
gs.info("Day = " + gdt.getDayOfMonthLocalTime());
gs.info("Month = " + gdt.getMonthLocalTime());
gs.info("Year = " + gdt.getYearLocalTime());
gdt.addMonthsLocalTime(-3);
gs.info("Date = " + gdt.getDate());
gs.info("Day = " + gdt.getDayOfMonthLocalTime());
gs.info("Month = " + gdt.getMonthLocalTime());
gs.info("Year = " + gdt.getYearLocalTime());</text>
     <sha1>qxl17k9w9oxxzebazt9s36dolnw304c</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
   <revision>
     <id>194</id>
     <parentid>193</parentid>
     <timestamp>2017-08-01T11:29:36Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <minor/>
     <comment>/* S-Now Background Script to Play With Dates */</comment>
     <text xml:space="preserve" bytes="1125">https://johnresig.com/blog/simple-class-instantiation/

S-Now Background Script to Play With Dates

[edit | edit source]
var myDate = "04/01/2018";
var myDate2 = myDate + " 12:00:00";
gs.info("myDate = " + myDate);
gs.info("myDate2 = " + myDate2);
var gdt = new GlideDateTime(myDate2);
gs.info("Numeric date value = " + gdt.getNumericValue());
var gdt2 = gdt.getDate();
//gdt.setDisplayValue(gdt2, "dd/MM/yyyy 12:00:00");
gs.info("Display value = " + gdt.getDisplayValue());
gs.info("Date = " + gdt.getDate());
gs.info("Day = " + gdt.getDayOfMonthLocalTime());
gs.info("Month = " + gdt.getMonthLocalTime());
gs.info("Year = " + gdt.getYearLocalTime());
var myValue = gdt.getNumericValue() - 7776000000;
gdt.setNumericValue(myValue);
gs.info("Numeric date value after subtraction = " + gdt.getNumericValue());
//gdt.addMonthsLocalTime(-3);
gs.info("Date = " + gdt.getDate());
gs.info("Day = " + gdt.getDayOfMonthLocalTime());
gs.info("Month = " + gdt.getMonthLocalTime());
gs.info("Year = " + gdt.getYearLocalTime());
//gdt.setDisplayValue(gdt2, "dd/MM/yyyy 12:00:00");
gs.info("Display value = " + gdt.getDisplayValue());</text>
     <sha1>m2ol1y8an3zmpxq9hu1ifwl2zujxrah</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
   <revision>
     <id>195</id>
     <parentid>194</parentid>
     <timestamp>2017-08-01T11:35:06Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <minor/>
     <comment>/* S-Now Background Script to Play With Dates */</comment>
     <text xml:space="preserve" bytes="1126">https://johnresig.com/blog/simple-class-instantiation/

S-Now Background Script to Play With Dates

[edit | edit source]
var myDate = "04/01/2018";
var myDate2 = myDate + " 12:00:00";
gs.info("myDate = " + myDate);
gs.info("myDate2 = " + myDate2);
var gdt = new GlideDateTime(myDate2);
gdt.setDisplayValue(myDate2, "dd/MM/yyyy 12:00:00");
gs.info("Numeric date value = " + gdt.getNumericValue());
var gdt2 = gdt.getDate();
gs.info("Display value = " + gdt.getDisplayValue());
gs.info("Date = " + gdt.getDate());
gs.info("Day = " + gdt.getDayOfMonthLocalTime());
gs.info("Month = " + gdt.getMonthLocalTime());
gs.info("Year = " + gdt.getYearLocalTime());
var myValue = gdt.getNumericValue() - 7776000000;
gdt.setNumericValue(myValue);
gs.info("Numeric date value after subtraction = " + gdt.getNumericValue());
//gdt.addMonthsLocalTime(-3);
gs.info("Date = " + gdt.getDate());
gs.info("Day = " + gdt.getDayOfMonthLocalTime());
gs.info("Month = " + gdt.getMonthLocalTime());
gs.info("Year = " + gdt.getYearLocalTime());
//gdt.setDisplayValue(gdt2, "dd/MM/yyyy 12:00:00");
gs.info("Display value = " + gdt.getDisplayValue());</text>
     <sha1>nzq0u01n6l9kxhuin8m7tle6ngrtvzo</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
 </page>
 <page>
   <title>Backlog</title>
   <ns>0</ns>
   <id>35</id>
   <revision>
     <id>202</id>
     <timestamp>2017-08-16T09:28:09Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <comment>Created page with "calculate client scripts that don't work."</comment>
     <text xml:space="preserve" bytes="41">calculate client scripts that don't work.</text>
     <sha1>4cf82z9cs2s099b3kyd9hw977r26q4h</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
 </page>
 <page>
   <title>File:BluQube workflow.JPG</title>
   <ns>6</ns>
   <id>36</id>
   <revision>
     <id>270</id>
     <timestamp>2017-11-29T15:36:35Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <comment>example ServiceNow Workflow</comment>
     <text xml:space="preserve" bytes="27">example ServiceNow Workflow</text>
     <sha1>7bltebgzxlchvre33p5zq3xkgzu5lwr</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
 </page>
 <page>
   <title>File:Team development.jpg</title>
   <ns>6</ns>
   <id>37</id>
   <revision>
     <id>273</id>
     <timestamp>2017-11-30T15:39:24Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <text xml:space="preserve" bytes="0" />
     <sha1>phoiac9h4m842xq45sp7s6u21eteeq1</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
 </page>
 <page>
   <title>CMNotes:Code Examples</title>
   <ns>0</ns>
   <id>38</id>
   <revision>
     <id>411</id>
     <timestamp>2018-08-09T14:07:08Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <comment>Created page with "=General= == Checking desktop vs mobile runtime ==  You might want to mark a client script compatible with both Desktop and Mobile but still do something different depending o..."</comment>
     <text xml:space="preserve" bytes="75069">=General=

Checking desktop vs mobile runtime

[edit | edit source]

You might want to mark a client script compatible with both Desktop and Mobile but still do something different depending on the runtime use this: <syntaxhighlight lang="javascript">

 if (window === null)
   // Write your mobile compatible code here
 else
   // Write your desktop compatible code here

</syntaxhighlight> back to top


Coding Best Practices

[edit | edit source]

Coding Best Practices

Writing to the debug log

[edit | edit source]

To write to the debug log in your client-side JavaScript, or UI policies, make a call to the global function jslog().

An example of using jslog() in JavaScript: <syntaxhighlight lang="javascript">

function logData (r ) {
    lastLogDate  = r. responseXML. documentElement. getAttribute ( "last_log_entry" ) ; var items  = r. responseXML. getElementsByTagName ( "log" 
) ;
    jslog ( "response=" + r. responseText ) ; }

</syntaxhighlight> Additionally, when client scripts run, the name of the client script and timing information is displayed. This can be useful in determining which scripts are running and whether they are impacting performance. back to top

ACL - Access Control Lists

[edit | edit source]

ACL script

[edit | edit source]

<syntaxhighlight lang="javascript">

gs.getUser().getRecord().getValue('u_data');
//REQ1128990 - grant write but not create access to IT - A&T groups
if (
	gs.getUser().hasRole("admin") ||
	gs.getUser().hasRole("portfolio_admin") ||
	gs.getUser().isMemberOf("IT - Servers & Storage") || 
	gs.getUser().isMemberOf("IT - Linux") || 
	gs.getUser().isMemberOf("IT - Mac Desktop") || 
	gs.getUser().isMemberOf("IT - Windows Desktop") ||
	gs.getUser().isMemberOf("IT - Applications Team") || 
	gs.getUser().isMemberOf("IT - Digital Solutions") || 
	gs.getUser().isMemberOf("IT - Windows Desktop")) 
{
    answer=true;
}

</syntaxhighlight> back to top

Attributes

[edit | edit source]

Attributes Example (sys_user)

[edit | edit source]

<syntaxhighlight lang="javascript">

ref_auto_completer=AJAXTableCompleter,ref_ac_columns=user_name;department,ref_ac_order_by=name,ref_ac_columns_search=true

</syntaxhighlight> back to top

Background Scripts

[edit | edit source]

Force Autoclose Background Script

[edit | edit source]

<syntaxhighlight lang="javascript">

 var gr = new GlideRecord('sc_request'); //Initialise new GlideRecord.
 gr.addQuery('number', 'REQ1113405').addOrCondition('number', 'REQ1113406').addOrCondition('number', 'REQ1113407').addOrCondition('number', 'REQ1113408'); //Build query
    gr.query(); //run query
    while(gr.next()) {    //Loop round query

      //gs.log(gr.number + ' is automatically closed after ' + pn + ' days');

      if(gr.request_state == 'resolved') {
          gr.request_state = 'closed_resolved';
      } else if(gr.request_state == 'cancelled_converted_to_incident') {
          gr.request_state = 'Closed - Converted to Incident';
      } else if(gr.request_state == 'cancelled_user_unavailable') {
          gr.request_state = 'closed_user_unavailable';
      } else if(gr.request_state == 'cancelled_duplicate') {
          gr.request_state = 'closed_duplicate';
      } else if(gr.request_state == 'cancelled_rejected') {
          gr.request_state = 'closed_rejected';
      } else if(gr.request_state == 'cancelled_other') {
          gr.request_state = 'closed_cancelled';
      }

      gr.work_notes = 'Request (' + gr.number + ') automatically closed by test script.\n';
      gr.active = false;  //Set active field to false
      gr.update();  //update record

    }

</syntaxhighlight> back to top

Search for text in Business Rules

[edit | edit source]

<syntaxhighlight lang="javascript">

findit('string you are searching for');

function findit(str) { 

 var scr = "";
var gr1 = new GlideRecord('sys_script');
gr1.query();
while (gr1.next()) {
  scr = gr1.script.toString();
  if (scr.indexOf(str) > -1) {
    gs.addInfoMessage(gr1.name);
  }
}

}

</syntaxhighlight> back to top

Promote an extended table field to the parent table

[edit | edit source]

<syntaxhighlight lang="javascript"> GlideDBUtil.promoteColumn('table_to_move_from', 'table_to_move_to', 'field_to_move', true); </syntaxhighlight> back to top

Script to update multiple records

[edit | edit source]

<syntaxhighlight lang="javascript" line='line'>

/**
 * Script to change any status '11' asset recrds to status '1' 
 * prior to re-labelling status 11 to 'Being Configured'
 **/
var count = 0; 
 processAssetState();
function processAssetState() {
	var gr = new GlideRecord("alm_asset");
//	gr.setLimit(5);
	gr.addQuery("install_status", "11");
	gr.query()
	while (gr.next()) { 
		gr.install_status = "1";
	    count++ ;	
	gr.update(); 						}
}
gs.info('Number of asset records processed is ' + count);

</syntaxhighlight>


<syntaxhighlight lang="javascript" line='line'>

/**
* Script to change any status '11' asset recrds to status '1' 
* prior to re-labelling status 11 to 'Being Configured'
**/
var count = 0;
// var queryString = "asset_tag=31108^install_status=11";
processAssetState();
 
function processAssetState() {
	var gr = new GlideRecord("alm_asset");
// 	gr.setLimit(1);
	gr.addQuery("install_status", "11");
//	gr.addEncodedQuery(queryString);
	gr.query();
 
	while (gr.next()) { 
		gr.install_status = "1";
	    gs.log('Selected assets: ' + gr.asset_tag + ' Serial number is: ' + gr.serial_number + 'Assigned to is: ' + gr.assigned_to.getDisplayValue());
		count++ ;
 		
// 	gr.update();  job de-activated & update commented out after CHG0044150 23/05/2017 at 15:42
 						}
}
 
gs.info('Number of asset records processed is ' + count);

</syntaxhighlight> back to top

DeleteMultiple Background Script

[edit | edit source]

<syntaxhighlight lang="javascript">

var gr = new GlideRecord('incident');
gr.addQuery('active', false);
gr.deleteMultiple(); //Delete all the queried records

</syntaxhighlight> back to top

Using A Script To Create Tables/Extensions

[edit | edit source]

From: www.servicenowgems.com/2017/08/07/creating-tables-via-script/

Uses out of the box TableDescriptor script include.


Create a copy of an existing table <syntaxhighlight lang="javascript">

copyTable("incident"); //table to copy
function copyTable(tableName) {
var gr = new GlideRecord(tableName);
gr.initialize();
//Get tabledetails
var td = GlideTableDescriptor.get(tableName);
var displayName = td.getDisplayName();
var tLabel = gr.getLabel();
var tName = "u_" + tableName; // If you don't name it with u_ you won't be able to delete it
var creator = new TableDescriptor(tName, tLabel);
//check if this table is an extension
var db = new GlideRecord("sys_db_object");
db.addEncodedQuery("super_classISNOTEMPTY^name=" + tableName);
db.setLimit(1);
db.query();
if (db.next()) {
creator.setExtends(db.super_class + );
}
creator.setFields(gr);
creator.copyAttributes(td);
//copies the security to the new table
creator.setRoles(td);
//Create the table
creator.create();
//copy indexes 
creator.copyIndexes(tableName, tName);
}

Create an extension of an existing table.

createExtension("u_my_new_app", "My new application", "task");
function createExtension(tableName, tableLabel, extends) {
var creator = new TableDescriptor(tableName, tableLabel);
creator.setExtends(extends);
creator.create();
}

</syntaxhighlight> back to top

Search on sys_id

[edit | edit source]

<syntaxhighlight lang="javascript"> findSysID('your mysterious sysid here');

function findSysID(id) {

 var gr = new GlideRecord('sys_db_object');
 gr.addEncodedQuery('super_class=NULL^nameNOT LIKEts_c_^nameNOT LIKEsysx_^nameNOT LIKEv_');
 gr.query();
 var searchTable, name;
 while (gr.next()) {
   name = gr.name + ;
   searchTable = new GlideRecord(name);
   if (searchTable.isValid()) {
     searchTable.addQuery('sys_id', id);
     searchTable.queryNoDomain()
     searchTable.setLimit(1);
     searchTable.query();
     if (searchTable.hasNext()) {
       gs.print('Found on table: ' + name);
     }
   }
 }

} </syntaxhighlight> back to top

Business Rules

[edit | edit source]

Business Rule to Hide Empty Records

[edit | edit source]

Create a Business Rule that runs before insert and contains the following type of script to filter out the unwanted records.

<syntaxhighlight lang="javascript">
current.addEncodedQuery('nameISNOTEMPTY^cmdb_model_categoryISNOTEMPTY'); //Encoded query
</syntaxhighlight>

back to top

Business Rule Examples

[edit | edit source]

<syntaxhighlight lang="javascript">

   (function executeRule(current, previous /*null when async*/) {

   var ci_room = current.u_room;
   var asset_id = current.asset_tag;
   var count = 0;

   var computers = new GlideRecord('alm_hardware');
   //
   // Add the asset_tag to our query to find the record we want in alm_hardware
   computers.addQuery('asset_tag', '=', asset_id );
   // 
   //
   computers.query();
   //
   //Find the matching record in alm_harware , update the CI room field
   //
   while (computers.next()) {
      computers.u_asset_room.setDisplayValue(ci_room);
      computers.update();
      count++;
   }

   })(current, previous);


   (function executeRule(current, previous /*null when async*/) {

   var asset_room = current.u_asset_room;
   var asset_id = current.asset_tag;
   var count = 0;

   var computers = new GlideRecord('cmdb_ci');
   //
   // Add the asset_tag to our query to find the record we want in cmdb_ci
   computers.addQuery('asset_tag', '=', asset_id );
   // 
   //
   computers.query();
   //
   //Find the matching record in cmdb_ci , update the CI room field
   //
   while (computers.next()) {
      computers.u_room.setDisplayValue(asset_room);
      computers.update();
      count++;
   }
   //
   //gs.log('Number of cmdb_ci records processed for room change is ' + count + '\n' + "computers.u_room = " + computers.u_room + '\n' + "computers.u_room.u_location_reference = " + computers.u_room.u_location_reference + "\n" + "Asset room = " + asset_room + "\n" + "Asset ID = " + asset_id);

   })(current, previous);

</syntaxhighlight>

Prevent Closure if Child Task is Active <syntaxhighlight lang="javascript">

(function executeRule(current, previous /*null when async*/ ) {
  //Prevents closing a task if any of the task's child tasks are still active.
  var gr = new GlideRecord('task');
  gr.addQuery('active', 'true');
  gr.addQuery('parent', current.sys_id);
  gr.query();
  if (gr.next()) {
    current.setAbortAction(true);
    gs.addInfoMessage("Unable to save as there are open tasks associated with this change request.");
  }
})(current, previous);

</syntaxhighlight>

Disable Mandatory Field Check On Save

Use in a before insert or update business rule <syntaxhighlight lang="javascript">

g_form.checkMandatory = false;

</syntaxhighlight> Remove yellow background from work notes in email notification Create a 'before' business rule <syntaxhighlight lang="javascript">

(function executeRule(current, previous /*null when async*/) {

  var str=current.body;
  var newStr=str.replaceAll("background-color:LightGoldenRodYellow;", "");
  current.body=newStr;

})(current, previous);

</syntaxhighlight> back to top

Set Contact Type Business Rule

[edit | edit source]

<syntaxhighlight lang="javascript">

 (function executeRule(current, previous /*null when async*/ ) {
   c_user = gs.getUser();

   if (current.contact_type != 'self-service') {
     if (gs.getUser().isMemberOf('IT - Service Desk') || gs.getUser().isMemberOf('IT - User Support Team')) {
       current.contact_type = 'phone';
     } else {
       if (c_user.getDisplayName() == "IT Ambassador") {
         current.contact_type = 'walk-in';
       } else {
         current.contact_type = 'Direct Input';
       }

     }
   }
 })(current, previous);

</syntaxhighlight> back to top

Display Business Rule to set scratchpad variables

[edit | edit source]

<syntaxhighlight lang="javascript"> (function executeRule(current, previous /*null when async*/) {

   g_scratchpad.grp_sd = gs.getUser().isMemberOf('IT - Service Desk'); 
   g_scratchpad.grp_ust = gs.getUser().isMemberOf('IT - User Support Team'); 

})(current, previous); </syntaxhighlight>

back to top

'On insert' Business Rule to omit the change requester from the approvers list

[edit | edit source]

<syntaxhighlight lang="javascript">

(function executeRule(current, previous /*null when async*/) {

if (current.approver.getDisplayValue() == current.sysapproval.requested_by.name) { current.setAbortAction(true);

   }
})(current, previous);

</syntaxhighlight> back to top

Catalog Client Scripts

[edit | edit source]

Catalog Client Script To Make Help Text Visible by Default

[edit | edit source]

<syntaxhighlight lang="javascript">

function onLoad() {
   var myVar = g_form.getControl('caller_id');
   toggleHelp(myVar.id);
}

</syntaxhighlight> back to top

Catalog Client Script To Populate Various User Fields

[edit | edit source]

<syntaxhighlight lang="javascript">

function onChange() {
	
	var user_ref = g_form.getReference('username', setUserID);		
}

function setUserID(user_ref) {
	g_form.setValue('student_id', user_ref.user_name);
	g_form.setValue('email_address', user_ref.email);
	g_form.setValue('contact_number', user_ref.phone);
	
}

</syntaxhighlight> back to top

Catalog Client Script To Check For Empty Fields on Submission

[edit | edit source]

<syntaxhighlight lang="javascript">

function onSubmit() {
//check users have been specified beofre submitting
   var ltype = g_form.getValue('email_group_new');
   var adduser = g_form.getValue('add_email_group_members');
   var remuser = g_form.getValue('remove_email_group_members');
   if (ltype == 'Existing' && adduser == '' && remuser == '') {
       alert('Please enter users to be added and/or removed');
       return false;
   }  
}

</syntaxhighlight> back to top

Populate a form using getReference() synchronous server call

[edit | edit source]

See https://community.servicenow.com/thread/167169

Catalog Client Script <syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading) {
userObject = g_form.getReference('bs_staff_username',setUserInfo);
}
 
function setUserInfo(userObject){
g_form.setValue('bs_contact', userObject.phone);
g_form.setValue('bs_location', userObject.u_room);
//g_form.setValue('u_whatever', userObject.field_on_sys_user); 
}

</syntaxhighlight> Can also use this format: <syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading) {
userObject = g_form.getReference('user_field'); 

g_form.setValue('u_manager_field', userObject.manager);
g_form.setValue('u_last_name', userObject.last_name);
g_form.setValue('u_whatever', userObject.field_on_sys_user); 

}

</syntaxhighlight> or the good old way: <syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading) {  
var id = g_form.getValue('u_first_field');//replace 'u_first_field' with the name of your reference field.  
var user = new GlideRecord('sys_user');  
      user.addQuery('sys_id',id);  
      user.query();  
if ( user.next() ) {  
  g_form.setValue('u_manager_field', user.manager);  
  g_form.setValue('u_last_name', user.last_name);  
  g_form.setValue('u_whatever', user.field_on_sys_user);  
}  
}  

</syntaxhighlight> back to top

Filtering a list collector

[edit | edit source]

<syntaxhighlight lang="javascript"> function onLoad() {

   //Apply a default filter to the list collector variable
   var collectorName = 'name_of_list_collector_variable';
   var filterString = 'eg. active=true';
   
   //Try Service Portal method
   try{
       var myListCollector = g_list.get(collectorName);
       myListCollector.reset();
       myListCollector.setQuery(filterString);
   }
   //Revert to Service Catalog method
   catch(e){
       //Hide the list collector until we've set the filter
       g_form.setDisplay(collectorName, false);
       setCollectorFilter();
   }
   
   function setCollectorFilter(){
       //Test if the g_filter property is defined on our list collector.
       //If it hasn't rendered yet, wait 100ms and try again.
       if(typeof(window[collectorName + 'g_filter']) == 'undefined'){
           setTimeout(setCollectorFilter, 100);
           return;
       }
       //Find and hide the filter elements (optional)
       //Simple method for items with only one list collector
       //$('ep').select('.row')[0].hide();
       //Advanced method for items with more than one list collector (more prone to upgrade failure)
       //var el = $('container_' + g_form.getControl(collectorName).id).select('div.row')[0].hide();
       
       //Reset the filter query
       window[collectorName + 'g_filter'].reset();
       window[collectorName + 'g_filter'].setQuery(filterString);
       window[collectorName + 'acRequest'](null);
       //Redisplay the list collector variable
       g_form.setDisplay(collectorName, true);
   }

} </syntaxhighlight> back to top

Resizing a slushbucket list

[edit | edit source]

<syntaxhighlight lang="javascript"> function onLoad(){

   var varName = 'idrive_name';
   var height = '100'; //Optional
   var width = '450'; //Optional
   try{
       //Get the left and right bucket input elements
       var leftBucket = $(varName + '_select_0');
       var rightBucket = $(varName + '_select_1');
       
       //If the element exists
       if(leftBucket){
           //Adjust the bucket height (default is 300px)
           if(height){
               leftBucket.style.height = height + 'px';
               rightBucket.style.height = height + 'px';
           }
           
           if(width){
               //Adjust the bucket width (default is 340px)
               leftBucket.style.width = width + 'px';
               rightBucket.style.width = width + 'px';
                               //Fix Fuji/Geneva width issue
                               leftBucket.up('.slushbucket').style.width = width*2 + 100 + 'px';
           }
           
           //Fix the expanding item preview issue
           $(varName + 'recordpreview').up('td').setAttribute('colSpan', '3');
       }
   }catch(e){}

} </syntaxhighlight> back to top

Validate IP Address Catalog Client Script

[edit | edit source]

<syntaxhighlight lang="javascript">

 function onChange(control, oldValue, newValue, isLoading) {
   if (isLoading || newValue == ) {
     return;
   }

   var regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
   var test_string = g_form.getValue('VM_IP');
   var valid = regex.test(test_string);

   if (!valid) {
     alert('Please enter a valid IP address');
     g_form.setValue('VM_IP', );
     return false;
   }
 }

</syntaxhighlight> back to top

Catalog client script to check for file attachment

[edit | edit source]

<syntaxhighlight lang="javascript"> function onSubmit() { //

var new_role = g_form.getValue('hrf_new_role');

// alert('New role = ' + new_role);

 try { //Works in non-portal ui
 var attachments = document.getElementById('header_attachment_list_label');
 if ((attachments.style.visibility == 'hidden' || attachments.style.display == 'none') && new_role == 'Yes') {
 alert('Please attach the UET approved ECF to this form before submitting.');
 return false;
 }
 } catch(e) { //For Service Portal
 var count = getSCAttachmentCount();
 if(count <= 0 && new_role == 'Yes') {
 alert('Please attach the UET approved ECF to this form before submitting.');
 return false;
 }
 }

} </syntaxhighlight>

back to top


Choice Lists

[edit | edit source]

Remove/Add Options From a Choice List

[edit | edit source]

<syntaxhighlight lang="javascript">

function onLoad() {
   
  if (g_form.getValue('<fieldname>') == '<value1>')
{
  g_form.removeOption('<fieldname>', '<Option 1>');
  g_form.removeOption('<fieldname>', '<Option 2>');
  g_form.removeOption('<fieldname>', '<Option 3>');
  g_form.removeOption('<fieldname>', '<Option 4>');
 
  }
  if (g_form.getValue('<fieldname>') == '<value2>')
{
  g_form.removeOption('<fieldname>', '<Option 5>');
  g_form.removeOption('<fieldname>', '<Option 6>');
  g_form.removeOption('<fieldname>', '<Option 7>');
  }
}

</syntaxhighlight> To add options:

<syntaxhighlight lang="javascript">

g_form.addOption('<name>', '<value>', '<label>');

</syntaxhighlight>

To clear list: <syntaxhighlight lang="javascript">

clearValue(fieldName)

</syntaxhighlight>

Example: restrict contact type by call type <syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
   if (isLoading || newValue === ) {
      return;
   }
 g_form.clearOptions('contact_type');
  g_form.addOption('contact_type', 'email', 'Email');
  g_form.addOption('contact_type', 'phone', 'Phone');
  g_form.addOption('contact_type', 'self-service', 'Self-service');
  g_form.addOption('contact_type', 'walk-in', 'Walk-in'); 
	
 if (g_form.getValue('call_type') == 'general_query')
 {
  g_form.removeOption('contact_type', 'walk-in');
  }  
	
 if (g_form.getValue('call_type') == 'counter_query')
 {
  g_form.removeOption('contact_type', 'email');
  g_form.removeOption('contact_type', 'phone');
  g_form.removeOption('contact_type', 'self-service'); 
  } 
 
}

</syntaxhighlight>


<syntaxhighlight lang="javascript"> Example: restrict_templates_choices

 function onChange(control, oldValue, newValue, isLoading) {
    if (isLoading || newValue == ) {
       return;
    }

    //VM Requests or Modifications - catalog client script to restrict template choice dependent on which operating system is selected
   g_form.clearOptions('VM_template');
 
   if (g_form.getValue('VM_OS') == 'RHEL 6')
  {
   g_form.addOption('VM_template', 'RHEL 6.x', 'RHEL 6.x');
   g_form.addOption('VM_template', 'RHEL 6 – Oracle 11g', 'RHEL 6 – Oracle 11g');
   g_form.addOption('VM_template', 'None', 'None');
   }  

 if (g_form.getValue('VM_OS') == 'RHEL 7')
  {
   g_form.addOption('VM_template', 'RHEL 7', 'RHEL 7');
   g_form.addOption('VM_template', 'RHEL 7 – Oracle 12c', 'RHEL 7 – Oracle 12c'); 
   g_form.addOption('VM_template', 'None', 'None');
   }

 if (g_form.getValue('VM_OS') == 'Windows Server 2012 R2')
  {
   g_form.addOption('VM_template', 'Windows 2012 R2', 'Windows 2012 R2');
   g_form.addOption('VM_template', 'Windows 2012 R2 – SQL', 'Windows 2012 R2 – SQL');
   g_form.addOption('VM_template', 'None', 'None');
   }

 if (g_form.getValue('VM_OS') == 'Windows Server 2016')
  {
   g_form.addOption('VM_template', 'Windows 2016', 'Windows 2016');
   g_form.addOption('VM_template', 'Windows 2016 - SQL', 'Windows 2016 - SQL');
   g_form.addOption('VM_template', 'None', 'None');
   }

 if (g_form.getValue('VM_OS') == 'Windows 7')
  {
     g_form.addOption('VM_template', 'None', 'None');  
   }

 if (g_form.getValue('VM_OS') == 'Other')
  {
     g_form.addOption('VM_template', 'None', 'None');
   }
 }

</syntaxhighlight> back to top

Client Scripts

[edit | edit source]

onChange Client Script To Populate Fields From a Task Table Lookup

[edit | edit source]

<syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
  if (isLoading || newValue === ) {
     return;
  }
  //alert(g_form.getValue('u_task_number'));
  var myLookup = new GlideRecord('task');
  myLookup.addQuery('sys_id', g_form.getValue('u_task_number'));
  myLookup.query();
  while (myLookup.next()) {

g_form.setValue('short_description', myLookup.short_description.toString()); g_form.setValue('description', myLookup.description.toString());

  }

} </syntaxhighlight> back to top

Date Handling

[edit | edit source]
<syntaxhighlight lang="javascript">
var sec=Date.parse("Thursday, July 14, 2016 11:00:43 PM");
var gdt = new GlideDateTime();
gdt.setNumericValue(sec);
gdt.getDisplayValue(); //this will give you the date in your format
</syntaxhighlight>

back to top

Subtract 3 months from a date (GlideAjax Client Script/Script Include

[edit | edit source]

Client Script

<syntaxhighlight lang="javascript">
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
   if (isLoading)  
      return;  
var ga = new GlideAjax('u_subtract_3_months'); //This argument should be the exact name of the script include.
ga.addParam('sysparm_name', 'subtract_3_months'); //sysparm_name is the name of the function in the script include to call.     
ga.addParam('sysparm_ends', g_form.getValue('ends')); //set a parameter to pass to script include
ga.getXML(myCallBack); //This is our callback function, which will process the response.
     
function myCallBack(response) { //the argument 'response' is automatically provided when the callback function is called by the system.
    var answer = response.responseXML.documentElement.getAttribute("answer"); //Dig out the 'answer' attribute, which is what our function returns. 
        g_form.setValue('renewal_date', answer); //set 'renewal_date' field to returned value.
     
 }
}
</syntaxhighlight>

Script Include

<syntaxhighlight lang="javascript">
//Script called by client script Renewal Process Start Date
//Calulates a date three months earlier than the contract end date.
//This was written in order to handle UK date format dd/MM/yyyy
//gdt.setDisplayValue(parm_data); is crucial to this working for all dates as without it day numbers of 12 or less are
//interpreted as months.
var u_subtract_3_months = Class.create();
 u_subtract_3_months.prototype = Object.extendsObject(AbstractAjaxProcessor, {
     subtract_3_months: function() {
         var parm_data = this.getParameter('sysparm_ends'); //retrieve parameter passed from client script
         var gdtDay = "";
         var gdtMonth = "";
         var gdtYear = "";
         var gdt = new GlideDateTime(parm_data);
         gdt.setDisplayValue(parm_data);
         gdt.addMonths(-3);
          
         if(gdt.getDayOfMonth().toString().length == 1) {
             gdtDay = "0" + gdt.getDayOfMonth();
         }
         else {
             gdtDay = gdt.getDayOfMonth();
         }
          
         if(gdt.getMonth().toString().length == 1) {
             gdtMonth = "0" + gdt.getMonth();
         }
         else {
             gdtMonth = gdt.getMonth();
         }
          
         gdtYear = gdt.getYear();
          
         var gdtDate = gdtDay + "/" + gdtMonth + "/" + gdtYear;
         return gdtDate;
     }

 });

</syntaxhighlight>

GlideAjax client script to populate a form field (calls script include below)

[edit | edit source]

<syntaxhighlight lang="javascript">

// Called by u_permitted_use client script.
// Auto-populates 'Permitted Use' field on the Software Installation request form.
function onChange(control, oldValue, newValue, isLoading) {
    if (isLoading)  
    return; 
    if (newValue);
//Code commented out below is the old method of poulating the Permitted Use field that 
//does not work in the Service Portal. Reatained for reference.
//	var parm_data = g_form.getReference('softins_name', popPermittedUse);
//    //var perm_use = perm_data.u_permitted_use;
//	function popPermittedUse(parm_data){
//		g_form.setValue('u_permitted_use', parm_data.u_permitted_use, parm_data.getDisplayValue('u_permitted_use'));
//	}
    var ga = new GlideAjax('u_get_permitted_use'); //This argument should be the exact name of the script include. 
    ga.addParam('sysparm_name', 'popPermittedUse'); //sysparm_name is the name of the function in the script include to call. 
    ga.addParam('sysparm_softins', g_form.getValue('softins_name')); //set a parameter to pass to script include
    ga.getXML(myCallBack); //This is our callback function, which will process the response.

    function myCallBack(response) { //the argument 'response' is automatically provided when the callback funciton is called by the system.
    var answer = response.responseXML.documentElement.getAttribute("answer"); //Dig out the 'answer' attribute, which is what our function returns. 
        g_form.setValue('u_permitted_use', answer); //set 'Permitted Use' field to returned value.
    }
}

</syntaxhighlight> back to top

GlideAjax script include (used by client script above to populate a form field)

[edit | edit source]

<syntaxhighlight lang="javascript">

//Used by client script u_permitted_use to populate the 'Permitted Use' field 
//on the Sofware Installation request form.

var u_get_permitted_use = Class.create();
u_get_permitted_use.prototype = Object.extendsObject(AbstractAjaxProcessor, {
    popPermittedUse: function() {
    var parm_data = this.getParameter('sysparm_softins'); //retrieve parameter passed from client script
    var permUse = ''; //declare variable to hold the Permitted Use data 
    var gr = new GlideRecord('cmdb_software_product_model'); 
        gr.addQuery('sys_id', parm_data); //parameter passed is a sysid so find matching record
        gr.query();
        while(gr.next())
        {
         permUse = gr.u_permitted_use; //assign value of u_permitted_use to permUse 
        }
         return permUse; //return value to calling client script
        }
});

</syntaxhighlight> back to top

Glide Ajax Date Handling Example

[edit | edit source]

<syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading) {  
 
 if (isLoading || newValue == ) {  
     return;  
 }  
 
 if (g_form.getValue('var_eventStart') != )  
 {  
 var cdt = g_form.getValue('var_eventStart'); //First Date/Time field  
 var sdt = g_form.getValue('var_eventEnd'); //Second Date/Time field  
 var dttype = 'second'; //this can be day, hour, minute, second. By default it will return seconds.  
  
 var ajax = new GlideAjax('ClientDateTimeUtils');  
 ajax.addParam('sysparm_name','getDateTimeDiff');  
 ajax.addParam('sysparm_fdt', cdt);  
 ajax.addParam('sysparm_sdt', sdt);  
 ajax.addParam('sysparm_difftype', dttype);  
 ajax.getXML(function () {  
   
   
   
  
 var answer = ajax.getAnswer();  
     
 if (answer <0){  
   alert('You cannot select an End Date prior to the Start Date.');  
   g_form.setValue('var_eventEnd', );  
 }  
     
 else if (answer == 0){  
   alert('You cannot select an End Date equal to Start Date.');  
         g_form.setValue('var_eventEnd',);  
 }   
 });  
}  
else{  
 alert('Start Date cannot be empty');  
   g_form.setValue('var_eventEnd', );  
}  
 
}

</syntaxhighlight> back to top

Glide Ajax getReference equivalent

[edit | edit source]

<syntaxhighlight lang="javascript">

//Script Include  
// Name: CallerLocation  
//Client Callable: checked  
//Script:  
var CallerLocation = Class.create();  
CallerLocation.prototype = Object.extendsObject(AbstractAjaxProcessor, {  
   getLocation: function() {  
  var loc = ;  
      var callerId = this.getParameter('sysparm_user_name');  
  var gr = new GlideRecord('sys_user');  
  gr.addQuery('sys_id',callerId);  
  gr.query();  
  if(gr.next())  
  {  
  loc = gr.location;  
  }  
  return loc;  
   }  
  
});  
  
//onChange Client Script of Caller field  
  
  
function onChange(control, oldValue, newValue, isLoading, isTemplate) {  
  if (isLoading)  
  return;  
  if(newValue != ){  
  var ga = new GlideAjax('CallerLocation');  
  ga.addParam('sysparm_name','getLocation');  
  ga.addParam('sysparm_user_name',newValue);  
  ga.getXML(LocationParse);  
  
  function LocationParse(response) {  
    var answer = response.responseXML.documentElement.getAttribute("answer");  
    g_form.setValue('location',answer);  
  }  
  }  
}

</syntaxhighlight> back to top

u_get_permitted_use

[edit | edit source]

<syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading) {
     
	if (newValue);
	var parm_data = g_form.getReference('softins_name', popPermittedUse);
    //var perm_use = perm_data.u_permitted_use;
	function popPermittedUse(parm_data){
		g_form.setValue('u_permitted_use', parm_data.u_permitted_use, parm_data.getDisplayValue('u_permitted_use'));
	}
}

</syntaxhighlight> back to top

Client Script with Switch statement to show/hide various fields

[edit | edit source]

<syntaxhighlight lang="javascript"> function onChange(control, oldValue, newValue, isLoading, isTemplate) {

  if (isLoading || newValue === ) {
     return;
  }
  //Show or hide on hold expiry/reason fields depending on incident state
  var myState = g_form.getValue('incident_state');
  switch(myState){
  	   case '8': 

case '13': g_form.setMandatory('u_on_hold_expiry', true);

	      g_form.setDisplay('u_on_hold_expiry', true);

g_form.setMandatory('u_on_hold_reason', false);

	      g_form.setDisplay('u_on_hold_reason', false);

break; case '14': g_form.setMandatory('u_on_hold_expiry', true);

	      g_form.setDisplay('u_on_hold_expiry', true);
             g_form.setMandatory('u_on_hold_reason', true);
	      g_form.setDisplay('u_on_hold_reason', true);

break; default: g_form.setMandatory('u_on_hold_expiry', false);

	      g_form.setDisplay('u_on_hold_expiry', false);

g_form.setMandatory('u_on_hold_reason', false);

	      g_form.setDisplay('u_on_hold_reason', false);
             g_form.setValue('u_on_hold_expiry',);

g_form.setValue('u_on_hold_reason',); }

} </syntaxhighlight> back to top

Client Script to Check if a Check Box is Ticked

[edit | edit source]

<syntaxhighlight lang="javascript">

function onSubmit() {
  var agree = g_form.getValue('variables.u_caveat_agree');
  
  if (agree == 'false') {  
   alert('You must agree to the caveat before submitting'); 
   return false;  
  }
}

</syntaxhighlight> back to top

Auto-populate Reference Field Variable Using Default Value

[edit | edit source]

UserID in this example <syntaxhighlight lang="javascript">

javascript:gs.getUserID(); //returns sys_id of currently logged in user
javascript:gs.getUserName(); 
javascript:gs.getUserDisplayName();

</syntaxhighlight> back to top

Jobs open 'IT - Service Desk' automatically assigned to opened by

[edit | edit source]

<syntaxhighlight lang="javascript">

function onLoad() 
{
   //run this script only for itil users
   if (!g_user.hasRole("itil"))
   		return;  

   var tblName = g_form.getTableName(); 

   if (g_form.isNewRecord() && (tblName == 'sc_request' || tblName == 'incident'))
   {
      //1. Assign group_sys_is (for 'IT- Service Desk') is the same for dev, test, & live  
      var group_id = '96bc6cec8c51dc00483188886d6e3dfd';   
 
      //2. Get user_sys_id
      var usrID = g_user.userID; 

      //3. Check if the current user (opened by) id the above group member 
      var grmember = new GlideRecord('sys_user_grmember');  
      grmember.addQuery('group',group_id); 
      grmember.addQuery('user',usrID); 
      grmember.query();  
      while(grmember.next())  
      {  
         //Assign assignment group to 'IT- Service Desk'
         g_form.setValue('assignment_group',group_id); 

         //assign this user if is a member
         g_form.setValue('assigned_to',usrID); 
      }  

    }// end if 
}

</syntaxhighlight> back to top

Client Script - on load - Hide Standard Change Type

[edit | edit source]

<syntaxhighlight lang="javascript">

 function onLoad() {

   var mySysID = getParmVal('sys_id');
   var myParm = getParmVal('sysparm_template');
   var this_sysparm_record_target = getParmVal('sysparm_record_target');
   /*
   We only want to hide type 'Standard' when creating a new non-templated change request.
   The following if statement checks:
   sys_id = -1 (new templated changes also have a sys_id of -1 until they are saved)
   sysparm_template is not present
   sysparm_record_target is not present (this is present on existing change requests)
   */
   if (myParm == "noTemplate" && this_sysparm_record_target == "noTemplate" && mySysID == -1) {
     g_form.removeOption('type', 'Standard');
   }

   function getParmVal(name) {
     var url = document.URL.parseQuery();
     if (url[name]) {
       return decodeURI(url[name]);
     } else {
       return "noTemplate";
     }
   }
 }

</syntaxhighlight>

Client Script - Advanced Type - CallTypeChanged

[edit | edit source]

Condition: current.call_type.changes() && current.transferred_to.nil()

<syntaxhighlight lang="javascript">
   var ctype = current.call_type;

   //if (ctype != 'hang_up' && ctype != 'wrong_number' && ctype != 'status_call' && ctype != 'general_inquiry' && ctype != 'sc_request' && ctype != 'status_call'){
   if (ctype == 'incident' || ctype == 'change_request'){
       var gr = new GlideRecord(ctype);
       gr.short_description = current.short_description;
       gr.description = current.description.getHTMLValue();
       gr.contact_type = current.contact_type;
       gr.company = current.company;
       gr.opened_by = current.opened_by;

       // update taks work notes
       var callerName = current.caller.name;
       var taskType = current.call_type.getDisplayValue();
       var currentLink = "[code]<a href='" + current.getLink() + "'>" + current.number + "</a>[/code]";
       var journalEntry = gs.getMessage("This {0} has been chased by {1} from {2}", [taskType, callerName, currentLink]);
       gr.work_notes = journalEntry;

       if (GlidePluginManager.isRegistered('com.glide.domain'))
           gr.sys_domain = getDomain();
       
       if (ctype == 'incident'){
                   if(isServiceDeskMember(current.opened_by))
                   {
                     gr.assigned_to = current.opened_by;
                     gr.assignment_group = '96bc6cec8c51dc00483188886d6e3dfd';
                     //gs.log('Update assigned_to and group');
                   } 
           gr.caller_id = current.caller;
           gr.location = current.caller.location;
           gr.comments = current.description.getHTMLValue();
       }
       
       if (ctype == 'change_request'){
           gr.requested_by = current.caller;
       }
       
       var sysID = gr.insert();
       current.transferred_to = sysID;
       var url = ctype + '.do?sys_id=' + sysID;
       gs.addInfoMessage(current.number + gs.getMessage(" transferred to ") + ":  <a href='" + url + "'>" + current.transferred_to.getDisplayValue() + "</a>");
   }
   else if (ctype == 'status_call')
   {
       var sysID = current.u_call_status_task;
       var taskName = current.u_call_status_task.getDisplayValue();

       //gs.log('Inside Status Call - ELSE IF - sysID: ' + sysID + ' | taskName: ' + taskName);

       //assign call redirect url
       current.transferred_to = sysID;

       var tableName = ;
       if(taskName.indexOf('INC') != -1)
       {
          tableName = 'incident';
       }
       else if (taskName.indexOf('REQ') != -1)
       {
          tableName = 'sc_request';
       }
       //gs.log('tableName: ' + tableName);

       if(tableName != ) 
       {
           //gs.log('Inside If tableName : ' + tableName);

       // update taks work notes
       var callerName = current.caller.name;
       var currentLink = "[code]<a href='" + current.getLink() + "'>" + current.number + "</a>[/code]";
           var journalEntry = current.short_description + '\n' + current.description.getHTMLValue() + '\n';
       journalEntry += gs.getMessage("This {0} has been chased by {1} from {2}", [taskName, callerName, currentLink]);
       
           //gs.log('Comments: ' + journalEntry);

           var gr = new GlideRecord(tableName);
           gr.addQuery('sys_id', sysID); 
           gr.query(); 
           if(gr.next())
           { 
             //gr.work_notes = journalEntry;
             gr.comments = journalEntry;
             gr.update();

             //gs.log('GlideRecord updated ');
           }

        }
   }

   //check if open_by is a member of Service Desk
   function isServiceDeskMember(usrID)
   {
         var returnValue = false;

         //1. Assign group_sys_is (for 'IT- Service Desk') is the same for dev, test, & live  
         var group_id = '96bc6cec8c51dc00483188886d6e3dfd';   
    
         //2. Check if the current user (opened by) id the above group member 
         var grmember = new GlideRecord('sys_user_grmember');  
         grmember.addQuery('group',group_id); 
         grmember.addQuery('user',usrID); 
         grmember.query();  
         if(grmember.next())  
         {  
            //gs.log('I am a Service Desk Member: ' + usrID);
            returnValue = true; 
         } 

         return returnValue;
   }

   function getDomain(){
       // only set the domain if the caller has a domain that is not global
       if (JSUtil.notNil(current.caller) && JSUtil.notNil(current.caller.sys_domain) && current.caller.sys_domain.getDisplayValue() != 'global') 
           return current.caller.sys_domain;
       else
           return getDefaultDomain();
   }

</syntaxhighlight> back to top

Re-calculate 'Priority' when value of 'Service Affected' changes.

[edit | edit source]

Name: u_calc_priority
Table: incident
UI Type: Both
Type: onChange
Field name: Service Affected

<syntaxhighlight lang="javascript">

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
   if (isLoading || newValue === ) {
      return;
   }
    g_form.setValue('priority',calculatePriority(g_form.getValue('impact'), g_form.getValue('u_service_affected.busines_criticality')));
   
}

</syntaxhighlight> back to top

[edit | edit source]

When to Apply: Call Type is Status Call

Execute if true <syntaxhighlight lang="javascript">

function onCondition() {
var list = $$('div[tab_caption="Tasks by Same Caller"]')[0];
if(list.hasClassName('embedded')){
   list.show();
}
}

</syntaxhighlight> Execute if false <syntaxhighlight lang="javascript">

function onCondition() {
var list = $$('div[tab_caption="Tasks by Same Caller"]')[0];
if(list.hasClassName('embedded')){
   list.hide();
}
}

</syntaxhighlight> back to top

[edit | edit source]

<syntaxhighlight lang="javascript">

g_form.hideRelatedList('table name');

</syntaxhighlight>

[edit | edit source]

<syntaxhighlight lang="javascript">

g_form.showRelatedList('table name');

</syntaxhighlight> back to top

Get next incident number

[edit | edit source]

condition: current.number.nil() <syntaxhighlight lang="javascript">

current.number = getNextObjNumberPadded();
gs.addInfoMessage(gs.getMessage("Created Incident") + " " + current.number );

</syntaxhighlight> back to top

Set Knowledgebase Article Review Date When State Changes to 'Published'

[edit | edit source]

Client Script to set the Published Date

[edit | edit source]

<syntaxhighlight lang="javascript">

 //Client Script - knowledge published date
 //Script to set the published date when 'State' changes to 'Published'. 
 //Calls script include u_ClientDateTimeUtils.
 function onChange(control, oldValue, newValue, isLoading, isTemplate) {
   if (isLoading)
     return;

   var state = g_form.getValue('workflow_state');

   if (state == 'published') {
     var ajax = new GlideAjax('u_ClientDateTimeUtils');
     ajax.addParam('sysparm_name', 'getNowDate');
     ajax.getXML(function() {
       g_form.setValue('published', ajax.getAnswer());
     });

   }

 }

</syntaxhighlight>

Client Script to set the Review Date to Published + 12 Months

[edit | edit source]

<syntaxhighlight lang="javascript">

 //Client Script - knowledge review date
 //Script to calculate a review date 12 months from the published date. Calls script include u_add_12_months.
 //calls script include u_add_12_months
 function onChange(control, oldValue, newValue, isLoading, isTemplate) {
   if (isLoading)
     return;

   var state = g_form.getValue('workflow_state');
   var review = g_form.getValue('u_review_date');

   if (state == 'published') {
     var ajax = new GlideAjax('u_ClientDateTimeUtils');
     ajax.addParam('sysparm_name', 'getNowDate');
     ajax.getXML(function() {
       g_form.setValue('published', ajax.getAnswer());
     });

     if (state == 'published' && review == ) {

       var ga = new GlideAjax('u_add_12_months'); //This argument should be the exact name of the script include.
       ga.addParam('sysparm_name', 'add_12_months'); //sysparm_name is the name of the function in the script include to call. 	
       ga.addParam('sysparm_published', g_form.getValue('published')); //set a parameter to pass to script include
       ga.getXML(myCallBack); //This is our callback function, which will process the response.
     }

   }


   function myCallBack(response) { //the argument 'response' is automatically provided when the callback function is called by the system.
     var answer = response.responseXML.documentElement.getAttribute("answer"); //Dig out the 'answer' attribute, which is what our function returns. 
     g_form.setValue('u_review_date', answer); //set 'renewal_date' field to returned value.

   }

 }

</syntaxhighlight>

Script Include called to add 12 months to Published date

[edit | edit source]

<syntaxhighlight lang="javascript">

 //Script called by client script knowledge review date
 //Calulates a date 12 months after the published date of a knowledge article.
 //This was written in order to handle UK date format dd/MM/yyyy
 //gdt.setDisplayValue(parm_data); is crucial to this working for all dates as without it day numbers of 12 or less are
 //interpreted as months.
 var u_add_12_months = Class.create();
 u_add_12_months.prototype = Object.extendsObject(AbstractAjaxProcessor, {
   add_12_months: function() {
     var parm_data = this.getParameter('sysparm_published'); //retrieve parameter passed from client script
     var gdtDay = "";
     var gdtMonth = "";
     var gdtYear = "";
     var gdt = new GlideDateTime(parm_data);
     gdt.setDisplayValue(parm_data);
     gdt.addMonths(12);

     if (gdt.getDayOfMonth().toString().length == 1) {
       gdtDay = "0" + gdt.getDayOfMonth();
     } else {
       gdtDay = gdt.getDayOfMonth();
     }

     if (gdt.getMonth().toString().length == 1) {
       gdtMonth = "0" + gdt.getMonth();
     } else {
       gdtMonth = gdt.getMonth();
     }

     gdtYear = gdt.getYear();

     var gdtDate = gdtDay + "/" + gdtMonth + "/" + gdtYear;
     return gdtDate;
   }

 });

</syntaxhighlight> back to top

Client script to set contact type based on user who creates the request.

[edit | edit source]

If member of Service Desk or UST contact type = phone If IT Ambassador then contact type = walk-in For all others - contcat type = Direct Input <syntaxhighlight lang="javascript"> function onLoad() {

 var openedBy = g_form.getDisplayBox('opened_by').value;
    if (g_form.contact_type != 'self-service' || g_form.contact_type != 'chat') {

if (g_scratchpad.grp_sd == true || g_scratchpad.grp_ust == true){ g_form.setValue('contact_type','phone'); } else if(openedBy == "IT Ambassador"){ g_form.setValue('contact_type','walk-in'); } else { g_form.setValue('contact_type','Direct Input'); }

  }

} </syntaxhighlight>

back to top

Loop to set all fields non mandatory

[edit | edit source]

<syntaxhighlight lang="javascript"> for (var i = 0; i < g_form.elements.length; i++) {

   var el = g_form.elements[i];
   var fieldName = el.fieldName;
   g_form.setMandatory(fieldName, false);

} </syntaxhighlight>

back to top

Email Notifications

[edit | edit source]

Email Scripts

[edit | edit source]

Email Script to populate email body with survey results

[edit | edit source]

Used encoded query to overcome issue of selecting incorrect records.

getDisplayValue() used to return value rather than sys_id.

Instance of survey stored in - asmt_assessment_instance.

Survey results stored in - asmt_metric_result. <syntaxhighlight lang="javascript">

 var ins = current.number.getDisplayValue();
 var queryString = "metric.metric_type.evaluation_method=survey^instance.number=" + ins;
 template.print("You have received some feedback for survey " + '${URI_REF}');
 template.print("<br />");
 var gr = new GlideRecord('asmt_metric_result');  
 gr.addEncodedQuery(queryString);
 gr.orderByDesc('actual_value');
 //gr.setLimit(10);
 gr.query();
 var counter = 1;
 while(gr.next()) { 
 
    if(counter == 1){ 
  	   template.print("Submitted by " + gr.user.getDisplayValue() + "<br />");
 	   template.print("<br />");
 	   counter++;
    }
    template.print(gr.instance_question.getDisplayValue() + "<br />");
    template.print(gr.string_value + "<br />");
    template.print("<br />");
 }

</syntaxhighlight> back to top

Hide redundant closed states

[edit | edit source]

<syntaxhighlight lang="javascript">

// Hide old "Closed" request states made redundant by REQ from everyone but admin

function onLoad() { 
	if (g_user.hasRole('admin'))
		 return;
	//don't show the 'old' label 'Closed Converted to Incident so it does not cause confusion with the 'new' naming format
		//of 'Cancelled - Converted to Incident' 
	if (g_form.getValue('request_state') != 'Cancelled - Converted to Incident')
		g_form.removeOption('request_state', 'Cancelled - Converted to Incident');
		//
		if (g_form.getValue('request_state') != 'closed_incomplete')
		g_form.removeOption('request_state', 'closed_incomplete');
		//
		if (g_form.getValue('request_state') != 'closed_complete')
		g_form.removeOption('request_state', 'closed_complete');
		//
		if (g_form.getValue('request_state') != 'Pending testing')
		g_form.removeOption('request_state', 'Pending testing');
 //
	if (g_form.getValue('request_state') != 'closed_cancelled')
		g_form.removeOption('request_state', 'closed_cancelled');
 //
	if (g_form.getValue('request_state') != 'Closed - Converted to Incident')
		g_form.removeOption('request_state', 'Closed - Converted to Incident'); 
//
	if (g_form.getValue('request_state') != 'closed_duplicate')
		g_form.removeOption('request_state', 'closed_duplicate');
 //
	if (g_form.getValue('request_state') != 'closed_rejected')
		g_form.removeOption('request_state', 'closed_rejected');
 //
	if (g_form.getValue('request_state') != 'closed_resolved')
		g_form.removeOption('request_state', 'closed_resolved');
 //
if (g_form.getValue('request_state') != 'closed_user_unavailable')
	g_form.removeOption('request_state', 'closed_user_unavailable');
}

</syntaxhighlight> back to top


Encoded Query

[edit | edit source]

<syntaxhighlight lang="javascript">

current.addEncodedQuery('nameISNOTEMPTY^cmdb_model_categoryISNOTEMPTY'); //Encoded query

</syntaxhighlight>

<syntaxhighlight lang="javascript">

gr.addEncodedQuery('active=true^state=2'); //Encoded query

</syntaxhighlight> back to top

Events

[edit | edit source]

Using an Event to Change The Status of a Task When Updated From Call Module

[edit | edit source]

Business Rule - CallTypeChanged amended to create events when status calls are created. <syntaxhighlight lang="javascript">

gs.eventQueue("call.incident.update", current, taskName, tableName);
gs.eventQueue("call.request.update", current, taskName, tableName);

</syntaxhighlight> Event Registry - call.request.update and call.incident.update created. Script Actions - Update Incident From Call and Update Request From Call created which set the status of incidents/requests to active

Update Incident From Call <syntaxhighlight lang="javascript">

//fired by event - call.incident.update.
//parameters: 1 - Incident number. 2 - table name
//Sets incident_state to active.
var incident_gr = new GlideRecord(event.parm2);
incident_gr.addQuery('number', event.parm1);
incident_gr.query();
if (incident_gr.next()) 
{
   incident_gr.incident_state = '2';
   incident_gr.update();
}

</syntaxhighlight> Update Request From Call <syntaxhighlight lang="javascript">

//fired by event - call.request.update.
//parameters: 1 - Request number. 2 - table name
//Sets request_state to active.
var request_gr = new GlideRecord(event.parm2);
request_gr.addQuery('number', event.parm1);
request_gr.query();
if (request_gr.next()) 
{
   request_gr.request_state = 'Active';
   request_gr.update();
}

</syntaxhighlight> back to top

Event creation Example

[edit | edit source]

Example of creating events for incidents. <syntaxhighlight lang="javascript">

if (current.operation() != 'insert' && current.comments.changes()) {
  gs.eventQueue("incident.commented", current, gs.getUserID(), gs.getUserName());
}

if (current.operation() == 'insert') {
 gs.eventQueue("incident.inserted", current, gs.getUserID(), gs.getUserName());
}

if (current.operation() == 'update') {
 gs.eventQueue("incident.updated", current, gs.getUserID(), gs.getUserName());
}

if (!current.assigned_to.nil() && current.assigned_to.changes()) {
  gs.eventQueue("incident.assigned", current, current.assigned_to.getDisplayValue() , previous.assigned_to.getDisplayValue());
}

if (!current.caller_id.nil() && current.caller_id.changes()) {
  gs.eventQueue("incident.caller", current, previous.caller_id.first_name , previous.caller_id);
}

if (!current.assignment_group.nil() && current.assignment_group.changes()) {
  gs.eventQueue("incident.assigned.to.group", current, current.assignment_group.getDisplayValue() , previous.assignment_group.getDisplayValue());
}

if (current.priority.changes() && current.priority == 1) {
  gs.eventQueue("incident.priority.1", current, current.priority, previous.priority);
}

if (current.priority.changes() && (current.priority == 1 || current.priority == 2)) { 
  gs.eventQueue("incident.priority.maj", current, current.priority, previous.priority);
}

if (current.severity.changes() && current.severity== 1) {
  gs.eventQueue("incident.severity.1", current, current.severity, previous.severity);
}

if (current.escalation.changes() && current.escalation > previous.escalation && previous.escalation != -1) {
  gs.eventQueue("incident.escalated", current, current.escalation , previous.escalation );
}

if(current.impact.changes() && current.impact == 9) { 
  gs.eventQueue("incident.impact.security_compromise", current, current.impact, previous.impact);
} 

if(current.active.changesTo(false)){
  gs.eventQueue("incident.inactive", current, current.incident_state, previous.incident_state);
  gs.workflowFlush(current);
}

</syntaxhighlight> back to top

Forms - Finding Assignment Groups for an Assignee

[edit | edit source]

Script Include <syntaxhighlight lang="javascript"> FindAssigneeGroups.getUserGroups = function(userID) {

var FoundGroups = new GlideRecord(‘sys_user_group’);

if (!gs.nil(userID)) { var AssigneeGroups = Packages.com.glide.sys.User.getMyGroups(userID); if (AssigneeGroups.size() != 0) { FoundGroups.addQuery('sys_id’, AssigneeGroups); } else { FoundGroups.addQuery('sys_id’,'No groups found for this asignee’); } }

return FoundGroups.getEncodedQuery(); }; </syntaxhighlight> Reference Qualifier <syntaxhighlight lang="javascript"> javascript:FindAssigneeGroups.getUserGroups('assigned_to) </syntaxhighlight>

back to top

Parameters

[edit | edit source]

Access URL Parameters

[edit | edit source]

<syntaxhighlight lang="javascript">

RP.getParameterValue('sysparm_xxxx');

</syntaxhighlight>

or

<syntaxhighlight lang="javascript">

//check if it exists
gs.action.getGlideURI().toString().indexOf('String to search for')
//retrieve url parameter
gs.action.getGlideURI().getMap().get('sysparm_xxxx')

</syntaxhighlight> back to top

Record Producer Scripts

[edit | edit source]

List Collector Array Handling

[edit | edit source]

<syntaxhighlight lang="javascript" line='line'>

//Start to build notes for the Desciption field of the generated request record.
var notes = "Permission required for the following staff: " ;

// REQ1137375 - Following loop added to retrieve userids from sys_user for each name entered in the list selector.
var listIDriveNames = producer.getValue('idrive_name'); // store contents of idrive_name variable in listIDriveNames. This will be a comma separated list of sysids
var arrayIDriveNames = listIDriveNames.split(','); // Split listIdriveNames at the commas and store in arrayIDriveNames. 
// Loop round array elements
for (var i = 0; i < arrayIDriveNames.length; i++) { // loop round a number of times equal to the number of elements in the array
	var gr = new GlideRecord('sys_user'); 
	gr.addQuery('sys_id', arrayIDriveNames[i]); //add query to search sys_user table for a record that matches the sys_id of one of the array elements.
	gr.query();  // Search sys_user table for a match on sys_id
	while(gr.next()) {
		// read values for name and user_name from the record that matches the sys_id
		notes += "\n     " + gr.name.getDisplayValue() + " (" + gr.user_name.getDisplayValue() + ")";
	}
}

</syntaxhighlight>

[edit | edit source]

<syntaxhighlight lang="javascript"> current.u_name = producer.u_name_of_user; current.u_software = producer.u_software; current.u_additional_email = producer.u_additional_email; if (producer.u_additional_email != ){ var u_add_email = producer.u_additional_email;

  }

var sw_package = current.u_software.getDisplayValue(); var gr = new GlideRecord('u_software_for_workathome_use'); gr.initialize(); gr.addQuery('u_name',sw_package); gr.query(); while (gr.next()){

  if (gr.u_link_to_web_request != ){
     producer.redirect = 'u_software_link_to_web_request.do?sysparm_software=' + current.u_software.getDisplayValue();  
  }
  else if (gr.u_link_to_web_request ==  && gr.u_licence_key ==  && gr.u_name == 'Adobe Creative Cloud Enterprise'){

var gr_request = new GlideRecord('sc_request'); gr_request.initialize(); gr_request.requested_for = producer.u_name_of_user; gr_request.assignment_group.setDisplayValue = 'IT - Service Desk'; gr_request.short_description = 'Software Installation - Adobe Creative Cloud Enterprise for work at home use'; var notes = 'Request for Adobe Creative Cloud Enterprise for work at home use';

  	   notes += "\n Requested for: " + producer.u_name_of_user.getDisplayValue();

if (producer.u_additional_email != ){ notes += "\n Preferred email: " + u_add_email;

} notes += "\n Service Desk - See Work Notes for details on how to enable access to Adobe Creative Cloud Enterprise for " + producer.u_name_of_user.getDisplayValue(); gr_request.description = notes; gr_request.priority = 5;

gr_request.work_notes = '[code]

Instructions for IT - Service Desk

[/code]' +

"\n Requested for: " + producer.u_name_of_user.getDisplayValue() +

       '\n Preferred email address: ' + u_add_email +

'\n To enable staff access to Adobe Creative Cloud Enterprise see knowledge base article - [code]<a href="https://bournemouth.service-now.com/nav_to.do?uri=%2Fkb_view.do%3Fsysparm_article%3DKB0015414%26sysparm_tsqueryId%3Dc3247ff8db8a9780f9bf9ee3db961951%26sysparm_rank%3D1" target="_blank">KB0015414</a>[/code]';

gr_request.insert();

producer.redirect = 'u_adobe_creative_cloud_requested.do?sysparm_software=' + sw_package;

  }
  else if (gr.u_link_to_web_request ==  && gr.u_licence_key == ){

producer.redirect = 'u_software_install_guide.do?sysparm_software=' + sw_package;

  }
  else if (gr.u_link_to_web_request ==  && gr.u_licence_key != ){
     producer.redirect = 'u_software_licence_key.do?sysparm_software=' + sw_package;
  }
  else {
     current.setAbortAction(true);
  }

} </syntaxhighlight>

back to top

Set assignment group and write notes to activity log when room matches criteria

[edit | edit source]

<syntaxhighlight lang="javascript" line='line'>

//Set assignment group and write notes to activity log when room matches criteria
var myRoom = producer.u_inc_location.getDisplayValue();
var row = new GlideRecord('u_rooms');
var roomFound = false;
row.addQuery('u_location_reference', 'STARTSWITH', myRoom);
row.query();
while (row.next() && !roomFound) {
  if (row.u_scitech == true) {
    current.assignment_group.setDisplayValue("<assignment_group>");
    current.work_notes = "Automatically assigned to <assignment_group> as room " + myRoom + " is marked in u_rooms as a <Dept.> room";
    roomFound = true;
  }
}


 current.short_description = "Software Installation";
 if (producer.username != ){
    current.requested_for = producer.username;
 }
 var notes = "Software name: " + producer.softins_name.display_name.getDisplayValue();
 notes += "\n Software licence model: " + "( " + producer.softins_name.u_licence_model.getDisplayValue() + " )";
 notes += "\n To device: " + producer.softins_dev.getDisplayValue();
 //CJ - added below to add additional details from Software Model form - REQ1045658
 notes += "\n Deployment type: \n" + producer.softins_name.u_deployment_type;
 notes += "\n Deployment method: \n" + producer.softins_name.u_deployment_method;
 notes += "\n Install AD group: \n" + producer.softins_name.u_install_ad_group;
 notes += "\n Install instructions: \n" + producer.softins_name.u_install_instructions.getDisplayValue();
 if(producer.softins_name.u_deployment_method.getDisplayValue() == "Manual"){
    notes += "\n Installer location: \n" + producer.softins_name.u_installer_location;
 }
 notes += "\n Comments: \n" + producer.Additional_Information;
 current.description = notes;
 //CJ - populate CI field with information provided in form - REQ1056173
 current.cmdb_ci = producer.softins_dev;
 //CJ - populate service affected and record which form used to generate request - REQ1055636
 current.u_form_name = "Software Installation";
 current.u_service_affected ='66bd2aad8cfc9400483188886d6e3d4e';
 //Has the request been raised by someone in IT?
 //if (current.opened_by.department.getDisplayValue() != "IT"){
 //current.contact_type = 'self-service';}
 // above condition changed under REQ1030520
 //Has the request been raised by a member of IT Service Desk? If so contact type is phone otherwise contact type is self-service
 if (gs.getUser().isMemberOf('IT - Service Desk'))
 {
 current.contact_type = 'phone';
 }
 else {
 current.contact_type = 'self-service';}
 if (producer.softins_name != ){
    current.priority = 5;
    }
    else
    {
  current.priority = 6;
    } 
 if (producer.softins_name.display_name.getDisplayValue() == ){
 gs.addErrorMessage("We could not find the software you asked for, please add it to the 'Additional comments' field of this request");
 }
 if(producer.softins_dev.getDisplayValue() == ){
 gs.addErrorMessage("We could not find the PC number you entered, please add it to the 'Additional comments field of this request");
 }
 //CJ - send to group listed on Software Model form if software is campus licenced or has no licence cost - REQ1045658
 if (producer.softins_name.u_licence_model.getDisplayValue() == "Campus"){
    current.assignment_group = producer.softins_name.u_install_team;
 }
 if (producer.softins_name.u_licence_model.getDisplayValue() == "Chargeable"){
    current.assignment_group.setDisplayValue("IT - Supplier and Licencing");
 }
 if (producer.softins_name.u_licence_model.getDisplayValue() == "Limited"){
 current.assignment_group.setDisplayValue("IT - Supplier and Licencing");
 }
 if (producer.softins_name.u_licence_model.getDisplayValue() == "No associated licence cost"){
 current.assignment_group = producer.softins_name.u_install_team;
 }

</syntaxhighlight> back to top


Reference Qualifiers

[edit | edit source]

Service Portal

[edit | edit source]

Sort Categories in Service Portal

[edit | edit source]

<syntaxhighlight lang="javascript">

data.categories.sort(function(a, b) {var stringA = a.label.toUpperCase();var stringB = b.label.toUpperCase();return (stringA < stringB) ? -1 : (stringA > stringB) ? 1 : 0;});

</syntaxhighlight> back to top

System Properties

[edit | edit source]

System Property 'glide.knowman.create_incident_link' Example =

[edit | edit source]

This was used to auto-assign icidents raised via a knowledge article to 'Knowledge'

incident.do?sys_id=-1&sysparm_query=active=true^contact_type=$⁠[HTML:knowledgeRecord.u_param_knowledge]^contact_type=knowledge^comments=(Created after Knowledge search: 
$[HTML:knowledgeRecord.short_description])&sysparm_stack=knowledge_home_launcher.do

back to top


UI Actions

[edit | edit source]

Delete audit/journal entries

[edit | edit source]

(from servicenowguru.com) UI Action

Name: Delete History Line
Table: History [sys_history_line]
Action name: delete_history_line
Show insert: false
Show update: true
Client: true
Form button: true
Onclick: confirmDelete()
Condition: gs.hasRole(‘admin’)
Script:
<syntaxhighlight lang="javascript">

function confirmDelete(){
   if(confirm('Are you sure you want to permanently delete this history line and all corresponding audit history?\n\nTHIS ACTION CANNOT BE 
UNDONE!')){
      //Call the UI Action and skip the 'onclick' function
      gsftSubmit(null, g_form.getFormElement(), 'delete_history_line'); //MUST call the 'Action name' set in this UI Action
   }
   else{
      return false;
   }
} 

//Code that runs without 'onclick'
//Ensure call to server-side function with no browser errors
if(typeof window == 'undefined')
   deleteHistoryLine(); 

function deleteHistoryLine(){
   var fieldVal = current["new"];
   var fieldName = current.field;
    
   //Query for and delete the 'sys_audit' record
   var aud = new GlideRecord('sys_audit');
   aud.addQuery('documentkey', current.set.id);
   aud.addQuery('fieldname', fieldName);
   aud.addQuery('newvalue', fieldVal);
   aud.query();
   if(aud.next()){
      aud.deleteRecord();
   }
   
   //Query for and delete the 'sys_journal_field' record (if applicable)
   var je = new GlideRecord('sys_journal_field');
   je.addQuery('element_id', current.set.id);
   je.addQuery('element', fieldName);
   je.addQuery('value', fieldVal);
   je.query();
   if(je.next()){
      je.deleteRecord();
   }
   
   //Set redirect and info message for the parent record
   gs.addInfoMessage(current.label + " entry '" + fieldVal + "' deleted.");
   action.setRedirectURL(current.set.getRefRecord());
    
   //Delete the 'sys_history_line' record
   current.deleteRecord();
}

</syntaxhighlight> back to top

Add Button to Form but Display Selectively

[edit | edit source]

Example - Requests & Incidents by This Caller

UI Action Table: Call [new_call] Action: sysverb_insert_and_stay Condition: isAdvancedUI() && current.canCreate()

Script: 
action.setRedirectURL(current);
current.insert();
gs.include('ActionUtils');
var au = new ActionUtils();
au.postInsert(current);

back to top

UI Policy Short Description: Show Display Button if Status Call

When to apply: When Call Tpe is Status call

Scripts:

Execute if true <syntaxhighlight lang="javascript">

function onCondition() {
	var items = $$('BUTTON').each(function(item){
       if(item.innerHTML.indexOf('Incidents by This Caller') > -1){
           item.show();
       }
   });
}

</syntaxhighlight> Execute if false <syntaxhighlight lang="javascript">

function onCondition() {
	var items = $$('BUTTON').each(function(item){
       if(item.innerHTML.indexOf('Incidents by This Caller') > -1){
           item.hide();
       }
   });
}

</syntaxhighlight> back to top

UI Policies

[edit | edit source]

UI Policy Example

[edit | edit source]

<syntaxhighlight lang="javascript">

function onCondition() {
g_form.setValue('u_response_complete', 'true');
}

</syntaxhighlight>

UI Policy Script to place text under a field

[edit | edit source]

<syntaxhighlight lang="javascript">

function onCondition() {
  // Display a message under the Other field to explain what to put in the
  // Other field.
  g_form.showFieldMsg('u_other','Briefly explain what you need.','info');
}

</syntaxhighlight> back to top

UI Policy Script Example - Hiding/Displaying Fields

[edit | edit source]

<syntaxhighlight lang="javascript">

function onCondition() {
	g_form.setMandatory('u_on_hold_expiry', true);	
	g_form.setDisplay('u_on_hold_expiry', true);
		if(g_form.getValue('request_state') == 'on_hold_other'){
		g_form.setMandatory('u_on_hold_reason', true);
		g_form.setDisplay('u_on_hold_reason', true);
	}
}

</syntaxhighlight>


<syntaxhighlight lang="javascript">

function onCondition() {
	g_form.setDisplay('u_on_hold_expiry', true);
	g_form.setMandatory('u_on_hold_expiry', true);
	g_form.setMandatory('u_on_hold_reason', false);
	g_form.setDisplay('u_on_hold_reason', false);
}

</syntaxhighlight>

Make type read only for type Minor change requests

[edit | edit source]

Check URL for parameter sysparm_template

When to Apply: Type IS Minor

<syntaxhighlight lang="javascript">

 function onCondition() {
   var myParm = getParmVal('sysparm_template');
   if (myParm != "") {
     g_form.removeOption('type', 'Standard');
     g_form.setReadOnly(type, true);
   }

   function getParmVal(name) {
     var url = document.URL.parseQuery();
     if (url[name]) {
       return decodeURI(url[name]);
     } else {
       return "";
     }
   }
 }

</syntaxhighlight> back to top

Restrict a field to members of a specific group or members of the Assignment Group

[edit | edit source]

<syntaxhighlight lang=javascript line>

function onCondition() {

   //Is currently logged on user a member of Assginment Group
   var isMemberOfAssignmentGroup = false;
   var usrID = g_user.userID; //Get current user ID
   var assignmentGroup = new GlideRecord('sys_user_grmember');
   assignmentGroup.addQuery('assignmentGroup', g_form.getUniqueValue('assignnment_group'));
   assignmentGroup.addQuery('user', usrID);
   assignmentGroup.query(assignmentGroupMemberCallback);
   //Check to see if assigned to is a member of selected group
   var grpName = 'Security Operations';
   //var usrID = g_user.userID; //Get current user ID
   var grp = new GlideRecord('sys_user_grmember');
   grp.addQuery('group.name', grpName);
   grp.addQuery('user', usrID);
   grp.query(groupMemberCallback);
   function assignmentGroupMemberCallback(assignmentGroup) {
       if (assignmentGroup.next()) {
           isMemberOfAssignmentGroup = true;
       }
   }
   function groupMemberCallback(grp) {
       //If user is a member of selected group
       if (grp.next()) {
           alert('Security Ops user');
           g_form.setDisplay('u_restricted', true);
           g_form.setReadOnly('u_restricted', false);
       } else {
           if (isMemberOfAssignmentGroup) {
               alert('Member of assignment group');
               if (g_form.getValue('assignment_group')) {
                   g_form.setDisplay('u_restricted', true);
                   g_form.setReadOnly('u_restricted', true);
               } else {
                   g_form.setDisplay('u_restricted', false);
               }
           } else {
               alert('Everyone else');
               g_form.setDisplay('u_restricted', false);
               //g_form.setReadOnly('u_restricted', true);
           }
       }
   }

} </syntaxhighlight> back to top

Workflows

[edit | edit source]

Embed font uploaded to sys_attachment by sysid

[edit | edit source]
 <div> //may be needed to trick browser
 @font face {
 font-family: 'Bitter-Regular';
 src: url('/sys_attachment.do?sys_id=287c4c224f133200a83444b18110c7c2');
 }
 </div>

This worked in a template!

<head>
<link href="https://fonts.googleapis.com/css?family=Bitter" rel="stylesheet">
</head>

</syntaxhighlight> back to top

Workflow Update Script

[edit | edit source]

<syntaxhighlight lang="javascript"> var wf = new Workflow(); var ri = new GlideRecord("sc_req_item"); if (ri.get(current.request_item)) {

      wf.runFlows(ri, 'update');   

} </syntaxhighlight> back to top

Workflow Script To Determine Departmental Email Address

[edit | edit source]

<syntaxhighlight lang="javascript">

// Set the variable 'answer' to a comma-separated list of group ids or an array of group ids to add as approvers.
//
// For example:
//       var answer = [];
//       answer.push('id1');
//       answer.push('id2');
//
//
// To add a new department add the following clause before the final else clause:
//
// else if (dept.id == '<insert department id>') {
//     group.get('name', '<insert approval group name>');
// }
//

var group = new GlideRecord('sys_user_group'),
    dept = new GlideRecord('cmn_department');

dept.get(current.u_requested_for_department);

if(dept.id == 'IT') {
    group.get('name', 'Idea Request Approval - IT');
} else if (dept.id == 'OVC' || dept.id == 'Office of Vice Chancellor') {
    group.get('name', 'Idea Request Approval - OVC');
} else if (dept.id == 'Estates') {
    group.get('name', 'Idea Request Approval - Estates');
} else if (dept.id == 'F&P' || dept.id == 'FAP' || dept.id == 'FandP') {
    group.get('name', 'Idea Request Approval - Finance');
} else if (dept.id == 'SU' || dept.id == 'SUBU') {
    group.get('name', 'Idea Request Approval - SUBU');
} else if (dept.id == 'Academic Services' || dept.id == 'AS') {
    group.get('name', 'Idea Request Approval - AS');
} else if (dept.id == 'PRIME') {
    group.get('name', 'Idea Request Approval - Prime');
} else if (dept.id == 'PMO') {
    group.get('name', 'Idea Request Approval - PMO');
} else if (dept.id == 'GEHUB') {
    group.get('name', 'Idea Request Approval - GEHUB');
} else if (dept.id == 'HR') {
    group.get('name', 'Idea Request Approval - HR');
} else if (dept.id == 'LS') {
    group.get('name', 'Idea Request Approval - LS');
} else if (dept.id == 'R&KEO' || dept.id == 'RKEO') {
    group.get('name', 'Idea Request Approval - R&KEO');
} else if (dept.id == 'HSC' || dept.id == 'HSS') {
    group.get('name', 'Idea Request Approval - Health & Social Sciences');
} else if (dept.id == 'M&C' || dept.id == 'MAC') {
    group.get('name', 'Idea Request Approval - M&C');
} else if (dept.id == 'CEL') {
    group.get('name', 'Idea Request Approval - CEL');
} else if (dept.id == 'MS' || dept.id == 'Media School' || dept.id == 'FMC') {
    group.get('name', 'Idea Request Approval - MS');
} else if (dept.id == 'Graduate School') {
    group.get('name', 'Idea Request Approval - Graduate School');
} else if (dept.id == 'BS' || dept.id == 'School of Tourism') {
    group.get('name', 'Idea Request Approval - Faculty of Management');
} else if (dept.id == 'Kaplan') {
    group.get('name', 'Idea Request Approval - Kaplan');
} else if (dept.id == 'H&S' || dept.id == 'HandS' || dept.id == 'Health and Safety') {
    group.get('name', 'Idea Request Approval - H&S');
} else if (dept.id == 'Alumni' || dept.id == 'Fundraising') {
    group.get('name', 'Idea Request Approval - Alumni & Fundraising');
} else if (dept.id == 'SSS' || dept.id == 'Student Support Services') {
    group.get('name', 'Idea Request Approval - SSS');
} else if (dept.id == 'SciTech' || dept.id == 'DEC' || dept.id == 'ApSci') {
    group.get('name', 'Idea Request Approval - SciTech');
} else if (dept.id == 'TEST') {
    group.get('name', 'Idea Request Approval - TEST');
} else if (dept.id == 'FM') {
    group.get('name', 'Idea Request Approval - Faculty of Management'); //added AA for INCINC0125618
} else {
    current.comments += 'Approval has not been requested.\n\nThe department this has been requested for does not exist in the workflow 
activity. \n\nPlease contact a Service Now administrator.';
    var workflow = new Workflow();
    workflow.cancelContext(current);  
}

var answer = [];
answer.push(group.sys_id);

</syntaxhighlight> back to top

Approval - user Script

[edit | edit source]

<syntaxhighlight lang="javascript">

var answer = []; //Array to store list of approvers excluding the change requester
var approvalGroups = []; //Array to store list of approval groups 
/****************************************************
Add approval groups to the approvalGroups array below
*****************************************************/
approvalGroups = ['CAB - Digital Solutions', 'CAB - Buisness Information Systems', 'CAB - Windows Desktop', 'CAB - Information Security', 'CAB - Systems Team', 'CAB 

- Communications Team', 'CAB - Service Operations', 'CAB - Governance'];

for (var i = 0; i < approvalGroups.length; i++) { // loop round a number of times equal to the number of elements in the array
  var approvers = new GlideRecord('sys_user_grmember');
  approvers.addQuery('group.name', approvalGroups[i]); //Matches assignment group on change with the group membership table
  approvers.addQuery('user.name', '!=', current.requested_by.getDisplayValue()); //exclude 'requested_by'user 
  approvers.query();
  while (approvers.next()) {
    answer.push(approvers.user.sys_id);
  }
}

</syntaxhighlight> back to top</text>

     <sha1>6yt6wp1up1rqxhfu57x36qo4g7wq7k3</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
 </page>
 <page>
   <title>File:Workflow.jpg</title>
   <ns>6</ns>
   <id>39</id>
   <revision>
     <id>519</id>
     <timestamp>2019-08-30T08:20:49Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <comment>Example screenshot of a ServiceNow workflow</comment>
     <text xml:space="preserve" bytes="43">Example screenshot of a ServiceNow workflow</text>
     <sha1>9vziqubk3onug9txseu5tqh280gjgnx</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
 </page>
 <page>
   <title>ServiceNow/Enabling Services</title>
   <ns>0</ns>
   <id>40</id>
   <revision>
     <id>557</id>
     <timestamp>2019-10-21T15:39:07Z</timestamp>
     <contributor>
       <username>Cm4u10</username>
       <id>0</id>
     </contributor>
     <comment>/* Ingestion */</comment>
     <text xml:space="preserve" bytes="15627">

Student Services Centre Enabling Services (SSC ES) uses a custom-built application on the ServiceNow platform to manage interactions with students.

TBC - this might be defined as a discrete service.

Development

[edit | edit source]

Business

[edit | edit source]
  • What processes does this development support

Enabling Services Scoped Application

[edit | edit source]

This application was developed for Enabling Services who provide a wide variety of support for students who have disabilities, mental health conditions and/or specific learning difficulties.

WNAC (RITM based)

[edit | edit source]

"The Wessex Needs Assessment Centre (WNAC) at the University of Southampton and our three regional outreach centres provides advice on applying for Disabled Students’ Allowances (DSA), and can assess what support you may need."

Important contacts

[edit | edit source]

Helen Rowland - Head of Student Support (Education)

Julie Blackler - Enabling Services Deputy Manager

DPIA documentation in Sharepoint

Logins and Access

[edit | edit source]

Roles

[edit | edit source]

x_uno49_enabl_svc.admin (elevated)

[edit | edit source]

sys admins need to elevate to this role to perform admin tasks

x_uno49_enabl_svc.user

[edit | edit source]

role require to access the Enabling Services application

x_uno49_enabl_svc.enabling_services_customer_user

[edit | edit source]

role require to access the Enabling Services customer table

x_uno49_enabl_svc.enabling_services_case_user

[edit | edit source]

role require to access the Enabling Services case (folder) table

x_uno49_enabl_svc.enabling_services_interaction_user

[edit | edit source]

role require to access the Enabling Services interaction table

x_uno49_enabl_svc.enabling_services_task_user

[edit | edit source]

role require to access the Enabling Services task table

x_uno49_enabl_svc.enabling_services_health_condition_user

[edit | edit source]

(access to the health conditions table. Role x_uno49_enabl_svc.lookup_admin needed to create new records.)

x_uno49_enabl_svc.user_manager

[edit | edit source]

role required to administer users

x_uno49_enabl_svc.lookup_user

[edit | edit source]

role required to lookup users

x_uno49_enabl_svc.lookup_admin

[edit | edit source]

Role for administering lookup users

x_uno49_enabl_svc.wnac_user

[edit | edit source]

role required to access the WNAC modules

x_uno49_enabl_svc.counsellor

[edit | edit source]

Enabling Services counsellor role

  • Who manages access
  • How access is granted
  • Any special accounts (system, email etc.)

Design

[edit | edit source]

Tables

[edit | edit source]

x_uno49_enabl_svc_customer

Enabling Services Customer table

x_uno49_enabl_svc_case

Enabling Services Folder table

x_uno49_enabl_svc_interaction

Enabling Services Interaction table

x_uno49_enabl_svc_task

Enabling Services Task table

x_uno49_enabl_svc_lookup_referred_to

Enabling Services Lookup Referred To

x_uno49_enabl_svc_health_condition

Enabling Services Health Condition lookup table


[edit | edit source]
  • Enabling Services
    • My Work
    • My Group's Work
    • New Interaction
    • Enabling Inbox
    • First Support Inbox
    • Customers
    • Folders
    • Interactions
    • Tasks
  • Administration
    • Health Conditions
    • Referred To
  • WNAC
    • WNAC Scheduled Jobs (Only visible to admins)

Workflows

[edit | edit source]

WNAC Assessment Request Workflow

ES Counselling SLA workflow

ES Counselling Appt

ES reasonable adjustments

RITM base solution for the Wessex Needs Assessment Centre

Online form for needs assessment applications created and maintained using Sitepublisher. Page is maintained by Enabling Services staff.

https://www.southampton.ac.uk/edusupport/assessment_centre/appointments/wnac-appt-booking-form.page

Submitted form creates a Request/Requested Item and triggers workflow 'WNAC Assessment Request Workflow'.

Several SLAs are defined for WNAC assessment requests:

WNAC – 1.1.5 Appt Confirmed

WNAC – 1.1.9 Appt Held

WNAC – 1.4.5 Draft NAR to Student

WNAC – 1.4.6 Final NAR to FB

WNAC 1.4.6 NAR to FB Draft Req

WNAC – 5. Student Journey

WNAC – 5.2 Audit - Respond to Query

See WNAC State Transition Diagram for detail

Significant gotchas/deviations from ServiceNow OOTB behaviour

[edit | edit source]

Scheduled jobs

[edit | edit source]

WNAC appointment reminder

[edit | edit source]

Send a text message to customers with appointments the next day.

Triggers email notification 'WNAC Appointment Reminder'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^cat_item=bbcbd724db8d33c0f81bee71ca9619d7^variables.01391172db757700f91c8c994b96199cONTomorrow@javascript:gs.beginningOfTomorrow()@javascript:gs.endOfTomorrow()"); gr.query(); while(gr.next()) { var phone = gr.variable_pool.wnac_phone_number.toString(); if (phone.substring(0,2) == '07') { var gDate = new GlideDate(); gDate.setValue(gr.variable_pool.date_and_time_of_meeting); var gDateTime = new GlideDateTime(gDate); gt = gDateTime.getLocalTime(); gs.eventQueue("x_uno49_enabl_svc.wnac_appointment", gr, gr.variable_pool.wnac_phone_number + '@sms.textapp.net', 'This is a reminder of your WNAC meeting on ' + gDate.getByFormat('dd/MM') + ' at ' + gt.getByFormat('HH:mm') + ' with ' + gr.variable_pool.wnac_assessor.toString().replace(/^([^\s]+)\s.*/, "$1") + ' at ' + gr.variable_pool.wnac_assessment_centre + '. If you cannot attend, please reply or call 02380 597233 asap.'); } } </syntaxhighlight>

WNAC 7 day survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 7 Day Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@8^closed_atRELATIVELT@dayofweek@ago@7^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_7day_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

WNAC 12 month survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 12 Month Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@365^closed_atRELATIVELT@dayofweek@ago@364^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_12month_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

global.LogEmailAttachment

[edit | edit source]

called to add a note to work notes detailing email attachments sent from WNAC REQTASKs

Write details of outbound email attachments to work notes.

Called from business rule 'ES log sent email attachments'

sys_id of email interaction passed as parameter 'esintref'

Lookup email(s) for interaction in sys_email table - lookup attachments for that email - write details of attachments found to Work Notes of the interaction.

<syntaxhighlight lang="javascript"> var LogEmailAttachment = Class.create(); LogEmailAttachment.prototype = { initialize: function() { }, LogEmailAttachment: function(esintref) { //sys_id of interaction var eml = new GlideRecord('sys_email'); eml.addQuery('instance', esintref); eml.addQuery('target_table', 'x_uno49_enabl_svc_interaction'); eml.query(); while(eml.next()) { var att = new GlideRecord('sys_attachment'); att.addQuery('table_name', 'sys_email'); att.addQuery('table_sys_id', eml.getUniqueValue()); //sys_id of the email att.query(); while(att.next()) { var esint = new GlideRecord('x_uno49_enabl_svc_interaction'); esint.addQuery('sys_id', esintref); //sys_id of interaction esint.query(); while(esint.next()) { esint.work_notes = 'Email ' + '"' + eml.subject + '" sent with attachment ' + '"' + att.file_name +'"'; esint.update(); }

} } }, type: 'LogEmailAttachment' }; </syntaxhighlight>

Maintenance

[edit | edit source]
  • Any regular activity to maintain/support the service

WNAC Assessors

[edit | edit source]

When adding a WNAC Assessor they should be added to the following groups:

  • ES WNAC - Assessors
  • ES Assessor - <user.name> (each assessor has their own group)
  • ES WNAC - QA
  • ES WNAC - AUB
  • ES WNAC - Highfield
  • ES WNAC - Solent

Duplicate Customers

[edit | edit source]

Use the following background script to find occurrences of duplicate ES customers

<syntaxhighlight lang="javascript"> var dupRecords = [];

    var gaDupCheck1 = new GlideAggregate('x_uno49_enabl_svc_customer');
    //gaDupCheck1.addQuery('active','true');
    gaDupCheck1.addAggregate('COUNT', 'user');
    gaDupCheck1.groupBy('user');
    gaDupCheck1.addHaving('COUNT', '>', 1);
    gaDupCheck1.query();
    while (gaDupCheck1.next()) {
          dupRecords.push(gaDupCheck1.user.toString());
    }
    gs.print(dupRecords);

</syntaxhighlight>

Find records where interaction customer != folder customer

[edit | edit source]

Background script to find interactions which have been put in the incorrect customer folder.

<syntaxhighlight lang="javascript"> gs.addInfoMessage('Here we go - Find records where interaction customer != folder customer'); var notMatch = []; var gr = new GlideRecord('x_uno49_enabl_svc_interaction');

   gr.addQuery('customer' != );
   gr.query();
   while (gr.next()) {

           if(gr.customer != gr.parent.customer && gr.customer !=  && gr.parent.customer !=  && gr.parent.customer != undefined) {
           gs.addInfoMessage(gr.number + ': Customer = ' + gr.customer.getDisplayValue() + ' : interaction is in folder ' + gr.parent.getDisplayValue() + ' which belongs to ' + gr.parent.customer.getDisplayValue());
           notMatch.push(gr.number.toString());
        }
   }

gs.print('Interactions where customer does not match customer on parent folder: ' + notMatch); </syntaxhighlight>


Retention policy

[edit | edit source]

Link to email regarding retention policy

Interfaces

[edit | edit source]

Ingestion

[edit | edit source]

Emails sent to enable@soton.ac.uk from a student account will create an 'Interaction' record for that student. Emails sent to enable@soton.ac.uk from non-student email accounts will create an interaction with no customer details. These will appear in the 'Inbox' where they can be triaged and customer details may be added. Emails to First Support email addresses are handled similarly. Watermarked emails are handled in the usual ServiceNow way and will be matched with and existing customer/folder.

Inbound Email Actions

[edit | edit source]
  • Create ES Interaction from new email
  • ES - Interaction from reply
  • Create ES Int and task FS

Business Rule to create a task for each attachment on an inbound email

[edit | edit source]
  • Create Task From Interaction
  • Create Task From Reply Interaction
  • Create Task From Interaction FS

Global Script Include

[edit | edit source]

global.CopySpecificAttachment

Global script include used by the Enabling Services application. Requires the parameters: donorTable, donorID, recipientTable, recipientID, fileName

Called by: Business Rule - Create Task From Interaction

Also deletes attachment from donor table after copying to recipient table. <syntaxhighlight lang="javascript"> var CopySpecificAttachment = Class.create(); CopySpecificAttachment.prototype = { initialize: function() { }, CopySpecificAttachment: function(donorTable, donorID, recipientTable, recipientID, fileName) { try{ var donorAttSysID; var newAttRecord; var linkToNewRecord; var attDataRecord; var newDocRecord; var attRecord = new GlideRecord('sys_attachment'); //find record(s) in sys_attachment attRecord.addQuery('table_name', donorTable); attRecord.addQuery('table_sys_id', donorID); attRecord.addQuery('file_name', fileName); attRecord.query(); while (attRecord.next()) { //loop for each record found in sys_attachment donorAttSysID = attRecord.getValue('sys_id'); newAttRecord = this.copyRecord(attRecord); //call function to copy attachment to the child record newAttRecord.setValue('table_name', recipientTable); newAttRecord.setValue('table_sys_id', recipientID); newAttRecord.update(); linkToNewRecord = gs.getProperty('glide.servlet.uri') + newAttRecord.getLink(); attDataRecord = new GlideRecord('sys_attachment_doc'); attDataRecord.addQuery('sys_attachment', donorAttSysID); //find matching records in sys_attachment_doc attDataRecord.query(); while (attDataRecord.next()) { newDocRecord = this.copyRecord(attDataRecord); //copy attachment parts to child record newDocRecord.setValue('sys_attachment', newAttRecord.getValue('sys_id')); newDocRecord.update(); } try{ attRecord.deleteRecord(); //delete attachment from parent record } catch(err) { gs.log('====> error deleting attachment: message['+err.message+']'); } } } catch(err) { gs.log('====> error in CopySpecificAttachment: message['+err.message+']'); }

}, copyRecord: function(record) { //function to copy attachment(s) to the child record try{ var recordElement; var recordElementName; var recordTable = record.getTableName(); var recordFields = record.getFields(); var newRecord = new GlideRecord(recordTable); newRecord.initialize(); for (var i = 0; i < recordFields.size(); i++) { recordElement = recordFields.get(i); if(recordElement.getName() != 'sys_id' && recordElement.getName() != 'number') { recordElementName = recordElement.getName(); newRecord.setValue(recordElementName, record.getValue(recordElementName)); } } var newSysId = newRecord.insert(); return newRecord; } catch(err) { gs.log('====> error in CopySpecificAttachment.copyRecord: message['+err.message+']'); } }, type: 'CopySpecificAttachment' }; </syntaxhighlight>

[edit | edit source]

See ServiceNow/Interface/Banner.

Reporting

[edit | edit source]
  • Reports delivered as part of the project</text>
     <sha1>5zmlr1evr9qab1koxfkvobmsvamqy8r</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
   <revision>
     <id>558</id>
     <parentid>557</parentid>
     <timestamp>2019-10-22T09:17:27Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <minor/>
     <comment>1 revision: Test</comment>
     <text xml:space="preserve" bytes="15627">

Student Services Centre Enabling Services (SSC ES) uses a custom-built application on the ServiceNow platform to manage interactions with students.

TBC - this might be defined as a discrete service.

Development

[edit | edit source]

Business

[edit | edit source]
  • What processes does this development support

Enabling Services Scoped Application

[edit | edit source]

This application was developed for Enabling Services who provide a wide variety of support for students who have disabilities, mental health conditions and/or specific learning difficulties.

WNAC (RITM based)

[edit | edit source]

"The Wessex Needs Assessment Centre (WNAC) at the University of Southampton and our three regional outreach centres provides advice on applying for Disabled Students’ Allowances (DSA), and can assess what support you may need."

Important contacts

[edit | edit source]

Helen Rowland - Head of Student Support (Education)

Julie Blackler - Enabling Services Deputy Manager

DPIA documentation in Sharepoint

Logins and Access

[edit | edit source]

Roles

[edit | edit source]

x_uno49_enabl_svc.admin (elevated)

[edit | edit source]

sys admins need to elevate to this role to perform admin tasks

x_uno49_enabl_svc.user

[edit | edit source]

role require to access the Enabling Services application

x_uno49_enabl_svc.enabling_services_customer_user

[edit | edit source]

role require to access the Enabling Services customer table

x_uno49_enabl_svc.enabling_services_case_user

[edit | edit source]

role require to access the Enabling Services case (folder) table

x_uno49_enabl_svc.enabling_services_interaction_user

[edit | edit source]

role require to access the Enabling Services interaction table

x_uno49_enabl_svc.enabling_services_task_user

[edit | edit source]

role require to access the Enabling Services task table

x_uno49_enabl_svc.enabling_services_health_condition_user

[edit | edit source]

(access to the health conditions table. Role x_uno49_enabl_svc.lookup_admin needed to create new records.)

x_uno49_enabl_svc.user_manager

[edit | edit source]

role required to administer users

x_uno49_enabl_svc.lookup_user

[edit | edit source]

role required to lookup users

x_uno49_enabl_svc.lookup_admin

[edit | edit source]

Role for administering lookup users

x_uno49_enabl_svc.wnac_user

[edit | edit source]

role required to access the WNAC modules

x_uno49_enabl_svc.counsellor

[edit | edit source]

Enabling Services counsellor role

  • Who manages access
  • How access is granted
  • Any special accounts (system, email etc.)

Design

[edit | edit source]

Tables

[edit | edit source]

x_uno49_enabl_svc_customer

Enabling Services Customer table

x_uno49_enabl_svc_case

Enabling Services Folder table

x_uno49_enabl_svc_interaction

Enabling Services Interaction table

x_uno49_enabl_svc_task

Enabling Services Task table

x_uno49_enabl_svc_lookup_referred_to

Enabling Services Lookup Referred To

x_uno49_enabl_svc_health_condition

Enabling Services Health Condition lookup table


[edit | edit source]
  • Enabling Services
    • My Work
    • My Group's Work
    • New Interaction
    • Enabling Inbox
    • First Support Inbox
    • Customers
    • Folders
    • Interactions
    • Tasks
  • Administration
    • Health Conditions
    • Referred To
  • WNAC
    • WNAC Scheduled Jobs (Only visible to admins)

Workflows

[edit | edit source]

WNAC Assessment Request Workflow

ES Counselling SLA workflow

ES Counselling Appt

ES reasonable adjustments

RITM base solution for the Wessex Needs Assessment Centre

Online form for needs assessment applications created and maintained using Sitepublisher. Page is maintained by Enabling Services staff.

https://www.southampton.ac.uk/edusupport/assessment_centre/appointments/wnac-appt-booking-form.page

Submitted form creates a Request/Requested Item and triggers workflow 'WNAC Assessment Request Workflow'.

Several SLAs are defined for WNAC assessment requests:

WNAC – 1.1.5 Appt Confirmed

WNAC – 1.1.9 Appt Held

WNAC – 1.4.5 Draft NAR to Student

WNAC – 1.4.6 Final NAR to FB

WNAC 1.4.6 NAR to FB Draft Req

WNAC – 5. Student Journey

WNAC – 5.2 Audit - Respond to Query

See WNAC State Transition Diagram for detail

Significant gotchas/deviations from ServiceNow OOTB behaviour

[edit | edit source]

Scheduled jobs

[edit | edit source]

WNAC appointment reminder

[edit | edit source]

Send a text message to customers with appointments the next day.

Triggers email notification 'WNAC Appointment Reminder'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^cat_item=bbcbd724db8d33c0f81bee71ca9619d7^variables.01391172db757700f91c8c994b96199cONTomorrow@javascript:gs.beginningOfTomorrow()@javascript:gs.endOfTomorrow()"); gr.query(); while(gr.next()) { var phone = gr.variable_pool.wnac_phone_number.toString(); if (phone.substring(0,2) == '07') { var gDate = new GlideDate(); gDate.setValue(gr.variable_pool.date_and_time_of_meeting); var gDateTime = new GlideDateTime(gDate); gt = gDateTime.getLocalTime(); gs.eventQueue("x_uno49_enabl_svc.wnac_appointment", gr, gr.variable_pool.wnac_phone_number + '@sms.textapp.net', 'This is a reminder of your WNAC meeting on ' + gDate.getByFormat('dd/MM') + ' at ' + gt.getByFormat('HH:mm') + ' with ' + gr.variable_pool.wnac_assessor.toString().replace(/^([^\s]+)\s.*/, "$1") + ' at ' + gr.variable_pool.wnac_assessment_centre + '. If you cannot attend, please reply or call 02380 597233 asap.'); } } </syntaxhighlight>

WNAC 7 day survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 7 Day Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@8^closed_atRELATIVELT@dayofweek@ago@7^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_7day_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

WNAC 12 month survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 12 Month Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@365^closed_atRELATIVELT@dayofweek@ago@364^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_12month_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

global.LogEmailAttachment

[edit | edit source]

called to add a note to work notes detailing email attachments sent from WNAC REQTASKs

Write details of outbound email attachments to work notes.

Called from business rule 'ES log sent email attachments'

sys_id of email interaction passed as parameter 'esintref'

Lookup email(s) for interaction in sys_email table - lookup attachments for that email - write details of attachments found to Work Notes of the interaction.

<syntaxhighlight lang="javascript"> var LogEmailAttachment = Class.create(); LogEmailAttachment.prototype = { initialize: function() { }, LogEmailAttachment: function(esintref) { //sys_id of interaction var eml = new GlideRecord('sys_email'); eml.addQuery('instance', esintref); eml.addQuery('target_table', 'x_uno49_enabl_svc_interaction'); eml.query(); while(eml.next()) { var att = new GlideRecord('sys_attachment'); att.addQuery('table_name', 'sys_email'); att.addQuery('table_sys_id', eml.getUniqueValue()); //sys_id of the email att.query(); while(att.next()) { var esint = new GlideRecord('x_uno49_enabl_svc_interaction'); esint.addQuery('sys_id', esintref); //sys_id of interaction esint.query(); while(esint.next()) { esint.work_notes = 'Email ' + '"' + eml.subject + '" sent with attachment ' + '"' + att.file_name +'"'; esint.update(); }

} } }, type: 'LogEmailAttachment' }; </syntaxhighlight>

Maintenance

[edit | edit source]
  • Any regular activity to maintain/support the service

WNAC Assessors

[edit | edit source]

When adding a WNAC Assessor they should be added to the following groups:

  • ES WNAC - Assessors
  • ES Assessor - <user.name> (each assessor has their own group)
  • ES WNAC - QA
  • ES WNAC - AUB
  • ES WNAC - Highfield
  • ES WNAC - Solent

Duplicate Customers

[edit | edit source]

Use the following background script to find occurrences of duplicate ES customers

<syntaxhighlight lang="javascript"> var dupRecords = [];

    var gaDupCheck1 = new GlideAggregate('x_uno49_enabl_svc_customer');
    //gaDupCheck1.addQuery('active','true');
    gaDupCheck1.addAggregate('COUNT', 'user');
    gaDupCheck1.groupBy('user');
    gaDupCheck1.addHaving('COUNT', '>', 1);
    gaDupCheck1.query();
    while (gaDupCheck1.next()) {
          dupRecords.push(gaDupCheck1.user.toString());
    }
    gs.print(dupRecords);

</syntaxhighlight>

Find records where interaction customer != folder customer

[edit | edit source]

Background script to find interactions which have been put in the incorrect customer folder.

<syntaxhighlight lang="javascript"> gs.addInfoMessage('Here we go - Find records where interaction customer != folder customer'); var notMatch = []; var gr = new GlideRecord('x_uno49_enabl_svc_interaction');

   gr.addQuery('customer' != );
   gr.query();
   while (gr.next()) {

           if(gr.customer != gr.parent.customer && gr.customer !=  && gr.parent.customer !=  && gr.parent.customer != undefined) {
           gs.addInfoMessage(gr.number + ': Customer = ' + gr.customer.getDisplayValue() + ' : interaction is in folder ' + gr.parent.getDisplayValue() + ' which belongs to ' + gr.parent.customer.getDisplayValue());
           notMatch.push(gr.number.toString());
        }
   }

gs.print('Interactions where customer does not match customer on parent folder: ' + notMatch); </syntaxhighlight>


Retention policy

[edit | edit source]

Link to email regarding retention policy

Interfaces

[edit | edit source]

Ingestion

[edit | edit source]

Emails sent to enable@soton.ac.uk from a student account will create an 'Interaction' record for that student. Emails sent to enable@soton.ac.uk from non-student email accounts will create an interaction with no customer details. These will appear in the 'Inbox' where they can be triaged and customer details may be added. Emails to First Support email addresses are handled similarly. Watermarked emails are handled in the usual ServiceNow way and will be matched with and existing customer/folder.

Inbound Email Actions

[edit | edit source]
  • Create ES Interaction from new email
  • ES - Interaction from reply
  • Create ES Int and task FS

Business Rule to create a task for each attachment on an inbound email

[edit | edit source]
  • Create Task From Interaction
  • Create Task From Reply Interaction
  • Create Task From Interaction FS

Global Script Include

[edit | edit source]

global.CopySpecificAttachment

Global script include used by the Enabling Services application. Requires the parameters: donorTable, donorID, recipientTable, recipientID, fileName

Called by: Business Rule - Create Task From Interaction

Also deletes attachment from donor table after copying to recipient table. <syntaxhighlight lang="javascript"> var CopySpecificAttachment = Class.create(); CopySpecificAttachment.prototype = { initialize: function() { }, CopySpecificAttachment: function(donorTable, donorID, recipientTable, recipientID, fileName) { try{ var donorAttSysID; var newAttRecord; var linkToNewRecord; var attDataRecord; var newDocRecord; var attRecord = new GlideRecord('sys_attachment'); //find record(s) in sys_attachment attRecord.addQuery('table_name', donorTable); attRecord.addQuery('table_sys_id', donorID); attRecord.addQuery('file_name', fileName); attRecord.query(); while (attRecord.next()) { //loop for each record found in sys_attachment donorAttSysID = attRecord.getValue('sys_id'); newAttRecord = this.copyRecord(attRecord); //call function to copy attachment to the child record newAttRecord.setValue('table_name', recipientTable); newAttRecord.setValue('table_sys_id', recipientID); newAttRecord.update(); linkToNewRecord = gs.getProperty('glide.servlet.uri') + newAttRecord.getLink(); attDataRecord = new GlideRecord('sys_attachment_doc'); attDataRecord.addQuery('sys_attachment', donorAttSysID); //find matching records in sys_attachment_doc attDataRecord.query(); while (attDataRecord.next()) { newDocRecord = this.copyRecord(attDataRecord); //copy attachment parts to child record newDocRecord.setValue('sys_attachment', newAttRecord.getValue('sys_id')); newDocRecord.update(); } try{ attRecord.deleteRecord(); //delete attachment from parent record } catch(err) { gs.log('====> error deleting attachment: message['+err.message+']'); } } } catch(err) { gs.log('====> error in CopySpecificAttachment: message['+err.message+']'); }

}, copyRecord: function(record) { //function to copy attachment(s) to the child record try{ var recordElement; var recordElementName; var recordTable = record.getTableName(); var recordFields = record.getFields(); var newRecord = new GlideRecord(recordTable); newRecord.initialize(); for (var i = 0; i < recordFields.size(); i++) { recordElement = recordFields.get(i); if(recordElement.getName() != 'sys_id' && recordElement.getName() != 'number') { recordElementName = recordElement.getName(); newRecord.setValue(recordElementName, record.getValue(recordElementName)); } } var newSysId = newRecord.insert(); return newRecord; } catch(err) { gs.log('====> error in CopySpecificAttachment.copyRecord: message['+err.message+']'); } }, type: 'CopySpecificAttachment' }; </syntaxhighlight>

[edit | edit source]

See ServiceNow/Interface/Banner.

Reporting

[edit | edit source]
  • Reports delivered as part of the project</text>
     <sha1>5zmlr1evr9qab1koxfkvobmsvamqy8r</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
   <revision>
     <id>559</id>
     <parentid>558</parentid>
     <timestamp>2019-10-22T09:20:05Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <text xml:space="preserve" bytes="15559">

Student Services Centre Enabling Services (SSC ES) uses a custom-built application on the ServiceNow platform to manage interactions with students.

TBC - this might be defined as a discrete service.

Development

[edit | edit source]

Business

[edit | edit source]
  • What processes does this development support

Enabling Services Scoped Application

[edit | edit source]

This application was developed for Enabling Services who provide a wide variety of support for students who have disabilities, mental health conditions and/or specific learning difficulties.

WNAC (RITM based)

[edit | edit source]

"The Wessex Needs Assessment Centre (WNAC) at the University of Southampton and our three regional outreach centres provides advice on applying for Disabled Students’ Allowances (DSA), and can assess what support you may need."

Important contacts

[edit | edit source]

Helen Rowland - Head of Student Support (Education)

Julie Blackler - Enabling Services Deputy Manager

DPIA documentation in Sharepoint

Logins and Access

[edit | edit source]

Roles

[edit | edit source]

x_uno49_enabl_svc.admin (elevated)

[edit | edit source]

sys admins need to elevate to this role to perform admin tasks

x_uno49_enabl_svc.user

[edit | edit source]

role require to access the Enabling Services application

x_uno49_enabl_svc.enabling_services_customer_user

[edit | edit source]

role require to access the Enabling Services customer table

x_uno49_enabl_svc.enabling_services_case_user

[edit | edit source]

role require to access the Enabling Services case (folder) table

x_uno49_enabl_svc.enabling_services_interaction_user

[edit | edit source]

role require to access the Enabling Services interaction table

x_uno49_enabl_svc.enabling_services_task_user

[edit | edit source]

role require to access the Enabling Services task table

x_uno49_enabl_svc.enabling_services_health_condition_user

[edit | edit source]

(access to the health conditions table. Role x_uno49_enabl_svc.lookup_admin needed to create new records.)

x_uno49_enabl_svc.user_manager

[edit | edit source]

role required to administer users

x_uno49_enabl_svc.lookup_user

[edit | edit source]

role required to lookup users

x_uno49_enabl_svc.lookup_admin

[edit | edit source]

Role for administering lookup users

x_uno49_enabl_svc.wnac_user

[edit | edit source]

role required to access the WNAC modules

x_uno49_enabl_svc.counsellor

[edit | edit source]

Enabling Services counsellor role

  • Who manages access
  • How access is granted
  • Any special accounts (system, email etc.)

Design

[edit | edit source]

Tables

[edit | edit source]

x_uno49_enabl_svc_customer

Enabling Services Customer table

x_uno49_enabl_svc_case

Enabling Services Folder table

x_uno49_enabl_svc_interaction

Enabling Services Interaction table

x_uno49_enabl_svc_task

Enabling Services Task table

x_uno49_enabl_svc_lookup_referred_to

Enabling Services Lookup Referred To

x_uno49_enabl_svc_health_condition

Enabling Services Health Condition lookup table


[edit | edit source]
  • Enabling Services
    • My Work
    • My Group's Work
    • New Interaction
    • Enabling Inbox
    • First Support Inbox
    • Customers
    • Folders
    • Interactions
    • Tasks
  • Administration
    • Health Conditions
    • Referred To
  • WNAC
    • WNAC Scheduled Jobs (Only visible to admins)

Workflows

[edit | edit source]

WNAC Assessment Request Workflow

ES Counselling SLA workflow

ES Counselling Appt

ES reasonable adjustments

RITM base solution for the Wessex Needs Assessment Centre

Online form for needs assessment applications created and maintained using Sitepublisher. Page is maintained by Enabling Services staff.

https://www.southampton.ac.uk/edusupport/assessment_centre/appointments/wnac-appt-booking-form.page

Submitted form creates a Request/Requested Item and triggers workflow 'WNAC Assessment Request Workflow'.

Several SLAs are defined for WNAC assessment requests:

WNAC – 1.1.5 Appt Confirmed

WNAC – 1.1.9 Appt Held

WNAC – 1.4.5 Draft NAR to Student

WNAC – 1.4.6 Final NAR to FB

WNAC 1.4.6 NAR to FB Draft Req

WNAC – 5. Student Journey

WNAC – 5.2 Audit - Respond to Query

See WNAC State Transition Diagram for detail

Significant gotchas/deviations from ServiceNow OOTB behaviour

[edit | edit source]

Scheduled jobs

[edit | edit source]

WNAC appointment reminder

[edit | edit source]

Send a text message to customers with appointments the next day.

Triggers email notification 'WNAC Appointment Reminder'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^cat_item=bbcbd724db8d33c0f81bee71ca9619d7^variables.01391172db757700f91c8c994b96199cONTomorrow@javascript:gs.beginningOfTomorrow()@javascript:gs.endOfTomorrow()"); gr.query(); while(gr.next()) { var phone = gr.variable_pool.wnac_phone_number.toString(); if (phone.substring(0,2) == '07') { var gDate = new GlideDate(); gDate.setValue(gr.variable_pool.date_and_time_of_meeting); var gDateTime = new GlideDateTime(gDate); gt = gDateTime.getLocalTime(); gs.eventQueue("x_uno49_enabl_svc.wnac_appointment", gr, gr.variable_pool.wnac_phone_number + '@sms.textapp.net', 'This is a reminder of your WNAC meeting on ' + gDate.getByFormat('dd/MM') + ' at ' + gt.getByFormat('HH:mm') + ' with ' + gr.variable_pool.wnac_assessor.toString().replace(/^([^\s]+)\s.*/, "$1") + ' at ' + gr.variable_pool.wnac_assessment_centre + '. If you cannot attend, please reply or call 02380 597233 asap.'); } } </syntaxhighlight>

WNAC 7 day survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 7 Day Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@8^closed_atRELATIVELT@dayofweek@ago@7^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_7day_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

WNAC 12 month survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 12 Month Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@365^closed_atRELATIVELT@dayofweek@ago@364^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_12month_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

global.LogEmailAttachment

[edit | edit source]

called to add a note to work notes detailing email attachments sent from WNAC REQTASKs

Write details of outbound email attachments to work notes.

Called from business rule 'ES log sent email attachments'

sys_id of email interaction passed as parameter 'esintref'

Lookup email(s) for interaction in sys_email table - lookup attachments for that email - write details of attachments found to Work Notes of the interaction.

<syntaxhighlight lang="javascript"> var LogEmailAttachment = Class.create(); LogEmailAttachment.prototype = { initialize: function() { }, LogEmailAttachment: function(esintref) { //sys_id of interaction var eml = new GlideRecord('sys_email'); eml.addQuery('instance', esintref); eml.addQuery('target_table', 'x_uno49_enabl_svc_interaction'); eml.query(); while(eml.next()) { var att = new GlideRecord('sys_attachment'); att.addQuery('table_name', 'sys_email'); att.addQuery('table_sys_id', eml.getUniqueValue()); //sys_id of the email att.query(); while(att.next()) { var esint = new GlideRecord('x_uno49_enabl_svc_interaction'); esint.addQuery('sys_id', esintref); //sys_id of interaction esint.query(); while(esint.next()) { esint.work_notes = 'Email ' + '"' + eml.subject + '" sent with attachment ' + '"' + att.file_name +'"'; esint.update(); }

} } }, type: 'LogEmailAttachment' }; </syntaxhighlight>

Maintenance

[edit | edit source]
  • Any regular activity to maintain/support the service

WNAC Assessors

[edit | edit source]

When adding a WNAC Assessor they should be added to the following groups:

  • ES WNAC - Assessors
  • ES Assessor - <user.name> (each assessor has their own group)
  • ES WNAC - QA
  • ES WNAC - AUB
  • ES WNAC - Highfield
  • ES WNAC - Solent

Duplicate Customers

[edit | edit source]

Use the following background script to find occurrences of duplicate ES customers

<syntaxhighlight lang="javascript"> var dupRecords = [];

    var gaDupCheck1 = new GlideAggregate('x_uno49_enabl_svc_customer');
    //gaDupCheck1.addQuery('active','true');
    gaDupCheck1.addAggregate('COUNT', 'user');
    gaDupCheck1.groupBy('user');
    gaDupCheck1.addHaving('COUNT', '>', 1);
    gaDupCheck1.query();
    while (gaDupCheck1.next()) {
          dupRecords.push(gaDupCheck1.user.toString());
    }
    gs.print(dupRecords);

</syntaxhighlight>

Find records where interaction customer != folder customer

[edit | edit source]

Background script to find interactions which have been put in the incorrect customer folder.

<syntaxhighlight lang="javascript"> gs.addInfoMessage('Here we go - Find records where interaction customer != folder customer'); var notMatch = []; var gr = new GlideRecord('x_uno49_enabl_svc_interaction');

   gr.addQuery('customer' != );
   gr.query();
   while (gr.next()) {

           if(gr.customer != gr.parent.customer && gr.customer !=  && gr.parent.customer !=  && gr.parent.customer != undefined) {
           gs.addInfoMessage(gr.number + ': Customer = ' + gr.customer.getDisplayValue() + ' : interaction is in folder ' + gr.parent.getDisplayValue() + ' which belongs to ' + gr.parent.customer.getDisplayValue());
           notMatch.push(gr.number.toString());
        }
   }

gs.print('Interactions where customer does not match customer on parent folder: ' + notMatch); </syntaxhighlight>


Retention policy

[edit | edit source]

Link to email regarding retention policy

Interfaces

[edit | edit source]

Ingestion

[edit | edit source]

Emails sent to enable@soton.ac.uk from a student account will create an 'Interaction' record for that student. Emails sent to enable@soton.ac.uk from non-student email accounts will create an interaction with no customer details. These will appear in the 'Inbox' where they can be triaged and customer details may be added. Emails to First Support email addresses are handled similarly. Watermarked emails are handled in the usual ServiceNow way and will be matched with and existing customer/folder.

Inbound Email Actions

[edit | edit source]
  • Create ES Interaction from new email
  • ES - Interaction from reply
  • Create ES Int and task FS

Business Rule to create a task for each attachment on an inbound email

[edit | edit source]
  • Create Task From Interaction
  • Create Task From Reply Interaction
  • Create Task From Interaction FS

Global Script Include

[edit | edit source]

global.CopySpecificAttachment

Global script include used by the Enabling Services application. Requires the parameters: donorTable, donorID, recipientTable, recipientID, fileName

Called by: Business Rule - Create Task From Interaction

Also deletes attachment from donor table after copying to recipient table. <syntaxhighlight lang="javascript"> var CopySpecificAttachment = Class.create(); CopySpecificAttachment.prototype = { initialize: function() { }, CopySpecificAttachment: function(donorTable, donorID, recipientTable, recipientID, fileName) { try{ var donorAttSysID; var newAttRecord; var linkToNewRecord; var attDataRecord; var newDocRecord; var attRecord = new GlideRecord('sys_attachment'); //find record(s) in sys_attachment attRecord.addQuery('table_name', donorTable); attRecord.addQuery('table_sys_id', donorID); attRecord.addQuery('file_name', fileName); attRecord.query(); while (attRecord.next()) { //loop for each record found in sys_attachment donorAttSysID = attRecord.getValue('sys_id'); newAttRecord = this.copyRecord(attRecord); //call function to copy attachment to the child record newAttRecord.setValue('table_name', recipientTable); newAttRecord.setValue('table_sys_id', recipientID); newAttRecord.update(); linkToNewRecord = gs.getProperty('glide.servlet.uri') + newAttRecord.getLink(); attDataRecord = new GlideRecord('sys_attachment_doc'); attDataRecord.addQuery('sys_attachment', donorAttSysID); //find matching records in sys_attachment_doc attDataRecord.query(); while (attDataRecord.next()) { newDocRecord = this.copyRecord(attDataRecord); //copy attachment parts to child record newDocRecord.setValue('sys_attachment', newAttRecord.getValue('sys_id')); newDocRecord.update(); } try{ attRecord.deleteRecord(); //delete attachment from parent record } catch(err) { gs.log('====> error deleting attachment: message['+err.message+']'); } } } catch(err) { gs.log('====> error in CopySpecificAttachment: message['+err.message+']'); }

}, copyRecord: function(record) { //function to copy attachment(s) to the child record try{ var recordElement; var recordElementName; var recordTable = record.getTableName(); var recordFields = record.getFields(); var newRecord = new GlideRecord(recordTable); newRecord.initialize(); for (var i = 0; i < recordFields.size(); i++) { recordElement = recordFields.get(i); if(recordElement.getName() != 'sys_id' && recordElement.getName() != 'number') { recordElementName = recordElement.getName(); newRecord.setValue(recordElementName, record.getValue(recordElementName)); } } var newSysId = newRecord.insert(); return newRecord; } catch(err) { gs.log('====> error in CopySpecificAttachment.copyRecord: message['+err.message+']'); } }, type: 'CopySpecificAttachment' }; </syntaxhighlight>

[edit | edit source]

See ServiceNow/Interface/Banner.

Reporting

[edit | edit source]
  • Reports delivered as part of the project</text>
     <sha1>ms7l9ri0t6ur6awy313pb2j54fxlgty</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
   <revision>
     <id>562</id>
     <parentid>559</parentid>
     <timestamp>2019-10-22T09:42:13Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <text xml:space="preserve" bytes="15579">Template:Toclimit

Student Services Centre Enabling Services (SSC ES) uses a custom-built application on the ServiceNow platform to manage interactions with students.

TBC - this might be defined as a discrete service.

Development

[edit | edit source]

Business

[edit | edit source]
  • What processes does this development support

Enabling Services Scoped Application

[edit | edit source]

This application was developed for Enabling Services who provide a wide variety of support for students who have disabilities, mental health conditions and/or specific learning difficulties.

WNAC (RITM based)

[edit | edit source]

"The Wessex Needs Assessment Centre (WNAC) at the University of Southampton and our three regional outreach centres provides advice on applying for Disabled Students’ Allowances (DSA), and can assess what support you may need."

Important contacts

[edit | edit source]

Helen Rowland - Head of Student Support (Education)

Julie Blackler - Enabling Services Deputy Manager

DPIA documentation in Sharepoint

Logins and Access

[edit | edit source]

Roles

[edit | edit source]

x_uno49_enabl_svc.admin (elevated)

[edit | edit source]

sys admins need to elevate to this role to perform admin tasks

x_uno49_enabl_svc.user

[edit | edit source]

role require to access the Enabling Services application

x_uno49_enabl_svc.enabling_services_customer_user

[edit | edit source]

role require to access the Enabling Services customer table

x_uno49_enabl_svc.enabling_services_case_user

[edit | edit source]

role require to access the Enabling Services case (folder) table

x_uno49_enabl_svc.enabling_services_interaction_user

[edit | edit source]

role require to access the Enabling Services interaction table

x_uno49_enabl_svc.enabling_services_task_user

[edit | edit source]

role require to access the Enabling Services task table

x_uno49_enabl_svc.enabling_services_health_condition_user

[edit | edit source]

(access to the health conditions table. Role x_uno49_enabl_svc.lookup_admin needed to create new records.)

x_uno49_enabl_svc.user_manager

[edit | edit source]

role required to administer users

x_uno49_enabl_svc.lookup_user

[edit | edit source]

role required to lookup users

x_uno49_enabl_svc.lookup_admin

[edit | edit source]

Role for administering lookup users

x_uno49_enabl_svc.wnac_user

[edit | edit source]

role required to access the WNAC modules

x_uno49_enabl_svc.counsellor

[edit | edit source]

Enabling Services counsellor role

  • Who manages access
  • How access is granted
  • Any special accounts (system, email etc.)

Design

[edit | edit source]

Tables

[edit | edit source]

x_uno49_enabl_svc_customer

Enabling Services Customer table

x_uno49_enabl_svc_case

Enabling Services Folder table

x_uno49_enabl_svc_interaction

Enabling Services Interaction table

x_uno49_enabl_svc_task

Enabling Services Task table

x_uno49_enabl_svc_lookup_referred_to

Enabling Services Lookup Referred To

x_uno49_enabl_svc_health_condition

Enabling Services Health Condition lookup table


[edit | edit source]
  • Enabling Services
    • My Work
    • My Group's Work
    • New Interaction
    • Enabling Inbox
    • First Support Inbox
    • Customers
    • Folders
    • Interactions
    • Tasks
  • Administration
    • Health Conditions
    • Referred To
  • WNAC
    • WNAC Scheduled Jobs (Only visible to admins)

Workflows

[edit | edit source]

WNAC Assessment Request Workflow

ES Counselling SLA workflow

ES Counselling Appt

ES reasonable adjustments

RITM base solution for the Wessex Needs Assessment Centre

Online form for needs assessment applications created and maintained using Sitepublisher. Page is maintained by Enabling Services staff.

https://www.southampton.ac.uk/edusupport/assessment_centre/appointments/wnac-appt-booking-form.page

Submitted form creates a Request/Requested Item and triggers workflow 'WNAC Assessment Request Workflow'.

Several SLAs are defined for WNAC assessment requests:

WNAC – 1.1.5 Appt Confirmed

WNAC – 1.1.9 Appt Held

WNAC – 1.4.5 Draft NAR to Student

WNAC – 1.4.6 Final NAR to FB

WNAC 1.4.6 NAR to FB Draft Req

WNAC – 5. Student Journey

WNAC – 5.2 Audit - Respond to Query

See WNAC State Transition Diagram for detail

Significant gotchas/deviations from ServiceNow OOTB behaviour

[edit | edit source]

Scheduled jobs

[edit | edit source]

WNAC appointment reminder

[edit | edit source]

Send a text message to customers with appointments the next day.

Triggers email notification 'WNAC Appointment Reminder'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^cat_item=bbcbd724db8d33c0f81bee71ca9619d7^variables.01391172db757700f91c8c994b96199cONTomorrow@javascript:gs.beginningOfTomorrow()@javascript:gs.endOfTomorrow()"); gr.query(); while(gr.next()) { var phone = gr.variable_pool.wnac_phone_number.toString(); if (phone.substring(0,2) == '07') { var gDate = new GlideDate(); gDate.setValue(gr.variable_pool.date_and_time_of_meeting); var gDateTime = new GlideDateTime(gDate); gt = gDateTime.getLocalTime(); gs.eventQueue("x_uno49_enabl_svc.wnac_appointment", gr, gr.variable_pool.wnac_phone_number + '@sms.textapp.net', 'This is a reminder of your WNAC meeting on ' + gDate.getByFormat('dd/MM') + ' at ' + gt.getByFormat('HH:mm') + ' with ' + gr.variable_pool.wnac_assessor.toString().replace(/^([^\s]+)\s.*/, "$1") + ' at ' + gr.variable_pool.wnac_assessment_centre + '. If you cannot attend, please reply or call 02380 597233 asap.'); } } </syntaxhighlight>

WNAC 7 day survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 7 Day Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@8^closed_atRELATIVELT@dayofweek@ago@7^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_7day_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

WNAC 12 month survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 12 Month Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@365^closed_atRELATIVELT@dayofweek@ago@364^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_12month_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

global.LogEmailAttachment

[edit | edit source]

called to add a note to work notes detailing email attachments sent from WNAC REQTASKs

Write details of outbound email attachments to work notes.

Called from business rule 'ES log sent email attachments'

sys_id of email interaction passed as parameter 'esintref'

Lookup email(s) for interaction in sys_email table - lookup attachments for that email - write details of attachments found to Work Notes of the interaction.

<syntaxhighlight lang="javascript"> var LogEmailAttachment = Class.create(); LogEmailAttachment.prototype = { initialize: function() { }, LogEmailAttachment: function(esintref) { //sys_id of interaction var eml = new GlideRecord('sys_email'); eml.addQuery('instance', esintref); eml.addQuery('target_table', 'x_uno49_enabl_svc_interaction'); eml.query(); while(eml.next()) { var att = new GlideRecord('sys_attachment'); att.addQuery('table_name', 'sys_email'); att.addQuery('table_sys_id', eml.getUniqueValue()); //sys_id of the email att.query(); while(att.next()) { var esint = new GlideRecord('x_uno49_enabl_svc_interaction'); esint.addQuery('sys_id', esintref); //sys_id of interaction esint.query(); while(esint.next()) { esint.work_notes = 'Email ' + '"' + eml.subject + '" sent with attachment ' + '"' + att.file_name +'"'; esint.update(); }

} } }, type: 'LogEmailAttachment' }; </syntaxhighlight>

Maintenance

[edit | edit source]
  • Any regular activity to maintain/support the service

WNAC Assessors

[edit | edit source]

When adding a WNAC Assessor they should be added to the following groups:

  • ES WNAC - Assessors
  • ES Assessor - <user.name> (each assessor has their own group)
  • ES WNAC - QA
  • ES WNAC - AUB
  • ES WNAC - Highfield
  • ES WNAC - Solent

Duplicate Customers

[edit | edit source]

Use the following background script to find occurrences of duplicate ES customers

<syntaxhighlight lang="javascript"> var dupRecords = [];

    var gaDupCheck1 = new GlideAggregate('x_uno49_enabl_svc_customer');
    //gaDupCheck1.addQuery('active','true');
    gaDupCheck1.addAggregate('COUNT', 'user');
    gaDupCheck1.groupBy('user');
    gaDupCheck1.addHaving('COUNT', '>', 1);
    gaDupCheck1.query();
    while (gaDupCheck1.next()) {
          dupRecords.push(gaDupCheck1.user.toString());
    }
    gs.print(dupRecords);

</syntaxhighlight>

Find records where interaction customer != folder customer

[edit | edit source]

Background script to find interactions which have been put in the incorrect customer folder.

<syntaxhighlight lang="javascript"> gs.addInfoMessage('Here we go - Find records where interaction customer != folder customer'); var notMatch = []; var gr = new GlideRecord('x_uno49_enabl_svc_interaction');

   gr.addQuery('customer' != );
   gr.query();
   while (gr.next()) {

           if(gr.customer != gr.parent.customer && gr.customer !=  && gr.parent.customer !=  && gr.parent.customer != undefined) {
           gs.addInfoMessage(gr.number + ': Customer = ' + gr.customer.getDisplayValue() + ' : interaction is in folder ' + gr.parent.getDisplayValue() + ' which belongs to ' + gr.parent.customer.getDisplayValue());
           notMatch.push(gr.number.toString());
        }
   }

gs.print('Interactions where customer does not match customer on parent folder: ' + notMatch); </syntaxhighlight>


Retention policy

[edit | edit source]

Link to email regarding retention policy

Interfaces

[edit | edit source]

Ingestion

[edit | edit source]

Emails sent to enable@soton.ac.uk from a student account will create an 'Interaction' record for that student. Emails sent to enable@soton.ac.uk from non-student email accounts will create an interaction with no customer details. These will appear in the 'Inbox' where they can be triaged and customer details may be added. Emails to First Support email addresses are handled similarly. Watermarked emails are handled in the usual ServiceNow way and will be matched with and existing customer/folder.

Inbound Email Actions

[edit | edit source]
  • Create ES Interaction from new email
  • ES - Interaction from reply
  • Create ES Int and task FS

Business Rule to create a task for each attachment on an inbound email

[edit | edit source]
  • Create Task From Interaction
  • Create Task From Reply Interaction
  • Create Task From Interaction FS

Global Script Include

[edit | edit source]

global.CopySpecificAttachment

Global script include used by the Enabling Services application. Requires the parameters: donorTable, donorID, recipientTable, recipientID, fileName

Called by: Business Rule - Create Task From Interaction

Also deletes attachment from donor table after copying to recipient table. <syntaxhighlight lang="javascript"> var CopySpecificAttachment = Class.create(); CopySpecificAttachment.prototype = { initialize: function() { }, CopySpecificAttachment: function(donorTable, donorID, recipientTable, recipientID, fileName) { try{ var donorAttSysID; var newAttRecord; var linkToNewRecord; var attDataRecord; var newDocRecord; var attRecord = new GlideRecord('sys_attachment'); //find record(s) in sys_attachment attRecord.addQuery('table_name', donorTable); attRecord.addQuery('table_sys_id', donorID); attRecord.addQuery('file_name', fileName); attRecord.query(); while (attRecord.next()) { //loop for each record found in sys_attachment donorAttSysID = attRecord.getValue('sys_id'); newAttRecord = this.copyRecord(attRecord); //call function to copy attachment to the child record newAttRecord.setValue('table_name', recipientTable); newAttRecord.setValue('table_sys_id', recipientID); newAttRecord.update(); linkToNewRecord = gs.getProperty('glide.servlet.uri') + newAttRecord.getLink(); attDataRecord = new GlideRecord('sys_attachment_doc'); attDataRecord.addQuery('sys_attachment', donorAttSysID); //find matching records in sys_attachment_doc attDataRecord.query(); while (attDataRecord.next()) { newDocRecord = this.copyRecord(attDataRecord); //copy attachment parts to child record newDocRecord.setValue('sys_attachment', newAttRecord.getValue('sys_id')); newDocRecord.update(); } try{ attRecord.deleteRecord(); //delete attachment from parent record } catch(err) { gs.log('====> error deleting attachment: message['+err.message+']'); } } } catch(err) { gs.log('====> error in CopySpecificAttachment: message['+err.message+']'); }

}, copyRecord: function(record) { //function to copy attachment(s) to the child record try{ var recordElement; var recordElementName; var recordTable = record.getTableName(); var recordFields = record.getFields(); var newRecord = new GlideRecord(recordTable); newRecord.initialize(); for (var i = 0; i < recordFields.size(); i++) { recordElement = recordFields.get(i); if(recordElement.getName() != 'sys_id' && recordElement.getName() != 'number') { recordElementName = recordElement.getName(); newRecord.setValue(recordElementName, record.getValue(recordElementName)); } } var newSysId = newRecord.insert(); return newRecord; } catch(err) { gs.log('====> error in CopySpecificAttachment.copyRecord: message['+err.message+']'); } }, type: 'CopySpecificAttachment' }; </syntaxhighlight>

[edit | edit source]

See ServiceNow/Interface/Banner.

Reporting

[edit | edit source]
  • Reports delivered as part of the project</text>
     <sha1>79hfmqrk52ibnbhl1fz975adz4lh5ni</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
   <revision>
     <id>563</id>
     <parentid>562</parentid>
     <timestamp>2019-10-22T09:42:43Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <text xml:space="preserve" bytes="15559">

Student Services Centre Enabling Services (SSC ES) uses a custom-built application on the ServiceNow platform to manage interactions with students.

TBC - this might be defined as a discrete service.

Development

[edit | edit source]

Business

[edit | edit source]
  • What processes does this development support

Enabling Services Scoped Application

[edit | edit source]

This application was developed for Enabling Services who provide a wide variety of support for students who have disabilities, mental health conditions and/or specific learning difficulties.

WNAC (RITM based)

[edit | edit source]

"The Wessex Needs Assessment Centre (WNAC) at the University of Southampton and our three regional outreach centres provides advice on applying for Disabled Students’ Allowances (DSA), and can assess what support you may need."

Important contacts

[edit | edit source]

Helen Rowland - Head of Student Support (Education)

Julie Blackler - Enabling Services Deputy Manager

DPIA documentation in Sharepoint

Logins and Access

[edit | edit source]

Roles

[edit | edit source]

x_uno49_enabl_svc.admin (elevated)

[edit | edit source]

sys admins need to elevate to this role to perform admin tasks

x_uno49_enabl_svc.user

[edit | edit source]

role require to access the Enabling Services application

x_uno49_enabl_svc.enabling_services_customer_user

[edit | edit source]

role require to access the Enabling Services customer table

x_uno49_enabl_svc.enabling_services_case_user

[edit | edit source]

role require to access the Enabling Services case (folder) table

x_uno49_enabl_svc.enabling_services_interaction_user

[edit | edit source]

role require to access the Enabling Services interaction table

x_uno49_enabl_svc.enabling_services_task_user

[edit | edit source]

role require to access the Enabling Services task table

x_uno49_enabl_svc.enabling_services_health_condition_user

[edit | edit source]

(access to the health conditions table. Role x_uno49_enabl_svc.lookup_admin needed to create new records.)

x_uno49_enabl_svc.user_manager

[edit | edit source]

role required to administer users

x_uno49_enabl_svc.lookup_user

[edit | edit source]

role required to lookup users

x_uno49_enabl_svc.lookup_admin

[edit | edit source]

Role for administering lookup users

x_uno49_enabl_svc.wnac_user

[edit | edit source]

role required to access the WNAC modules

x_uno49_enabl_svc.counsellor

[edit | edit source]

Enabling Services counsellor role

  • Who manages access
  • How access is granted
  • Any special accounts (system, email etc.)

Design

[edit | edit source]

Tables

[edit | edit source]

x_uno49_enabl_svc_customer

Enabling Services Customer table

x_uno49_enabl_svc_case

Enabling Services Folder table

x_uno49_enabl_svc_interaction

Enabling Services Interaction table

x_uno49_enabl_svc_task

Enabling Services Task table

x_uno49_enabl_svc_lookup_referred_to

Enabling Services Lookup Referred To

x_uno49_enabl_svc_health_condition

Enabling Services Health Condition lookup table


[edit | edit source]
  • Enabling Services
    • My Work
    • My Group's Work
    • New Interaction
    • Enabling Inbox
    • First Support Inbox
    • Customers
    • Folders
    • Interactions
    • Tasks
  • Administration
    • Health Conditions
    • Referred To
  • WNAC
    • WNAC Scheduled Jobs (Only visible to admins)

Workflows

[edit | edit source]

WNAC Assessment Request Workflow

ES Counselling SLA workflow

ES Counselling Appt

ES reasonable adjustments

RITM base solution for the Wessex Needs Assessment Centre

Online form for needs assessment applications created and maintained using Sitepublisher. Page is maintained by Enabling Services staff.

https://www.southampton.ac.uk/edusupport/assessment_centre/appointments/wnac-appt-booking-form.page

Submitted form creates a Request/Requested Item and triggers workflow 'WNAC Assessment Request Workflow'.

Several SLAs are defined for WNAC assessment requests:

WNAC – 1.1.5 Appt Confirmed

WNAC – 1.1.9 Appt Held

WNAC – 1.4.5 Draft NAR to Student

WNAC – 1.4.6 Final NAR to FB

WNAC 1.4.6 NAR to FB Draft Req

WNAC – 5. Student Journey

WNAC – 5.2 Audit - Respond to Query

See WNAC State Transition Diagram for detail

Significant gotchas/deviations from ServiceNow OOTB behaviour

[edit | edit source]

Scheduled jobs

[edit | edit source]

WNAC appointment reminder

[edit | edit source]

Send a text message to customers with appointments the next day.

Triggers email notification 'WNAC Appointment Reminder'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^cat_item=bbcbd724db8d33c0f81bee71ca9619d7^variables.01391172db757700f91c8c994b96199cONTomorrow@javascript:gs.beginningOfTomorrow()@javascript:gs.endOfTomorrow()"); gr.query(); while(gr.next()) { var phone = gr.variable_pool.wnac_phone_number.toString(); if (phone.substring(0,2) == '07') { var gDate = new GlideDate(); gDate.setValue(gr.variable_pool.date_and_time_of_meeting); var gDateTime = new GlideDateTime(gDate); gt = gDateTime.getLocalTime(); gs.eventQueue("x_uno49_enabl_svc.wnac_appointment", gr, gr.variable_pool.wnac_phone_number + '@sms.textapp.net', 'This is a reminder of your WNAC meeting on ' + gDate.getByFormat('dd/MM') + ' at ' + gt.getByFormat('HH:mm') + ' with ' + gr.variable_pool.wnac_assessor.toString().replace(/^([^\s]+)\s.*/, "$1") + ' at ' + gr.variable_pool.wnac_assessment_centre + '. If you cannot attend, please reply or call 02380 597233 asap.'); } } </syntaxhighlight>

WNAC 7 day survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 7 Day Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@8^closed_atRELATIVELT@dayofweek@ago@7^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_7day_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

WNAC 12 month survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 12 Month Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@365^closed_atRELATIVELT@dayofweek@ago@364^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_12month_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

global.LogEmailAttachment

[edit | edit source]

called to add a note to work notes detailing email attachments sent from WNAC REQTASKs

Write details of outbound email attachments to work notes.

Called from business rule 'ES log sent email attachments'

sys_id of email interaction passed as parameter 'esintref'

Lookup email(s) for interaction in sys_email table - lookup attachments for that email - write details of attachments found to Work Notes of the interaction.

<syntaxhighlight lang="javascript"> var LogEmailAttachment = Class.create(); LogEmailAttachment.prototype = { initialize: function() { }, LogEmailAttachment: function(esintref) { //sys_id of interaction var eml = new GlideRecord('sys_email'); eml.addQuery('instance', esintref); eml.addQuery('target_table', 'x_uno49_enabl_svc_interaction'); eml.query(); while(eml.next()) { var att = new GlideRecord('sys_attachment'); att.addQuery('table_name', 'sys_email'); att.addQuery('table_sys_id', eml.getUniqueValue()); //sys_id of the email att.query(); while(att.next()) { var esint = new GlideRecord('x_uno49_enabl_svc_interaction'); esint.addQuery('sys_id', esintref); //sys_id of interaction esint.query(); while(esint.next()) { esint.work_notes = 'Email ' + '"' + eml.subject + '" sent with attachment ' + '"' + att.file_name +'"'; esint.update(); }

} } }, type: 'LogEmailAttachment' }; </syntaxhighlight>

Maintenance

[edit | edit source]
  • Any regular activity to maintain/support the service

WNAC Assessors

[edit | edit source]

When adding a WNAC Assessor they should be added to the following groups:

  • ES WNAC - Assessors
  • ES Assessor - <user.name> (each assessor has their own group)
  • ES WNAC - QA
  • ES WNAC - AUB
  • ES WNAC - Highfield
  • ES WNAC - Solent

Duplicate Customers

[edit | edit source]

Use the following background script to find occurrences of duplicate ES customers

<syntaxhighlight lang="javascript"> var dupRecords = [];

    var gaDupCheck1 = new GlideAggregate('x_uno49_enabl_svc_customer');
    //gaDupCheck1.addQuery('active','true');
    gaDupCheck1.addAggregate('COUNT', 'user');
    gaDupCheck1.groupBy('user');
    gaDupCheck1.addHaving('COUNT', '>', 1);
    gaDupCheck1.query();
    while (gaDupCheck1.next()) {
          dupRecords.push(gaDupCheck1.user.toString());
    }
    gs.print(dupRecords);

</syntaxhighlight>

Find records where interaction customer != folder customer

[edit | edit source]

Background script to find interactions which have been put in the incorrect customer folder.

<syntaxhighlight lang="javascript"> gs.addInfoMessage('Here we go - Find records where interaction customer != folder customer'); var notMatch = []; var gr = new GlideRecord('x_uno49_enabl_svc_interaction');

   gr.addQuery('customer' != );
   gr.query();
   while (gr.next()) {

           if(gr.customer != gr.parent.customer && gr.customer !=  && gr.parent.customer !=  && gr.parent.customer != undefined) {
           gs.addInfoMessage(gr.number + ': Customer = ' + gr.customer.getDisplayValue() + ' : interaction is in folder ' + gr.parent.getDisplayValue() + ' which belongs to ' + gr.parent.customer.getDisplayValue());
           notMatch.push(gr.number.toString());
        }
   }

gs.print('Interactions where customer does not match customer on parent folder: ' + notMatch); </syntaxhighlight>


Retention policy

[edit | edit source]

Link to email regarding retention policy

Interfaces

[edit | edit source]

Ingestion

[edit | edit source]

Emails sent to enable@soton.ac.uk from a student account will create an 'Interaction' record for that student. Emails sent to enable@soton.ac.uk from non-student email accounts will create an interaction with no customer details. These will appear in the 'Inbox' where they can be triaged and customer details may be added. Emails to First Support email addresses are handled similarly. Watermarked emails are handled in the usual ServiceNow way and will be matched with and existing customer/folder.

Inbound Email Actions

[edit | edit source]
  • Create ES Interaction from new email
  • ES - Interaction from reply
  • Create ES Int and task FS

Business Rule to create a task for each attachment on an inbound email

[edit | edit source]
  • Create Task From Interaction
  • Create Task From Reply Interaction
  • Create Task From Interaction FS

Global Script Include

[edit | edit source]

global.CopySpecificAttachment

Global script include used by the Enabling Services application. Requires the parameters: donorTable, donorID, recipientTable, recipientID, fileName

Called by: Business Rule - Create Task From Interaction

Also deletes attachment from donor table after copying to recipient table. <syntaxhighlight lang="javascript"> var CopySpecificAttachment = Class.create(); CopySpecificAttachment.prototype = { initialize: function() { }, CopySpecificAttachment: function(donorTable, donorID, recipientTable, recipientID, fileName) { try{ var donorAttSysID; var newAttRecord; var linkToNewRecord; var attDataRecord; var newDocRecord; var attRecord = new GlideRecord('sys_attachment'); //find record(s) in sys_attachment attRecord.addQuery('table_name', donorTable); attRecord.addQuery('table_sys_id', donorID); attRecord.addQuery('file_name', fileName); attRecord.query(); while (attRecord.next()) { //loop for each record found in sys_attachment donorAttSysID = attRecord.getValue('sys_id'); newAttRecord = this.copyRecord(attRecord); //call function to copy attachment to the child record newAttRecord.setValue('table_name', recipientTable); newAttRecord.setValue('table_sys_id', recipientID); newAttRecord.update(); linkToNewRecord = gs.getProperty('glide.servlet.uri') + newAttRecord.getLink(); attDataRecord = new GlideRecord('sys_attachment_doc'); attDataRecord.addQuery('sys_attachment', donorAttSysID); //find matching records in sys_attachment_doc attDataRecord.query(); while (attDataRecord.next()) { newDocRecord = this.copyRecord(attDataRecord); //copy attachment parts to child record newDocRecord.setValue('sys_attachment', newAttRecord.getValue('sys_id')); newDocRecord.update(); } try{ attRecord.deleteRecord(); //delete attachment from parent record } catch(err) { gs.log('====> error deleting attachment: message['+err.message+']'); } } } catch(err) { gs.log('====> error in CopySpecificAttachment: message['+err.message+']'); }

}, copyRecord: function(record) { //function to copy attachment(s) to the child record try{ var recordElement; var recordElementName; var recordTable = record.getTableName(); var recordFields = record.getFields(); var newRecord = new GlideRecord(recordTable); newRecord.initialize(); for (var i = 0; i < recordFields.size(); i++) { recordElement = recordFields.get(i); if(recordElement.getName() != 'sys_id' && recordElement.getName() != 'number') { recordElementName = recordElement.getName(); newRecord.setValue(recordElementName, record.getValue(recordElementName)); } } var newSysId = newRecord.insert(); return newRecord; } catch(err) { gs.log('====> error in CopySpecificAttachment.copyRecord: message['+err.message+']'); } }, type: 'CopySpecificAttachment' }; </syntaxhighlight>

[edit | edit source]

See ServiceNow/Interface/Banner.

Reporting

[edit | edit source]
  • Reports delivered as part of the project</text>
     <sha1>ms7l9ri0t6ur6awy313pb2j54fxlgty</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
   <revision>
     <id>566</id>
     <parentid>563</parentid>
     <timestamp>2019-10-22T10:23:18Z</timestamp>
     <contributor>
       <username>Cm4u10</username>
       <id>0</id>
     </contributor>
     <comment>/* Logins and Access */</comment>
     <text xml:space="preserve" bytes="15990">

Student Services Centre Enabling Services (SSC ES) uses a custom-built application on the ServiceNow platform to manage interactions with students.

TBC - this might be defined as a discrete service.

Development

[edit | edit source]

Business

[edit | edit source]
  • What processes does this development support

Enabling Services Scoped Application

[edit | edit source]

This application was developed for Enabling Services who provide a wide variety of support for students who have disabilities, mental health conditions and/or specific learning difficulties.

WNAC (RITM based)

[edit | edit source]

"The Wessex Needs Assessment Centre (WNAC) at the University of Southampton and our three regional outreach centres provides advice on applying for Disabled Students’ Allowances (DSA), and can assess what support you may need."

Important contacts

[edit | edit source]

Helen Rowland - Head of Student Support (Education)

Julie Blackler - Enabling Services Deputy Manager

DPIA documentation in Sharepoint

Logins and Access

[edit | edit source]

Access is granted via membership of any Enabling Services group (other than assessor groups) i.e ES Customer Service Team which will grant the x_uno49_enabl_svc.user role

Group membership is administered by the Group Manager (Enabling Services staff).

Role 'x_uno49_enabl_svc.admin' is an elevated role and gives access to the Administration menu items and the ability to edit customer fields on folders, interactions and tasks.

Roles

[edit | edit source]

x_uno49_enabl_svc.admin (elevated)

[edit | edit source]

sys admins need to elevate to this role to perform admin tasks

x_uno49_enabl_svc.user

[edit | edit source]

role require to access the Enabling Services application

x_uno49_enabl_svc.enabling_services_customer_user

[edit | edit source]

role require to access the Enabling Services customer table

x_uno49_enabl_svc.enabling_services_case_user

[edit | edit source]

role require to access the Enabling Services case (folder) table

x_uno49_enabl_svc.enabling_services_interaction_user

[edit | edit source]

role require to access the Enabling Services interaction table

x_uno49_enabl_svc.enabling_services_task_user

[edit | edit source]

role require to access the Enabling Services task table

x_uno49_enabl_svc.enabling_services_health_condition_user

[edit | edit source]

(access to the health conditions table. Role x_uno49_enabl_svc.lookup_admin needed to create new records.)

x_uno49_enabl_svc.user_manager

[edit | edit source]

role required to administer users

x_uno49_enabl_svc.lookup_user

[edit | edit source]

role required to lookup users

x_uno49_enabl_svc.lookup_admin

[edit | edit source]

Role for administering lookup users

x_uno49_enabl_svc.wnac_user

[edit | edit source]

role required to access the WNAC modules

x_uno49_enabl_svc.counsellor

[edit | edit source]

Enabling Services counsellor role

Design

[edit | edit source]

Tables

[edit | edit source]

x_uno49_enabl_svc_customer

Enabling Services Customer table

x_uno49_enabl_svc_case

Enabling Services Folder table

x_uno49_enabl_svc_interaction

Enabling Services Interaction table

x_uno49_enabl_svc_task

Enabling Services Task table

x_uno49_enabl_svc_lookup_referred_to

Enabling Services Lookup Referred To

x_uno49_enabl_svc_health_condition

Enabling Services Health Condition lookup table


[edit | edit source]
  • Enabling Services
    • My Work
    • My Group's Work
    • New Interaction
    • Enabling Inbox
    • First Support Inbox
    • Customers
    • Folders
    • Interactions
    • Tasks
  • Administration
    • Health Conditions
    • Referred To
  • WNAC
    • WNAC Scheduled Jobs (Only visible to admins)

Workflows

[edit | edit source]

WNAC Assessment Request Workflow

ES Counselling SLA workflow

ES Counselling Appt

ES reasonable adjustments

RITM base solution for the Wessex Needs Assessment Centre

Online form for needs assessment applications created and maintained using Sitepublisher. Page is maintained by Enabling Services staff.

https://www.southampton.ac.uk/edusupport/assessment_centre/appointments/wnac-appt-booking-form.page

Submitted form creates a Request/Requested Item and triggers workflow 'WNAC Assessment Request Workflow'.

Several SLAs are defined for WNAC assessment requests:

WNAC – 1.1.5 Appt Confirmed

WNAC – 1.1.9 Appt Held

WNAC – 1.4.5 Draft NAR to Student

WNAC – 1.4.6 Final NAR to FB

WNAC 1.4.6 NAR to FB Draft Req

WNAC – 5. Student Journey

WNAC – 5.2 Audit - Respond to Query

See WNAC State Transition Diagram for detail

Significant gotchas/deviations from ServiceNow OOTB behaviour

[edit | edit source]

Scheduled jobs

[edit | edit source]

WNAC appointment reminder

[edit | edit source]

Send a text message to customers with appointments the next day.

Triggers email notification 'WNAC Appointment Reminder'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^cat_item=bbcbd724db8d33c0f81bee71ca9619d7^variables.01391172db757700f91c8c994b96199cONTomorrow@javascript:gs.beginningOfTomorrow()@javascript:gs.endOfTomorrow()"); gr.query(); while(gr.next()) { var phone = gr.variable_pool.wnac_phone_number.toString(); if (phone.substring(0,2) == '07') { var gDate = new GlideDate(); gDate.setValue(gr.variable_pool.date_and_time_of_meeting); var gDateTime = new GlideDateTime(gDate); gt = gDateTime.getLocalTime(); gs.eventQueue("x_uno49_enabl_svc.wnac_appointment", gr, gr.variable_pool.wnac_phone_number + '@sms.textapp.net', 'This is a reminder of your WNAC meeting on ' + gDate.getByFormat('dd/MM') + ' at ' + gt.getByFormat('HH:mm') + ' with ' + gr.variable_pool.wnac_assessor.toString().replace(/^([^\s]+)\s.*/, "$1") + ' at ' + gr.variable_pool.wnac_assessment_centre + '. If you cannot attend, please reply or call 02380 597233 asap.'); } } </syntaxhighlight>

WNAC 7 day survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 7 Day Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@8^closed_atRELATIVELT@dayofweek@ago@7^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_7day_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

WNAC 12 month survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 12 Month Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@365^closed_atRELATIVELT@dayofweek@ago@364^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_12month_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

global.LogEmailAttachment

[edit | edit source]

Golbal script include called to add a note to work notes detailing email attachments sent from WNAC REQTASKs

Write details of outbound email attachments to work notes.

Called from business rule 'ES log sent email attachments'

sys_id of email interaction passed as parameter 'esintref'

Lookup email(s) for interaction in sys_email table - lookup attachments for that email - write details of attachments found to Work Notes of the interaction.

<syntaxhighlight lang="javascript"> var LogEmailAttachment = Class.create(); LogEmailAttachment.prototype = { initialize: function() { }, LogEmailAttachment: function(esintref) { //sys_id of interaction var eml = new GlideRecord('sys_email'); eml.addQuery('instance', esintref); eml.addQuery('target_table', 'x_uno49_enabl_svc_interaction'); eml.query(); while(eml.next()) { var att = new GlideRecord('sys_attachment'); att.addQuery('table_name', 'sys_email'); att.addQuery('table_sys_id', eml.getUniqueValue()); //sys_id of the email att.query(); while(att.next()) { var esint = new GlideRecord('x_uno49_enabl_svc_interaction'); esint.addQuery('sys_id', esintref); //sys_id of interaction esint.query(); while(esint.next()) { esint.work_notes = 'Email ' + '"' + eml.subject + '" sent with attachment ' + '"' + att.file_name +'"'; esint.update(); }

} } }, type: 'LogEmailAttachment' }; </syntaxhighlight>

Maintenance

[edit | edit source]
  • Any regular activity to maintain/support the service

WNAC Assessors

[edit | edit source]

When adding a WNAC Assessor they should be added to the following groups:

  • ES WNAC - Assessors
  • ES Assessor - <user.name> (each assessor has their own group)
  • ES WNAC - QA
  • ES WNAC - AUB
  • ES WNAC - Highfield
  • ES WNAC - Solent

Duplicate Customers

[edit | edit source]

Use the following background script to find occurrences of duplicate ES customers

<syntaxhighlight lang="javascript"> var dupRecords = [];

    var gaDupCheck1 = new GlideAggregate('x_uno49_enabl_svc_customer');
    //gaDupCheck1.addQuery('active','true');
    gaDupCheck1.addAggregate('COUNT', 'user');
    gaDupCheck1.groupBy('user');
    gaDupCheck1.addHaving('COUNT', '>', 1);
    gaDupCheck1.query();
    while (gaDupCheck1.next()) {
          dupRecords.push(gaDupCheck1.user.toString());
    }
    gs.print(dupRecords);

</syntaxhighlight>

Find records where interaction customer != folder customer

[edit | edit source]

Background script to find interactions which have been put in the incorrect customer folder.

<syntaxhighlight lang="javascript"> gs.addInfoMessage('Here we go - Find records where interaction customer != folder customer'); var notMatch = []; var gr = new GlideRecord('x_uno49_enabl_svc_interaction');

   gr.addQuery('customer' != );
   gr.query();
   while (gr.next()) {

           if(gr.customer != gr.parent.customer && gr.customer !=  && gr.parent.customer !=  && gr.parent.customer != undefined) {
           gs.addInfoMessage(gr.number + ': Customer = ' + gr.customer.getDisplayValue() + ' : interaction is in folder ' + gr.parent.getDisplayValue() + ' which belongs to ' + gr.parent.customer.getDisplayValue());
           notMatch.push(gr.number.toString());
        }
   }

gs.print('Interactions where customer does not match customer on parent folder: ' + notMatch); </syntaxhighlight>


Retention policy

[edit | edit source]

Link to email regarding retention policy

Interfaces

[edit | edit source]

Ingestion

[edit | edit source]

Emails sent to enable@soton.ac.uk from a student account will create an 'Interaction' record for that student. Emails sent to enable@soton.ac.uk from non-student email accounts will create an interaction with no customer details. These will appear in the 'Inbox' where they can be triaged and customer details may be added. Emails to First Support email addresses are handled similarly. Watermarked emails are handled in the usual ServiceNow way and will be matched with and existing customer/folder.

Inbound Email Actions

[edit | edit source]
  • Create ES Interaction from new email
  • ES - Interaction from reply
  • Create ES Int and task FS

Business Rule to create a task for each attachment on an inbound email

[edit | edit source]
  • Create Task From Interaction
  • Create Task From Reply Interaction
  • Create Task From Interaction FS

Global Script Include

[edit | edit source]

global.CopySpecificAttachment

Global script include used by the Enabling Services application. Requires the parameters: donorTable, donorID, recipientTable, recipientID, fileName

Called by: Business Rule - Create Task From Interaction

Also deletes attachment from donor table after copying to recipient table. <syntaxhighlight lang="javascript"> var CopySpecificAttachment = Class.create(); CopySpecificAttachment.prototype = { initialize: function() { }, CopySpecificAttachment: function(donorTable, donorID, recipientTable, recipientID, fileName) { try{ var donorAttSysID; var newAttRecord; var linkToNewRecord; var attDataRecord; var newDocRecord; var attRecord = new GlideRecord('sys_attachment'); //find record(s) in sys_attachment attRecord.addQuery('table_name', donorTable); attRecord.addQuery('table_sys_id', donorID); attRecord.addQuery('file_name', fileName); attRecord.query(); while (attRecord.next()) { //loop for each record found in sys_attachment donorAttSysID = attRecord.getValue('sys_id'); newAttRecord = this.copyRecord(attRecord); //call function to copy attachment to the child record newAttRecord.setValue('table_name', recipientTable); newAttRecord.setValue('table_sys_id', recipientID); newAttRecord.update(); linkToNewRecord = gs.getProperty('glide.servlet.uri') + newAttRecord.getLink(); attDataRecord = new GlideRecord('sys_attachment_doc'); attDataRecord.addQuery('sys_attachment', donorAttSysID); //find matching records in sys_attachment_doc attDataRecord.query(); while (attDataRecord.next()) { newDocRecord = this.copyRecord(attDataRecord); //copy attachment parts to child record newDocRecord.setValue('sys_attachment', newAttRecord.getValue('sys_id')); newDocRecord.update(); } try{ attRecord.deleteRecord(); //delete attachment from parent record } catch(err) { gs.log('====> error deleting attachment: message['+err.message+']'); } } } catch(err) { gs.log('====> error in CopySpecificAttachment: message['+err.message+']'); }

}, copyRecord: function(record) { //function to copy attachment(s) to the child record try{ var recordElement; var recordElementName; var recordTable = record.getTableName(); var recordFields = record.getFields(); var newRecord = new GlideRecord(recordTable); newRecord.initialize(); for (var i = 0; i < recordFields.size(); i++) { recordElement = recordFields.get(i); if(recordElement.getName() != 'sys_id' && recordElement.getName() != 'number') { recordElementName = recordElement.getName(); newRecord.setValue(recordElementName, record.getValue(recordElementName)); } } var newSysId = newRecord.insert(); return newRecord; } catch(err) { gs.log('====> error in CopySpecificAttachment.copyRecord: message['+err.message+']'); } }, type: 'CopySpecificAttachment' }; </syntaxhighlight>

[edit | edit source]

See ServiceNow/Interface/Banner.

Reporting

[edit | edit source]
  • Reports delivered as part of the project</text>
     <sha1>53wy0aec46x06i3shp8hwbjquidml8r</sha1>
     <model>wikitext</model>
     <format>text/x-wiki</format>
   </revision>
   <revision>
     <id>567</id>
     <parentid>566</parentid>
     <timestamp>2019-10-22T15:32:06Z</timestamp>
     <contributor>
       <username>CMPadmin</username>
       <id>1</id>
     </contributor>
     <minor/>
     <comment>1 revision</comment>
     <text xml:space="preserve" bytes="15990">

Student Services Centre Enabling Services (SSC ES) uses a custom-built application on the ServiceNow platform to manage interactions with students.

TBC - this might be defined as a discrete service.

Development

[edit | edit source]

Business

[edit | edit source]
  • What processes does this development support

Enabling Services Scoped Application

[edit | edit source]

This application was developed for Enabling Services who provide a wide variety of support for students who have disabilities, mental health conditions and/or specific learning difficulties.

WNAC (RITM based)

[edit | edit source]

"The Wessex Needs Assessment Centre (WNAC) at the University of Southampton and our three regional outreach centres provides advice on applying for Disabled Students’ Allowances (DSA), and can assess what support you may need."

Important contacts

[edit | edit source]

Helen Rowland - Head of Student Support (Education)

Julie Blackler - Enabling Services Deputy Manager

DPIA documentation in Sharepoint

Logins and Access

[edit | edit source]

Access is granted via membership of any Enabling Services group (other than assessor groups) i.e ES Customer Service Team which will grant the x_uno49_enabl_svc.user role

Group membership is administered by the Group Manager (Enabling Services staff).

Role 'x_uno49_enabl_svc.admin' is an elevated role and gives access to the Administration menu items and the ability to edit customer fields on folders, interactions and tasks.

Roles

[edit | edit source]

x_uno49_enabl_svc.admin (elevated)

[edit | edit source]

sys admins need to elevate to this role to perform admin tasks

x_uno49_enabl_svc.user

[edit | edit source]

role require to access the Enabling Services application

x_uno49_enabl_svc.enabling_services_customer_user

[edit | edit source]

role require to access the Enabling Services customer table

x_uno49_enabl_svc.enabling_services_case_user

[edit | edit source]

role require to access the Enabling Services case (folder) table

x_uno49_enabl_svc.enabling_services_interaction_user

[edit | edit source]

role require to access the Enabling Services interaction table

x_uno49_enabl_svc.enabling_services_task_user

[edit | edit source]

role require to access the Enabling Services task table

x_uno49_enabl_svc.enabling_services_health_condition_user

[edit | edit source]

(access to the health conditions table. Role x_uno49_enabl_svc.lookup_admin needed to create new records.)

x_uno49_enabl_svc.user_manager

[edit | edit source]

role required to administer users

x_uno49_enabl_svc.lookup_user

[edit | edit source]

role required to lookup users

x_uno49_enabl_svc.lookup_admin

[edit | edit source]

Role for administering lookup users

x_uno49_enabl_svc.wnac_user

[edit | edit source]

role required to access the WNAC modules

x_uno49_enabl_svc.counsellor

[edit | edit source]

Enabling Services counsellor role

Design

[edit | edit source]

Tables

[edit | edit source]

x_uno49_enabl_svc_customer

Enabling Services Customer table

x_uno49_enabl_svc_case

Enabling Services Folder table

x_uno49_enabl_svc_interaction

Enabling Services Interaction table

x_uno49_enabl_svc_task

Enabling Services Task table

x_uno49_enabl_svc_lookup_referred_to

Enabling Services Lookup Referred To

x_uno49_enabl_svc_health_condition

Enabling Services Health Condition lookup table


[edit | edit source]
  • Enabling Services
    • My Work
    • My Group's Work
    • New Interaction
    • Enabling Inbox
    • First Support Inbox
    • Customers
    • Folders
    • Interactions
    • Tasks
  • Administration
    • Health Conditions
    • Referred To
  • WNAC
    • WNAC Scheduled Jobs (Only visible to admins)

Workflows

[edit | edit source]

WNAC Assessment Request Workflow

ES Counselling SLA workflow

ES Counselling Appt

ES reasonable adjustments

RITM base solution for the Wessex Needs Assessment Centre

Online form for needs assessment applications created and maintained using Sitepublisher. Page is maintained by Enabling Services staff.

https://www.southampton.ac.uk/edusupport/assessment_centre/appointments/wnac-appt-booking-form.page

Submitted form creates a Request/Requested Item and triggers workflow 'WNAC Assessment Request Workflow'.

Several SLAs are defined for WNAC assessment requests:

WNAC – 1.1.5 Appt Confirmed

WNAC – 1.1.9 Appt Held

WNAC – 1.4.5 Draft NAR to Student

WNAC – 1.4.6 Final NAR to FB

WNAC 1.4.6 NAR to FB Draft Req

WNAC – 5. Student Journey

WNAC – 5.2 Audit - Respond to Query

See WNAC State Transition Diagram for detail

Significant gotchas/deviations from ServiceNow OOTB behaviour

[edit | edit source]

Scheduled jobs

[edit | edit source]

WNAC appointment reminder

[edit | edit source]

Send a text message to customers with appointments the next day.

Triggers email notification 'WNAC Appointment Reminder'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^cat_item=bbcbd724db8d33c0f81bee71ca9619d7^variables.01391172db757700f91c8c994b96199cONTomorrow@javascript:gs.beginningOfTomorrow()@javascript:gs.endOfTomorrow()"); gr.query(); while(gr.next()) { var phone = gr.variable_pool.wnac_phone_number.toString(); if (phone.substring(0,2) == '07') { var gDate = new GlideDate(); gDate.setValue(gr.variable_pool.date_and_time_of_meeting); var gDateTime = new GlideDateTime(gDate); gt = gDateTime.getLocalTime(); gs.eventQueue("x_uno49_enabl_svc.wnac_appointment", gr, gr.variable_pool.wnac_phone_number + '@sms.textapp.net', 'This is a reminder of your WNAC meeting on ' + gDate.getByFormat('dd/MM') + ' at ' + gt.getByFormat('HH:mm') + ' with ' + gr.variable_pool.wnac_assessor.toString().replace(/^([^\s]+)\s.*/, "$1") + ' at ' + gr.variable_pool.wnac_assessment_centre + '. If you cannot attend, please reply or call 02380 597233 asap.'); } } </syntaxhighlight>

WNAC 7 day survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 7 Day Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@8^closed_atRELATIVELT@dayofweek@ago@7^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_7day_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

WNAC 12 month survey

[edit | edit source]

Create an event to trigger an email survey to be sent to customers 7 days after their request is closed.

Triggers email notification 'WNAC 12 Month Survey'

Run: Daily

Run as: Service Now

Run at: 10:00:00

Script: <syntaxhighlight lang="javascript"> var gr = new GlideRecord('sc_req_item'); gr.addEncodedQuery("u_logged_for=wnac^closed_atRELATIVEGE@dayofweek@ago@365^closed_atRELATIVELT@dayofweek@ago@364^state=3"); gr.query(); while(gr.next()) { gs.eventQueue("x_uno49_enabl_svc.wnac_12month_survey",gr,gr.variable_pool.wnac_email_address); } </syntaxhighlight>

global.LogEmailAttachment

[edit | edit source]

Golbal script include called to add a note to work notes detailing email attachments sent from WNAC REQTASKs

Write details of outbound email attachments to work notes.

Called from business rule 'ES log sent email attachments'

sys_id of email interaction passed as parameter 'esintref'

Lookup email(s) for interaction in sys_email table - lookup attachments for that email - write details of attachments found to Work Notes of the interaction.

<syntaxhighlight lang="javascript"> var LogEmailAttachment = Class.create(); LogEmailAttachment.prototype = { initialize: function() { }, LogEmailAttachment: function(esintref) { //sys_id of interaction var eml = new GlideRecord('sys_email'); eml.addQuery('instance', esintref); eml.addQuery('target_table', 'x_uno49_enabl_svc_interaction'); eml.query(); while(eml.next()) { var att = new GlideRecord('sys_attachment'); att.addQuery('table_name', 'sys_email'); att.addQuery('table_sys_id', eml.getUniqueValue()); //sys_id of the email att.query(); while(att.next()) { var esint = new GlideRecord('x_uno49_enabl_svc_interaction'); esint.addQuery('sys_id', esintref); //sys_id of interaction esint.query(); while(esint.next()) { esint.work_notes = 'Email ' + '"' + eml.subject + '" sent with attachment ' + '"' + att.file_name +'"'; esint.update(); }

} } }, type: 'LogEmailAttachment' }; </syntaxhighlight>

Maintenance

[edit | edit source]
  • Any regular activity to maintain/support the service

WNAC Assessors

[edit | edit source]

When adding a WNAC Assessor they should be added to the following groups:

  • ES WNAC - Assessors
  • ES Assessor - <user.name> (each assessor has their own group)
  • ES WNAC - QA
  • ES WNAC - AUB
  • ES WNAC - Highfield
  • ES WNAC - Solent

Duplicate Customers

[edit | edit source]

Use the following background script to find occurrences of duplicate ES customers

<syntaxhighlight lang="javascript"> var dupRecords = [];

    var gaDupCheck1 = new GlideAggregate('x_uno49_enabl_svc_customer');
    //gaDupCheck1.addQuery('active','true');
    gaDupCheck1.addAggregate('COUNT', 'user');
    gaDupCheck1.groupBy('user');
    gaDupCheck1.addHaving('COUNT', '>', 1);
    gaDupCheck1.query();
    while (gaDupCheck1.next()) {
          dupRecords.push(gaDupCheck1.user.toString());
    }
    gs.print(dupRecords);

</syntaxhighlight>

Find records where interaction customer != folder customer

[edit | edit source]

Background script to find interactions which have been put in the incorrect customer folder.

<syntaxhighlight lang="javascript"> gs.addInfoMessage('Here we go - Find records where interaction customer != folder customer'); var notMatch = []; var gr = new GlideRecord('x_uno49_enabl_svc_interaction');

   gr.addQuery('customer' != );
   gr.query();
   while (gr.next()) {

           if(gr.customer != gr.parent.customer && gr.customer !=  && gr.parent.customer !=  && gr.parent.customer != undefined) {
           gs.addInfoMessage(gr.number + ': Customer = ' + gr.customer.getDisplayValue() + ' : interaction is in folder ' + gr.parent.getDisplayValue() + ' which belongs to ' + gr.parent.customer.getDisplayValue());
           notMatch.push(gr.number.toString());
        }
   }

gs.print('Interactions where customer does not match customer on parent folder: ' + notMatch); </syntaxhighlight>


Retention policy

[edit | edit source]

Link to email regarding retention policy

Interfaces

[edit | edit source]

Ingestion

[edit | edit source]

Emails sent to enable@soton.ac.uk from a student account will create an 'Interaction' record for that student. Emails sent to enable@soton.ac.uk from non-student email accounts will create an interaction with no customer details. These will appear in the 'Inbox' where they can be triaged and customer details may be added. Emails to First Support email addresses are handled similarly. Watermarked emails are handled in the usual ServiceNow way and will be matched with and existing customer/folder.

Inbound Email Actions

[edit | edit source]
  • Create ES Interaction from new email
  • ES - Interaction from reply
  • Create ES Int and task FS

Business Rule to create a task for each attachment on an inbound email

[edit | edit source]
  • Create Task From Interaction
  • Create Task From Reply Interaction
  • Create Task From Interaction FS

Global Script Include

[edit | edit source]

global.CopySpecificAttachment

Global script include used by the Enabling Services application. Requires the parameters: donorTable, donorID, recipientTable, recipientID, fileName

Called by: Business Rule - Create Task From Interaction

Also deletes attachment from donor table after copying to recipient table. <syntaxhighlight lang="javascript"> var CopySpecificAttachment = Class.create(); CopySpecificAttachment.prototype = { initialize: function() { }, CopySpecificAttachment: function(donorTable, donorID, recipientTable, recipientID, fileName) { try{ var donorAttSysID; var newAttRecord; var linkToNewRecord; var attDataRecord; var newDocRecord; var attRecord = new GlideRecord('sys_attachment'); //find record(s) in sys_attachment attRecord.addQuery('table_name', donorTable); attRecord.addQuery('table_sys_id', donorID); attRecord.addQuery('file_name', fileName); attRecord.query(); while (attRecord.next()) { //loop for each record found in sys_attachment donorAttSysID = attRecord.getValue('sys_id'); newAttRecord = this.copyRecord(attRecord); //call function to copy attachment to the child record newAttRecord.setValue('table_name', recipientTable); newAttRecord.setValue('table_sys_id', recipientID); newAttRecord.update(); linkToNewRecord = gs.getProperty('glide.servlet.uri') + newAttRecord.getLink(); attDataRecord = new GlideRecord('sys_attachment_doc'); attDataRecord.addQuery('sys_attachment', donorAttSysID); //find matching records in sys_attachment_doc attDataRecord.query(); while (attDataRecord.next()) { newDocRecord = this.copyRecord(attDataRecord); //copy attachment parts to child record newDocRecord.setValue('sys_attachment', newAttRecord.getValue('sys_id')); newDocRecord.update(); } try{ attRecord.deleteRecord(); //delete attachment from parent record } catch(err) { gs.log('====> error deleting attachment: message['+err.message+']'); } } } catch(err) { gs.log('====> error in CopySpecificAttachment: message['+err.message+']'); }

}, copyRecord: function(record) { //function to copy attachment(s) to the child record try{ var recordElement; var recordElementName; var recordTable = record.getTableName(); var recordFields = record.getFields(); var newRecord = new GlideRecord(recordTable); newRecord.initialize(); for (var i = 0; i < recordFields.size(); i++) { recordElement = recordFields.get(i); if(recordElement.getName() != 'sys_id' && recordElement.getName() != 'number') { recordElementName = recordElement.getName(); newRecord.setValue(recordElementName, record.getValue(recordElementName)); } } var newSysId = newRecord.insert(); return newRecord; } catch(err) { gs.log('====> error in CopySpecificAttachment.copyRecord: message['+err.message+']'); } }, type: 'CopySpecificAttachment' }; </syntaxhighlight>