Repository: ServiceNowDevProgram/code-snippets Branch: main Commit: e33c87f9eba2 Files: 2956 Total size: 4.6 MB Directory structure: gitextract_wclm7_b1/ ├── .github/ │ ├── pull_request_template.md │ ├── scripts/ │ │ └── validate-structure.js │ └── workflows/ │ ├── hacktrack.yml │ ├── pages.yml │ ├── pr-auto-unassign-stale.yml │ └── validate-structure.yml ├── .gitignore ├── AGENTS.md ├── CONTRIBUTING.md ├── Client-Side Components/ │ ├── Catalog Client Script/ │ │ ├── Add Label For Attachment/ │ │ │ ├── README.md │ │ │ └── add_label_for_attachment.js │ │ ├── Add Rows in MRVS/ │ │ │ ├── README.md │ │ │ └── addrows.js │ │ ├── Auto Save Draft Feature/ │ │ │ ├── README.md │ │ │ ├── advanced_implementation.js │ │ │ ├── basic_implementation.js │ │ │ └── script.js │ │ ├── Auto-populate field from URL/ │ │ │ ├── README.md │ │ │ └── popdatefromurl.js │ │ ├── Autofilling the request details from previous request/ │ │ │ ├── Auto fill script include.JS │ │ │ ├── Client script Autofill.js │ │ │ └── Readme.md │ │ ├── Autopopulate Department/ │ │ │ ├── README.md │ │ │ └── autopopulateDepartment.js │ │ ├── Autopopulate user information fields/ │ │ │ ├── ClientCallableScriptInclude.js │ │ │ ├── README.md │ │ │ └── onChangeClientScript.js │ │ ├── Block Submit/ │ │ │ ├── README.md │ │ │ └── block_submit.js │ │ ├── Calculate age on based on date of birth/ │ │ │ ├── Calculate age based on dob.js │ │ │ └── README.md │ │ ├── Catalog Approval/ │ │ │ ├── Readme.md │ │ │ ├── client script.js │ │ │ └── script include.js │ │ ├── Clear all fields/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Combine variables into Description/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Control all RITM variables in one go/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Currency Validation/ │ │ │ ├── README.md │ │ │ └── currency_validation.js │ │ ├── CustomAlert/ │ │ │ ├── README.md │ │ │ ├── Screenshots/ │ │ │ │ └── README.md │ │ │ ├── custom_alert.js │ │ │ └── custom_alert_box.js │ │ ├── Date Management/ │ │ │ ├── Date Management.js │ │ │ └── README.md │ │ ├── Document validation/ │ │ │ ├── Client script.JS │ │ │ ├── Readme.md │ │ │ └── Script include.js │ │ ├── Dynamically Update Reference Qualifier/ │ │ │ ├── Catalog Item onLoad.js │ │ │ ├── README.md │ │ │ ├── Script Include.js │ │ │ └── Variable Set onLoad.js │ │ ├── Get Display Value of MRVS/ │ │ │ ├── README.md │ │ │ └── mrvs.js │ │ ├── Get MRVS Values from Parent/ │ │ │ ├── README.md │ │ │ └── onload.js │ │ ├── Hide Variables of Catalog Item on Order Guide/ │ │ │ ├── Hide Variables.js │ │ │ └── README.md │ │ ├── Hide attachment icon/ │ │ │ ├── Hide Attachment icon.js │ │ │ └── README.md │ │ ├── Incident Sentiment Detector (Using Simple Word Matching, No AI)/ │ │ │ ├── SentimentAnalyzer.js │ │ │ ├── onChangeClientscript.js │ │ │ └── readme.md │ │ ├── MRVS Email Validation with Mutation Observer/ │ │ │ ├── EmailValidationOnCatalogUI.js │ │ │ └── README.md │ │ ├── MRVS Interact With Parent Form/ │ │ │ ├── Portal onLoad.js │ │ │ ├── README.md │ │ │ └── Write to Parent Form.js │ │ ├── MRVS Loop Rows/ │ │ │ ├── README.md │ │ │ └── loopRows.js │ │ ├── MRVS Reference Qualifier from Catalog Item Variable/ │ │ │ ├── Catalog Client Script.js │ │ │ ├── README.md │ │ │ └── Script Include.js │ │ ├── MRVS dependent ref qual 1st row/ │ │ │ ├── AccountUtils.js │ │ │ ├── README.md │ │ │ └── onLoad.js │ │ ├── Make OOB Attachment Mandatory/ │ │ │ ├── README.md │ │ │ └── onChange.js │ │ ├── Mandatory Attachments with 'n' numbers/ │ │ │ ├── README.md │ │ │ └── onSubmitClientScript.js │ │ ├── Multi-User Collaboration/ │ │ │ ├── Readme.md │ │ │ ├── Script include.JS │ │ │ ├── client script.JS │ │ │ ├── widget client controller.JS │ │ │ └── widget server script.JS │ │ ├── Normalise and Reset a MRVS based on Variable Changes/ │ │ │ ├── README.md │ │ │ ├── mrvs_normalise_onchange.js │ │ │ └── variant_reset_specific_columns.js │ │ ├── Onsubmit validation/ │ │ │ ├── Readme.md │ │ │ ├── on submit scriptinclude.JS │ │ │ └── submit validation client script.js │ │ ├── Open modal widget in an Onsubmit/ │ │ │ ├── README.md │ │ │ └── openmodal.js │ │ ├── PAN Validation/ │ │ │ ├── PAN Validation.js │ │ │ └── README.md │ │ ├── Passport Validation/ │ │ │ ├── README.md │ │ │ └── passportvalidity.js │ │ ├── Password Validation Script/ │ │ │ ├── README.md │ │ │ └── Script.js │ │ ├── Percentage Symbol/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── PopulateDropdown/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Prevent duplicate records on MRVS/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Real time count of letters/ │ │ │ ├── Count letters.js │ │ │ └── readme.md │ │ ├── Remove reference icon from portal/ │ │ │ ├── README.md │ │ │ └── remove-reference-icon-from-portal-onLoad.js │ │ ├── Restrict Number of rows in Multi Row Variable/ │ │ │ ├── README.md │ │ │ └── restrict_multi_row.js │ │ ├── Return Date Validation/ │ │ │ ├── README.md │ │ │ └── validateReturndate.js │ │ ├── Reusable GlideAjax Client Script/ │ │ │ ├── DynamicTableQueryUtil.js │ │ │ ├── Readme.md │ │ │ └── clientscript.js │ │ ├── Rounding Money or Price Field/ │ │ │ ├── README.md │ │ │ ├── catalog_client_script.js │ │ │ └── catalog_client_script_config.md │ │ ├── Schedule Request/ │ │ │ ├── Readme.JS │ │ │ ├── scheduled client script.js │ │ │ ├── scheduled scriptinclude.JS │ │ │ └── scheduled_job.JS │ │ ├── Set User Field Values on Load/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Set and Lock Variable by Group/ │ │ │ ├── README.md │ │ │ └── set_lock_variable_by_grp.js │ │ ├── Set fields from URL Parameter 2/ │ │ │ ├── CatalogClientScript.js │ │ │ ├── KMXOUtils.js │ │ │ ├── README.md │ │ │ └── UtilsAjax.js │ │ ├── Set fields from URL Parameters/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Validate a Credit Card Number/ │ │ │ ├── README.md │ │ │ └── Script.js │ │ ├── catalog Draft/ │ │ │ ├── Readm.md │ │ │ ├── Script include.JS │ │ │ └── client script.js │ │ ├── onCellEdit Catalog Task State Change Restriction/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── previous Request/ │ │ │ ├── Readme.MD │ │ │ ├── previous request client script.js │ │ │ └── previous request script include.js │ │ └── spModal for Sweet Alerts/ │ │ ├── readme.md │ │ └── spModalSweetAlerts.js │ ├── Client Scripts/ │ │ ├── Abort action when description is empty/ │ │ │ ├── Code.js │ │ │ └── ReadMe.md │ │ ├── Abort direct incident closure without Resolve State/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Add Field Decoration/ │ │ │ ├── Add Field Decoration.js │ │ │ └── README.md │ │ ├── Add Image to Field Based on Company/ │ │ │ ├── AddImageToFieldBasedOnCompany.js │ │ │ ├── README.md │ │ │ └── SetCompanyScratchPadValue.js │ │ ├── Adding Placeholder on Resolution Notes/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Auto Update Priority based on Impact and Urgency/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Auto-Populate Planned End Date/ │ │ │ ├── README.md │ │ │ └── autoPopulatePlannedEndDate.js │ │ ├── Auto-Populate Short Discription/ │ │ │ ├── Auto populate short description.js │ │ │ ├── README.md │ │ │ ├── Readme.md │ │ │ └── autoPopulateSD.js │ │ ├── Auto-populate watch_list based on company/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Call SI to recover User data/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Change Label of Field/ │ │ │ ├── Change Label of Field.js │ │ │ └── README.md │ │ ├── Change incident Number label color based on priority/ │ │ │ ├── Script.js │ │ │ └── readme.md │ │ ├── Check all mandatory fields using mandatoryCheck()/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Client Validation of Attachments by File Type and Count/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Client script using getMessage() function without filling Messages field/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Color-coded Priority field for improved UX/ │ │ │ ├── README.md │ │ │ └── setColor.js │ │ ├── Conditional Auto-Routing and Dynamic Mandatory Fields/ │ │ │ ├── Conditional_AutoRouting_Dynamic_Mandatory_Fields.js │ │ │ └── Readme.md │ │ ├── Conditional Form Section based on Role/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Control Form Behaviour from Reference Lookup/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Count Assigned To Field/ │ │ │ ├── README.md │ │ │ └── getAssignedToCount.js │ │ ├── Date Range Validation (Within 30 Days)/ │ │ │ ├── README.md │ │ │ └── dateRangeValidation.js │ │ ├── Detect oldValue newValue and Operation in Glide List Type Fields/ │ │ │ ├── detectOldValuenewValueOperation.js │ │ │ ├── readme.md │ │ │ └── watchListCandidatesUtil.js │ │ ├── Display Custom Field Based on Incident Channel Field and populate with Caller Information/ │ │ │ ├── GlideAjaxCallerInfo.js │ │ │ ├── README.md │ │ │ └── clientScriptOnChangeCaller.js │ │ ├── Display Incident Count of Assigned-To User When Field Changes/ │ │ │ ├── README.md │ │ │ ├── clientScript.js │ │ │ └── glideAjaxIncidentCount.js │ │ ├── Display Section on State/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Display a Live Word Count for Description Field/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Dynamic Field Dependencies with GlideAjax/ │ │ │ ├── README.md │ │ │ ├── ajax_script_include.js │ │ │ ├── conditional_field_loader.js │ │ │ └── dynamic_category_subcategory.js │ │ ├── Dynamic Location Validation Approach/ │ │ │ ├── Readme.md │ │ │ ├── User Location Validator.js │ │ │ └── UserLocationUtils.js │ │ ├── Dynamic Reference Qualifier with Filtering/ │ │ │ ├── README.md │ │ │ └── reference_qual_dynamic.js │ │ ├── Dynamic UI Actions/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Dynamic script to make fields read only/ │ │ │ ├── reame.md │ │ │ └── script.js │ │ ├── Dynamically Switch Form View Based on Field Value/ │ │ │ ├── README.md │ │ │ └── dynamic-form-view-onchange.js │ │ ├── Enable VIP and Senior VIP Checkboxes and read only/ │ │ │ ├── README.md │ │ │ └── ScriptToEnableVIP_superVIP users │ │ ├── End Date can't be before Start Date/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Expanding Info Message/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Field Completion Counter/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Field Placeholder/ │ │ │ ├── README.md │ │ │ └── fieldplaceholder.js │ │ ├── Field Validations/ │ │ │ ├── README.md │ │ │ └── fieldValidation.js │ │ ├── Form Dirty State Prevention/ │ │ │ ├── README.md │ │ │ └── form_dirty_state_manager.js │ │ ├── Get Field Value From List on Client/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get Form Elements/ │ │ │ ├── README.MD │ │ │ └── getFormElements.js │ │ ├── Get Logged in User Information/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get URL Parameters/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get Value from URL Parameter/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Health Scan Prevent Insert Update in Before BRs/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Hide Dependent Choice field if there no dependent choices/ │ │ │ ├── HideDepnedentField.js │ │ │ ├── NumberOfDependentChoices.js │ │ │ └── README.md │ │ ├── Hide Work Notes section/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── How to adjust the Date format within a client script to align with the User Date format/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Incident Count of Selected CI with Clickable Link to Related Incidents/ │ │ │ ├── README.md │ │ │ ├── clientScriptCiIncident.js │ │ │ └── glideAjaxIncidentCiCount.js │ │ ├── Live Character Counter and Validator/ │ │ │ ├── README.md │ │ │ └── character_counter.js │ │ ├── MRVS variables validations/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Major Incident Proposal/ │ │ │ ├── Client_Script.js │ │ │ ├── README.md │ │ │ └── Script_Include.js │ │ ├── Make Variable Editor Read Only for Catalog Items containing MRVS/ │ │ │ ├── CheckMRVSDetails.js │ │ │ ├── MakeVariableSetReadOnly.js │ │ │ └── README.md │ │ ├── Make all fields read only/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Make fields read only in specific states/ │ │ │ ├── Make fields read only in specific state.md │ │ │ └── code.js │ │ ├── Mandatory Field Highlighter/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── MultiSelect in Portal/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── On load Switch-Case Testing/ │ │ │ ├── README.md │ │ │ └── Switch-Case code snippet.js │ │ ├── Only number validation for input/ │ │ │ ├── OnChange.js │ │ │ └── README.md │ │ ├── Open Record in Agne Workspace Tab/ │ │ │ ├── README.md │ │ │ └── openrecawtab.js │ │ ├── Populate Jelly Slushbucket with Values/ │ │ │ ├── ClientScript.js │ │ │ └── README.md │ │ ├── Prevent Rejection Without Comments/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Price field restriction to one currency/ │ │ │ ├── README.md │ │ │ └── price_field_restriction_to_one_currency.js │ │ ├── Redact Sensitive Information/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Reinstate Error status/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Remove Option from Choice List/ │ │ │ ├── README.md │ │ │ └── Remove Options from Choice List.js │ │ ├── Require comment onPriority change/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Restrict Creation of P1, P2 Incidents/ │ │ │ ├── README.md │ │ │ └── Restrict_Creation_Of_P1_P2_Incidents.js │ │ ├── Restrict Fields on Template/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Set Severity, state & assigned to/ │ │ │ ├── README.md │ │ │ ├── script.js │ │ │ └── script_include.js │ │ ├── Set Urgency to High onChange of caller field/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Set field style like font and background/ │ │ │ ├── OnLoad client script.js │ │ │ └── README.md │ │ ├── Show Current Domain/ │ │ │ ├── DomainCheckUtil.js │ │ │ ├── Readme.md │ │ │ └── Show Current Domain.js │ │ ├── Show Message On Both Form and List/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Show field if x things are checked/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Smart-Field-Suggestions/ │ │ │ ├── README.md │ │ │ └── Smart Field Suggestions Based on Keyword.js │ │ ├── State changes to On Hold then worknotes should be mandatory/ │ │ │ ├── README.md │ │ │ └── worknotes.js │ │ ├── Sync Ajax with no getXMLWait/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Toggle Annotation On Forms With Script/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Toggle form section visibility/ │ │ │ ├── README.md │ │ │ └── toggleFormSection.js │ │ ├── Translate Message/ │ │ │ ├── README.md │ │ │ └── getMessage.js │ │ ├── Update Category from Short Description Keywords/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Use case of addOption() and removeOption()/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Validate Interaction record for FCR(First Call Resolution)/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Validate Short Description/ │ │ │ ├── README.md │ │ │ ├── ShortDescriptionLength.js │ │ │ ├── validShortDescription.js │ │ │ └── validateSpecialChar.js │ │ ├── Validate date is in future without GlideAjax/ │ │ │ ├── OnChange Client Script.js │ │ │ └── README.md │ │ ├── Verify if e-mail already exists with Ajax call/ │ │ │ ├── README.md │ │ │ ├── clientScript.js │ │ │ └── scriptInclude.js │ │ ├── Verify whether a date falls within a hour range/ │ │ │ ├── README.md │ │ │ └── verifyWhetherADateFallsWithinAHourRange.js │ │ ├── Whitespace Validation/ │ │ │ ├── README.md │ │ │ └── whitespaceValidation.js │ │ ├── Zurich - Upgraded info messages/ │ │ │ ├── README.md │ │ │ └── infoMessages.js │ │ ├── field-character-counter/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── g_form console access in workspace/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── onfocus and onblur/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── state-edit-for-grpmem/ │ │ │ ├── README.md │ │ │ └── grpmemstateedit.js │ │ └── validate phone number/ │ │ ├── README.md │ │ ├── Readme.md │ │ ├── validate phone number.js │ │ └── validate_phone_format_(123)_456-7890_no_regex.js.js │ ├── UI Actions/ │ │ ├── Add Loggedin user as Incident assigned to/ │ │ │ ├── ReadMe.md │ │ │ └── code.js │ │ ├── Add Show Workflow Related link/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Add collapsible element in knowledge article/ │ │ │ ├── README.md │ │ │ ├── UI_Action.js │ │ │ └── add_collapsible.js │ │ ├── Call Subflow/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── CallingPopUpBoxInListView/ │ │ │ ├── README.md │ │ │ └── calling_pop_up_box_in_list_view.js │ │ ├── Cancel Flow Executions/ │ │ │ ├── README.md │ │ │ └── cancelFlow.js │ │ ├── Cancel Incident/ │ │ │ ├── README.md │ │ │ ├── SETUP.md │ │ │ └── script.js │ │ ├── Clone incident on Agent Workspace/ │ │ │ ├── README.md │ │ │ ├── clone_incident.js │ │ │ └── workspace_client_script.js │ │ ├── Close Related HR cases & HR tasks/ │ │ │ ├── README.md │ │ │ ├── Script Include.js │ │ │ └── UI Action script.js │ │ ├── Close child incident/ │ │ │ ├── README.md │ │ │ ├── clientScript.js │ │ │ └── serverScript.js │ │ ├── CloseChildCases/ │ │ │ ├── CloseChildCases.js │ │ │ └── README.md │ │ ├── Convert Request to Incident/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Copy Bulk SysIDs/ │ │ │ ├── Copy Bulk Sysids.js │ │ │ └── README.md │ │ ├── Copy Variable Set/ │ │ │ ├── README.md │ │ │ └── scripts.js │ │ ├── Copy incident details and create a child incident/ │ │ │ ├── README.md │ │ │ └── ui_action_script.js │ │ ├── Create Incident from Record - Open in both Platform and Workspace/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Create New blank incident from the incident/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Create Problem Record from any Table/ │ │ │ ├── CreateProblemRecord.js │ │ │ └── README.md │ │ ├── Create Problem Task from the Problem/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Create Update Set on DEV/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Create incident task and relate to incident/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Create story/ │ │ │ ├── Create story from other task.js │ │ │ └── README.md │ │ ├── Display a 2-choice confirmation dialog/ │ │ │ ├── README.md │ │ │ └── choice_dialog.js │ │ ├── Email Watermark Utility/ │ │ │ ├── GenericEmailUtility.js │ │ │ ├── README.md │ │ │ └── Send Email UI Action.js │ │ ├── Expire Timer in Flows/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Field Review of User Record when on form using action button/ │ │ │ ├── README.md │ │ │ └── actionButtonScript.js │ │ ├── Force to Update Set/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Generate QR for Assets/ │ │ │ ├── ReadMe.md │ │ │ └── ui-action-script.js │ │ ├── Generate a PDF/ │ │ │ ├── README.md │ │ │ └── serverscript.js │ │ ├── GlideModalForm - Open New Record and Pass Query/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── GlideModalUiPagePopUp/ │ │ │ ├── README.md │ │ │ └── glide_modal_ui_pop_up.js │ │ ├── Go to Agent Workspace Home Page/ │ │ │ ├── README.md │ │ │ └── ui_action_script.js │ │ ├── Group Membership Admin Util/ │ │ │ ├── README.md │ │ │ ├── addMeUIActionScript.js │ │ │ └── removeMeUIActionScript.js │ │ ├── Group dependency/ │ │ │ ├── README.md │ │ │ ├── uiaction.js │ │ │ └── uipage.js │ │ ├── Kill flow timers/ │ │ │ ├── README.md │ │ │ ├── UI action.js │ │ │ └── UI page for ui action.js │ │ ├── Knowledge Link Validator/ │ │ │ ├── Readme.md │ │ │ ├── uiaction.js │ │ │ └── uipage.js │ │ ├── Mark Records Inactive - List Action/ │ │ │ ├── README.md │ │ │ ├── listAction.js │ │ │ └── scriptInclude.js │ │ ├── Open Email Client using UI Action/ │ │ │ ├── README.md │ │ │ └── open_email_client.js │ │ ├── Open LIST UI Action/ │ │ │ ├── README.md │ │ │ └── UIActionscript.js │ │ ├── Open Record in Alternate Instance/ │ │ │ ├── README.md │ │ │ ├── RESTMessageV2/ │ │ │ │ ├── sys_rest_message_config.md │ │ │ │ └── sys_rest_message_fn_config.md │ │ │ ├── Script Includes/ │ │ │ │ ├── README.md │ │ │ │ ├── sys_script_include.js │ │ │ │ └── sys_script_include_config.md │ │ │ ├── Scripted REST API/ │ │ │ │ ├── sys_ws_definition_config.md │ │ │ │ └── sys_ws_operation/ │ │ │ │ ├── sys_ws_operation.js │ │ │ │ └── sys_ws_operation_config.md │ │ │ └── UI Action/ │ │ │ ├── sys_ui_action.js │ │ │ └── sys_ui_action_config.md │ │ ├── Open Record producer from Form Button In Configurable workspace/ │ │ │ ├── OpenItem.js │ │ │ ├── ParseUrl.js │ │ │ └── README.md │ │ ├── Open a new blank form/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Open in Service Operations Workspace/ │ │ │ ├── README.md │ │ │ └── ui_action_script.js │ │ ├── Populate Due Date based on Priority/ │ │ │ ├── Readme.md │ │ │ ├── ScriptInclude.js │ │ │ └── UI Action.js │ │ ├── Preview context record during approval/ │ │ │ ├── README.md │ │ │ ├── TestScriptInclude.js │ │ │ └── UI_Action.js │ │ ├── Printer Friendly Version/ │ │ │ ├── README.md │ │ │ └── printer_friendly_verison.js │ │ ├── Publish a Retired Knowledge Article again/ │ │ │ ├── README.md │ │ │ └── publishretiredkb.js │ │ ├── Select Random User From Group/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Send notification if the incident remains unassigned/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Send notification to the assigned user/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Set Incident Priority Critical/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Show Today Emails Logs/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Smart Assign to available member/ │ │ │ ├── README.md │ │ │ └── smartAssigntoAvailablemember.js │ │ ├── Test and Debug Scheduled Scripts/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Try Catalog item in Portal view/ │ │ │ ├── README.md │ │ │ └── TryItInSP.js │ │ ├── UI Action to Mark Incident as Escalated/ │ │ │ ├── README.md │ │ │ └── Script.js │ │ ├── Variable Ownership/ │ │ │ ├── Readme.md │ │ │ └── script.js │ │ └── View in Portal Page/ │ │ ├── README.md │ │ └── View in Portal Page.js │ ├── UI Macros/ │ │ ├── Copy To Clipboard/ │ │ │ ├── README.md │ │ │ └── copy_field_to_clipboard.xml │ │ ├── FormBackground/ │ │ │ ├── form_background.xml │ │ │ └── readme.md │ │ ├── JSON Formatter and Viewer/ │ │ │ ├── README.md │ │ │ └── json_formatter.xml │ │ ├── Purchase Order Approval Summarizer/ │ │ │ ├── README.md │ │ │ └── approval_summarizer_proc_po.xml │ │ ├── Show Open Incident of Caller/ │ │ │ ├── Readme.md │ │ │ └── macro.js │ │ └── Variable Copy Context Options/ │ │ ├── README.md │ │ └── element context.xml │ ├── UI Pages/ │ │ ├── Add Multiple Items to Order Guide/ │ │ │ ├── README.md │ │ │ ├── Script Include.js │ │ │ ├── UI Action.js │ │ │ └── UI Page.js │ │ ├── BulkUpdate Worknotes/ │ │ │ ├── Readme.md │ │ │ ├── UI Action.js │ │ │ ├── UI Page_ClientScript.js │ │ │ ├── UI Page_HTML.html │ │ │ └── UI Page_ProcessingScript.js │ │ ├── CMDB CI Management UI page/ │ │ │ ├── ci_client_script.js │ │ │ ├── ci_lifecycle_ui_page.xml │ │ │ └── ci_script_include.js │ │ ├── Custom Alert using UI Page/ │ │ │ ├── README.md │ │ │ ├── client script.js │ │ │ └── custom alert.js │ │ ├── Dynamic program status overview/ │ │ │ ├── Program details dynamic content block.xml │ │ │ ├── README.md │ │ │ ├── program_list.html │ │ │ └── program_list.js │ │ ├── EDM DocUnifiedSearch/ │ │ │ ├── EDMSearch.js │ │ │ ├── README.md │ │ │ ├── client script.js │ │ │ └── code.html │ │ ├── Edit Last WorkNotes/ │ │ │ ├── README.md │ │ │ ├── UIaction.js │ │ │ ├── scriptinclude.js │ │ │ ├── uipage_client.html │ │ │ └── uipage_clientcode.js │ │ ├── Export UI pages to word docx/ │ │ │ ├── Export to word.js │ │ │ └── README.md │ │ ├── Fetch Table(Incident) fields in UI Page/ │ │ │ ├── Fetch incident fields.js │ │ │ └── README.md │ │ ├── Populate Glide List field/ │ │ │ ├── README.md │ │ │ ├── populateGlideListClientScript.js │ │ │ └── populateGlideListHTML.js │ │ ├── Progress Loader/ │ │ │ ├── REAMDE.md │ │ │ ├── example_use_GlideModal.js │ │ │ └── loaderHTML.html │ │ ├── Real time log watcher/ │ │ │ ├── README.md │ │ │ ├── TestScriptInclude.js │ │ │ └── log_watcher.js │ │ ├── Resolve Incident UI Page/ │ │ │ ├── README.md │ │ │ ├── UI_action.js │ │ │ ├── scriptinclude.js │ │ │ ├── ui_page_client.js │ │ │ └── ui_page_html.html │ │ ├── Send Email On Form Incident/ │ │ │ ├── EmailScript.js │ │ │ ├── Notification.js │ │ │ ├── Readme.md │ │ │ ├── UIAction.js │ │ │ ├── UIPage_Html.html │ │ │ └── UIPage_ProcessingScript.js │ │ ├── Share reports with users and groups/ │ │ │ ├── README.md │ │ │ ├── UI Page/ │ │ │ │ ├── ui_page.html │ │ │ │ ├── ui_page_client_script.js │ │ │ │ └── ui_page_processing_script.js │ │ │ └── ui_action_script.js │ │ └── UI Page Auto Populate Assigned to based on Assignment group/ │ │ ├── README.md │ │ ├── client_script.js │ │ ├── jelly_script.xml │ │ └── script_include.js │ ├── UI Scripts/ │ │ ├── Custom Change Schedule/ │ │ │ ├── README.md │ │ │ ├── change_soc.js │ │ │ ├── config.js │ │ │ └── data.js │ │ ├── Disable Copy Paste For Portal/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Display number of created records/ │ │ │ ├── README.md │ │ │ ├── onLoad.js │ │ │ └── ui_script.js │ │ ├── Make OOB Attachment Mandatory/ │ │ │ ├── README.md │ │ │ └── setAttachmentMandatory.js │ │ ├── Observe MRVS Events/ │ │ │ ├── MRVSUtils.js │ │ │ └── README.md │ │ ├── PersistentAnnouncementBanner/ │ │ │ ├── README.md │ │ │ └── annoucement_banner.js │ │ ├── Prevent right click on portals/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Restrict URL Hack using UI script/ │ │ │ ├── README.md │ │ │ └── script.js │ │ └── User acknowledgement Using UI script and user preferences/ │ │ ├── UIpage.js │ │ ├── UIscript.js │ │ └── readme.md │ ├── UX Client Script Include/ │ │ ├── Access global object from page scripts/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Record Operation Utilities/ │ │ │ ├── README.md │ │ │ └── script.js │ │ └── Reusable Debounce/ │ │ ├── DebounceUtil.js │ │ └── README.md │ ├── UX Client Scripts/ │ │ ├── debug-event/ │ │ │ ├── README.md │ │ │ └── debug-event.js │ │ └── debug-state/ │ │ ├── README.md │ │ └── debug-state.js │ └── UX Data Broker Transform/ │ ├── FetchSysProperty/ │ │ ├── README.md │ │ └── sysPropdataBroker.js │ ├── create-update-user-preference/ │ │ ├── README.md │ │ └── create-update-user-preference.js │ └── starter-template/ │ ├── README.md │ └── template.js ├── Core ServiceNow APIs/ │ ├── GlideAggregate/ │ │ ├── Count All Open Incidents Per Priority/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Count Inactive Users with Active incidents/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Count incidents based on category/ │ │ │ ├── Count incidents based on category.js │ │ │ └── README.md │ │ ├── Count open Incidents per Priority and State using GlideAggregate/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Create Problem based on incident volume/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Find Oldest Open Incidents per Group/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get Incident Count by Priority/ │ │ │ ├── README.md │ │ │ └── get-incident-count-by-priority.js │ │ ├── Get top 5 CIs with most number of Open Incidents/ │ │ │ ├── README.md │ │ │ └── getCIwithmostActiveInc.js │ │ ├── Group Count/ │ │ │ ├── GlideQuery.js │ │ │ ├── GlideQuery_readme.md │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Grouping by three columns/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Improve incident handling/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Incident Analysis and Resolution Calculation using Glideaggregate/ │ │ │ ├── Incident_analysis_Resolution_Calculation_GlideAggregate.js │ │ │ └── README.md │ │ ├── Incident resolution percentile by assignment group/ │ │ │ ├── PercentileMetrics.js │ │ │ ├── README.md │ │ │ └── example_background_usage.js │ │ ├── LicensedUserCount/ │ │ │ ├── licensed_user_count_by_role.js │ │ │ └── readme.md │ │ ├── List of Child Incident of All Incidents/ │ │ │ ├── README.md │ │ │ └── allChildIncidents.js │ │ ├── List of Managers in User Table/ │ │ │ ├── ListOfManagers.js │ │ │ └── README.md │ │ ├── List the incident priority count under each category/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── SLA Compliance Ratio by Assignment Group/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── SLA Count by Assignment Group/ │ │ │ ├── README.md │ │ │ └── SLA Count by Assignment Group.js │ │ ├── ScheduleJob by ExectionTime_perDay/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── SimpleGlideAggregate/ │ │ │ ├── Readme.md │ │ │ └── SimpleGlideAggregate.js │ │ ├── Tiered grouping of an integer column/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Top 5 Users with Most Incidents/ │ │ │ ├── README.md │ │ │ └── top-five-users-with-most-incidents.js │ │ ├── Using addHaving/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── addTrend/ │ │ │ ├── README.md │ │ │ └── addTrend.js │ │ ├── getCountAfterDate/ │ │ │ ├── README.md │ │ │ └── getCountAfterDate.js │ │ └── getTotal of aggregate value/ │ │ ├── README.md │ │ └── getTotal.js │ ├── GlideAjax/ │ │ ├── AjaxAsyncOnSubmit/ │ │ │ ├── README.md │ │ │ ├── ajaxasynconsubmitclient.js │ │ │ └── ajaxasynconsubmitserver.js │ │ ├── Check Weekend - Client Side/ │ │ │ ├── README.md │ │ │ └── WeekendChecker.js │ │ ├── EfficientGlideRecord (Client-side)/ │ │ │ ├── ClientGlideRecordAJAX.js │ │ │ ├── EfficientGlideRecord (minified) UI Script.js │ │ │ ├── EfficientGlideRecord UI Script.js │ │ │ ├── Example usage (client-side code).js │ │ │ └── README.md │ │ ├── Fetch Multiple Values in GlideAjax without JSON/ │ │ │ ├── ClientScript.js │ │ │ ├── README.md │ │ │ └── TestScriptInclude.js │ │ ├── Get Field Values/ │ │ │ ├── GetFieldValuesAjax.js │ │ │ └── README.md │ │ ├── Get choices from Decision Table/ │ │ │ ├── GetChoicesFromDT.js │ │ │ ├── README.md │ │ │ └── addChoicesClient.js │ │ ├── GlideAjax Example Template/ │ │ │ ├── README.md │ │ │ ├── code.js │ │ │ └── example from community.js │ │ ├── Return Asset(s) for User/ │ │ │ ├── FindUserAsset.js │ │ │ └── README.md │ │ ├── ReturnMultipleProperties/ │ │ │ ├── README.md │ │ │ └── ReturnMultipleProperties.js │ │ ├── Reusable GlideAjax/ │ │ │ ├── README.md │ │ │ ├── clientCallableScriptInclude.js │ │ │ └── clientSideGlideAjax.js │ │ └── Reusable glideajax table query/ │ │ ├── README.md │ │ └── getTableColumnsClientSide.js │ ├── GlideDate/ │ │ └── Convert text date to GlideDate Format/ │ │ ├── Extract and Convert Date in a Text or String to GlideDate Format.js │ │ └── README.md │ ├── GlideDateTime/ │ │ ├── AddDays/ │ │ │ ├── README.md │ │ │ └── addDays.js │ │ ├── Business time utilities (add, diff, next open, in schedule)/ │ │ │ ├── BusinessTimeUtils.js │ │ │ ├── README.md │ │ │ └── example_background_usage.js │ │ ├── Calculate Due date using user defined schedules/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Check if today is weekend/ │ │ │ ├── Check if today is weekend.js │ │ │ └── README.md │ │ ├── Convert UTC Time To Local Time/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Convert date format/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── ConvertTicksToGlideDateTime/ │ │ │ ├── ConvertTicksToGlideDateTime.js │ │ │ └── README.md │ │ ├── Current Date with Fixed Time/ │ │ │ ├── CurrentDateFixedTime.js │ │ │ └── README.md │ │ ├── Due date generation/ │ │ │ ├── README.md │ │ │ └── duedate_generation.js │ │ ├── Find Incidents Older Than X Days/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Get Date Difference/ │ │ │ ├── GetDiffernceBtnDates.js │ │ │ └── README.md │ │ ├── Get Next Monday Date/ │ │ │ ├── GetNextMondayDate.js │ │ │ └── README.md │ │ ├── Get last day of a month/ │ │ │ ├── README.md │ │ │ └── getLastDayOfMonth.js │ │ ├── Set time zone and date format to output string dates/ │ │ │ ├── README.md │ │ │ └── output_string_dates.js │ │ ├── Start, End, and Duration Updates/ │ │ │ ├── README.md │ │ │ └── start_end_duration_updates.js │ │ └── Use timezone in Scoped App/ │ │ ├── README.md │ │ └── script.js │ ├── GlideElement/ │ │ ├── Display available choices/ │ │ │ ├── DisplayAvailableChoices.js │ │ │ └── README.md │ │ ├── Display base table for each field/ │ │ │ ├── README.md │ │ │ └── displayBaseTablesForEachField.js │ │ ├── Smart Field Validation and Dependent Field Derivation Using getError() and setError()/ │ │ │ ├── README.md │ │ │ ├── br_derive_dependent_fields.js │ │ │ └── br_validate_short_description.js │ │ └── getDependent/ │ │ ├── README.md │ │ └── glideelement.js │ ├── GlideFilter/ │ │ └── checkRecord/ │ │ ├── README.md │ │ └── example.js │ ├── GlideHTTPRequest/ │ │ └── Retrieve table records via GlideHTTPRequest/ │ │ ├── README.md │ │ └── glidehttprequest.js │ ├── GlideJsonPath/ │ │ ├── Basic-Example/ │ │ │ ├── README.md │ │ │ └── examples.js │ │ └── Create Critical P1 Incident from Alert using GlideJsonPath/ │ │ ├── README.md │ │ └── script.js │ ├── GlideModal/ │ │ ├── Add HTML Input Field in GlideModal Window/ │ │ │ ├── README.md │ │ │ ├── UI Action │ │ │ └── UI page │ │ ├── Confirm Message/ │ │ │ ├── README.md │ │ │ └── glide_confirm.js │ │ └── Information Message/ │ │ ├── README.md │ │ ├── glide_info.js │ │ └── glide_warn.js │ ├── GlideQuery/ │ │ ├── Basic Wrappers/ │ │ │ ├── README.md │ │ │ ├── delete_records.js │ │ │ ├── get_records.js │ │ │ ├── insert_records.js │ │ │ └── update_records.js │ │ ├── Conditional Field Selection/ │ │ │ ├── README.md │ │ │ └── conditional_field_selection.js │ │ ├── Field Default/ │ │ │ ├── GlideQueryFieldDefault.js │ │ │ └── README.md │ │ ├── FlatMap to Nest New Queries/ │ │ │ ├── README.md │ │ │ └── getIncidentInfoWithFlatMap.js │ │ ├── Get Delegates/ │ │ │ ├── GlideQueryGetDelegates.js │ │ │ └── README.md │ │ ├── Get User's Roles from User Name/ │ │ │ ├── README.md │ │ │ └── getUserRoles.js │ │ ├── Nested WHERE orWHERE GlideQueries/ │ │ │ ├── README.md │ │ │ └── nestedWhereQueries.js │ │ └── Remote Table/ │ │ ├── README.md │ │ └── remotetabldef.js │ ├── GlideRecord/ │ │ ├── ACL enforcement using GlideRecord/ │ │ │ ├── README.md │ │ │ └── glideRecordSecure.js │ │ ├── Add n number of users to n number of groups using server scripts/ │ │ │ ├── AddUserstoGroups.js │ │ │ └── README.md │ │ ├── Archiving Old Incident Records to Improve Performance/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── CheckDuplicate-Server/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Choose Window for better performance/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Compare_2_records/ │ │ │ ├── README.md │ │ │ └── compareRecords.js │ │ ├── Conditional Batch Update/ │ │ │ ├── README.md │ │ │ └── batchUpdate.js │ │ ├── Count Records By Column/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Display list of records based on Users Location/ │ │ │ ├── README.md │ │ │ └── listOfRecordsBasedOnLocation.js │ │ ├── Fetch active incidents assigned to a specific group/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Fetch groups that have no members in them/ │ │ │ ├── README.md │ │ │ └── fetchEmptyGroups.js │ │ ├── Field Level Audit/ │ │ │ ├── README.md │ │ │ └── fieldLevelAudit.js │ │ ├── Find Date Overlapping/ │ │ │ ├── README.md │ │ │ └── isSimilarDates.js │ │ ├── Find No Of Days/ │ │ │ ├── README.md │ │ │ └── findNoOfDays.js │ │ ├── Get All Groups without Manager/ │ │ │ ├── README.md │ │ │ └── getGroupsWithoutManager.js │ │ ├── Get Contains role of a role/ │ │ │ ├── Get contains role of a role.js │ │ │ └── README.md │ │ ├── Get Record Fields in JSON/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get Reference Record/ │ │ │ ├── README.md │ │ │ ├── get_assignment_group_from_incident.js │ │ │ └── get_requested_by_user.js │ │ ├── Get Variables from RITM/ │ │ │ ├── README.md │ │ │ └── getVariablesJSON.js │ │ ├── Get all task records with at least on active child task/ │ │ │ ├── Get all task records with at least on active child task.md │ │ │ └── code.js │ │ ├── Get all user's group based on username/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get all users whose email is empty/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get field from GlideRecord/ │ │ │ ├── README.md │ │ │ └── getField.js │ │ ├── Get link for the Record/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get-task-containing-sensitive-data/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Gets the display value according to the specified language/ │ │ │ ├── README.md │ │ │ └── specified_language.js │ │ ├── GlideRecord to Object/ │ │ │ ├── README.md │ │ │ └── _grToObject.js │ │ ├── GlideRecord with Performance Enhancement Condtions/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── LEFT Join/ │ │ │ ├── README.md │ │ │ ├── example.js │ │ │ └── script.js │ │ ├── List of Child Incidents/ │ │ │ ├── README.md │ │ │ └── listofchildIncident.js │ │ ├── Multi Row Variable Set(MRVS)/ │ │ │ ├── InsertMRVSRecords.js │ │ │ └── README.md │ │ ├── Performance Optimization Techniques/ │ │ │ ├── README.md │ │ │ ├── indexed_field_queries.js │ │ │ ├── optimized_batch_processing.js │ │ │ └── query_performance_comparison.js │ │ ├── Populate the type of device on any record/ │ │ │ ├── README.md │ │ │ └── typeOfDevice.js │ │ ├── Record Activity Collector/ │ │ │ ├── ActivityCollector.js │ │ │ └── README.md │ │ ├── Safe Bulk Delete/ │ │ │ ├── README.md │ │ │ └── safeDelete.js │ │ ├── Set Template/ │ │ │ ├── README.md │ │ │ └── setTemplate.js │ │ ├── Unique Record/ │ │ │ ├── README.md │ │ │ └── uniquerecord.js │ │ ├── UpdateMultiple/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Watch_List_functions/ │ │ │ ├── README.md │ │ │ └── removeSpecificUser.js │ │ ├── comments on gr.update/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── findDuplicate/ │ │ │ ├── GlideAggregateScript.js │ │ │ └── README.md │ │ ├── getEncodedQuery/ │ │ │ ├── README.md │ │ │ └── code.js │ │ └── isValidGlideRecord/ │ │ ├── README.md │ │ └── isValidGlideRecord.js │ ├── GlideSystem/ │ │ ├── Impersonate/ │ │ │ ├── README.md │ │ │ └── impersonate.js │ │ ├── Session/ │ │ │ ├── README.md │ │ │ └── session.js │ │ ├── Table/ │ │ │ ├── README.md │ │ │ ├── tableExists.js │ │ │ └── truncateTable.js │ │ ├── Trigger Event/ │ │ │ ├── README.md │ │ │ ├── eventQueue.js │ │ │ └── eventQueueScheduled.js │ │ ├── User/ │ │ │ ├── README.md │ │ │ └── userID.js │ │ ├── User Display Name/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── date-time/ │ │ │ ├── README.md │ │ │ ├── beginningOfLastMonth.js │ │ │ ├── minutesAgoEnd.js │ │ │ ├── minutesAgoStart.js │ │ │ ├── monthsAgo.js │ │ │ ├── monthsAgoEnd.js │ │ │ ├── monthsAgoStart.js │ │ │ ├── quartersAgo.js │ │ │ ├── quartersAgoEnd.js │ │ │ ├── quartersAgoStart.js │ │ │ ├── setDisplayValueInternalWithAlternates.js │ │ │ ├── yearsAgo.js │ │ │ └── yesterday.js │ │ ├── hasRoleExactly/ │ │ │ ├── README.md │ │ │ └── script.js │ │ └── workflowFlush/ │ │ ├── README.md │ │ └── workflowFlush.js │ └── GlideTableDescriptor/ │ └── getFirstTableName()/ │ ├── GlideTableDescriptor.js │ └── README.md ├── Integration/ │ ├── Attachments/ │ │ ├── Attachment to Base64/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Attachment to base64 in scope/ │ │ │ ├── README.md │ │ │ └── attachmentToBase64Scope.js │ │ ├── Base 64 to Attachment/ │ │ │ ├── README.md │ │ │ └── base64toattachment.js │ │ ├── CSVParser/ │ │ │ ├── README.md │ │ │ ├── csvparser.js │ │ │ └── script.js │ │ ├── Calculate attachment hash code/ │ │ │ ├── README.md │ │ │ └── calculateHash.js │ │ ├── Convert KnowledgePage to PDF/ │ │ │ ├── Convert_KnowledgePage_to_PDF.js │ │ │ └── README.md │ │ ├── Create Attachments/ │ │ │ ├── Create attachment via script.js │ │ │ └── README.md │ │ ├── Delete RITM Attachment/ │ │ │ ├── README.md │ │ │ └── deleteattachment.js │ │ ├── ExportAttachmentsToMidServer/ │ │ │ ├── README.md │ │ │ └── exportattachmentstomid.js │ │ ├── ExportRecordsAnyFormat/ │ │ │ ├── README.md │ │ │ └── exportRecords.js │ │ ├── Send Attachment to MID Server/ │ │ │ ├── README.md │ │ │ └── Send Attachment to MID Server.js │ │ ├── Show RITM has Attachments/ │ │ │ ├── README.md │ │ │ ├── ShowRITMhasAttachment_BR.js │ │ │ └── ShowRITMhasAttachment_CS.js │ │ └── attachmentToXMLParse/ │ │ ├── README.md │ │ └── code.js │ ├── AzureAD Integration/ │ │ └── Dynamically create reference records/ │ │ ├── Load_cmn_location.js │ │ └── README.md │ ├── Data Export to ML Pipeline/ │ │ └── Export Data for ML Training/ │ │ ├── README.md │ │ ├── data_export_script_include.js │ │ └── export_data_rest_api.js │ ├── GraphQL Integration API/ │ │ └── Incident GraphQL resolvers/ │ │ ├── CI Resolver.js │ │ ├── GraphQL schema.js │ │ ├── Incident Resolver.js │ │ ├── README.md │ │ └── User Resolver.js │ ├── ITSM/ │ │ └── Bulk task_ci REST API/ │ │ ├── README.md │ │ └── liveCItoTAsk.js │ ├── Import Set API/ │ │ └── Attachment Handler/ │ │ ├── README.md │ │ ├── attachmentParser.js │ │ └── attachmentParserUtil.js │ ├── Import Sets/ │ │ ├── Import sets overview/ │ │ │ ├── ModelManufacture.README.md │ │ │ ├── ModelManufacture.js │ │ │ ├── README.md │ │ │ ├── TriggerDataSource.README.md │ │ │ └── TriggerDataSource.js │ │ └── debug/ │ │ ├── README.md │ │ └── debugImportSet.js │ ├── Import Sets Debug/ │ │ └── Debug import set payloads/ │ │ ├── README.md │ │ └── debugImportSet.js │ ├── MIDServer/ │ │ └── API Class Examples/ │ │ ├── README.md │ │ └── scripts.js │ ├── Mail Scripts/ │ │ ├── Add Checklist/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Add HTML Table for Requested Item Variables/ │ │ │ ├── README.md │ │ │ └── requested_items_detail.js │ │ ├── Add Users in Watchlist to CC/ │ │ │ ├── Add Users in Watchlist to CC │ │ │ └── README.md │ │ ├── Add a link which opens ticket in Service Portal/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Call Script Include in Notification Mail Script/ │ │ │ ├── README.md │ │ │ └── call_script_include.js │ │ ├── Call UI Message or System Property in Notification Mail Script/ │ │ │ ├── README.md │ │ │ └── call_UIMessage_or_sysProperty.js │ │ ├── Configurer Approve Reject Buttons Using Email Scripts/ │ │ │ ├── Email Script.js │ │ │ └── README.md │ │ ├── Convert DateTime to Date/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Exclude DateTime details from Comments/ │ │ │ ├── README.md │ │ │ └── commentsWithoutDateTime.js │ │ ├── HTML Table Creation from ServiceNow Table/ │ │ │ ├── MailScript.js │ │ │ └── README.md │ │ ├── Open Survey In Portal/ │ │ │ ├── README.md │ │ │ └── open_survey.js │ │ ├── Print variables to mail/ │ │ │ ├── README.md │ │ │ └── printVarsToMail.js │ │ ├── PrintRecordDetailsinEmailBody/ │ │ │ ├── README.md │ │ │ └── print_record_details_to_body.js │ │ ├── RITM Reject Reason/ │ │ │ ├── README.md │ │ │ └── reject_reason_new.js │ │ ├── Redact PII from outbound email body/ │ │ │ ├── README.md │ │ │ └── mail_redact_pii.js │ │ └── cc all group members/ │ │ ├── README.md │ │ └── cc all group members.js │ ├── RESTMessageV2/ │ │ ├── API for Automatic Group creation/ │ │ │ ├── Automate Group Creation.js │ │ │ └── README.md │ │ ├── Aadhaar Verification/ │ │ │ ├── Readme.md │ │ │ └── script.js │ │ ├── Auth2 client credentials token cache with auto-refresh/ │ │ │ ├── OAuthClientCredsHelper.js │ │ │ ├── README.md │ │ │ └── example_background_usage.js │ │ ├── AzureDevOps/ │ │ │ ├── README.md │ │ │ └── azure.js │ │ ├── Currency Conversion - Using CurrencyFreaks API/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── DynamicOutboundEnpoints/ │ │ │ ├── README.md │ │ │ └── scriptinclude.js │ │ ├── External ML Model Integration/ │ │ │ └── Call ML Prediction API/ │ │ │ ├── README.md │ │ │ └── ml_prediction_script_include.js │ │ ├── GET with backoff, telemetry, and simple pagination/ │ │ │ ├── README.md │ │ │ ├── RestGetWithBackoff.js │ │ │ └── example_background_usage.js │ │ ├── Google-Chat/ │ │ │ ├── README.md │ │ │ └── sendgchatmessage.js │ │ ├── Integration Between 2 Instance/ │ │ │ ├── IntegrationBetweenTwoInstancesWithReturnoftheCreatedRecordNumber.js │ │ │ └── README.md │ │ ├── Jira/ │ │ │ ├── README.md │ │ │ └── createJiraTask.js │ │ ├── Reusable RESTMessageV2 retry pattern/ │ │ │ ├── README.md │ │ │ └── ReusableRESTMesaageV2Retry.js │ │ ├── Smart Incident Categorizer AI/ │ │ │ ├── README.md │ │ │ └── smart-incident.js │ │ ├── UPS Tracking/ │ │ │ ├── README.md │ │ │ └── trackUPS.js │ │ ├── Web Scraping REST Message/ │ │ │ ├── README.md │ │ │ └── code.js │ │ └── youtubeclient/ │ │ ├── README.md │ │ └── youtubeclient.js │ ├── Rest Integration Send Attachment Payload/ │ │ └── Send attachment payload via REST/ │ │ ├── DataLoaderFromIntuneAPI.js │ │ ├── DataloaderFromIntuneAPI_README.md │ │ ├── README.md │ │ └── attachment_payload_script.js │ ├── Scripted REST Api/ │ │ ├── Approval APIs/ │ │ │ ├── ApprovalRestResource.js │ │ │ ├── README.md │ │ │ ├── RejectRestResource.js │ │ │ └── SRApprovalsAPI.js │ │ ├── Approval on Behalf/ │ │ │ ├── README.md │ │ │ └── approval_on_behalf.js │ │ ├── CMDB API/ │ │ │ ├── CmdbApi.js │ │ │ ├── CreateCIs.js │ │ │ ├── CreateCiRelationship.js │ │ │ ├── DeleteCI.js │ │ │ ├── DeleteCiRelationship.js │ │ │ ├── README.md │ │ │ ├── RetrieveCiGroup.js │ │ │ ├── RetrieveCis.js │ │ │ ├── Retrieve_CI_Relationships.js │ │ │ ├── RetriveCiRelationshipTypes.js │ │ │ └── UpdateCi.js │ │ ├── CURL Script to create incident via tableAPI/ │ │ │ ├── README.md │ │ │ └── script.curl │ │ ├── CopyAI Generative AI example/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Create Catalog Items Dynamically/ │ │ │ ├── README.md │ │ │ └── catalog.js │ │ ├── Difference between two users/ │ │ │ ├── README.md │ │ │ ├── output.txt │ │ │ └── script.js │ │ ├── DomainSeperation/ │ │ │ ├── README.md │ │ │ └── create.js │ │ ├── Get_Choices/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Group Membership API/ │ │ │ ├── README.md │ │ │ └── group_membership.js │ │ ├── MID Server status JSON endpoint/ │ │ │ ├── README.md │ │ │ └── mid_server_status_api.js │ │ ├── Promise API Call/ │ │ │ ├── README.md │ │ │ └── promiseApiCall.js │ │ ├── Retrieve all variables from RITM/ │ │ │ ├── CHVarUtils_ScriptInclude.js │ │ │ ├── README.md │ │ │ ├── output_example.js │ │ │ └── scripted_rest_api.js │ │ ├── Tag API/ │ │ │ ├── README.md │ │ │ └── insert-tag.js │ │ ├── Update the variables or fields in sc_task or RITM/ │ │ │ ├── README.md │ │ │ └── Scripted_rest_api.js │ │ ├── Webhook receiver with HMAC SHA-256 validation/ │ │ │ ├── HmacUtils.js │ │ │ ├── README.md │ │ │ └── WebhookHmacReceiver.js │ │ └── compare roles/ │ │ ├── README.md │ │ ├── output.txt │ │ └── script.js │ └── Scripted SOAP Incident Creation/ │ └── Scripted SOAP incident creation/ │ ├── README.md │ └── Scripted SOAP incident creation.js ├── Modern Development/ │ ├── ECMASCript 2021/ │ │ └── Server-side ECMAScript 2021 examples/ │ │ ├── README.md │ │ ├── arrowfunctions.js │ │ ├── class.js │ │ ├── const.js │ │ ├── defaultparms.js │ │ ├── destructuring.js │ │ ├── forof.js │ │ ├── let.js │ │ ├── map.js │ │ ├── set.js │ │ ├── spread.js │ │ ├── symbol.js │ │ └── templatestringsandliterals.js │ ├── GraphQL/ │ │ ├── Sample INC Details GraphQL Code Snippet/ │ │ │ ├── CMDB Resolver.js │ │ │ ├── GetIncident_Details.gql │ │ │ ├── Incident Resolver.js │ │ │ ├── README.md │ │ │ ├── User Resolver.js │ │ │ └── resolver mapping.js │ │ ├── Sample group query/ │ │ │ ├── README.md │ │ │ ├── addUserToGroup.js │ │ │ ├── add_user_to_group_broker_properties.json │ │ │ ├── add_user_to_group_broker_query.gql │ │ │ ├── getGroups.js │ │ │ ├── get_groups_broker_query.gql │ │ │ ├── resolver_mappings.json │ │ │ └── schema.gql │ │ └── Sample users query/ │ │ ├── README.md │ │ ├── getUserGroups.js │ │ ├── getUserObject.js │ │ ├── getUserRoles.js │ │ ├── get_user_broker_properties.json │ │ ├── get_user_broker_query.gql │ │ ├── resolver_mappings.json │ │ └── schema.gql │ ├── NOW Experience/ │ │ └── JSX Cheat Sheet/ │ │ └── README.md │ ├── Service Portal/ │ │ ├── Active Tickets Dashboard/ │ │ │ ├── README.md │ │ │ ├── active_tasks_service_script.js │ │ │ ├── active_tickets_client_script.js │ │ │ ├── active_tickets_dashboard.css │ │ │ └── active_tickets_dashboard.html │ │ ├── Search Sources/ │ │ │ ├── Approvals.html │ │ │ ├── DataFetchScript.js │ │ │ └── README.md │ │ ├── dark-mode-switcher/ │ │ │ ├── README.md │ │ │ ├── avatarDropDown.js │ │ │ ├── dark_mode.scss │ │ │ ├── portal_theme.scss │ │ │ └── themeSwitcherMenu.js │ │ ├── instance-badge/ │ │ │ └── README.md │ │ ├── sn-avatar/ │ │ │ └── README.md │ │ ├── sn-choice-list/ │ │ │ └── README.md │ │ ├── sn-record-picker/ │ │ │ └── README.md │ │ ├── sn-time-ago/ │ │ │ ├── README.md │ │ │ └── sp_widget_sn_timeago_demo.xml │ │ ├── sn-watchlist/ │ │ │ ├── README.md │ │ │ └── snWatchListDirective.js │ │ ├── sp-date-picker/ │ │ │ └── README.md │ │ ├── sp-editable-field/ │ │ │ └── README.md │ │ ├── sp-modal/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── spGlideAjax/ │ │ │ ├── README.md │ │ │ └── spGlideAjaxService.js │ │ ├── sparkling/ │ │ │ └── README.md │ │ ├── userPreferences/ │ │ │ └── README.md │ │ └── validate-data-field/ │ │ ├── README.md │ │ └── script.js │ └── Service Portal Widgets/ │ ├── Accordion Widget/ │ │ ├── CSS-SCSS.scss │ │ ├── HTML Template.html │ │ └── README.md │ ├── AngularJS Directives and Filters/ │ │ ├── README.md │ │ ├── client_script.js │ │ ├── css.css │ │ ├── html.html │ │ └── server_script.js │ ├── Animated Notification Badge/ │ │ ├── README.md │ │ ├── notification-badge.css │ │ ├── notification-badge.html │ │ └── notification-badge.js │ ├── ApplyCSSDynamically/ │ │ ├── README.md │ │ ├── applycssdynamically.html │ │ ├── applycssdynamicallyclient.js │ │ └── applycssdynamicallyserver.js │ ├── Batman Animation/ │ │ ├── README.md │ │ ├── client_script.js │ │ ├── html_template.html │ │ └── style.css │ ├── Calendar widget/ │ │ ├── README.md │ │ ├── index.html │ │ ├── script.js │ │ └── style.css │ ├── Card Image Link/ │ │ ├── README.md │ │ ├── mm_card.html │ │ ├── mm_card_client_script.js │ │ ├── mm_card_css.css │ │ ├── mm_card_server_script.js │ │ └── mm_card_widget_schema.json │ ├── Catalog Item Explorer/ │ │ ├── README.md │ │ ├── client_script.js │ │ ├── css.scss │ │ ├── options_schema.json │ │ ├── script.js │ │ └── template.html │ ├── Change Calendar Report/ │ │ ├── Body HTML template.html │ │ ├── CSS │ │ ├── Client Controller │ │ ├── README.md │ │ └── Server Side Script │ ├── Change Notification Preferences/ │ │ ├── README.md │ │ ├── client.js │ │ ├── css.css │ │ ├── html.html │ │ ├── notification_preference.js │ │ └── server.js │ ├── Check if user has specific role inside the widget/ │ │ ├── ClientScript.js │ │ └── README.md │ ├── Clickable SVG Image/ │ │ ├── README.md │ │ ├── client.js │ │ ├── echarts.js │ │ └── html.html │ ├── Client side pagination/ │ │ ├── README.md │ │ └── script.js │ ├── Configurable Card Widget/ │ │ ├── CSS-SCSS.scss │ │ ├── Client Script.js │ │ ├── HTML Template.html │ │ ├── README.md │ │ └── Widget Options.json │ ├── Create diagram using GoJS library/ │ │ ├── README.md │ │ ├── body.html │ │ ├── client.js │ │ └── style.css │ ├── Create diagram using Highcharts library/ │ │ ├── README.md │ │ ├── body.html │ │ └── client.js │ ├── Custom Greetings in portal homepage/ │ │ ├── README.md │ │ ├── homepage-search-client.js │ │ └── homepage-search.html │ ├── Custom attachment variable/ │ │ ├── README.md │ │ ├── controller.js │ │ └── template.html │ ├── Digital Clock/ │ │ ├── README.md │ │ ├── index.html │ │ ├── script.js │ │ └── style.css │ ├── Drag & drop Widget/ │ │ ├── README.md │ │ ├── client.js │ │ ├── html.html │ │ └── server.js │ ├── Drawer Buttons/ │ │ ├── README.md │ │ ├── html_template.html │ │ └── style.css │ ├── Dropdown Widget/ │ │ ├── README.md │ │ ├── index.html │ │ ├── script.js │ │ └── style.css │ ├── Dynamic Table and Record Selector/ │ │ ├── Client Side.js │ │ ├── HTML.html │ │ └── README.md │ ├── Emoji Replacer Widget/ │ │ ├── CSS-SCSS.css │ │ ├── Client Script.js │ │ ├── HTML.html │ │ └── README.md │ ├── Export table in portal/ │ │ ├── README.md │ │ ├── export.html │ │ └── export.js │ ├── Fill survey or item from url/ │ │ ├── README.md │ │ └── Survey or form filler widget.js │ ├── Floater Feedback Widget/ │ │ ├── README.md │ │ └── feeback_floater.js │ ├── Generate QrCode/ │ │ ├── Dependencies/ │ │ │ └── qrcode.js │ │ ├── README.md │ │ ├── qrcode.html │ │ └── qrcode_client.js │ ├── Guest Login Modal/ │ │ ├── README.md │ │ ├── Service Portal - Guest Login Modal Widget.xml │ │ ├── client.js │ │ ├── ng-template.html │ │ ├── optionSchema.json │ │ └── server.js │ ├── HR Task Progress Bar/ │ │ ├── CSS.js │ │ ├── HTML.js │ │ ├── README.md │ │ ├── Server.js │ │ └── client_script.js │ ├── HTML List Table from GlideRecord with JSon/ │ │ ├── README.md │ │ ├── ServerScript.js │ │ └── html.html │ ├── Image icon Menu/ │ │ ├── README.md │ │ ├── Server.js │ │ ├── client.js │ │ ├── iconWidgetinstance.js │ │ ├── iconmenu.css │ │ └── iconmenu.html │ ├── ImportXml/ │ │ ├── README.md │ │ └── importXml.js │ ├── Incident Sound Alerts/ │ │ ├── README.md │ │ └── incident_alerts_widget.js │ ├── JSON Beautifier/ │ │ ├── Client_side.js │ │ ├── HTML.html │ │ └── README.md │ ├── Konami Code Easter Egg/ │ │ ├── KonamiCodeEasterEgg.js │ │ ├── KonamiCodeEasterEggV2.js │ │ └── README.md │ ├── Language Selector/ │ │ ├── README.md │ │ ├── language-selector.client.js │ │ ├── language-selector.css │ │ ├── language-selector.html │ │ └── language-selector.server.js │ ├── Live Ticket Counter Service Portal Widget/ │ │ ├── Client Controller │ │ ├── Live Ticket Counter .css │ │ ├── Live Ticket Counter.html │ │ ├── README.md │ │ └── Server Script │ ├── Location hierarchy/ │ │ ├── README.md │ │ ├── client controller.js │ │ ├── css.css │ │ ├── html.html │ │ └── server script.js │ ├── Manage Delegates Widget/ │ │ ├── README.md │ │ ├── delegates.html │ │ ├── delegates.scss │ │ ├── delegates_client.css │ │ └── delegates_server.js │ ├── Mouse Effect/ │ │ ├── README.md │ │ ├── index.html │ │ ├── script.js │ │ └── style.css │ ├── My Assets/ │ │ ├── README.md │ │ ├── my_assets.css │ │ ├── my_assets.html │ │ └── my_assets_server_side.js │ ├── My Mentioned Items/ │ │ ├── CSS.js │ │ ├── HTML.js │ │ ├── README.md │ │ └── server script.js │ ├── My Reminders/ │ │ ├── README.md │ │ ├── reminders.html │ │ ├── reminders.scss │ │ ├── reminders_client.js │ │ └── reminders_server.js │ ├── Open in Platform/ │ │ ├── README.md │ │ ├── body.html │ │ ├── css.js │ │ ├── option schema.js │ │ └── server.js │ ├── Paginated Data/ │ │ ├── README.md │ │ ├── paginated-data-server-script.js │ │ └── paginated-data.html │ ├── Pagination widget with next and previous button/ │ │ ├── CSS.css │ │ ├── HTML_Script.html │ │ ├── README.md │ │ ├── client_controller.js │ │ └── server_script.js │ ├── Portal widgets Performance Test/ │ │ ├── README.md │ │ └── code.js │ ├── Recently Viewed Items/ │ │ ├── README.md │ │ └── recently-viewed-items-widget.js │ ├── Record process flow/ │ │ ├── PortalUtils.js │ │ ├── PortalUtilsBase.js │ │ ├── README.md │ │ ├── body.html │ │ ├── server.js │ │ └── style.css │ ├── RecordPickerForListReference/ │ │ ├── README.md │ │ ├── recordpicker.html │ │ ├── recordpickerclient.js │ │ └── recordpickerserver.js │ ├── Redirect to different portals based on browser/ │ │ ├── README.md │ │ └── redirect_to_different_portals_based_on_browser.js │ ├── Set Variables By Url/ │ │ ├── README.md │ │ └── client.js │ ├── Signature Pad Widget/ │ │ ├── Client Controller.js │ │ ├── HTML File.html │ │ └── README.md │ ├── Spiderman Animation/ │ │ ├── README.md │ │ ├── client_script.js │ │ ├── html_template.html │ │ └── style.css │ ├── Squid Game Themed Incident-Request-Knowledge/ │ │ ├── README.md │ │ ├── squid_game_irk.css │ │ └── squid_game_irk.html │ ├── Squid Game Themed Simple List/ │ │ ├── README.md │ │ ├── squid_game_simple_list.css │ │ └── squid_game_simple_list.html │ ├── Squid Game Themed User Profile Card/ │ │ ├── README.md │ │ ├── squid_game_user_profile_card.css │ │ └── squid_game_user_profile_card.html │ ├── Standard Ticket Header With On Hold Reason/ │ │ └── README.md │ ├── Standard Ticket Page Enhanced Action Widget/ │ │ ├── README.md │ │ └── sp_widget_std_action.xml │ ├── Stepper/ │ │ ├── CSS.css │ │ ├── HTML.html │ │ ├── README.md │ │ └── Server Script.js │ ├── Sticky Notes/ │ │ ├── CSS-SCSS.css │ │ ├── Client Script.js │ │ ├── HTML.html │ │ ├── README.md │ │ └── Server Side.js │ ├── Tab Panel Widget/ │ │ ├── CSS-SCSS.scss │ │ ├── HTML Template.html │ │ ├── Options.json │ │ └── README.md │ ├── Upload Files/ │ │ ├── README.md │ │ ├── body.html │ │ ├── client.js │ │ ├── server.js │ │ └── style.css │ ├── custom404/ │ │ ├── HTML.js │ │ ├── README.md │ │ ├── css.js │ │ └── server.js │ ├── g_form on SP/ │ │ ├── README.md │ │ ├── gform_on_sp.html │ │ ├── gform_on_sp_client.js │ │ └── gform_on_sp_server.js │ └── iFrame/ │ ├── README.md │ ├── Service Portal - iFrame Widget.xml │ ├── client.js │ ├── optionsSchema.json │ ├── server.js │ ├── style.scss │ └── template.html ├── PAGES.md ├── README.md ├── Server-Side Components/ │ ├── Background Scripts/ │ │ ├── ACL Audit Utility/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Access Analysis Utility/ │ │ │ ├── Access alaysis script.js │ │ │ └── README.md │ │ ├── Add Bookmarks - ITIL Users/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Add Comments/ │ │ │ ├── README.md │ │ │ └── addComment.js │ │ ├── Add No Audit Attribute To Multiple Dictionary Entries/ │ │ │ ├── AddNoAuditAttributeToMultipleDictionaryEntries.js │ │ │ └── README.md │ │ ├── Add Standard Change Model/ │ │ │ ├── README.md │ │ │ └── addStandardChgModel.js │ │ ├── Adding bookmark to Favorites tab/ │ │ │ ├── Adding bookmark into Favorites tab.js │ │ │ └── README.md │ │ ├── Analyze user access UI page/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Approval Reminders/ │ │ │ ├── README.md │ │ │ └── approvalReminderToDelegates.js │ │ ├── Attach Workflow to Existing Record/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Attachment Size Audit/ │ │ │ ├── README.md │ │ │ └── attachmentSizeAudit.js │ │ ├── Auto-Deactivate Users Not Logged In for X Days/ │ │ │ ├── Auto_Deactivate_Users_Not_Logged_In_for_X_Days.js │ │ │ └── README.md │ │ ├── Bulk Change of Incident Priority Based on Category/ │ │ │ ├── Bulk Change of Incident Priority Based on Category.js │ │ │ └── README.md │ │ ├── Bulk Create Records in Multiple Tables/ │ │ │ ├── BulkCreateRecordsMultipleTables.js │ │ │ └── README.md │ │ ├── Bulk Delete Records in Multiple Tables with Conditions/ │ │ │ ├── BulkDeleteRecordsMultipleTablesWithConditions.js │ │ │ └── README.md │ │ ├── Bulk Resolve Old Incidents/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Bulk Role Assignment Based on Conditions/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Bulk Share - Reports/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Bulk Update Tables/ │ │ │ ├── BulkUpdateWithConditions.js │ │ │ └── README.md │ │ ├── Bulk Update of Fulfillment Group References in Published KB Articles/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Calculate Week/ │ │ │ ├── Readme.md │ │ │ └── week_calculation.js │ │ ├── Capitalize Title Words/ │ │ │ ├── CapitalizeTitleWords.js │ │ │ └── README.md │ │ ├── Capture Scheduled Job in an Update set/ │ │ │ ├── README.md │ │ │ └── captureScheduledJob.js │ │ ├── Capturing a record in to the current update set/ │ │ │ ├── Capturing a record in to the current update set using background script.js │ │ │ └── README.md │ │ ├── Change Approver/ │ │ │ ├── BgScript.js │ │ │ └── README.md │ │ ├── Change Update Set Application Scope/ │ │ │ ├── README.md │ │ │ └── changeApplicationScope.js │ │ ├── Check Record Creation over 90 days and output age/ │ │ │ ├── README.md │ │ │ └── checkdateover90.js │ │ ├── Check String is Valid JSON/ │ │ │ ├── README.md │ │ │ └── checkStringisValidJson.js │ │ ├── Check for duplicates on multiple criteria/ │ │ │ ├── README.md │ │ │ └── check-for-ducplicates.js │ │ ├── Clone User Groups/ │ │ │ ├── cloneUserGroups.js │ │ │ └── readme.md │ │ ├── Clone User Record/ │ │ │ ├── README.md │ │ │ └── cloneUserRec.js │ │ ├── Clone User with Roles and Groups/ │ │ │ ├── README.md │ │ │ └── cloneUser.js │ │ ├── Compare Roles Between Two Users/ │ │ │ ├── README.md │ │ │ └── compare-roles-2-users.js │ │ ├── Console timing API/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Convert Date Time/ │ │ │ ├── README.md │ │ │ └── convertTimeZone.js │ │ ├── Convert Incident Records to JSON/ │ │ │ ├── README.md │ │ │ ├── Readme.md │ │ │ ├── convert code.js │ │ │ └── convert incidents to JSON.js │ │ ├── Convert comma separated values in string to columns/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Copy Field Values and Insert in Target Record/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Copy Source User Group Memberships to Selected Users/ │ │ │ ├── Readme.md │ │ │ └── copy_source_user_group_memberships_to_selected_users.js │ │ ├── Copy table fields from one table to another/ │ │ │ ├── Copy fields from one table to another │ │ │ └── README.md │ │ ├── Copy table name list header action/ │ │ │ ├── README.md │ │ │ ├── addMenuItem.js │ │ │ └── removeMenuItem.js │ │ ├── Currency Conversion/ │ │ │ ├── README.md │ │ │ └── currencyConvert.js │ │ ├── Currency Formatting/ │ │ │ ├── README.md │ │ │ └── currencyFormatting.js │ │ ├── Custom Table Usage/ │ │ │ ├── README.md │ │ │ └── customTableUsage.js │ │ ├── Deactivate groups with no members and inactive manager/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Decrypt Password Field/ │ │ │ ├── README.md │ │ │ └── decryptfield.js │ │ ├── Delete Attachments - Closed Approvals/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Discover Datacenters for Service Accounts/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Duplicate Client Script Audit for Tables/ │ │ │ ├── README.md │ │ │ └── backGroundScriptDuplicateClientScript.js │ │ ├── Duplicate Finder/ │ │ │ ├── findDuplicatesByCombination.js │ │ │ ├── findDuplicatesBySingleField.js │ │ │ └── readme.md │ │ ├── Encode and Decode URI/ │ │ │ ├── README.md │ │ │ └── encodeURIdecodeURI.js │ │ ├── Encrypt & decrypt payload via base64/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Execute Logic on Weekdays/ │ │ │ ├── README.md │ │ │ └── doTaskonWeekdays.js │ │ ├── Extend Code Search Base/ │ │ │ ├── README.md │ │ │ └── add_more_tables_to_code_search.js │ │ ├── Extract Value from JSON/ │ │ │ ├── extractValueFromJSON.js │ │ │ └── readme.md │ │ ├── Fetch Active Groups list without members/ │ │ │ ├── README.md │ │ │ └── activeGroupsWithoutMembers.js │ │ ├── Find All Categories Related to a Knowledge Base/ │ │ │ ├── GetAllTheCategoriesRelatedToAKnowledgeBase.js │ │ │ └── README.md │ │ ├── Find Groups Without Members/ │ │ │ ├── Get Groups with no Members.js │ │ │ └── README.md │ │ ├── Find Similar Tickets/ │ │ │ ├── README.md │ │ │ └── findSimilarTickets.js │ │ ├── Find Top-Level Manager Hierarchy/ │ │ │ ├── Readme.md │ │ │ └── script.js │ │ ├── Find sys_id named records/ │ │ │ ├── README.md │ │ │ └── findSysIdNamedFiles.js │ │ ├── Finding groups with inactive managers/ │ │ │ ├── Finding groups with inactive managers.js │ │ │ └── README.md │ │ ├── Fix reference to Choice/ │ │ │ ├── README.md │ │ │ ├── script_v1.js │ │ │ └── script_v2.js │ │ ├── FlushOutbox/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Force new value to read only or protected field/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Form Field Count/ │ │ │ ├── Form Field Count.js │ │ │ └── README.md │ │ ├── Generate JWT Token/ │ │ │ ├── README.md │ │ │ └── generateJWTToken.js │ │ ├── Generate Random Incident Records/ │ │ │ ├── README.md │ │ │ └── generate_random_incident.js │ │ ├── Generate statistics about events created today/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get Active MID Servers/ │ │ │ ├── GetActiveMidServer.js │ │ │ └── README.md │ │ ├── Get Agent log from Mid Server/ │ │ │ ├── README.md │ │ │ └── getMidServerAgentLog.js │ │ ├── Get All Child Roles/ │ │ │ ├── GetAllChildRolesRecursive.js │ │ │ └── README.md │ │ ├── Get All the CI classes/ │ │ │ ├── README.md │ │ │ └── getAllCiClasses.js │ │ ├── Get Array of Records with Attachments/ │ │ │ ├── incHavingAttachments.js │ │ │ └── readme.md │ │ ├── Get Duplicate/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get GlideRecord Reference Field/ │ │ │ ├── README.md │ │ │ └── get_glide_record_reference_field.js │ │ ├── Get Installed Plugins details/ │ │ │ ├── README.md │ │ │ └── getInstalledPluginInfo.js │ │ ├── Get Instance DB Size/ │ │ │ ├── README.md │ │ │ └── getInstDBSize.js │ │ ├── Get Instance Info/ │ │ │ ├── README.md │ │ │ └── getInstanceInfo.js │ │ ├── Get Journal Entry as HTML Without Header/ │ │ │ ├── GetJournalEntryAsHTMLWithoutHeader.js │ │ │ └── README.md │ │ ├── Get My Groups/ │ │ │ ├── README.md │ │ │ └── getMyGroups.js │ │ ├── Get Outstanding Incidents/ │ │ │ ├── README.md │ │ │ └── get-outstanding-incidents.js │ │ ├── Get Risk and Controls in Project/ │ │ │ ├── README.md │ │ │ └── risksandcontrolsinProject.js │ │ ├── Get The Last Journal Comment Date/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get User's Favorite Hierarchy/ │ │ │ ├── GetUsersFavoriteHierarchy.js │ │ │ └── README.md │ │ ├── Get all users where manager is empty/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get current logged in user count in all nodes of instance/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get incident count based on priority/ │ │ │ ├── IncidentCount.js │ │ │ └── README.md │ │ ├── Get list of Update Set types/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get the current version of an application/ │ │ │ ├── README.md │ │ │ └── currentversionscript.js │ │ ├── GetFlowNames/ │ │ │ ├── README.md │ │ │ └── getFlowNames.js │ │ ├── GetRecordsFromMultipleTables/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── GreenHouse ServiceNow Integration Snippet/ │ │ │ ├── GreenHouse_SN_Snippet.js │ │ │ └── README.md │ │ ├── IRE Simulation/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Identification and Reconciliation/ │ │ │ ├── README.md │ │ │ └── identificationReconciliationOnPayload.js │ │ ├── Identify Inactive users with tickets/ │ │ │ ├── README.md │ │ │ └── identify_inactive_users_with_open_tickets.js │ │ ├── Incident Auto-Categorization Based on Keywords/ │ │ │ ├── Incident Auto-Categorization Based on Keywords.js │ │ │ └── README.md │ │ ├── Limit String and Add Elipses/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── List Stories and Tasks by User and Date Range/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── List fields in table/ │ │ │ ├── README.md │ │ │ └── listFieldsInTable.js │ │ ├── Logout User/ │ │ │ ├── README.md │ │ │ └── logoutUser.js │ │ ├── Merge Duplicate User Records Automatically/ │ │ │ ├── Readme.md │ │ │ └── script.js │ │ ├── Move Customer Updates/ │ │ │ ├── README.md │ │ │ └── moveCustomerUpdates.js │ │ ├── Notify User of Password Expiry/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Orphaned Users/ │ │ │ ├── README.md │ │ │ └── Users with no groups and roles.js │ │ ├── Parse ISO8601 Date/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Prevent unnecessary notifications from being sent out/ │ │ │ ├── Prevent_unnecessary_notifications_from_being_sent_out.js │ │ │ └── README.md │ │ ├── QuickCurrent/ │ │ │ ├── README.md │ │ │ └── quickCurrent.js │ │ ├── Read Encoded Query/ │ │ │ ├── README.md │ │ │ └── readQuery.js │ │ ├── Reassignment of Manager from Group and User Table/ │ │ │ ├── README.md │ │ │ └── backgroundScriptManagerReassign.js │ │ ├── Remove Inactive User/ │ │ │ ├── README.md │ │ │ └── Remove Inactive user from active group.js │ │ ├── Remove element from list field/ │ │ │ ├── README.md │ │ │ └── removeFromList.js │ │ ├── Remove roles from inactive user/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Rename reports - Avoid duplicate names/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Replace Text/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Restart RITM Flow/ │ │ │ ├── README.md │ │ │ └── restart-ritm-flow.js │ │ ├── Retiring KB articles in bulk/ │ │ │ ├── Readme.md │ │ │ └── Retire Articles.js │ │ ├── Retrieve Impersonation Insights/ │ │ │ ├── impersonationInsights.js │ │ │ └── readme.md │ │ ├── Retrieve MRVS Name and Value/ │ │ │ ├── RetrieveMRVSNameAndValue.js │ │ │ └── readme.md │ │ ├── Retrieve age of Incident/ │ │ │ ├── README.md │ │ │ └── ageOfIncidents.js │ │ ├── RetrieveAPIKey/ │ │ │ ├── Readme.md │ │ │ └── getSubscriptionKey.js │ │ ├── Run a Scheduled Job/ │ │ │ ├── README.md │ │ │ └── run-scheduled-job.js │ │ ├── SQL Checker/ │ │ │ ├── README.md │ │ │ └── SQLChecker.js │ │ ├── Safe Bulk Update with Logging/ │ │ │ ├── README.md │ │ │ └── bulk_update_with_progress.js │ │ ├── Set the status to Retired on Ec2 Instance/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Set update sets to Complete/ │ │ │ ├── README.md │ │ │ └── set_update_sets_to_complete.js │ │ ├── Silent update on GlideRecord/ │ │ │ ├── README.md │ │ │ └── slientUpdateOnGlideRecord.js │ │ ├── Stale Tasks Auto-Close/ │ │ │ ├── README.md │ │ │ └── Stale Tasks Auto-Close.js │ │ ├── Table Growth Analysis/ │ │ │ ├── README.md │ │ │ └── tableGrowthAnalysis.js │ │ ├── Tag Incident Outliers/ │ │ │ ├── README.md │ │ │ └── tag_resolution_outliers.js │ │ ├── To check incidents having a VIP caller/ │ │ │ ├── VIP-caller-incidents.js │ │ │ └── readme.md │ │ ├── Typed Array Elements/ │ │ │ ├── README.md │ │ │ └── typed_array_elements.js │ │ ├── Update All Store Apps/ │ │ │ ├── README.md │ │ │ └── update_all_apps.js │ │ ├── Update reference field from CI relationship/ │ │ │ ├── README.md │ │ │ └── Reference field value update from CI relationship connection.js │ │ ├── Updating a record in the sys_user table/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── User Access Tester/ │ │ │ ├── README.md │ │ │ └── userAccessTester.js │ │ ├── User Email mismatch with Cmn Notif device/ │ │ │ ├── README.md │ │ │ └── userEmailMismatchwithcmnNotifDevice.js │ │ ├── User Has Role Exactly/ │ │ │ ├── README.md │ │ │ └── user_has_role_exactly.js │ │ ├── Version Checker/ │ │ │ ├── README.md │ │ │ └── VersionUpdateChecker.js │ │ ├── Virtual Agent Conversation Analytics/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Virtual Agent Topic Coverage Report/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── add member to groups/ │ │ │ ├── Add the members to List of the Groups using GlideRecord.js │ │ │ └── README.md │ │ ├── encryptAndDecryptNonPasswordFields/ │ │ │ ├── README.md │ │ │ └── encryptAndDecryptNonPasswordFields.js │ │ ├── encryptAndDecryptPasswordFields/ │ │ │ ├── Encrypt and Decrypt Non-Password Fields.js │ │ │ └── README.md │ │ ├── findTableSize/ │ │ │ ├── README.md │ │ │ └── findTableSize.js │ │ └── inserting a new record into the sys_user table/ │ │ ├── README.md │ │ └── script.js │ ├── Business Rules/ │ │ ├── ATF Duplicate Execution Order/ │ │ │ ├── ATF_Duplicate_Execution_order.js │ │ │ └── README.md │ │ ├── Abort Parent Incident Closure When Child is Open/ │ │ │ ├── README.md │ │ │ └── scriptBR.js │ │ ├── Add HR task for HR case/ │ │ │ ├── Add HR Task for VIP HR Case.js │ │ │ └── README.md │ │ ├── Add itil role to ootb user query to also see inactive users/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Add notes on tag addition or removal/ │ │ │ ├── README.md │ │ │ ├── update_notes_tag_addition.js │ │ │ └── update_notes_tag_removal.js │ │ ├── Add or remove a tag from the ticket whenever the comments are updated/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Add woknotes for 75 percent SLA/ │ │ │ ├── README.md │ │ │ └── addWorknotesForSLA.js │ │ ├── Add work notes for relevant Change Requests for Incident/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── After-BR to generate approvals for catalog tasks/ │ │ │ ├── README.md │ │ │ └── approval.js │ │ ├── Allow only unique insert/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Approval Matrix/ │ │ │ ├── Matrix.js │ │ │ └── README.md │ │ ├── Assign specific role to user/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Async REST Call/ │ │ │ ├── README.md │ │ │ └── callAsynREST.js │ │ ├── Attachment Variable from Activity Stream to Clip Icon/ │ │ │ ├── Attachment Variable Fix.js │ │ │ └── README.md │ │ ├── AttachmentFormatValidator/ │ │ │ ├── AttachmentFormatValidator.js │ │ │ └── Readme.md │ │ ├── Attachments check High-Risk or High-Impact Change request/ │ │ │ ├── README.md │ │ │ └── attachmentcheckonhighriskimpactChange.js │ │ ├── Auto Approve VIP Approvals/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Auto Create Problem Records for Recurring Incidents/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Auto Incident Notification and Escalation/ │ │ │ ├── README.md │ │ │ └── incident_notification.js │ │ ├── Auto Tag VTB Based on Record States/ │ │ │ ├── README.md │ │ │ └── code_snippet.js │ │ ├── Auto add email recipients to the message body when Email Override is on/ │ │ │ ├── IncludeEmailRecipientsInBody.js │ │ │ └── README.md │ │ ├── Auto approve if previously approved/ │ │ │ ├── Auto_approve approvals.js │ │ │ └── README.md │ │ ├── Auto close incident if all related changes are closed/ │ │ │ ├── Readme.md │ │ │ └── auto_close_incident_if_all_related_change_requests_closed.js │ │ ├── Auto tag incident/ │ │ │ ├── Auto_Tag_Incident.js │ │ │ └── README.md │ │ ├── Auto-Assign Incident Based on Keywords, CI, and Department/ │ │ │ ├── Readme.md │ │ │ └── script.js │ │ ├── Auto-Generate Knowledge Article for Resolved Incidents/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Auto-approved opened by is approver/ │ │ │ ├── ReadMe.md │ │ │ └── code.js │ │ ├── Auto-assign and notify owners of Affected CIs/ │ │ │ ├── BRScript.js │ │ │ └── README.md │ │ ├── AutoApplyTemplateOnRecord/ │ │ │ ├── README.md │ │ │ └── auto_apply_template_on_record.js │ │ ├── AutoAssignment/ │ │ │ ├── Auto Assign Incident.js │ │ │ └── README.md │ │ ├── AutoCreation of Problem from Incident/ │ │ │ ├── README.md │ │ │ └── whenMajorIncidentIsTrueCreateProblem.js │ │ ├── Automate Role Assignment for New User/ │ │ │ ├── README.md │ │ │ └── autoRoleAssignment.js │ │ ├── Automated Incident Categorization Based on Keywords/ │ │ │ ├── Automated Incident Categorization Based on Keywords.js │ │ │ └── README.md │ │ ├── Automated SLA Monitoring and Escalation/ │ │ │ ├── Automated SLA Monitoring and Escalation.js │ │ │ └── README.md │ │ ├── Automatic Group Membership Updates via API/ │ │ │ ├── README.md │ │ │ └── autoGroupMembershipUpdate.js │ │ ├── Automatic Relationship Builder/ │ │ │ ├── README.md │ │ │ └── relationship.js │ │ ├── Automatically Populate Incident with Work Order Number/ │ │ │ ├── README.md │ │ │ └── businessRuleFsmUsecase.js │ │ ├── Automatically Throttle Incidents Raised by Same User Within Short Timeframe/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Backup Critical Table Data/ │ │ │ ├── README.md │ │ │ └── backupCriticalTableData.js │ │ ├── Block Attachments for specific conditions/ │ │ │ ├── Block Attachments.js │ │ │ └── README.md │ │ ├── CMDB Auto-Relationship Builder/ │ │ │ ├── README.md │ │ │ └── code_snippet.js │ │ ├── Calculate Incident Duration and Validation/ │ │ │ ├── Duration.js │ │ │ └── Readme.md │ │ ├── Call JavaScript Probe/ │ │ │ ├── Call JavaScript Probe.js │ │ │ └── README.md │ │ ├── Cancel Incomplete Playbooks on Closure/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Capture Implementation Status of Change Request/ │ │ │ ├── Implementation Status of Change Request.js │ │ │ └── Readme.js │ │ ├── Captures the time it took to assign a task/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Cascade Priority Change from Parent to Child Incidents/ │ │ │ ├── README.md │ │ │ └── code_snippet.js │ │ ├── Cascade Problem Worknote to Origin Task/ │ │ │ ├── Readme.md │ │ │ └── cascade.js │ │ ├── Change Lead Time Calculations/ │ │ │ ├── README.md │ │ │ └── change_lead_time_calculations.js │ │ ├── Change Risk Assesment mandatory before state change/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Check domain of record against user session/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Check for active tickets before inactivating user/ │ │ │ ├── Check for active tickets before inactivating user.js │ │ │ └── README.md │ │ ├── Close parent RITM when SC Task is Closed/ │ │ │ ├── README.md │ │ │ └── closeParentRITMwhenSCTaskisClosed.js │ │ ├── Compare two date fields/ │ │ │ ├── README.md │ │ │ └── compareTwoDateFields.js │ │ ├── Copy Attachment INC to Case/ │ │ │ ├── README.md │ │ │ └── copyAttachement.js │ │ ├── Copy Attachment on Email/ │ │ │ ├── README.md │ │ │ └── copyAttachment.js │ │ ├── Copy Comments from RITM to SCTASK Vice versa/ │ │ │ ├── README.md │ │ │ └── copyCommentsfromRitmToSctask.js │ │ ├── Copy MRVS to SC Task/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Copy Worknotes/ │ │ │ ├── read.md │ │ │ └── script.js │ │ ├── Copy attachments from idea to demand/ │ │ │ ├── README.md │ │ │ └── copy attach from idea to demand.js │ │ ├── Copy details to Request/ │ │ │ ├── Move Sc_task Assign group and assigne to Request.js │ │ │ └── README.md │ │ ├── Copy fields from Employee from/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Copy latest comment from RITM to SCTASK/ │ │ │ ├── CopyComments.js │ │ │ └── README.md │ │ ├── Copy worknotes from SCTASK to RITM comments/ │ │ │ ├── README.md │ │ │ └── sctaskToRitmAdditionalComments.js │ │ ├── CopyAttachmentsFromApprovalToChange/ │ │ │ ├── CopyAttachmentsApprovalToChange.js │ │ │ └── README.md │ │ ├── Count Associated Incidents in Problem/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Create a copy of incident in another servicenow instance/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Create catalog task for each row of MRVS/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Create choice sets if required for new choices/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Create comment on referenced record/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Cross-Table Dependency Analyzer/ │ │ │ ├── businessrule.js │ │ │ ├── readme.md │ │ │ └── scriptInclude.js │ │ ├── Currency conversion to USD/ │ │ │ ├── currenct_Converstion_to_USD.js │ │ │ ├── currency conversion usd.js │ │ │ └── readme.md │ │ ├── DeleteUserRole/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Display BR to get groupInfo of logged in User/ │ │ │ ├── README.md │ │ │ ├── displayBr.js │ │ │ └── onLoadClientScript.js │ │ ├── Display current user display name on top of form/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Display warning message when peer reviewer and Requested by are same person/ │ │ │ ├── README.md │ │ │ └── Warningmessage_Business rule.jss │ │ ├── Due date calculation based on priority/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Dynamic Business Rule to Update User Roles Based on Department Changes/ │ │ │ ├── Dynamic Business Rule to Update User Roles Based on Department Changes.js │ │ │ └── README.md │ │ ├── Dynamic Field Population from CMDB/ │ │ │ ├── beforeBusinessRule.js │ │ │ ├── readme.md │ │ │ └── scriptinclude.js │ │ ├── Dynamic GlideList Field Sync/ │ │ │ ├── README.md │ │ │ └── code_snippet.js │ │ ├── Emergency Change Cannot be closed without AttachedIncident/ │ │ │ ├── README.md │ │ │ └── Review to Close Without Incident.js │ │ ├── Enforce CI maintenance window on Change schedule/ │ │ │ ├── README.md │ │ │ └── br_enforce_ci_maintenance_window.js │ │ ├── Enforce File Upload Restrictions for HR Document Submission/ │ │ │ ├── Code.js │ │ │ └── README.md │ │ ├── Enforce Percentage/ │ │ │ ├── README.md │ │ │ └── enforce_percentage.js │ │ ├── Enforce Single Attachment Rule for HR Core Tasks/ │ │ │ ├── README.md │ │ │ └── codingfile.js │ │ ├── Enforce Unique Rank/ │ │ │ ├── README.md │ │ │ └── UniqueRank.js │ │ ├── Enforce a 1-1 relationship/ │ │ │ ├── README.md │ │ │ └── enforce_1_1.js │ │ ├── EnhanceIncidentWithProblem/ │ │ │ ├── EnhanceIncidentWithProblem.js │ │ │ └── README.md │ │ ├── Exclude Redundant Email Recipients/ │ │ │ ├── README.md │ │ │ └── exclude_redundant_email_recipients.js │ │ ├── Fetching reference field value from higher-level parents/ │ │ │ ├── Fetching reference field value from higher-level parents.js │ │ │ └── README.md │ │ ├── Field Validation based on form view in Server side/ │ │ │ ├── README.md │ │ │ └── fieldValidationinBR.js │ │ ├── Find MRVS Total/ │ │ │ ├── mrvs_total_sum.js │ │ │ └── readme.md │ │ ├── GRC Policy Retirement Gaurd/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Generate event/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Group Manager changes, remove old manager & add new manager/ │ │ │ ├── Code.js │ │ │ └── ReadMe.md │ │ ├── Hide from Interceptor/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── If Conflicts are there restrict change resquest to move further/ │ │ │ ├── README.md │ │ │ └── ifConflictStopChangeRequestToAssessState.js │ │ ├── Incident Root Cause Suggestion/ │ │ │ ├── business rule.js │ │ │ ├── readme.md │ │ │ └── script include.js │ │ ├── Make Attachment Mandatory/ │ │ │ ├── MakeAttachmentMandatory.js │ │ │ └── README.md │ │ ├── Mandatory Attachment/ │ │ │ ├── README.md │ │ │ └── threeAttachementsMandatory.js │ │ ├── Manipulating system properties values/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Mark an Email High Importance initiated from Email Client/ │ │ │ ├── Mark an Email High Importance │ │ │ └── README.md │ │ ├── Mask Sensitive Data in Description Field/ │ │ │ ├── maskData.js │ │ │ └── readme.md │ │ ├── Move attachment from variable to record/ │ │ │ ├── README.md │ │ │ └── Script.js │ │ ├── Name Change Profile Update/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Notification/ │ │ │ ├── README.md │ │ │ └── Send Notification on New Incident Creation.js │ │ ├── Pass Attachments/ │ │ │ ├── read.md │ │ │ └── script.js │ │ ├── Pass server info to client/ │ │ │ ├── README.md │ │ │ └── passServerInfo.js │ │ ├── Pdf Letter create/ │ │ │ ├── README.md │ │ │ └── letter.js │ │ ├── Preserve enhancement when deleting project/ │ │ │ ├── Preserve enhancement when deleting project.js │ │ │ └── README.md │ │ ├── Prevent Duplicate CI (Configuration Item) Names/ │ │ │ ├── README.md │ │ │ └── code_snippets.js │ │ ├── Prevent Duplicate Incident Creation within 24 Hours/ │ │ │ ├── BeforeBRScript.js │ │ │ └── README.md │ │ ├── Prevent Invalid User ID/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Prevent RITM to get closed/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Prevent adding user to group if manager is inactive/ │ │ │ ├── README.md │ │ │ └── Script.js │ │ ├── Prevent closure if change tasks are open/ │ │ │ ├── Before_BR.js │ │ │ └── README.md │ │ ├── Prevent duplicate update sets/ │ │ │ ├── README.md │ │ │ └── preventDuplcateUpdateSets.js │ │ ├── Prevent invalid fiscal period in cost plan breakdown/ │ │ │ ├── Prevent invalid fiscal period on cost plan breakdown.js │ │ │ └── README.md │ │ ├── Preventing Recursive Updates from Integrations/ │ │ │ ├── README.md │ │ │ └── prevent_continous_update.js │ │ ├── Previous Approval Check/ │ │ │ ├── README.md │ │ │ └── previous_approval_check.js │ │ ├── QueryBR-restrict users to see their company records/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── RITM Assignment Sync/ │ │ │ ├── Populate_Assigned _To_on_RITMs_for_Specific_Catalog_Item.js │ │ │ └── README.md │ │ ├── RITM state change/ │ │ │ ├── README.md │ │ │ └── Related_task_state_update.js │ │ ├── RITM_to_SCTASK/ │ │ │ ├── README.md │ │ │ └── RITM_to_SCTASK.js │ │ ├── Randomly distrubite events between custom queues/ │ │ │ ├── DistrubuteEvents.js │ │ │ └── README.md │ │ ├── ReRank item/ │ │ │ ├── README.md │ │ │ └── rerank.js │ │ ├── Read Workspace URL/ │ │ │ ├── README.md │ │ │ └── ReadWS_URL.js │ │ ├── Reassign Tasks When Assigned User is Inactive/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Recursive logic/ │ │ │ ├── README.md │ │ │ └── Recursive BR.js │ │ ├── Reduce syslog query/ │ │ │ ├── README.md │ │ │ └── reduce_syslog_query.js │ │ ├── Replace KB Author with Manager/ │ │ │ ├── README.md │ │ │ └── Replace Script.js │ │ ├── Restrict Service Account to Query data/ │ │ │ ├── README.md │ │ │ └── restrictedDataQuery.js │ │ ├── RoleUpdateOnGroupManagerChange/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Round Robin for Incident Assignment/ │ │ │ ├── README.md │ │ │ └── Round Robin method for Incident Assignment.js │ │ ├── SCTASK_to_RITM/ │ │ │ ├── README.md │ │ │ └── SCTASK_to_RITM.js │ │ ├── Set program business duration/ │ │ │ ├── PPM_Utils.js │ │ │ ├── README.md │ │ │ └── set pgm business duration.js │ │ ├── Smart Attachment Size Limiter/ │ │ │ ├── Readme.md │ │ │ └── Smart Attachment Size Limiter.js │ │ ├── Sync CI operational status with child related CIs/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Sync Fields for two tables/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Sync Integer Field/ │ │ │ ├── README.md │ │ │ └── sync_integer_field.js │ │ ├── Synchronize RITM comments to Active tasks/ │ │ │ ├── README.md │ │ │ └── synccommentfromritmtotask.js │ │ ├── Track Tag Removal Using Delete Business Rule/ │ │ │ ├── BusinessRule.js │ │ │ └── readme.md │ │ ├── Transform Data from Attachment/ │ │ │ ├── README.md │ │ │ └── data_xform_from_attachment.js │ │ ├── Trigger Event when a member is added to a list/ │ │ │ ├── README.md │ │ │ └── Trigger an event when user is added to field.js │ │ ├── Update CI status on Change Request closure/ │ │ │ ├── README.md │ │ │ └── Script.js │ │ ├── Update Child Incident based on Parent Incident/ │ │ │ ├── README.md │ │ │ └── Update_Child_Incident.js │ │ ├── Update Incident Description on Insert with Telephone Icon Concatenated/ │ │ │ ├── README.md │ │ │ └── code_snippet.js │ │ ├── Update Locked Out field when Active field changes on User record/ │ │ │ ├── README.md │ │ │ └── update_locked_out_field.js │ │ ├── Update Related Tasks When Parent Changes/ │ │ │ ├── Readme.md │ │ │ └── script.js │ │ ├── Update Set Scope Validation/ │ │ │ ├── README.md │ │ │ └── ValidateScopesForUpdateSet.js │ │ ├── Update worknotes of Ptask to parent Problem record/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── UpdateFavouritedKnowledgeArticleWhenItIsUpgradedToNewVersion/ │ │ │ ├── README.md │ │ │ └── update_favourited_knowledge_artice.js │ │ ├── Use_case_Base64-Encode-Before-Save-And-Decode-on-Display/ │ │ │ ├── Before_BR_script.js │ │ │ ├── Display_BR_script.js │ │ │ └── Readme.md │ │ ├── User Activity Log Tracking/ │ │ │ ├── README.md │ │ │ └── userActivityLog.js │ │ ├── User Impersonation Activity Logger/ │ │ │ ├── README.md │ │ │ └── UserImpersonationActiityLogger.js │ │ ├── User Profile Field Validation/ │ │ │ ├── README.md │ │ │ └── userProfileValidation.js │ │ ├── Validate Checklist items/ │ │ │ ├── README.md │ │ │ └── incompleteChkListShowErrMsg.js │ │ ├── Validate Email on Import/ │ │ │ ├── README.md │ │ │ └── validateEmailOnImport.js │ │ ├── Validate JSON Property/ │ │ │ ├── README.md │ │ │ └── jsonPropertyValidator.js │ │ ├── Warn for changed OOTB artifacts/ │ │ │ ├── README.md │ │ │ └── warn_for_changed_ootb_artifacts.js │ │ ├── When Group is making inactive/ │ │ │ ├── Code.js │ │ │ └── ReadMe.md │ │ ├── duplicateattachments/ │ │ │ ├── AvoidduplicateattachmentsonIncident.js │ │ │ └── README.md │ │ ├── openAI/ │ │ │ ├── README.md │ │ │ └── chatGpt.js │ │ ├── setting future week dates/ │ │ │ ├── README.md │ │ │ └── setting future dates.js │ │ └── user-activity-logger/ │ │ ├── README.md │ │ └── user-activity-logger.js │ ├── Extension Points/ │ │ ├── CustomNotificationHandlerInterfaceExtension.js │ │ ├── CustomNotificationHandlerInterfaceImplementation.js │ │ ├── ExtensionPointCallingExample.js │ │ └── README.md │ ├── Inbound Actions/ │ │ ├── Advanced Scripts/ │ │ │ ├── README.md │ │ │ ├── cancel_catalog_item_from_email.js │ │ │ ├── create_catalog_item_from_email.js │ │ │ └── update_catalog_item_from_email.js │ │ ├── Auto Incident Creation from Case Email/ │ │ │ ├── README.md │ │ │ └── create_incident_from_case_email.js │ │ ├── Auto Reply Email/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Automate creation of incidents through inbound actions/ │ │ │ ├── README.md │ │ │ └── incidentCreation.js │ │ ├── Duplicate Incident Detection and Creation/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Email Text as Attachment/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Inbound Email Action to Create User and Assign Groups/ │ │ │ ├── Readme.md │ │ │ └── auto_create_user_through_inbound_email_action.js │ │ ├── Incident Creation from Email with Attachments Handling/ │ │ │ ├── README.md │ │ │ └── Script.js │ │ └── Reply Task/ │ │ ├── README.md │ │ └── reply.js │ ├── Processors/ │ │ └── Dynamic Sitemap/ │ │ ├── README.md │ │ └── dynamicSitemapProcessor.js │ ├── Scheduled Jobs/ │ │ ├── API Token Expiry Warning/ │ │ │ ├── API Token Expiry Warning.js │ │ │ └── Readme.md │ │ ├── Approval Reminder/ │ │ │ ├── README.md │ │ │ ├── change_reminder_scheduled_job.js │ │ │ ├── requested_item_approval_reminder_approver.js │ │ │ └── requested_item_approval_reminder_requestor.js │ │ ├── Auto Disable account/ │ │ │ ├── Disable Service accounts due to inactivity │ │ │ └── Readme.md │ │ ├── Auto close changes requests updated 30 days prior/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Auto upgrade store applications/ │ │ │ ├── Readme.md │ │ │ ├── script.js │ │ │ └── sysproperty.js │ │ ├── Auto-Assign Unassigned Incidents Older Than 30 Minutes/ │ │ │ ├── Readme.md │ │ │ └── auto_assign_unassigned_incidents_via_scheduled_job.js │ │ ├── Bucket Group Reporting/ │ │ │ ├── Bucket Group Age Calculation.js │ │ │ └── readme.md │ │ ├── Calculate Ticket's Aging/ │ │ │ ├── Calculate Ticket's Aging.js │ │ │ └── README.md │ │ ├── CancelApproval/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Clean up Inactive User access/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Condition script to trigger the scheduled job on Quarterly basis/ │ │ │ ├── Condition script to trigger the scheduled job on Quarterly basis.js │ │ │ └── README.md │ │ ├── Create Scheduled Imports Graphviz file/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Create send reminders weekly/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Daily Summary Email/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Daily detection of customer updates made in 'Default' update set/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Deactivate INC in 90 days/ │ │ │ ├── 90daysInactiveScript.js │ │ │ └── README.md │ │ ├── Deactivate Inactive Users and Notify Managers/ │ │ │ ├── Readme.md │ │ │ └── auto_deactivate_inactive_users.js │ │ ├── Deactivate Memberless Group/ │ │ │ ├── README.md │ │ │ └── deactivate group.js │ │ ├── Deactive and Reactivate Catalog Items/ │ │ │ ├── README.md │ │ │ └── schedule-deactivation.js │ │ ├── Delete Retired CI Rel/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Employee Probation case/ │ │ │ ├── Probation.js │ │ │ └── README.md │ │ ├── Export Filtered Records to CSV Automatically/ │ │ │ ├── README.md │ │ │ └── code_snippet.js │ │ ├── Get All Catalog Tasks without Request items/ │ │ │ ├── catalogTaskWithoutReqItem.js │ │ │ └── catalogTaskWithoutReqItem.md │ │ ├── Licensed User Access Job/ │ │ │ ├── Weekly_LicensedUser_Access_Revoke_90Days.js │ │ │ └── readme.md │ │ ├── Lock out users who have not logged into the system longer than 30 days/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Paginated Export/ │ │ │ ├── Paginated Export.md │ │ │ └── paginatedExport.js │ │ ├── PostUserDisabledActivity/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Proactive Change Request Reminder/ │ │ │ ├── README.md │ │ │ └── proactive_change_reminder.js │ │ ├── Reject approvals created before an year/ │ │ │ ├── README.md │ │ │ └── Reject Approvals Created Before an Year.js │ │ ├── Remove Inactive and locked out users from All Groups and Roles/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Role Usage Analyzer/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── ScheduleAtSpecificDaysAndTimes/ │ │ │ ├── README.md │ │ │ └── schedulejobcondition.js │ │ ├── Scheduled Data Import for Groups Population(Support and Managed By) for CMDB Classes/ │ │ │ ├── ReadMe.md │ │ │ └── post-script.js │ │ ├── Scheduled Job to Email Incident Count Report by Category/ │ │ │ ├── Readme.md │ │ │ └── Send_monthly_incident_category_count_report.js │ │ ├── Submit catalog item/ │ │ │ ├── README.md │ │ │ └── submit_catalog_item.js │ │ ├── Survey Trigger Scheduled Script/ │ │ │ ├── README.md │ │ │ └── survey_trigger_sj.js │ │ ├── Top10jobsbyprocessingtime/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Unpublish Public Reports/ │ │ │ ├── README.md │ │ │ └── UnpublishReports.js │ │ ├── Update Inactive Application Owner/ │ │ │ ├── README.md │ │ │ └── Update Inactive Application Owner.js │ │ ├── Weekly Incident Trend Analysis/ │ │ │ ├── README.md │ │ │ └── incident_trend_analyzer.js │ │ └── trigger on weekday/ │ │ ├── read.md │ │ └── script.js │ ├── Script Actions/ │ │ ├── Attachment Downloads Logger/ │ │ │ ├── README.md │ │ │ └── scriptActionCode.js │ │ ├── Custom Table Helper/ │ │ │ ├── README.md │ │ │ └── Script Action.js │ │ └── Deactivate Inactive Users and Notify Managers/ │ │ ├── Readme.md │ │ └── user_deactivation_notify_manager_script_action.js │ ├── Script Includes/ │ │ ├── API Model Template for New Application/ │ │ │ ├── AbstractStrategy.js │ │ │ ├── AbstractStrategyBase.js │ │ │ ├── ApplicationCore.js │ │ │ ├── ApplicationCoreBase.js │ │ │ ├── Engine.js │ │ │ ├── EngineBase.js │ │ │ ├── ExampleObject.js │ │ │ ├── ExampleObjectBase.js │ │ │ ├── ExampleStrategy1.js │ │ │ ├── ExampleStrategy1Base.js │ │ │ ├── ExampleStrategy2.js │ │ │ ├── ExampleStrategy2Base.js │ │ │ └── README.md │ │ ├── Add Business Days/ │ │ │ ├── README.md │ │ │ └── addBusinessDays.js │ │ ├── Add and Remove Group Member/ │ │ │ ├── README.md │ │ │ └── groupMember.js │ │ ├── Advanced REST API Integration with Retry Logic/ │ │ │ ├── README.md │ │ │ ├── RESTIntegrationExample.js │ │ │ └── RESTIntegrationHandler.js │ │ ├── Approval Rule Builder/ │ │ │ ├── ApprovalRuleBuilder.js │ │ │ └── README.md │ │ ├── Array prototypes/ │ │ │ ├── ArrayPrototypes.js │ │ │ └── README.md │ │ ├── ArrayUtil/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Assign role for a day Util/ │ │ │ ├── AssignRoleToUserForADay.js │ │ │ └── README.md │ │ ├── Auto Execute Import Set on File Attachment/ │ │ │ ├── CreateImportSetAndRunTransform.js │ │ │ ├── CreateSysTrigger.js │ │ │ └── README.md │ │ ├── Autopopulate caller location in short description/ │ │ │ ├── README.md │ │ │ ├── getCallerLocation.js │ │ │ └── updateCallerLocationinShortDesc.js │ │ ├── BackfillAssignmentGroup/ │ │ │ ├── BackfillAssignmentGroup.js │ │ │ └── README.md │ │ ├── BenchmarkRunner/ │ │ │ ├── BenchmarkRunner.js │ │ │ └── README.md │ │ ├── CSV Parser/ │ │ │ ├── CSVParser.js │ │ │ └── README.md │ │ ├── CacheHelper/ │ │ │ ├── CacheHelper.js │ │ │ └── README.md │ │ ├── Calculate Business days dynamically/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Catalog Item Pricing/ │ │ │ ├── README.md │ │ │ └── calculate_catalog_item_price.js │ │ ├── CatalogUtils/ │ │ │ ├── CatalogUtils.js │ │ │ └── README.md │ │ ├── Check User Criteria for Catalog Item/ │ │ │ ├── README.md │ │ │ └── checkUserCriteria.js │ │ ├── Check User Has Role/ │ │ │ ├── README.md │ │ │ └── UserHasRole.js │ │ ├── Check Valid Choice/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Check writer/ │ │ │ ├── CheckWriter.js │ │ │ └── README.md │ │ ├── Client and Server Callable Script Include/ │ │ │ ├── README.md │ │ │ └── script include.js │ │ ├── Collect Field Values from Any One Table Record/ │ │ │ └── universalRecordCollector.js │ │ ├── Collect Field Values from Any Table/ │ │ │ └── README.md │ │ ├── CommandInjectionChecker/ │ │ │ ├── CommandInjectionChecker.js │ │ │ └── README.md │ │ ├── ConnectionCredentialsUtils/ │ │ │ ├── ConnectionCredentialUtils.js │ │ │ └── README.md │ │ ├── ConversationUtils/ │ │ │ ├── ConversationUtils.js │ │ │ └── README.md │ │ ├── Convert base64 to Hex (Object GUID)/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Convert image into base64/ │ │ │ ├── Convert image into base63 encoded string.js │ │ │ └── README.md │ │ ├── Copy record Attachment to Email Client/ │ │ │ ├── CopyAttachmentToEmailClient.js.js │ │ │ └── ReadMe.md │ │ ├── Count Assigned To Field/ │ │ │ ├── README.md │ │ │ └── countAssignedUtil.js │ │ ├── Create Multiple RITMS from MRVS/ │ │ │ ├── CreateMultipleRITMSFromMRVS.js │ │ │ └── README.md │ │ ├── Custom Discovery Schedule With Freeze Periods/ │ │ │ ├── DiscoveryScheduleWithFreezePriod.js │ │ │ └── README.md │ │ ├── CustomArrayUtils/ │ │ │ ├── CustomArrayUtils.js │ │ │ └── README.md │ │ ├── CustomDateUtils/ │ │ │ ├── CustomDateUtils.js │ │ │ └── README.md │ │ ├── CustomObjectUtils/ │ │ │ ├── CustomObjectUtils.js │ │ │ └── README.md │ │ ├── CustomUserUtils/ │ │ │ ├── CustomUserUtils.js │ │ │ └── README.md │ │ ├── Data Lookup Table Utils/ │ │ │ ├── DataLookupUtils.js │ │ │ └── README.md │ │ ├── Data Normalizer/ │ │ │ ├── README.md │ │ │ └── dataNormalizer.js │ │ ├── Delete Multiple Records Async/ │ │ │ ├── DeleteMultipleRecordsAsync.js │ │ │ └── README.md │ │ ├── Deprecate Field/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Developer Debug Utility/ │ │ │ ├── DeveloperDebugUtility.js │ │ │ └── README.md │ │ ├── Dynamic Dropdown List/ │ │ │ ├── Client.js │ │ │ ├── README.md │ │ │ └── UtilScript.js │ │ ├── Dynamic Record Archiving/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Emotion-Aware/ │ │ │ ├── EmotionAnalyzer.js │ │ │ └── README.md │ │ ├── EvtMgmtCustom_PostTransformHandler/ │ │ │ ├── EvtMgmtCustom_PostTransformHandler.js │ │ │ └── README.md │ │ ├── Excel Attachment Via script/ │ │ │ ├── README.md │ │ │ └── excelAttachment.js │ │ ├── Excel Parser/ │ │ │ ├── README.md │ │ │ └── excelParser.js │ │ ├── Execution Time Tracker/ │ │ │ ├── README.md │ │ │ └── execution_time_tracker.js │ │ ├── Extending OOB TableUtils/ │ │ │ ├── EXT_TablesUtils.js │ │ │ └── README.md │ │ ├── Filter record/ │ │ │ ├── read.md │ │ │ └── script.js │ │ ├── Financial Service Utilities/ │ │ │ ├── FinancialServiceUtilities.js │ │ │ └── README.md │ │ ├── Find months between two dates/ │ │ │ ├── DateUtil.js │ │ │ └── README.md │ │ ├── Generate QR Code and attach to RITM/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get Approvers of a Ticket/ │ │ │ ├── GetApproversForATicket.js │ │ │ └── README.md │ │ ├── Get Choice Display Value/ │ │ │ ├── README.md │ │ │ └── getChoiceDisplayValue.js │ │ ├── Get Current User Information/ │ │ │ ├── README.md │ │ │ └── getCurrentUserInformation.js │ │ ├── Get Field Label in Specific Language/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get Group Members/ │ │ │ ├── README.md │ │ │ └── getGroupMembers.js │ │ ├── Get Profile Picture/ │ │ │ ├── README.md │ │ │ └── getProfilePicture.js │ │ ├── Get Reference Display Value/ │ │ │ ├── README.md │ │ │ └── getReferenceDisplayValue.js │ │ ├── Get User Data by Id/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── GetCallerDetails/ │ │ │ ├── Calling Script Include from client.js │ │ │ ├── README.md │ │ │ └── scriptinclude.js │ │ ├── GetClickableURL/ │ │ │ ├── readme.md │ │ │ └── scipt.js │ │ ├── GetServiceDeskAgentHelpAIUtil/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── GlideDateTimeUtils/ │ │ │ ├── ClientDateTimeUtils.js │ │ │ └── README.md │ │ ├── GlideRecord to JSON/ │ │ │ ├── README.md │ │ │ └── gr2obj.js │ │ ├── GlideRecordHelper/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── GroupMembershipUtils for client and server/ │ │ │ ├── groupMembershipUtils.js │ │ │ └── readme.md │ │ ├── HTMLUtils/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Hybrid Script Include for AJAX or Server Side Parameters/ │ │ │ ├── README.md │ │ │ └── ScriptInclude.js │ │ ├── Inactive User/ │ │ │ ├── InactiveUserCleanup.js │ │ │ └── README.md │ │ ├── Install base items with active cases/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── JSON Mapping for Incident Creation/ │ │ │ ├── JsonMapping.js │ │ │ └── README.md │ │ ├── JSONPath/ │ │ │ ├── JSONPath.js │ │ │ └── README.md │ │ ├── JSONtoYAML/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── KBArticleExpPDF/ │ │ │ ├── ArticlePDFHelper.js │ │ │ └── README.md │ │ ├── ListFieldUtil/ │ │ │ ├── ListFieldUtil.js │ │ │ └── README.md │ │ ├── Log Utils/ │ │ │ ├── Code.js │ │ │ └── README.md │ │ ├── Logger/ │ │ │ ├── Logger.js │ │ │ └── README.md │ │ ├── ManagerRecursiveUtil/ │ │ │ ├── README.md │ │ │ └── RecursiveByManager.js │ │ ├── Match URL with a String/ │ │ │ ├── MatchURLByRegex.js │ │ │ └── README.md │ │ ├── Next Business Window Calculator/ │ │ │ ├── README.md │ │ │ └── next_business_window.js │ │ ├── Non Prod Instance Password Reset/ │ │ │ ├── README.md │ │ │ └── passwordReset.js │ │ ├── NonProdLogUtils/ │ │ │ ├── LogUtilsNonProd.js │ │ │ └── README.md │ │ ├── NotificationUtil/ │ │ │ ├── NotificationUtil.js │ │ │ └── README.md │ │ ├── Number Padding/ │ │ │ ├── README.md │ │ │ └── numberPadding.js │ │ ├── OAuth token helper/ │ │ │ ├── OAuthTokenHelper.js │ │ │ └── README.md │ │ ├── OrderedRecords/ │ │ │ ├── README.md │ │ │ └── orderedRecords.js │ │ ├── PII Redactor/ │ │ │ ├── README.md │ │ │ └── piiRedaction.js │ │ ├── Password Generator with specific length/ │ │ │ ├── PasswordGenerator.js │ │ │ └── README.md │ │ ├── PerformanceAnalyticsUtils/ │ │ │ ├── PerformanceAnalyticsUtils.js │ │ │ └── README.md │ │ ├── Populate MRVS from Excel/ │ │ │ ├── ParsingScript.js │ │ │ └── README.md │ │ ├── Prevent circular dependencies in task relationships/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Project Base Line/ │ │ │ ├── README.md │ │ │ └── latestPlannedBaseline.js │ │ ├── Public Script Include search/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── PullEmptySerialNumberAssetRecords/ │ │ │ ├── README.md │ │ │ └── pull_empty_serial_number_record.js │ │ ├── Query ldap server/ │ │ │ ├── LDAPquery.js │ │ │ └── README.md │ │ ├── Read CSV file from Mid Server/ │ │ │ ├── CSVReaderUtil.js │ │ │ └── README.md │ │ ├── RecordProducerVariableUtils/ │ │ │ ├── README.md │ │ │ ├── RecordProducerVariableUtils_v1.0.xml │ │ │ └── script.js │ │ ├── Records Calculator/ │ │ │ ├── README.md │ │ │ └── RecordsCalculator.js │ │ ├── Recursive GlideRecord Fetcher/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Regex utils/ │ │ │ ├── README.md │ │ │ └── RegexUtils.js │ │ ├── Reparent Table/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Request Approval Helper/ │ │ │ ├── README.md │ │ │ └── RequestApprovalHelper.js │ │ ├── RequestNotificationUtil/ │ │ │ ├── README.md │ │ │ └── RequestNotificationUtil.js │ │ ├── RestMessageUtils/ │ │ │ ├── README.md │ │ │ └── RestMessageUtils.js │ │ ├── Retrieve Last Comment by Ticket/ │ │ │ ├── README.md │ │ │ └── RetrieveLastCommentByTicket.js │ │ ├── Return Object/ │ │ │ ├── README.md │ │ │ └── ReturnObject.js │ │ ├── Role Checker Util/ │ │ │ ├── README.md │ │ │ └── checkUserRole.js │ │ ├── Root-Cause Predictor/ │ │ │ ├── README.md │ │ │ └── incident.js │ │ ├── SCIM Custom Mapping Handler/ │ │ │ ├── README.md │ │ │ └── SCIMCustomMappingHandler.js │ │ ├── SCIM Payload Generator/ │ │ │ ├── GenerateSCIMPayload.js │ │ │ └── README.md │ │ ├── SQLInjectionChecker/ │ │ │ ├── README.md │ │ │ └── SQLInjectionChecker.js │ │ ├── SRAPIUtil/ │ │ │ ├── README.md │ │ │ └── SRAPIUtil.js │ │ ├── Safe Bulk Update Runner/ │ │ │ ├── README.md │ │ │ └── safe_bulk_update_runner.js │ │ ├── Scheduled Recursion/ │ │ │ ├── README.md │ │ │ ├── background_script.js │ │ │ └── scheduled_recursion.js │ │ ├── Script Include Usage Tracker/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Sends Slack/ │ │ │ └── Teams notifications when specific fields change on configured tables/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Single Sign-On (SSO) Direct Login URL Generator/ │ │ │ ├── README.md │ │ │ └── UserHelper.js │ │ ├── Slack JSON Block Factory/ │ │ │ ├── README.md │ │ │ ├── Slacktory.js │ │ │ └── sys_script_include_config.md │ │ ├── SmartData/ │ │ │ ├── SmartData.scriptinclude.js │ │ │ ├── SmartDataAjax.scriptinclude.js │ │ │ └── readme.md │ │ ├── Standard Change Creator/ │ │ │ ├── README.md │ │ │ ├── sys_script_include.js │ │ │ └── sys_script_include_config.md │ │ ├── StarterPack/ │ │ │ ├── AjaxClientScript.js │ │ │ ├── AjaxSI.js │ │ │ ├── README.md │ │ │ ├── classless.js │ │ │ ├── constants.js │ │ │ ├── reference.js │ │ │ └── utilsExample.js │ │ ├── Stopwatch/ │ │ │ ├── README.md │ │ │ └── Stopwatch.js │ │ ├── Store data in User Session/ │ │ │ ├── README.md │ │ │ └── storeDataInSession.js │ │ ├── StripHTML/ │ │ │ ├── README.md │ │ │ └── StripHTML.js │ │ ├── SubProdLogger/ │ │ │ ├── README.md │ │ │ └── SubProdLogger.js │ │ ├── Table List Copy Context Options/ │ │ │ ├── Copy Field Display Value Context Menu.js │ │ │ ├── Copy Field Name Context Menu.js │ │ │ ├── Copy Field Value Context Menu.js │ │ │ ├── README.md │ │ │ └── Script Include.js │ │ ├── TableUtils Extension/ │ │ │ ├── Enhanced_TableUtils.js │ │ │ └── README.md │ │ ├── Testing Script Include Using Jasmine/ │ │ │ ├── README.md │ │ │ ├── Sample Calculator Script Include.js │ │ │ └── Sample Jasmine Script.js │ │ ├── TimeZoneUtils/ │ │ │ ├── README.md │ │ │ └── TimeZoneUtils.js │ │ ├── TinyURLHelper/ │ │ │ ├── README.md │ │ │ └── TinyUrlHelper.js │ │ ├── TranslationUtil/ │ │ │ ├── README.md │ │ │ └── translationUtil.js │ │ ├── Translations Import/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── UnloadXml/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── UpdateWatchlistFromJSON/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── UserCriteriaUtil/ │ │ │ ├── README.md │ │ │ └── UserCriteriaUtil.js │ │ ├── UserUtil/ │ │ │ ├── README.md │ │ │ └── UserUtil.js │ │ ├── Validate Data Before Insert/ │ │ │ ├── DataValidationUtils.js │ │ │ └── README.md │ │ ├── VariableHelper/ │ │ │ ├── README.md │ │ │ └── variableHelper.js │ │ ├── VariableToDescription/ │ │ │ ├── README.md │ │ │ └── VariableToDescription.js │ │ ├── attachments/ │ │ │ ├── README.md │ │ │ └── attachment.js │ │ ├── get field values for multiple records from a table/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── getCountFunction/ │ │ │ ├── README.md │ │ │ ├── callingSI.js │ │ │ └── code.js │ │ ├── getGlideRecordObject/ │ │ │ ├── README.md │ │ │ └── getGlideRecordObject.js │ │ └── regexCheckerScript/ │ │ ├── README.md │ │ └── regexCheckerScript.js │ ├── Server Side/ │ │ ├── CallScriptIncludeWithParameters/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── CheckTableExtension/ │ │ │ ├── README.md │ │ │ └── istableextended.js │ │ ├── Create Admin Users/ │ │ │ ├── README.md │ │ │ └── create_admin_user.js │ │ ├── Create Tiny Url with API's/ │ │ │ ├── README.md │ │ │ └── tinyUrl.js │ │ ├── CreateUpdateCIThroughIRE/ │ │ │ ├── README.md │ │ │ └── createupdateciinire.js │ │ ├── Custom Relationship/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── DiscoveryDeviceHistory/ │ │ │ ├── devicehistory.js │ │ │ └── readme.me │ │ ├── Dynamic Catalog Task Creation/ │ │ │ ├── README.md │ │ │ └── dynamic_catalog_task.js │ │ ├── Email Bounce Alert System/ │ │ │ ├── Email_Bounce_Alert_System.js │ │ │ └── README.md │ │ ├── ExecuteWorkOnMidServer/ │ │ │ ├── README.md │ │ │ └── executeworkonmid.js │ │ ├── Fetch dynamic value from decision table/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── FetchJSONObject/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Generate Attachment and add it to the email/ │ │ │ ├── README.md │ │ │ └── emailAttachment.js │ │ ├── Get all Catalog items associated to variable set/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get all variables of catalog item/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── MarkInactiveUsersonList/ │ │ │ ├── README.md │ │ │ └── fieldStyleforListfields.js │ │ ├── Parse csv file and read each row/ │ │ │ ├── README.md │ │ │ └── read csv logic.js │ │ ├── Phone Number formating(US Region)/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Random Password generator/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Record as Link/ │ │ │ ├── README.md │ │ │ ├── example-pdf-generation.js │ │ │ └── recordAsLink.js │ │ ├── Remove HTML Tags/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Restart Flow on RITM/ │ │ │ ├── README.md │ │ │ └── restartFlow.js │ │ ├── Restart a workflow via any server side script/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Trigger Assessments through Script/ │ │ │ ├── README.md │ │ │ └── trigger assessment.js │ │ ├── Update Sets Scopes Issues Fix Automation/ │ │ │ ├── FixUpdatesScope.js │ │ │ ├── PreventCompletiononScopeConflict.js │ │ │ ├── README.md │ │ │ └── UpdateSetUtilCustom.js │ │ ├── Update Variable Choices/ │ │ │ ├── README.md │ │ │ └── updateVariableChoices.js │ │ ├── Use System Property as an Object to Store Multiple Values and Retrieve Attributes When Needed/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── User Criteria/ │ │ │ ├── Does User Match Criteria(s).js │ │ │ └── README.md │ │ ├── Version 4 UUID Generator/ │ │ │ ├── README.md │ │ │ └── uuid_generator.js │ │ └── getUserGroupMembers/ │ │ ├── README.md │ │ └── script.js │ └── Transform Map Scripts/ │ ├── Check if the Import file is valid/ │ │ ├── README.md │ │ └── script.js │ ├── Choice Field Validator/ │ │ ├── README.md │ │ └── choiceValidatorUtil.js │ ├── Conditional Coalesce/ │ │ ├── README.md │ │ └── conditional_coalasce.js │ ├── Email Formatter/ │ │ ├── README.md │ │ └── emailFormatterValidator │ ├── Global Variable in Transform Map/ │ │ ├── README.md │ │ ├── manager_email_to_sysid_map_onStartScript.js │ │ └── onBefore_transform_script.js │ ├── Incident Priority Set on Insert Only/ │ │ ├── README.md │ │ └── onBefore_set_priority_insert_only.js │ └── Verify headers of a CSV attached file/ │ ├── README.md │ ├── example.csv │ └── verifyCSVHeaders.js ├── Specialized Areas/ │ ├── ATF Steps/ │ │ ├── Count table records/ │ │ │ ├── README.md │ │ │ └── script.js │ │ └── Validate RITM Due Date/ │ │ ├── README.md │ │ └── atf_ritm_due_date_script.js │ ├── Advanced Conditions/ │ │ ├── Exclude Email Reply Comment Notifications by Group/ │ │ │ ├── README.md │ │ │ └── exclude_email_reply_comment_notifications_by_group.js │ │ └── Group Approval Check/ │ │ ├── README.md │ │ └── group_approval_check.js │ ├── Agile Development/ │ │ └── Burndown Chart/ │ │ ├── README.md │ │ ├── requirements.txt │ │ └── sprint_burndown_chart.py │ ├── Browser Bookmarklets/ │ │ ├── Copy URL to ServiceNow Journal/ │ │ │ ├── README.md │ │ │ └── copy url to servicenow journal.js │ │ ├── Create new update set/ │ │ │ ├── README.md │ │ │ └── create_update_set.js │ │ ├── Create story task/ │ │ │ ├── README.md │ │ │ └── create_story_task.js │ │ ├── Highlight Mandatory fields on form/ │ │ │ ├── Highlight mandatory fields on form.js │ │ │ └── README.md │ │ ├── Impersonation/ │ │ │ ├── README.md │ │ │ └── impersonation.js │ │ ├── Load List with Query/ │ │ │ ├── listquery.html │ │ │ └── readme.md │ │ ├── Open copied record/ │ │ │ ├── README.md │ │ │ └── open copied record bookmarklet.js │ │ ├── Open g_form modal/ │ │ │ ├── Open modal to use g_form.js │ │ │ └── README.md │ │ ├── Open record in another instance/ │ │ │ ├── Open record in another instance.js │ │ │ └── README.md │ │ ├── Open tinymce for journal/ │ │ │ ├── Open tinymce editor in modal for journal fields.js │ │ │ └── README.md │ │ ├── Quick Notes/ │ │ │ ├── README.md │ │ │ └── quick_note.html │ │ ├── Quick login to current instance/ │ │ │ ├── Quick login.js │ │ │ └── README.md │ │ ├── ServiceNow Instance Collection/ │ │ │ ├── README.md │ │ │ └── bookmarklets.html │ │ └── Show Logged-in User roles/ │ │ ├── readme.md │ │ └── user_info_bookmarklet.js │ ├── Browser Utilities/ │ │ └── Custom Search Engines/ │ │ └── README.md │ ├── CMDB/ │ │ ├── CMDB CI Deduplication Task Generator/ │ │ │ ├── CI Deduplication Tasks Generation with UI Action.js │ │ │ └── README.md │ │ ├── CMDB Dynamic Status Update Function/ │ │ │ ├── README.md │ │ │ └── updateCMDBOperationalStatus.js │ │ ├── CMDB Get CI Relationships/ │ │ │ ├── README.md │ │ │ └── getCIRelationships.js │ │ ├── CMDB Health Check/ │ │ │ ├── README.md │ │ │ └── health_check.js │ │ ├── CMDB Utility Scripts/ │ │ │ ├── README.md │ │ │ ├── ReadME.md │ │ │ ├── TechTrekwithAJ-AutoPopulateManufacturer.js │ │ │ ├── detectDuplicateCIs.js │ │ │ ├── populateMissingManufacturers.js │ │ │ ├── softwareCreation.js │ │ │ ├── softwareCreationREADME.md │ │ │ └── unUsedCIs.js │ │ ├── CMDB record count/ │ │ │ ├── README.md │ │ │ ├── TechTrekwithAJ-CMDBCIcountonclass.js │ │ │ ├── TechTrekwithAJ-readme.md │ │ │ └── cmdb-record-count.js │ │ ├── CSDM Maturity Report/ │ │ │ ├── README.md │ │ │ └── csdm-maturity-report.js │ │ ├── IRE Errors/ │ │ │ ├── IRE Errors.js │ │ │ └── README.md │ │ ├── IRE Queridentify/ │ │ │ ├── Identify and query with IRE.js │ │ │ └── README.md │ │ ├── Mandatory Field Analysis/ │ │ │ ├── README.md │ │ │ ├── TechTrekwithAJ-CMDBMandatoryfieldanalysis.js │ │ │ ├── TechTrekwithAJ-cmdbmandatoryfielddescription_readme.md │ │ │ └── mandatory-field-analysis.js │ │ └── UnsyncCI from Asset/ │ │ ├── readme.md │ │ └── script.js │ ├── Cost Optimization/ │ │ └── Instance Cost Analyzer/ │ │ ├── README.md │ │ ├── cost_analyzer.js │ │ └── scheduled_job.js │ ├── Data Quality/ │ │ └── Similarity Calculator/ │ │ ├── Change Table/ │ │ │ ├── README.md │ │ │ └── similarity_change_calculator.js │ │ └── Incident Table/ │ │ ├── README.md │ │ └── similarity_incident_calculator.js │ ├── Dynamic Filters/ │ │ └── getMyDirectReports/ │ │ ├── README.md │ │ └── getMyReportsUtil.js │ ├── Fix scripts/ │ │ ├── Add Fields On All List Views/ │ │ │ ├── AddFieldsToLists.js │ │ │ └── README.md │ │ ├── Add Variable set to multiple catalog items/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Add bulk users to VTB/ │ │ │ ├── README.md │ │ │ └── addBulkUsersToVTB.js │ │ ├── Adjust Variable Order on Catalog Item/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Anonymise Data/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Assign user list to a specific group/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Authenticate using ScriptedRESTAPI/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── AutoNumberIssueFix/ │ │ │ ├── README.md │ │ │ └── autonumberingcode.js │ │ ├── Bulk Update Catalog Item Images/ │ │ │ ├── README.md │ │ │ └── bulk-cat-item-image-update.js │ │ ├── Calculate Business Duration/ │ │ │ ├── README.md │ │ │ └── calculate-business-duration.js │ │ ├── Cancel Struck Slack Conversations/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Cancel Workflow/ │ │ │ ├── Cancel Workflow Context.js │ │ │ └── README.md │ │ ├── Cancel in progress flow executions using flow name/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Check if Fiscal Year is safe for deletion/ │ │ │ ├── Check if Fiscal Year is safe for deletion │ │ │ └── README.md │ │ ├── Clean update set/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Copy favourite to other users/ │ │ │ ├── README.md │ │ │ └── favCopy.js │ │ ├── CreateMultipleRecords/ │ │ │ ├── Add Manager to Group │ │ │ ├── README.md │ │ │ └── create_multiple_records.js │ │ ├── De-Provision Admin user (configurable)/ │ │ │ ├── README.md │ │ │ └── deprovisionAdmin.js │ │ ├── Delete Change Conflict/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Delete Duplicate Mobile records/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Employee document management/ │ │ │ ├── read.md │ │ │ └── regenerate_document.js │ │ ├── Find Reports Assigned to inactive Groups/ │ │ │ ├── README.md │ │ │ └── reportAssignedToInactiveGroups.js │ │ ├── Find duplicate records/ │ │ │ ├── Find duplicate records.js │ │ │ ├── README.md │ │ │ ├── TechTrekwithAJ_DuplicateCIreadme.md │ │ │ └── TechTrekwithAJ_FindDuplicateonCMDB_CI.JS │ │ ├── Find the reports assigned to Inactive Users/ │ │ │ ├── README.md │ │ │ └── reportsassignedtoinactiveusers.js │ │ ├── Fiscal period renamer/ │ │ │ ├── README.md │ │ │ └── fiscal period renamer.js │ │ ├── Fix model names after enabling glide.cmdb_model.display_name.shorten/ │ │ │ ├── README.md │ │ │ └── fixModelNames.js │ │ ├── Fix teams token/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Format JSON in String Fields/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get Catalog Items not used in last few months/ │ │ │ ├── README.md │ │ │ └── getUnusedCatItems.js │ │ ├── Group Sync Script/ │ │ │ ├── README.md │ │ │ └── fix_script.js │ │ ├── Ignore outbound emails/ │ │ │ ├── README.md │ │ │ └── ignoremail.js │ │ ├── Install Base PDI Plugins/ │ │ │ ├── README.md │ │ │ └── install_plugins.js │ │ ├── Install Demo Data/ │ │ │ ├── InstallDemoData.js │ │ │ └── README.md │ │ ├── Log out active User sessions/ │ │ │ ├── README.md │ │ │ └── log_out_active_user_sessions.js │ │ ├── Mass Email Domain Update/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Mass Update RITM Variable/ │ │ │ ├── README.md │ │ │ └── Update RITM Variable.js │ │ ├── Measure code time execution/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Merge stages or choice/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Migrate data from one table to another/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Multiply records from filer breadcrumbs/ │ │ │ ├── Multiply records.js │ │ │ └── README.md │ │ ├── Post-clone Clear Email Queue/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Post-clone Email Properties Script/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Post-clone Set Banner Name/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Post-clone Set Instance Banner/ │ │ │ ├── README.md │ │ │ └── set_instance_banner.js │ │ ├── Reject RITM via fix script/ │ │ │ ├── README.md │ │ │ └── Reject RITM.js │ │ ├── Remove User Groups/ │ │ │ ├── README.md │ │ │ └── removeuser.js │ │ ├── Remove extra spaces/ │ │ │ ├── readme.md │ │ │ └── removeSpaces.js │ │ ├── Remove leading and trailing spaces/ │ │ │ ├── README.md │ │ │ └── RemoveLeadingTrailingSpaces.js │ │ ├── Replace inactive group managers with group members/ │ │ │ └── script.js │ │ ├── Restart Flow (CatalogItem)/ │ │ │ ├── README.md │ │ │ └── restart_flow.js │ │ ├── Restore From Audit History/ │ │ │ ├── README.md │ │ │ └── restore_from_audit_fix.js │ │ ├── Run Subscription Job On Demand/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── SchemaGenerator/ │ │ │ ├── README.md │ │ │ ├── cmdb_ci_server_schema.json │ │ │ └── schemaGenerator.js │ │ ├── Search Results Weight/ │ │ │ ├── README.md │ │ │ └── sr_weight.js │ │ ├── Swiss German Language Update/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Sync Data Between Instances/ │ │ │ ├── README.md │ │ │ └── SyncData.js │ │ ├── Syslog_top10Contributors/ │ │ │ ├── README.md │ │ │ └── syslog_script.js │ │ ├── Update field with value in sys_audit/ │ │ │ ├── README.md │ │ │ └── updateFromAudit.js │ │ ├── Updateset checker/ │ │ │ ├── README.md │ │ │ └── Updateset_checker.js │ │ ├── cleanupOrphanedWorkflowContexts/ │ │ │ ├── README.md │ │ │ └── cleanupOrphanedWorkflowContexts.js │ │ ├── deleteMultiple/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── get Groups without Member/ │ │ │ ├── README.md │ │ │ └── getGroupsWithoutMember.js │ │ ├── update variable role/ │ │ │ ├── README.md │ │ │ └── updateWriteRolesOfVariables.js │ │ └── updateMultipleRecords/ │ │ ├── README.md │ │ ├── readme.md │ │ ├── update_multiple_records.js │ │ └── update_multiple_records_v2.js │ ├── Flow Actions/ │ │ ├── Add signature and update fields to a fillable PDF document/ │ │ │ ├── README.md │ │ │ └── pdf_signature.js │ │ ├── Adhoc Assessment Generator Flow Action/ │ │ │ ├── Assessment.js │ │ │ └── README.md │ │ ├── Assign Role/ │ │ │ ├── README.md │ │ │ └── assignRole.js │ │ ├── Calculate Ticket Age/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Check MID Server Availability/ │ │ │ ├── README.md │ │ │ └── check_mid_server_availablility.js │ │ ├── Create Student Weekday Pickup Schedule/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Data Stream/ │ │ │ ├── README.md │ │ │ └── pagination-setup.js │ │ ├── Extract JSON Key without Flow Transformation/ │ │ │ ├── README.md │ │ │ └── remove-html-tags.js │ │ ├── Generate unique value based on sequence/ │ │ │ ├── README.md │ │ │ └── next-unique-sequence.js │ │ ├── Get Days difference/ │ │ │ ├── README.md │ │ │ └── getDaysDifference.js │ │ ├── Get KB Article Permalink/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Get Property/ │ │ │ ├── README.md │ │ │ └── getPropertyFlowAction.js │ │ ├── Get choice field value (mitigating known error)/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── GetIPRange/ │ │ │ ├── README.md │ │ │ └── getIPRange.js │ │ ├── Group Similar Assessments Using Flow Designer Action Native UI/ │ │ │ ├── FlowAction.js │ │ │ └── readme.md │ │ ├── How to refer MID server cluster in integration flow step/ │ │ │ ├── MID Server Cluster.js │ │ │ └── README.md │ │ ├── Look Up MRVS Rows/ │ │ │ ├── LookUpMrvsRowIndex.js │ │ │ └── README.md │ │ ├── Look up Support group of CI/ │ │ │ ├── LookUpSupportGroupFromCI.js │ │ │ └── README.md │ │ ├── Milliseconds to Duration/ │ │ │ ├── README.md │ │ │ └── millisecondsToDuration.js │ │ ├── Remove HTML Tags from a String in a Flow/ │ │ │ ├── README.md │ │ │ └── removeHtmlTags.js │ │ ├── Runscript activities/ │ │ │ ├── CatalogvariablesSummary.js │ │ │ └── README.md │ │ ├── Scheduled data import trigger/ │ │ │ ├── README.md │ │ │ └── Scheduled data import trigger.js │ │ ├── ShuffleArrayMatches/ │ │ │ ├── README.md │ │ │ └── Shuffle Array Matches.js │ │ ├── Trigger event action/ │ │ │ ├── README.md │ │ │ └── trigger event flow action.js │ │ ├── Validate MID servers status inside cluster/ │ │ │ ├── MID Server Availibility inside MID Server Cluster.js │ │ │ └── README.md │ │ └── get Catalog Variables as JSON/ │ │ ├── README.md │ │ └── getCatVarsAsJson.js │ ├── Formula Builder/ │ │ ├── Formula builder overview/ │ │ │ └── README.md │ │ └── Get Age From Birthdate/ │ │ ├── README.md │ │ └── script.js │ ├── ITOM/ │ │ ├── Bulk Location Update/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Discovery/ │ │ │ ├── Pre README.md │ │ │ ├── PrePost Pattern designer.js │ │ │ ├── README.md │ │ │ └── Script to trigger quick discovery from workflow or flow.js │ │ ├── Generate Discovery Schedule/ │ │ │ ├── README.md │ │ │ └── script.js │ │ └── Track Discovery Status/ │ │ ├── CheckDiscoveryStatus.js │ │ ├── ReadME.md │ │ ├── discovery_ip_status.js │ │ └── readme.md │ ├── Notifications/ │ │ ├── Add KB Article Link Dynamic Email Script to Notification/ │ │ │ ├── readme.md │ │ │ └── script.js │ │ ├── Conditional Trigger/ │ │ │ ├── Notification_AdvancedCondition.js │ │ │ └── README.md │ │ ├── Modern Email Layout Designs/ │ │ │ ├── Readme.md │ │ │ └── script.html │ │ └── Notify Users on Specific Date/ │ │ ├── NotifyUsers.js │ │ └── README.md │ ├── On-Call Calendar/ │ │ └── Show group on-call schedule/ │ │ ├── README.md │ │ └── show_group_on_call_schedule.js │ ├── Performance Analytics/ │ │ └── Configure Indicators in Batch/ │ │ ├── ConfigurePAIndicators.js │ │ └── README.md │ ├── Predictive Intelligence/ │ │ └── Training Data Preparer/ │ │ ├── CMDB Configuration Items/ │ │ │ ├── README.md │ │ │ └── analyze_cmdb_data_training_quality.js │ │ ├── Change Table/ │ │ │ ├── README.md │ │ │ └── analyze_change_data_training_quality.js │ │ ├── Incident Table/ │ │ │ ├── README.md │ │ │ └── analyze_incident_data_training_quality.js │ │ └── Problem Table/ │ │ ├── README.md │ │ └── analyze_problem_data_training_quality.js │ ├── Project Management/ │ │ └── Create Explain Project EAC (Estimate At Completion) Value/ │ │ ├── Create Explain Project EAC (Estimate At Completion) Value │ │ └── README.md │ ├── Record Producer/ │ │ ├── Create Records By Import Set/ │ │ │ ├── README.md │ │ │ └── createRecordsByImportSet.js │ │ └── Update Incident Record from Record Producer/ │ │ ├── README.md │ │ └── code_snippet.js │ ├── Regular Expressions/ │ │ ├── Adhaar validation/ │ │ │ ├── Adhaar Validation Script.js │ │ │ └── README.md │ │ ├── Allow Characters + - ) ( for Phone numbers/ │ │ │ ├── README.md │ │ │ └── allowsCharsForPhnNumber.js │ │ ├── AllowAnyLanguage/ │ │ │ ├── README.md │ │ │ └── allowanylanguage.js │ │ ├── Check for special characters/ │ │ │ ├── readme.md │ │ │ └── specialcharacter.js │ │ ├── Check if number has 10 digits/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Consecutive duplicate words/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Credit Card Number Validator/ │ │ │ ├── README.md │ │ │ └── validateCreditCard.js │ │ ├── Email Address Validation/ │ │ │ ├── README.md │ │ │ └── isEmail.js │ │ ├── Encode spaces for URLs/ │ │ │ ├── README.md │ │ │ └── convert.js │ │ ├── Ethiopia country code/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Extracting Product Codes and Prices from a String/ │ │ │ ├── Extracting Product Codes and Prices from a String.js │ │ │ └── README.md │ │ ├── Find Emoji/ │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── Format mobile into Australian mobile format/ │ │ │ ├── Format phone in Australian mobile format.js │ │ │ └── README.md │ │ ├── Hashtag & Mention Extractor/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Hexadecimal color/ │ │ │ ├── README.md │ │ │ └── validateHexColor.js │ │ ├── IP Address Validation/ │ │ │ ├── README.md │ │ │ ├── getIP4OrIPV6address.js │ │ │ └── validateIPInput.js │ │ ├── ISBN Validator/ │ │ │ ├── README.md │ │ │ └── validateISBN.js │ │ ├── Img Tag Regex validator/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Indian Mobile Numbers/ │ │ │ ├── README.md │ │ │ └── code.js │ │ ├── Match URL's from ServiceNow domain/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Negative RegExp for Condition Builder/ │ │ │ ├── NegativeRegExExample.js │ │ │ └── README.md │ │ ├── PAN Card Validation Script/ │ │ │ ├── README.md │ │ │ └── Script.js │ │ ├── Password Strength Checker/ │ │ │ ├── README.md │ │ │ └── passwordStrength.js │ │ ├── Poland country code/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── Positive int with 2 decimals/ │ │ │ ├── Allow positive and decimal values.js │ │ │ └── readme.md │ │ ├── Regular-expression-for-alphanumeric-characters/ │ │ │ ├── AlphanumericCharacters.js │ │ │ └── README.md │ │ ├── Remove Extra Spaces/ │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── Remove all HTML Tags/ │ │ │ ├── README.md │ │ │ └── Remove all html tags.js │ │ ├── Remove newline and carriage return/ │ │ │ ├── README.md │ │ │ └── remove cr and nl.js │ │ ├── SSN Formatting/ │ │ │ ├── Index.js │ │ │ └── README.md │ │ ├── UK Country Code/ │ │ │ ├── README.md │ │ │ └── script.js │ │ ├── URL Validation/ │ │ │ ├── README.md │ │ │ └── validateURL.js │ │ ├── Username validation/ │ │ │ ├── README.md │ │ │ └── Script.js │ │ └── Validate IPv6 Address/ │ │ ├── README.md │ │ └── script.js │ ├── Resource Management/ │ │ └── Resource Capacity And Availability Viewer (Daily Basis)/ │ │ ├── README.md │ │ └── ResourceCapacityAndAvailabilityViewerDailyBasis.js │ └── Styles/ │ ├── Add Background Color to a field/ │ │ ├── README.md │ │ └── style.css │ ├── Add attachment icon-list view/ │ │ ├── README.md │ │ ├── style.css │ │ └── value.js │ ├── Change text color of a field/ │ │ ├── README.md │ │ └── style.css │ ├── Hide MRVS Buttons/ │ │ ├── README.md │ │ └── styles.html │ ├── ServiceNow Custom Style/ │ │ ├── README.md │ │ └── style.css │ └── Zoom Catalog Image on Hover/ │ ├── CSS.js │ └── README.md ├── _config.yml ├── assets/ │ ├── css/ │ │ └── custom.css │ ├── footer.js │ └── js/ │ └── site.js ├── count-files.js ├── index.html ├── pages/ │ ├── client-side-components.html │ ├── core-apis.html │ ├── integration.html │ ├── modern-development.html │ ├── server-side-components.html │ └── specialized-areas.html └── sitemap.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/pull_request_template.md ================================================ # PR Description: replace this with your description # Pull Request Checklist ## Overview - [x] Put an x inside of the square brackets to check each item. - [ ] I have read and understood the [CONTRIBUTING.md](CONTRIBUTING.md) guidelines - [ ] My pull request has a descriptive title that accurately reflects the changes and the description has been filled in above. - [ ] I've included only files relevant to the changes described in the PR title and description - [ ] I've created a new branch in my forked repository for this contribution ## Code Quality - [ ] My code is relevant to ServiceNow developers - [ ] My code snippets expand meaningfully on official ServiceNow documentation (if applicable) - [ ] I've disclosed use of ES2021 features (if applicable) - [ ] I've tested my code snippets in a ServiceNow environment (where possible) ## Repository Structure Compliance - [ ] I've placed my code snippet(s) in one of the required top-level categories: - `Core ServiceNow APIs/` - `Server-Side Components/` - `Client-Side Components/` - `Modern Development/` - `Integration/` - `Specialized Areas/` - [ ] I've used appropriate sub-categories within the top-level categories - [ ] Each code snippet has its own folder with a descriptive name ## Documentation - [ ] I've included a README.md file for each code snippet - [ ] The README.md includes: - Description of the code snippet functionality - Usage instructions or examples - Any prerequisites or dependencies - (Optional) Screenshots or diagrams if helpful ## Restrictions - [ ] My PR does not include XML exports of ServiceNow records - [ ] My PR does not contain sensitive information (passwords, API keys, tokens) - [ ] My PR does not include changes that fall outside the described scope ================================================ FILE: .github/scripts/validate-structure.js ================================================ #!/usr/bin/env node const { execSync } = require('child_process'); const allowedCategories = new Set([ 'Core ServiceNow APIs', 'Server-Side Components', 'Client-Side Components', 'Modern Development', 'Integration', 'Specialized Areas' ]); function resolveDiffRange() { if (process.argv[2]) { return process.argv[2]; } const inCI = process.env.GITHUB_ACTIONS === 'true'; if (!inCI) { return 'origin/main...HEAD'; } const base = process.env.GITHUB_BASE_REF ? `origin/${process.env.GITHUB_BASE_REF}` : 'origin/main'; const head = process.env.GITHUB_SHA || 'HEAD'; return `${base}...${head}`; } function getChangedFiles(diffRange) { let output; try { output = execSync(`git diff --name-only --diff-filter=ACMR ${diffRange}`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); } catch (error) { console.error('Failed to collect changed files. Ensure the base branch is fetched.'); console.error(error.stderr?.toString() || error.message); process.exit(1); } return output .split('\n') .map((line) => line.trim()) .filter(Boolean); } function validateFilePath(filePath) { const normalized = filePath.replace(/\\/g, '/'); const segments = normalized.split('/'); // Check for invalid characters that break local file systems for (let i = 0; i < segments.length; i++) { const segment = segments[i]; // Check for trailing periods (invalid on Windows) if (segment.endsWith('.')) { return `Invalid folder/file name '${segment}' in path '${normalized}': Names cannot end with a period (.) as this breaks local file system sync on Windows.`; } // Check for trailing spaces (invalid on Windows) if (segment.endsWith(' ')) { return `Invalid folder/file name '${segment}' in path '${normalized}': Names cannot end with a space as this breaks local file system sync on Windows.`; } // Check for reserved Windows names const reservedNames = ['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9']; const nameWithoutExt = segment.split('.')[0].toUpperCase(); if (reservedNames.includes(nameWithoutExt)) { return `Invalid folder/file name '${segment}' in path '${normalized}': '${nameWithoutExt}' is a reserved name on Windows and will break local file system sync.`; } // Check for invalid characters (Windows and general file system restrictions) const invalidChars = /[<>:"|?*\x00-\x1F]/; if (invalidChars.test(segment)) { return `Invalid folder/file name '${segment}' in path '${normalized}': Contains characters that are invalid on Windows file systems (< > : " | ? * or control characters).`; } } if (!allowedCategories.has(segments[0])) { return null; } // Files must live under: Category/Subcategory/SpecificUseCase/ if (segments.length < 4) { return `Move '${normalized}' under a valid folder hierarchy (Category/Subcategory/Use-Case/your-file). Files directly inside '${segments[0]}' or its subcategories are not allowed.`; } return null; } function main() { const diffRange = resolveDiffRange(); const changedFiles = getChangedFiles(diffRange); if (changedFiles.length === 0) { console.log('No relevant file changes detected.'); return; } const problems = []; for (const filePath of changedFiles) { const issue = validateFilePath(filePath); if (issue) { problems.push(issue); } } if (problems.length > 0) { console.error('Folder structure violations found:'); for (const msg of problems) { console.error(` - ${msg}`); } process.exit(1); } console.log('Folder structure looks good.'); } main(); ================================================ FILE: .github/workflows/hacktrack.yml ================================================ #This file is for ServiceNow Dev Program Hacktoberfest Tracking and can be ignored or deleted. name: Record Hacktrack Event on: push: branches: main fork: branches: main issues: types: [opened, closed] branches: main pull_request_target: types: [opened, closed] branches: main jobs: deployment: if: github.repository == 'ServiceNowDevProgram/code-snippets' runs-on: ubuntu-latest steps: # - name: Log payload # env: # GITHUB_CONTEXT: ${{ toJson(github) }} # run: | # echo "$GITHUB_CONTEXT" - name: Contact DPR id: myRequest uses: fjogeleit/http-request-action@v1.8.1 with: url: ${{ format('https://{0}.service-now.com/api/x_snc_hacktrack/hacktrack', secrets.HT_INSTANCE_NAME) }} method: 'POST' contentType: application/json data: ${{ toJson(github) }} username: ${{ secrets.DPRD_USERNAME }} password: ${{ secrets.DPRD_PASSWORD }} - name: Show Response run: echo ${{ steps.myRequest.outputs.response }} ================================================ FILE: .github/workflows/pages.yml ================================================ # GitHub Pages deployment workflow name: Deploy GitHub Pages on: workflow_dispatch: # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read pages: write id-token: write # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: group: pages-${{ github.ref }} cancel-in-progress: true jobs: # Build and optimize job build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '18' cache: 'npm' - name: Install build dependencies run: | npm init -y npm install --save-dev html-minifier-terser clean-css-cli terser html-validate - name: Validate HTML files run: | echo "Validating HTML files..." npx html-validate *.html pages/*.html || echo "HTML validation completed with warnings" - name: Optimize assets run: | echo "Optimizing HTML files..." # Create backup directory mkdir -p .backup # Minify HTML files (preserve original structure) find . -name "*.html" -not -path "./node_modules/*" -not -path "./.backup/*" | while read file; do echo "Minifying: $file" npx html-minifier-terser \ --collapse-whitespace \ --remove-comments \ --remove-optional-tags \ --remove-redundant-attributes \ --remove-script-type-attributes \ --remove-style-link-type-attributes \ --minify-css \ --minify-js \ "$file" -o "$file.tmp" && mv "$file.tmp" "$file" done # Minify CSS files if any exist if find . -name "*.css" -not -path "./node_modules/*" -not -path "./.backup/*" | grep -q .; then echo "Optimizing CSS files..." find . -name "*.css" -not -path "./node_modules/*" -not -path "./.backup/*" | while read file; do echo "Minifying: $file" npx cleancss "$file" -o "$file" done fi # Remove build dependencies from final artifact rm -rf node_modules package*.json - name: Setup Pages id: pages uses: actions/configure-pages@v4 - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: '.' # Deployment job deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ================================================ FILE: .github/workflows/pr-auto-unassign-stale.yml ================================================ name: Auto-unassign stale PR assignees on: schedule: - cron: "*/15 * * * *" # run every 15 minutes workflow_dispatch: inputs: enabled: description: "Enable this automation" type: boolean default: true max_age_minutes: description: "Unassign if assigned longer than X minutes" type: number default: 60 dry_run: description: "Preview only; do not change assignees" type: boolean default: false permissions: pull-requests: write issues: write env: # Defaults (can be overridden via workflow_dispatch inputs) ENABLED: "true" MAX_ASSIGN_AGE_MINUTES: "60" DRY_RUN: "false" jobs: sweep: runs-on: ubuntu-latest steps: - name: Resolve inputs into env run: | # Prefer manual run inputs when present if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then echo "ENABLED=${{ inputs.enabled }}" >> $GITHUB_ENV echo "MAX_ASSIGN_AGE_MINUTES=${{ inputs.max_age_minutes }}" >> $GITHUB_ENV echo "DRY_RUN=${{ inputs.dry_run }}" >> $GITHUB_ENV fi echo "Effective config: ENABLED=$ENABLED, MAX_ASSIGN_AGE_MINUTES=$MAX_ASSIGN_AGE_MINUTES, DRY_RUN=$DRY_RUN" - name: Exit if disabled if: ${{ env.ENABLED != 'true' && env.ENABLED != 'True' && env.ENABLED != 'TRUE' }} run: echo "Disabled via ENABLED=$ENABLED. Exiting." && exit 0 - name: Unassign stale assignees uses: actions/github-script@v7 with: script: | const owner = context.repo.owner; const repo = context.repo.repo; const MAX_MIN = parseInt(process.env.MAX_ASSIGN_AGE_MINUTES || "60", 10); const DRY_RUN = ["true","True","TRUE","1","yes"].includes(String(process.env.DRY_RUN)); const now = new Date(); core.info(`Scanning open PRs. Threshold = ${MAX_MIN} minutes. DRY_RUN=${DRY_RUN}`); // List all open PRs const prs = await github.paginate(github.rest.pulls.list, { owner, repo, state: "open", per_page: 100 }); let totalUnassigned = 0; for (const pr of prs) { if (!pr.assignees || pr.assignees.length === 0) continue; const number = pr.number; core.info(`PR #${number}: "${pr.title}" — assignees: ${pr.assignees.map(a => a.login).join(", ")}`); // Pull reviews (to see if an assignee started a review) const reviews = await github.paginate(github.rest.pulls.listReviews, { owner, repo, pull_number: number, per_page: 100 }); // Issue comments (general comments) const issueComments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number: number, per_page: 100 }); // Review comments (file-level) const reviewComments = await github.paginate(github.rest.pulls.listReviewComments, { owner, repo, pull_number: number, per_page: 100 }); // Issue events (to find assignment timestamps) const issueEvents = await github.paginate(github.rest.issues.listEvents, { owner, repo, issue_number: number, per_page: 100 }); for (const a of pr.assignees) { const assignee = a.login; // Find the most recent "assigned" event for this assignee const assignedEvents = issueEvents .filter(e => e.event === "assigned" && e.assignee && e.assignee.login === assignee) .sort((x, y) => new Date(y.created_at) - new Date(x.created_at)); if (assignedEvents.length === 0) { core.info(` - @${assignee}: no 'assigned' event found; skipping.`); continue; } const assignedAt = new Date(assignedEvents[0].created_at); const ageMin = (now - assignedAt) / 60000; // Has the assignee commented (issue or review comments) or reviewed? const hasIssueComment = issueComments.some(c => c.user?.login === assignee); const hasReviewComment = reviewComments.some(c => c.user?.login === assignee); const hasReview = reviews.some(r => r.user?.login === assignee); const eligible = ageMin >= MAX_MIN && !hasIssueComment && !hasReviewComment && !hasReview && pr.state === "open"; core.info(` - @${assignee}: assigned ${ageMin.toFixed(1)} min ago; commented=${hasIssueComment || hasReviewComment}; reviewed=${hasReview}; open=${pr.state==='open'} => ${eligible ? 'ELIGIBLE' : 'skip'}`); if (!eligible) continue; if (DRY_RUN) { core.notice(`Would unassign @${assignee} from PR #${number}`); } else { try { await github.rest.issues.removeAssignees({ owner, repo, issue_number: number, assignees: [assignee] }); totalUnassigned += 1; // Optional: leave a gentle heads-up comment await github.rest.issues.createComment({ owner, repo, issue_number: number, body: `👋 Unassigning @${assignee} due to inactivity (> ${MAX_MIN} min without comments/reviews). This PR remains open for other reviewers.` }); core.info(` Unassigned @${assignee} from #${number}`); } catch (err) { core.warning(` Failed to unassign @${assignee} from #${number}: ${err.message}`); } } } } core.summary .addHeading('Auto-unassign report') .addRaw(`Threshold: ${MAX_MIN} minutes\n\n`) .addRaw(`Total unassignments: ${totalUnassigned}\n`) .write(); result-encoding: string ================================================ FILE: .github/workflows/validate-structure.yml ================================================ name: Validate Folder Structure on: pull_request_target: branches: - main permissions: contents: read pull-requests: write concurrency: group: folder-structure-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true jobs: structure: runs-on: ubuntu-latest steps: - name: Checkout base repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Cache validation script run: cp .github/scripts/validate-structure.js "$RUNNER_TEMP/validate-structure.js" - name: Fetch pull request head id: fetch_head env: PR_REMOTE_URL: https://github.com/${{ github.event.pull_request.head.repo.full_name }}.git PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} run: | git remote remove pr >/dev/null 2>&1 || true git remote add pr "$PR_REMOTE_URL" if git fetch pr "$PR_HEAD_REF":pr-head --no-tags; then git checkout pr-head git fetch origin "${{ github.event.pull_request.base.ref }}" echo "fetched=true" >> "$GITHUB_OUTPUT" else echo "::warning::Unable to fetch fork repository. Skipping structure validation." echo "fetched=false" >> "$GITHUB_OUTPUT" fi - name: Use Node.js 18 uses: actions/setup-node@v4 with: node-version: 18 - name: Validate folder layout if: ${{ steps.fetch_head.outputs.fetched == 'true' }} id: validate run: | set -euo pipefail tmp_output=$(mktemp) tmp_error=$(mktemp) set +e node "$RUNNER_TEMP/validate-structure.js" origin/${{ github.event.pull_request.base.ref }}...HEAD >"$tmp_output" 2>"$tmp_error" status=$? set -e cat "$tmp_output" cat "$tmp_error" >&2 if grep -q 'Folder structure violations found' "$tmp_output" "$tmp_error"; then # Save validation output for use in PR comment cat "$tmp_output" "$tmp_error" > "$RUNNER_TEMP/validation_output.txt" echo "status=failed" >> "$GITHUB_OUTPUT" exit 0 fi if [ $status -ne 0 ]; then echo "::warning::Structure validation skipped because the diff could not be evaluated (exit code $status)." echo "status=skipped" >> "$GITHUB_OUTPUT" exit 0 fi echo "status=passed" >> "$GITHUB_OUTPUT" - name: Close pull request on failure if: ${{ steps.validate.outputs.status == 'failed' }} uses: actions/github-script@v6 with: github-token: ${{ github.token }} script: | const pullNumber = context.payload.pull_request.number; const owner = context.repo.owner; const repo = context.repo.repo; const fs = require('fs'); const output = fs.readFileSync(process.env.RUNNER_TEMP + '/validation_output.txt', 'utf8'); let commentBody = `Thank you for your contribution. However, it doesn't comply with our contributing guidelines.\n\n`; // Check if the error is about invalid file/folder names if (output.includes('Names cannot end with a period') || output.includes('Names cannot end with a space') || output.includes('is a reserved name on Windows') || output.includes('Contains characters that are invalid')) { commentBody += `**❌ Invalid File/Folder Names Detected**\n\n`; commentBody += `Your contribution contains file or folder names that will break when syncing to local file systems (especially Windows):\n\n`; commentBody += `\`\`\`\n${output}\n\`\`\`\n\n`; commentBody += `**Common issues:**\n`; commentBody += `- Folder/file names ending with a period (.) - not allowed on Windows\n`; commentBody += `- Folder/file names ending with spaces - not allowed on Windows\n`; commentBody += `- Reserved names like CON, PRN, AUX, NUL, COM1-9, LPT1-9 - not allowed on Windows\n`; commentBody += `- Invalid characters: < > : " | ? * or control characters\n\n`; commentBody += `Please rename these files/folders to be compatible with all operating systems.\n\n`; } else { commentBody += `As a reminder, the general requirements (as outlined in the [CONTRIBUTING.md file](https://github.com/ServiceNowDevProgram/code-snippets/blob/main/CONTRIBUTING.md)) are the following: follow the folder+subfolder guidelines and include a README.md file explaining what the code snippet does.\n\n`; commentBody += `**Validation errors:**\n\`\`\`\n${output}\n\`\`\`\n\n`; } commentBody += `Review your contribution against the guidelines and make the necessary adjustments. Closing this for now. Once you make additional changes, feel free to re-open this Pull Request or create a new one.`; await github.rest.issues.createComment({ owner, repo, issue_number: pullNumber, body: commentBody.trim() }); await github.rest.pulls.update({ owner, repo, pull_number: pullNumber, state: 'closed' }); - name: Mark job as failed if validation failed if: ${{ steps.validate.outputs.status == 'failed' }} run: exit 1 ================================================ FILE: .gitignore ================================================ .DS_Store # Claude Code settings .claude/ settings.local.json ================================================ FILE: AGENTS.md ================================================ # AGENTS.md This file provides guidance to AI Coding Agents when working with code in this repository. ## Repository Overview This is the **ServiceNow Developer Program's Code Snippets Repository** - a community-driven collection of ServiceNow development code examples and utilities. The repository contains 900+ code snippets organized into 50+ categories covering all aspects of ServiceNow platform development. ## Repository Structure and Organization ### Directory Structure All code snippets follow a standardized four-level structure: ``` Top-Level Category/ ├── Sub-Category/ │ ├── Specific-Use-Case/ │ │ ├── README.md # Description and usage instructions │ │ ├── script.js # Main code implementation │ │ └── variant.js # Optional code variations ``` ### Top-Level Categories (REQUIRED Structure) The repository is organized into **6 major categories**. All contributions MUST use these categories: - **Core ServiceNow APIs/**: Essential ServiceNow JavaScript APIs - `GlideRecord/`, `GlideAjax/`, `GlideSystem/`, `GlideDate/`, `GlideDateTime/`, `GlideElement/`, `GlideFilter/`, `GlideAggregate/`, `GlideHTTPRequest/`, `GlideModal/`, `GlideQuery/`, `GlideTableDescriptor/` - **Server-Side Components/**: Server-executed code - `Background Scripts/`, `Business Rules/`, `Script Includes/`, `Script Actions/`, `Scheduled Jobs/`, `Transform Map Scripts/`, `Server Side/`, `Inbound Actions/`, `Processors/` - **Client-Side Components/**: Browser-executed code - `Client Scripts/`, `Catalog Client Script/`, `UI Actions/`, `UI Scripts/`, `UI Pages/`, `UI Macros/`, `UX Client Scripts/`, `UX Client Script Include/`, `UX Data Broker Transform/` - **Modern Development/**: Modern ServiceNow frameworks - `Service Portal/`, `Service Portal Widgets/`, `NOW Experience/`, `GraphQL/`, `ECMASCript 2021/` - **Integration/**: External systems and data exchange - `Integration/` (original), `RESTMessageV2/`, `Import Set API/`, `Scripted REST Api/`, `Mail Scripts/`, `MIDServer/`, `Attachments/` - **Specialized Areas/**: Domain-specific functionality - `CMDB/`, `ITOM/`, `Performance Analytics/`, `ATF Steps/`, `Agile Development/`, `Advanced Conditions/`, `Browser Bookmarklets/`, `Browser Utilities/`, `Dynamic Filters/`, `Fix scripts/`, `Flow Actions/`, `Formula Builder/`, `Notifications/`, `On-Call Calendar/`, `Record Producer/`, `Regular Expressions/`, `Styles/` ## Development Guidelines ### Code Quality Standards - Each snippet must include comprehensive README.md documentation - Code should be relevant to ServiceNow developers - ES2021 features are allowed but should be clearly documented - Examples should expand meaningfully on official ServiceNow documentation - Quality over quantity - low-effort submissions are rejected ### Contribution Requirements - **Mandatory Category Structure**: All contributions MUST use the 6 top-level categories. PRs with incorrect structure will be rejected. - **Descriptive Structure**: Changes must match pull request scope exactly - **No XML Exports**: Avoid ServiceNow record exports in favor of JavaScript code - **Documentation**: Every code snippet requires accompanying README.md - **Proper Categorization**: Place snippets in appropriate existing sub-categories within the required top-level structure ### File Organization - Use descriptive folder names that clearly indicate functionality - Group related code variations in the same folder - Include screenshots or examples when helpful for understanding - Maintain consistent naming conventions across similar snippets ## Common ServiceNow Development Patterns ### GlideRecord Usage - Most snippets demonstrate proper GlideRecord query patterns - Security considerations with ACL enforcement examples - Performance optimization through proper query construction - Reference field handling and relationship traversal ### Integration Patterns - REST API consumption and production examples - Authentication handling for external systems - Data transformation and mapping utilities - Error handling and logging best practices ### Business Logic Implementation - Business Rules for event-driven processing - Background Scripts for administrative tasks - Client Scripts for form behavior and validation - UI Actions for custom user interactions ## Testing and Validation ### Code Verification - Test all code snippets in development environments before submission - Validate against multiple ServiceNow versions when possible - Consider security implications and access controls - Document any prerequisites or dependencies ### Community Review Process - All contributions undergo peer review by Developer Advocates - Changes outside described scope result in rejection - Multiple submissions require separate branches in forked repositories ## Security Considerations - Never include sensitive information (passwords, API keys, tokens) - Consider security implications of all code examples - Demonstrate proper access control patterns where applicable - Include warnings for potentially dangerous operations ## Repository Maintenance ### Branch Management - Main branch contains all approved code snippets - Use descriptive branch names for contributions - Create separate branches for different feature additions ### File Management - No build processes or compilation required - Direct GitHub editing supported - Git-enabled IDEs like VS Code recommended for larger contributions - Standard .gitignore excludes only .DS_Store files This repository serves as a comprehensive reference for ServiceNow developers at all skill levels, emphasizing practical, tested solutions for real-world development scenarios. ================================================ FILE: CONTRIBUTING.md ================================================ # How to Contribute We welcome contributions to the **ServiceNow Developer Program's Code Snippets Repository**! Follow these steps to get involved: ## Steps to Contribute 1. **Fork the Repository**: Click the "Fork" button on the top right of this page to create your own copy of the repository. 2. **Create a New Branch**: - Name your branch according to the functionality you are adding (e.g., `feature/new-snippet` or `bugfix/fix-issue`). - Switch to your new branch from the main branch dropdown. 3. **Add or Edit Code Snippets**: - Navigate to the appropriate folders and files to add, edit, or reorganize code snippets. - Commit your changes to your forked repository. 4. **Submit a Pull Request**: - Go to the original repository and click on the "Pull Requests" tab. - Click "New Pull Request" and select your branch. - Ensure your pull request has a descriptive title and comment that outlines what changes you made. - Only include files relevant to the changes described in the pull request title and description. - Avoid submitting XML exports of ServiceNow records. That's it! A Developer Advocate or a designated approver from the ServiceNow Dev Program will review your pull request. If approved, it will be merged into the main repository for everyone's benefit! ### Note on Multiple Submissions If you plan to submit another pull request while your original is still pending, make sure to create a new branch in your forked repository first. ## General Requirements - **Descriptive Pull Request Titles**: Your pull request must have explicit and descriptive titles that accurately represent the changes made. - **Scope Adherence**: Changes that fall outside the described scope will result in the entire pull request being rejected. - **Quality Over Quantity**: Low-effort or spam pull requests will be marked accordingly. - **Expanded Snippets**: Code snippets reused from the [ServiceNow Documentation](https://docs.servicenow.com/) or [API References](https://developer.servicenow.com/dev.do#!/reference/) are acceptable only if they are expanded in a meaningful way (e.g., with additional context, documentation, or variations). Remember: *"QUANTITY IS FUN, QUALITY IS KEY."* - **Relevance**: Code should be relevant to ServiceNow Developers. - **ES2021 Compatibility**: While ES2021 is allowed, we encourage you to disclose if your code is using ES2021 features, as not everyone may be working with ES2021-enabled applications. ## Core Documentation File Changes **IMPORTANT**: For changes to core documentation files (README.md, CONTRIBUTING.md, LICENSE, etc.), contributors must: 1. **Submit an Issue First**: Before making any changes to core documentation files, create an issue describing: - What you intend to edit - Why the change is needed - Your proposed approach 2. **Get Assignment**: Wait to be assigned to the issue by a maintainer before submitting a PR. 3. **Reference the Issue**: Include the issue number in your PR title and description. This process helps prevent merge conflicts when multiple contributors want to update the same documentation files and ensures all changes align with the project's direction. ## Repository Structure **IMPORTANT**: The repository has been reorganized into major categories. All new contributions MUST follow this structure for PR approval. Please follow this directory structure when organizing your code snippets: - **Top-Level Categories**: These are fixed categories that represent major areas of ServiceNow development: - `Core ServiceNow APIs/` - GlideRecord, GlideAjax, GlideSystem, GlideDate, etc. - `Server-Side Components/` - Background Scripts, Business Rules, Script Includes, etc. - `Client-Side Components/` - Client Scripts, Catalog Client Scripts, UI Actions, etc. - `Modern Development/` - Service Portal, NOW Experience, GraphQL, ECMAScript 2021 - `Integration/` - RESTMessageV2, Import Sets, Mail Scripts, MIDServer, etc. - `Specialized Areas/` - CMDB, ITOM, Performance Analytics, ATF Steps, etc. - **Sub-Categories**: Each top-level category contains sub-folders for specific ServiceNow technologies or use cases. - **Snippet Folders**: Each sub-category contains folders for **each code snippet**. - **Snippet Folder Contents**: Within each snippet folder, include: - A `README.md` file that describes the code snippet. - Individual files for each variant of the code snippet. ### New Structure Example ``` Core ServiceNow APIs/ ├── GlideRecord/ │ ├── Query Performance Optimization/ │ │ ├── README.md # Description of the optimization snippet │ │ ├── basic_query.js # Basic query example │ │ └── optimized_query.js # Performance-optimized version │ └── Reference Field Handling/ │ ├── README.md # Description of reference handling │ └── reference_query.js # Reference field query example └── GlideAjax/ ├── Async Data Loading/ │ ├── README.md # Description of async loading │ ├── client_script.js # Client-side implementation │ └── script_include.js # Server-side Script Include Server-Side Components/ ├── Business Rules/ │ ├── Auto Assignment Logic/ │ │ ├── README.md # Description of auto assignment │ │ └── assignment_rule.js # Business rule implementation ``` ### Category Placement Guidelines - **Core ServiceNow APIs**: All Glide* APIs and core ServiceNow JavaScript APIs - **Server-Side Components**: Code that runs on the server (Business Rules, Background Scripts, etc.) - **Client-Side Components**: Code that runs in the browser (Client Scripts, UI Actions, etc.) - **Modern Development**: Modern ServiceNow development approaches and frameworks - **Integration**: External system integrations, data import/export, and communication - **Specialized Areas**: Domain-specific functionality (CMDB, ITOM, Testing, etc.) ## Final Checklist Before submitting your pull request, ensure that: - All code snippet files are in the appropriate folders. - Each folder is correctly placed within its category. - Your code snippet is accompanied by a `readme.md` file that describes it. Thank you for contributing! Your efforts help create a richer resource for the ServiceNow development community. ================================================ FILE: Client-Side Components/Catalog Client Script/Add Label For Attachment/README.md ================================================ Code Snippet to add a label to the attachment for a Catalog Item on the Portal. ================================================ FILE: Client-Side Components/Catalog Client Script/Add Label For Attachment/add_label_for_attachment.js ================================================ function onLoad() { var attachmentElement = top.document.querySelectorAll('[ng-if="c.showAttachments()"]'); if (attachmentElement[0]) { var label = top.document.createElement('label'); label.innerHTML = 'LABEL COMES HERE'; attachmentElement[0].prepend(label); } } ================================================ FILE: Client-Side Components/Catalog Client Script/Add Rows in MRVS/README.md ================================================ With this simple script you can through a client script add rows in the MRVS if you like. Ex. you want to prefill a few rows that you get from a GlideAjax call depending on what the user choosed in another variable ================================================ FILE: Client-Side Components/Catalog Client Script/Add Rows in MRVS/addrows.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading) return; var obj = (g_form.getValue('multi_test').length != 0) ? JSON.parse(g_form.getValue('multi_test')): [];//Get the MRVS //Push in what you want obj.push({var_one: 'test1', var_two: 'test2'}); g_form.setValue('multi_test', JSON.stringify(obj));//And set the value again } ================================================ FILE: Client-Side Components/Catalog Client Script/Auto Save Draft Feature/README.md ================================================ # Auto Save Draft Feature for Catalog Items This snippet provides automatic draft saving functionality for ServiceNow Catalog Items, helping prevent data loss by automatically saving form data at regular intervals. ## Overview The feature includes two implementations: 1. Basic Implementation (`basic_implementation.js`) 2. Advanced Implementation (`advanced_implementation.js`) ## Basic Implementation ### Features - Auto-saves form data every minute - Stores single draft in sessionStorage - Provides draft restoration on form load - Basic error handling and user feedback ### Usage ```javascript // Apply in Catalog Client Script // Select "onLoad" for "Client script runs" // Copy content from basic_implementation.js ``` ## Advanced Implementation ### Enhanced Features - Multiple draft support (keeps last 3 drafts) - Advanced draft management - Draft selection dialog - Detailed metadata tracking - Improved error handling - User-friendly notifications ### Usage ```javascript // Apply in Catalog Client Script // Select "onLoad" for "Client script runs" // Copy content from advanced_implementation.js ``` ## Technical Details ### Dependencies - ServiceNow Platform UI Framework - GlideForm API - GlideModal (advanced implementation only) ### Browser Support - Modern browsers with sessionStorage support - ES5+ compatible ### Security Considerations - Uses browser's sessionStorage (cleared on session end) - No sensitive data transmission - Instance-specific storage ## Implementation Guide 1. Create a new Catalog Client Script: - Table: Catalog Client Script [catalog_script_client] - Type: onLoad - Active: true 2. Choose implementation: - For basic needs: Copy `basic_implementation.js` - For advanced features: Copy `advanced_implementation.js` 3. Apply to desired Catalog Items: - Select applicable Catalog Items - Test in dev environment first ## Best Practices 1. Testing: - Test with various form states - Verify draft restoration - Check browser storage limits 2. Performance: - Default 60-second interval is recommended - Adjust based on form complexity - Monitor browser memory usage 3. User Experience: - Clear feedback messages - Confirmation dialogs - Error notifications ## Limitations - Browser session dependent - Storage size limits - Form field compatibility varies ## Troubleshooting Common issues and solutions: 1. Draft not saving - Check browser console for errors - Verify sessionStorage availability - Check form modification detection 2. Restoration fails - Validate stored data format - Check browser storage permissions - Verify form field compatibility ## Version Information - Compatible with ServiceNow: Rome and later - Browser Requirements: Modern browsers with ES5+ support - Last Updated: October 2025 ================================================ FILE: Client-Side Components/Catalog Client Script/Auto Save Draft Feature/advanced_implementation.js ================================================ /** * Advanced Auto-save Draft Implementation with Enhanced Features * This version adds multi-draft support and advanced error handling */ function onLoad() { var autosaveInterval = 60000; // 1 minute var maxDrafts = 3; // Maximum number of drafts to keep // Initialize draft manager initializeDraftManager(); // Try to restore previous draft restoreLastDraft(); // Set up auto-save interval setInterval(function() { if (g_form.isModified()) { saveAdvancedDraft(); } }, autosaveInterval); } function initializeDraftManager() { window.draftManager = { maxDrafts: 3, draftPrefix: 'catalogDraft_' + g_form.getUniqueValue() + '_', getAllDrafts: function() { var drafts = []; for (var i = 0; i < sessionStorage.length; i++) { var key = sessionStorage.key(i); if (key.startsWith(this.draftPrefix)) { drafts.push({ key: key, data: JSON.parse(sessionStorage.getItem(key)) }); } } return drafts.sort((a, b) => b.data.timestamp - a.data.timestamp); }, cleanup: function() { var drafts = this.getAllDrafts(); if (drafts.length > this.maxDrafts) { drafts.slice(this.maxDrafts).forEach(function(draft) { sessionStorage.removeItem(draft.key); }); } } }; } function saveAdvancedDraft() { try { var draftData = {}; g_form.serialize(draftData); // Add metadata var draftKey = window.draftManager.draftPrefix + new Date().getTime(); var draftInfo = { timestamp: new Date().getTime(), data: draftData, user: g_user.userName, catalog_item: g_form.getTableName(), fields_modified: g_form.getModifiedFields() }; sessionStorage.setItem(draftKey, JSON.stringify(draftInfo)); window.draftManager.cleanup(); // Show success message with draft count var remainingDrafts = window.draftManager.getAllDrafts().length; g_form.addInfoMessage('Draft saved. You have ' + remainingDrafts + ' saved draft(s).'); } catch (e) { console.error('Error saving draft: ' + e); g_form.addErrorMessage('Failed to save draft: ' + e.message); } } function restoreLastDraft() { try { var drafts = window.draftManager.getAllDrafts(); if (drafts.length > 0) { // If multiple drafts exist, show selection dialog if (drafts.length > 1) { showDraftSelectionDialog(drafts); } else { promptToRestoreDraft(drafts[0].data); } } } catch (e) { console.error('Error restoring draft: ' + e); g_form.addErrorMessage('Failed to restore draft: ' + e.message); } } function showDraftSelectionDialog(drafts) { var dialog = new GlideModal('select_draft_dialog'); dialog.setTitle('Available Drafts'); var html = '
'; drafts.forEach(function(draft, index) { var date = new Date(draft.data.timestamp).toLocaleString(); html += '
'; html += 'Draft ' + (index + 1) + ' - ' + date; html += '
Modified fields: ' + draft.data.fields_modified.join(', '); html += '
'; }); html += '
'; dialog.renderWithContent(html); } function promptToRestoreDraft(draftInfo) { var timestamp = new Date(draftInfo.timestamp); if (confirm('A draft from ' + timestamp.toLocaleString() + ' was found. Would you like to restore it?')) { Object.keys(draftInfo.data).forEach(function(field) { g_form.setValue(field, draftInfo.data[field]); }); g_form.addInfoMessage('Draft restored from ' + timestamp.toLocaleString()); } } ================================================ FILE: Client-Side Components/Catalog Client Script/Auto Save Draft Feature/basic_implementation.js ================================================ /** * Basic Auto-save Draft Implementation * This version provides core functionality for auto-saving catalog item form data */ function onLoad() { var autosaveInterval = 60000; // 1 minute // Try to restore previous draft restoreLastDraft(); // Set up auto-save interval setInterval(function() { if (g_form.isModified()) { saveDraft(); } }, autosaveInterval); } function saveDraft() { try { var draftData = {}; g_form.serialize(draftData); var draftKey = 'catalogDraft_' + g_form.getUniqueValue(); sessionStorage.setItem(draftKey, JSON.stringify({ timestamp: new Date().getTime(), data: draftData })); g_form.addInfoMessage('Draft saved automatically'); } catch (e) { console.error('Error saving draft: ' + e); } } function restoreLastDraft() { try { var draftKey = 'catalogDraft_' + g_form.getUniqueValue(); var savedDraft = sessionStorage.getItem(draftKey); if (savedDraft) { var draftData = JSON.parse(savedDraft); var timestamp = new Date(draftData.timestamp); if (confirm('A draft from ' + timestamp.toLocaleString() + ' was found. Would you like to restore it?')) { Object.keys(draftData.data).forEach(function(field) { g_form.setValue(field, draftData.data[field]); }); g_form.addInfoMessage('Draft restored from ' + timestamp.toLocaleString()); } else { sessionStorage.removeItem(draftKey); } } } catch (e) { console.error('Error restoring draft: ' + e); } } ================================================ FILE: Client-Side Components/Catalog Client Script/Auto Save Draft Feature/script.js ================================================ /** * Auto-save draft feature for Catalog Client Script * * This script automatically saves form data as a draft in the browser's sessionStorage * every minute if changes are detected. It also provides functionality to restore * the last saved draft when the form is loaded. */ // Executes when the form loads function onLoad() { var autosaveInterval = 60000; // 1 minute // Try to restore previous draft restoreLastDraft(); // Set up auto-save interval setInterval(function() { if (g_form.isModified()) { saveDraft(); } }, autosaveInterval); } // Saves the current form state as a draft function saveDraft() { try { var draftData = {}; g_form.serialize(draftData); var draftKey = 'catalogDraft_' + g_form.getUniqueValue(); sessionStorage.setItem(draftKey, JSON.stringify({ timestamp: new Date().getTime(), data: draftData })); g_form.addInfoMessage('Draft saved automatically'); } catch (e) { console.error('Error saving draft: ' + e); } } // Restores the last saved draft if available function restoreLastDraft() { try { var draftKey = 'catalogDraft_' + g_form.getUniqueValue(); var savedDraft = sessionStorage.getItem(draftKey); if (savedDraft) { var draftData = JSON.parse(savedDraft); var timestamp = new Date(draftData.timestamp); // Ask user if they want to restore the draft if (confirm('A draft from ' + timestamp.toLocaleString() + ' was found. Would you like to restore it?')) { Object.keys(draftData.data).forEach(function(field) { g_form.setValue(field, draftData.data[field]); }); g_form.addInfoMessage('Draft restored from ' + timestamp.toLocaleString()); } else { // Clear the draft if user chooses not to restore sessionStorage.removeItem(draftKey); } } } catch (e) { console.error('Error restoring draft: ' + e); } } ================================================ FILE: Client-Side Components/Catalog Client Script/Auto-populate field from URL/README.md ================================================ This piece of code is designed for an usecase where you might want to populate a field value that you're passing as a query in the URL which redirects to a catalog item. In this case, a custom field 'u_date' is chosen as an example to be shown: 1. You open a catalog item record via a URL that carries a date in the query string. Example: https://your-instance.service-now.com/your_form.do?sysparm_u_date=2025-10-31 -(This URL includes a parameter named sysparm_u_date with the value 2025-10-31.) 2. The catalog client script reads the page URL and extracts that specific parameter which returns the value "2025-10-31". 3. If the parameter is present, the script populates the form field. Calling g_form.setValue('u_date', '2025-10-31') sets the date field on the form to 31 October 2025. Result: The date field in the form is prefilled from the URL ================================================ FILE: Client-Side Components/Catalog Client Script/Auto-populate field from URL/popdatefromurl.js ================================================ //Logic to fetch the u_date field value passed in the url and setting it in the actual field. var fetchUrl = top.location.href; //get the URL var setDate = new URLSearchParams(gUrl).get("sysparm_u_date"); //fetch the value of date from the query parameter g_form.setValue('u_date', setDate); //set the value to the actual field ================================================ FILE: Client-Side Components/Catalog Client Script/Autofilling the request details from previous request/Auto fill script include.JS ================================================ var GetRecentRequestValues = Class.create(); GetRecentRequestValues.prototype = Object.extendsObject(AbstractAjaxProcessor, { getValues: function() { var userID = this.getParameter('sysparm_user'); var itemID = this.getParameter('sysparm_item'); var result = { found: false, values: {} }; var gr = new GlideRecord('sc_req_item'); gr.addQuery('requested_for', userID); gr.addQuery('cat_item', itemID); gr.orderByDesc('sys_created_on'); gr.setLimit(1); gr.query(); if (gr.next()) { result.found = true; var vars = gr.variables; result.values = { 'requested_for': vars.requested_for + '', 'location': vars.location + '', 'department': vars.department + '' }; } return JSON.stringify(result); } }); ================================================ FILE: Client-Side Components/Catalog Client Script/Autofilling the request details from previous request/Client script Autofill.js ================================================ function onLoad() { var user = g_user.userID; var itemID = g_form.getUniqueValue(); var ga = new GlideAjax('GetRecentRequestValues'); ga.addParam('sysparm_name', 'getValues'); ga.addParam('sysparm_user', user); ga.addParam('sysparm_item', itemID); ga.getXMLAnswer(function(response) { var data = JSON.parse(response); if (data && data.found) { var confirmFill = confirm("We found a similar request. Do you want to autofill fields?"); if (confirmFill) { for (var field in data.values) { if (g_form.getControl(field)) { g_form.setValue(field, data.values[field]); console.log("Set " + field + " to " + data.values[field]); } else { console.log("Field not found: " + field); } } } } else { console.log("No previous request found."); } }); } ================================================ FILE: Client-Side Components/Catalog Client Script/Autofilling the request details from previous request/Readme.md ================================================ Recent Request Autofill for ServiceNow Catalog.it automatically offers to fill in fields based on the user's most recent similar request. Features - Detects previous requests for the same catalog item - Prompts user to reuse values from their last submission - Autofills fields like location, department, and justification image ================================================ FILE: Client-Side Components/Catalog Client Script/Autopopulate Department/README.md ================================================ # Autopopulate Department Catalog Client Script Use this onChange catalog client script to populate a **department** variable in a catalog item based on a modifiable **requested_for**. Both variables must be reference type pointing to their respective tables. ## Use case The GlideUser API (g_user) does not provide a way to retrieve a user's department in a client-side script. Here is a small snippet using `g_form.getReference()` to retrieve the user from the **requested_for** variable and populate the **department** variable in a catalog item while allowing the submitter to still change the department if the data is incorrect. Attach this client script to a variable set for easy reuse. It can be augmented with a number of other fields from the user record such as email, phone number, manager, etc. Just be mindful of field types and whether the desired field will return a sys_id or display text. ================================================ FILE: Client-Side Components/Catalog Client Script/Autopopulate Department/autopopulateDepartment.js ================================================ function onChange(control, oldValue, newValue, isLoading) { g_form.getReference("requested_for", function (gr) { g_form.setValue("department", gr.department); g_form.setValue("email", gr.email); g_form.setValue("phone", gr.phone); }); } ================================================ FILE: Client-Side Components/Catalog Client Script/Autopopulate user information fields/ClientCallableScriptInclude.js ================================================ /* * The following is a client callable script include. This can be used with the onChange Client script to be able to gather the data on the server side */ var ReferenceQualifierAjaxHelper = Class.create(); ReferenceQualifierAjaxHelper.prototype = Object.extendsObject(AbstractAjaxProcessor, { getUserInformation : function() { var userID = this.getParameter('sysparm_user'); var userRec = new GlideRecord('sys_user'); if(userRec.get(userID)) { var results = { "email" : userRec.getValue('email'), "department" : userRec.getValue('department'), "title" : userRec.getValue('title'), "phone" : userRec.getValue('phone') }; return JSON.stringify(results) } }, type: 'ReferenceQualifierAjaxHelper' }); ================================================ FILE: Client-Side Components/Catalog Client Script/Autopopulate user information fields/README.md ================================================ ## Overview This onchange catalog client script and script inlcude work together autopopulate the user fields that might show up on a catalog item. In the global scope you will have to create the client callable script include to be able to use the Ajax call that is in the on change client script. In this example we use the OOB Requested For field that already auto populates the user that is logged in then we go to the server to get that users information. The fields that are brough back are the ones that are in the code but you can modify to bring back more or less fields if needed. ================================================ FILE: Client-Side Components/Catalog Client Script/Autopopulate user information fields/onChangeClientScript.js ================================================ /* * In order for this to work make sure to have an onChange catalog client script on a variable that is type Requested For. This variable * already autopopulates the logged in user with its OOB functionality. In the updateUserFields function you can add any other user fields * that you might need. */ function onChange(control, oldValue, newValue, isLoading) { //This variable will store the sys_id of the user that populates in your requested for variable var userID = newValue; var ga = new GlideAjax(ReferenceQualifierAjaxHelper); ga.addParam('sysparm_name', 'getUserInformation'); ga.addParam('sysparm_user', userID); ga.getXMLAnswer(updateUserFields); function updateUserFields(response) { var returnedData = JSON.parse(response); g_form.setValue("email", returnedData.email); g_form.setValue("department", returnedData.department); g_form.setValue("title", returnedData.title); g_form.setValue("phone", returnedData.phone); } } ================================================ FILE: Client-Side Components/Catalog Client Script/Block Submit/README.md ================================================ Code Snippet to block submission of catalog item based on answer to other yes/no variable. #update To fix a task from issue #745 Replace JavaScript alert() method to GlideModal() API. ================================================ FILE: Client-Side Components/Catalog Client Script/Block Submit/block_submit.js ================================================ //Block the user from submitting the form based on variable answer function onSubmit() { var someVariable = g_form.getValue("someVariable"); if(someVariable == 'No'){ var gm = new GlideModal('glide_warn',false); gm.setTitle("Submit Blocked! You can only use this form for someReason. Review someInstructions"); gm.render(); return false; // this stops user from submitting the form } return true; // allow form submit } ================================================ FILE: Client-Side Components/Catalog Client Script/Calculate age on based on date of birth/Calculate age based on dob.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue == '') { return; } function calculateAge(dateOfBirth) { if (!dateOfBirth || isNaN(Date.parse(dateOfBirth))) { alert('Invalid date of birth provided.'); return; } var dob = new Date(dateOfBirth); var currentDate = new Date(); var age = calculateAgeDifference(dob, currentDate); var ageString = ''; if (age.years > 0) { ageString += age.years + ' years'; if (age.months > 0 || age.days > 0) { ageString += ', '; } } if (age.months > 0) { ageString += age.months + ' months'; if (age.days > 0) { ageString += ', '; } } if (age.days > 0) { ageString += age.days + ' days'; } return ageString; } function calculateAgeDifference(startDate, endDate) { var years = endDate.getFullYear() - startDate.getFullYear(); var months = endDate.getMonth() - startDate.getMonth(); var days = endDate.getDate() - startDate.getDate(); if (days < 0) { months--; days += new Date(endDate.getFullYear(), endDate.getMonth(), 0).getDate(); } if (months < 0) { years--; months += 12; } return { years: years, months: months, days: days }; } var dateOfBirth = newValue; var age = calculateAge(dateOfBirth); g_form.setValue('age', age); } ================================================ FILE: Client-Side Components/Catalog Client Script/Calculate age on based on date of birth/README.md ================================================ # Age Calculator - On Change Client Script This script is designed to calculate a person's age based on their date of birth. ## Functions - `calculateAge(dateOfBirth)`: Calculates the age in years, months, and days based on the provided date of birth. - `calculateAgeDifference(startDate, endDate)`: Computes the difference in years, months, and days between two dates. ## Sample Input and Output ![age_calculator](image.png) ================================================ FILE: Client-Side Components/Catalog Client Script/Catalog Approval/Readme.md ================================================ This project adds a dynamic preview feature to Service Catalog items, allowing users to see the full approval chain before submitting a request. It improves transparency, reduces confusion, and helps users understand who will be involved in the approval process based on their selections. ================================================ FILE: Client-Side Components/Catalog Client Script/Catalog Approval/client script.js ================================================ function onLoad() { var ga = new GlideAjax('ApprovalChainHelper'); ga.addParam('sysparm_name', 'getApprovers'); ga.addParam('sysparm_item_id', g_form.getUniqueValue()); ga.getXMLAnswer(function(response) { var approvers = JSON.parse(response); var message = 'This request will be approved by: ' + approvers.join(', '); g_form.showFieldMsg('requested_for', message, 'info'); }); } ================================================ FILE: Client-Side Components/Catalog Client Script/Catalog Approval/script include.js ================================================ var ApprovalChainHelper = Class.create(); ApprovalChainHelper.prototype = Object.extendsObject(AbstractAjaxProcessor, { getApprovers: function() { var itemId = this.getParameter('sysparm_item_id'); var userId = gs.getUserID(); var approvers = []; // Example logic: fetch approval rules based on item and user var ruleGR = new GlideRecord('sysapproval_approver'); ruleGR.addQuery('document_id', 80f8920bc3e4b2105219daec050131e3); ruleGR.query(); while (ruleGR.next()) { approvers.push(ruleGR.approver.name.toString()); } return JSON.stringify(approvers); } }); ================================================ FILE: Client-Side Components/Catalog Client Script/Clear all fields/README.md ================================================ # Clear all fields on a catalog item form This function clears all editable fields on a form, except those explicitly excluded. It works on both the native platform (Classic UI) and Service Portal / Mobile. Typically used with an OnChange catalog client script when you want to clear all fields after a certain variable changes. The function returns an array of the field names that were cleared, which can be used for logging or further processing. ### Exclusion Support You can pass an array of field names to exclude from being cleared. This is useful when you want to preserve the value of the field that triggered the change or other important fields. ### Example ``` clearFields(['short_description', 'priority']); ``` // Clears all fields except 'short_description' and 'priority' ================================================ FILE: Client-Side Components/Catalog Client Script/Clear all fields/script.js ================================================ /** * Clears or resets all editable fields on a form, except those explicitly excluded. * Compatible with Classic UI and Service Portal/Mobile. * Intended for use in onChange client scripts. * * @function clearFields * @param {Array} dontClearFieldsArray - Array of field names to exclude from clearing. * @returns {Array} - Array of field names that were cleared. * * @example * // Clears all fields except 'short_description' and 'priority' * clearFields(['short_description', 'priority']); */ function clearFields(dontClearFieldsArray) { // Ensure the exclusion list is defined and is an array dontClearFieldsArray = Array.isArray(dontClearFieldsArray) ? dontClearFieldsArray : []; // Helper function to check if a field should be cleared function shouldClear(fieldName) { return dontClearFieldsArray.indexOf(fieldName) === -1; } var clearedFields = []; try { // Classic UI: use g_form.nameMap to get all fields var allFields = g_form.nameMap; allFields.forEach(function(field) { var fieldName = field.prettyName; if (shouldClear(fieldName)) { g_form.clearValue(fieldName); clearedFields.push(fieldName); } }); } catch (e) { // Service Portal or Mobile: use getEditableFields() var editableFields = g_form.getEditableFields(); editableFields.forEach(function(fieldName) { if (shouldClear(fieldName)) { g_form.clearValue(fieldName); clearedFields.push(fieldName); } }); } return clearedFields; } ================================================ FILE: Client-Side Components/Catalog Client Script/Combine variables into Description/README.md ================================================ OnSUbmit Catalog Client script is created to Combine all variable values required and display in Description field. Steps: 1. Navigate to your instance open catalog client script table [catalog_script_client] 2. Create new catalog client script -> click new 3. Provide following values: - Name: Any relevant to your script - Applies to: A catalog Item - UI Type: All - Isolated script: checked - Application: Application scope applies to - Type: onSubmit - Catalog item: select your catalog item 4. create the script as per script.js file. ================================================ FILE: Client-Side Components/Catalog Client Script/Combine variables into Description/script.js ================================================ // Combine variables into Description // Type: onSubmit function onSubmit() { // Combine provided all fields to Description field var description = g_form.getValue ('description'); var first = g_form.getDisplayValue ('first_variable'); var second = g_form.getDisplayValue ('second_variable'); var third = g_form.getDisplayValue ('third_variable'); g_form.setValue('description', ''+ description + '\n\nType: ' + first + '\n Text: ' + second + '\nText: ' + third); return true; } ================================================ FILE: Client-Side Components/Catalog Client Script/Control all RITM variables in one go/README.md ================================================ # Control all RITM variables in one go Requirement : We need to make all the variables on the RITM (sc_req_item) form read only or editable Problem : If there are so many variables then it becomes difficult to write multiple UI policies or writing multiple lines of code in catalog client script for each variable seperately. Solution : With the above code snippet you can control(make read-only or editable) all the variables in the RITM form with very minimal code) ================================================ FILE: Client-Side Components/Catalog Client Script/Control all RITM variables in one go/script.js ================================================ function onLoad() { g_form.setVariablesReadOnly(true); //if you want to make all variables read-only g_form.setVariablesReadOnly(false); //if you want to make all variables editable } ================================================ FILE: Client-Side Components/Catalog Client Script/Currency Validation/README.md ================================================ ## Currency Validation Use this catalog client script to validate the value of a variable used to get currency. As of now 3 things are being checked in the script but you can make changes as per requirement. 1) Characters after the $ sign should be numerics. 2) Entered value should have a decimal point. 3) There must be 2 digits only after the decimal. ================================================ FILE: Client-Side Components/Catalog Client Script/Currency Validation/currency_validation.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue == '') { return; } var cost = g_form.getValue('variable_name'); //update variable name used for currency cost = cost.trim(); // first character should be dollar sign var firstChar = cost.substring(0, 1); if (firstChar != '$') { validationAlert(oldValue); } // characters after the $ sign should be numerics var costType = isNaN(cost.substring(1)); if (costType == true) { validationAlert(oldValue); } // entered value should have a decimal point var num = cost.substring(1); if (num.indexOf('.') == -1) { validationAlert(oldValue); } // there must be 2 digits only after the decimal var decNum = num.substring(num.indexOf('.') + 1, num.length); if (decNum.length != 2) { validationAlert(oldValue); } } function validationAlert(oldValue) { g_form.setValue("variable_name", oldValue); var gm = new GlideModal("glide_warn"); gm.setTitle("Currency formatting problem"); gm.setPreference("title", "Please enter cost in $0.00 format"); gm.render(); return; } ================================================ FILE: Client-Side Components/Catalog Client Script/CustomAlert/README.md ================================================ These scripts helps you to create custom popup easily. follow the below steps to implement it on your instance. Steps 1. Create UI page with name "custom_alert_box" 2. Copy HTML section from customn_alert_box.js to HTML field of UI Page 3. Copy Client Script section from customn_alert_box.js to Client Script field of UI Page 4. UI Page is ready to be used. 5. Refer the "custom_alert.js" to create client script or catalog client script to alert HTML messages or links in custom alert format. 6. Refer the screenshots folder for quick look. ================================================ FILE: Client-Side Components/Catalog Client Script/CustomAlert/Screenshots/README.md ================================================ Please see the example Screenshots ================================================ FILE: Client-Side Components/Catalog Client Script/CustomAlert/custom_alert.js ================================================ function onLoad() { // Function to show a custom link in a GlideModal function showCustomLinkInGlideModal() { // Create an instance of GlideModal using the 'custom_alert_box' UI page // The second parameter 'true' indicates that the modal should be a dialog, // and '600' sets the width of the modal to 600 pixels. var gm = new GlideModal("custom_alert_box", true, 600); // Set the modal's title to anything you want gm.setTitle('Important Information'); //for e.g. Important Information // Set a preference for the modal indicating the type of alert // This can be used to style the modal or control its behavior. // available choices {info, danger, warning, success} gm.setPreference('alertType', 'danger'); // Custom HTML content to be displayed in the modal // This includes a paragraph and a link to an external website. var htmlContent = '

Please visit the following link:

' + 'Click here to go to Example.com'; // Set the HTML content of the modal using the 'infoText' preference. // We disable escaping since we're providing our own HTML. gm.setPreference('infoText', htmlContent); // Render the modal on the screen gm.render(); } // Call the function to display the modal when the form loads showCustomLinkInGlideModal(); } ================================================ FILE: Client-Side Components/Catalog Client Script/CustomAlert/custom_alert_box.js ================================================ *****************HTML Section Start***************** var infoText = "${RP.getWindowProperties().get('infoText')}"; infoText = new GlideStringUtil().unEscapeHTML(infoText); var warning = "${RP.getWindowProperties().get('warning')}"; warning = new GlideStringUtil().unEscapeHTML(warning); var alertType = "${RP.getWindowProperties().get('alertType')}";
*****************HTML Section End***************** *****************Client Script Start***************** function unescapeHTML(html) { var textarea = document.createElement('textarea'); textarea.innerHTML = html; // Set the HTML content return textarea.value; // Return the unescaped text } var infoText = "${RP.getWindowProperties().get('infoText')}"; infoText = unescapeHTML(infoText); // Unescape the HTML // Now set the title to your dialog or display it document.getElementById('bodycell').innerHTML = infoText; // Assuming there's a titleCell in your HTML function invokePromptCallBack() { var gdw = GlideDialogWindow.get(); gdw.destroy(); return false; } var gdw = GlideDialogWindow.get(); gel('ok_button').focus(); *****************Client Script Start***************** ================================================ FILE: Client-Side Components/Catalog Client Script/Date Management/Date Management.js ================================================ //Various useful ways to interact with dates on the client without needing to make a trip to the server //Use the following to take a date / date time variable and turn it to a JS Date value /*newValue = date(/time) variable value, i.e from an onChange script /*getDateFromFormat = This is a ServiceNow provided function loaded into the browser /*g_user_date_format = a variable loaded into the browser on session load that stores the users date format */ var date = new Date(getDateFromFormat(newValue, g_user_date_format)); //Validate a value is a date //This is a function loaded into the browser by ServiceNow //i.e isDate(newValue , g_user_date_format); //Returns a Boolean var checkDate = isDate(value , format); //Compare two dates //This is a function loaded into the browser by ServiceNow //i.e compareDates("29-10-2021" , "dd-MM-yyyy" , "20-10-2021" , "dd-MM-yyyy"); //Returns -1 if either date value is not a valid date //Returns 1 if date1 is greater than date2 //Returns 0 otherwise var isSecondDateLarger = compareDates("29-10-2021" , "dd-MM-yyyy" , "20-10-2021" , "dd-MM-yyyy"); //Format a date value to the user session format to save in a variable/field var date = formatDate(date,format); /*Exampe of the above in use*/ var dateNumber = getDateFromFormat(newValue , g_user_date_format); var date = new Date(dateNumber); date.setDate(date.getDate() - 1); g_form.setValue('date' , formatDate(date , g_user_date_format)); ================================================ FILE: Client-Side Components/Catalog Client Script/Date Management/README.md ================================================ #### Useful ways to interact with variable date/time values without needing to make a trip to the server The following functions and variables are defined by ServiceNow and loaded at run-time. They are accessible from within the client script without the need to turn off script isolation. - g_user_date_format - g_user_date_time_format - isDate() - compareDates() - formatDate() - getDateFromFormat() ================================================ FILE: Client-Side Components/Catalog Client Script/Document validation/Client script.JS ================================================ function onSubmit() { var ga = new GlideAjax('DocumentValidationHelper'); ga.addParam('sysparm_name', 'validateAttachments'); ga.addParam('sysparm_item_id', g_form.getUniqueValue()); ga.getXMLAnswer(function(response) { if (response !== 'valid') { alert('Document validation failed: ' + response); return false; } }); return true; } ================================================ FILE: Client-Side Components/Catalog Client Script/Document validation/Readme.md ================================================ This project enhances a Service Catalog item by allowing users to upload supporting documents (e.g., ID proof, approval letters) and validating them before the request is submitted. It ensures compliance, completeness, and proper documentation for sensitive or regulated requests. ================================================ FILE: Client-Side Components/Catalog Client Script/Document validation/Script include.js ================================================ var DocumentValidationHelper = Class.create(); DocumentValidationHelper.prototype = Object.extendsObject(AbstractAjaxProcessor, { validateAttachments: function() { var itemId = this.getParameter('sysparm_item_id'); var attachmentGR = new GlideRecord('sys_attachment'); attachmentGR.addQuery('table_name', 'sc_req_item'); attachmentGR.addQuery('table_sys_id', itemId); attachmentGR.query(); while (attachmentGR.next()) { var fileName = attachmentGR.file_name.toLowerCase(); if (!fileName.endsWith('.pdf') && !fileName.endsWith('.docx')) { return 'Only PDF or DOCX files are allowed.'; } if (attachmentGR.size_bytes > 5 * 1024 * 1024) { return 'File size exceeds 5MB limit.'; } } return 'valid'; } }); ================================================ FILE: Client-Side Components/Catalog Client Script/Dynamically Update Reference Qualifier/Catalog Item onLoad.js ================================================ function onLoad() { var filterString = 'sys_class_name=cmdb_ci_ip_router^ORsys_class_name=cmdb_ci_ip_switch^ORsys_class_name=cmdb_ci_vpn' //Reference qualifier for this Catalog Item //alternate method for Service Portal only // if (window == null){ //Service Portal method // var setfilter = g_list.get('v_configuration_item'); // setfilter.setQuery(filterString); // } else { //native UI method var ga = new GlideAjax('refQualUtils'); //Client callable Script Include Name ga.addParam('sysparm_name', 'setSysProp'); //Function in Script Include ga.addParam('sysparm_sys_prop_name', 'sr.ref_qual.ci'); //System Property Name used in Reference qualifier ga.addParam('sysparm_sys_prop_value', filterString); ga.getXML(getResponse); function getResponse(response) { //to avoid Service Portal 'There is a JavaScript error in your browser console' var answer = response.responseXML.documentElement.getAttribute("answer"); } //} } ================================================ FILE: Client-Side Components/Catalog Client Script/Dynamically Update Reference Qualifier/README.md ================================================ When we have a reference variable that is used in a (single row) variable set, sometimes we want to update the Reference qualifier only for specific Catalog Item(s). In this example, I have a Configuration item (named v_configuration_item) reference variable (cmdb_ci table) in a single row variable set that has been included in a number of Catalog Items. The simple Reference qualifier for this variable is: sys_class_name=cmdb_ci_ip_router^ORsys_class_name=cmdb_ci_ip_switch Let's say for one particular Catalog Item, I also want to include the class of VPN (cmdb_ci_vpn). To do this without having to create another variable with the new qualifier and hiding the variable set variable, there are some preparation steps, including those which ensure that the other Catalog Items using the variable set are not disrupted: 1) Change the advanced reference qualifier on the variable to: **javascript: gs.getProperty("sr.ref_qual.ci");** using a System Property Name of your choice 2) Create a System Property with the same Name used in the Reference qualifier, leaving the Value empty. 3) Add the included 'Variable Set onLoad' Catalog Client Script that applies to the Variable set with... 4) The included Client Callable Script Include to update the System Property Value to the Reference Qualifier that was replaced. This Script Include uses parameters for the System Property Name and Value, so it can be re-used in every instance of this solution. Now that the other Catalog Items using the variable set are still working as they were, all you need to do to update the Reference qualifier on certain Catalog Item(s) is: 5) Add the included 'Catalog Item onLoad' Catalog Client Script that applies to the Catalog Item. Set the Order of this script to a high number (10,000) so that it runs after the variable set one. This solution works in both the Native UI and Service Portal. The Catalog Client scripts contain an alternate Service Portal only approach in the commented if block that can be used in conjunction with the native UI approach in the else block. This alternate Service Portal solution was developed in collaboration with Chris Perry. ================================================ FILE: Client-Side Components/Catalog Client Script/Dynamically Update Reference Qualifier/Script Include.js ================================================ var refQualUtils = Class.create(); refQualUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, { setSysProp: function(){ var propertyName = this.getParameter('sysparm_sys_prop_name'); var propertyValue = this.getParameter('sysparm_sys_prop_value'); var property = gs.getProperty(propertyName); gs.setProperty(propertyName, propertyValue); return; }, type: 'refQualUtils' }); ================================================ FILE: Client-Side Components/Catalog Client Script/Dynamically Update Reference Qualifier/Variable Set onLoad.js ================================================ function onLoad() { var filterString = 'sys_class_name=cmdb_ci_ip_router^ORsys_class_name=cmdb_ci_ip_switch' //Reference qualifier that was replaced //alternate method for Service Portal only // if (window == null){ //Service Portal method // var setfilter = g_list.get('v_configuration_item'); // setfilter.setQuery(filterString); // } else { //native UI method var ga = new GlideAjax('refQualUtils'); //Client callable Script Include Name ga.addParam('sysparm_name', 'setSysProp'); //Function in Script Include ga.addParam('sysparm_sys_prop_name', 'sr.ref_qual.ci'); //System Property Name used in Reference qualifier ga.addParam('sysparm_sys_prop_value', filterString); ga.getXML(getResponse); function getResponse(response) { //to avoid Service Portal 'There is a JavaScript error in your browser console' var answer = response.responseXML.documentElement.getAttribute("answer"); } //} } ================================================ FILE: Client-Side Components/Catalog Client Script/Get Display Value of MRVS/README.md ================================================ ## Get Display Value of MultiRow Variableset (MRVS) While there are different ways to do this, the easiest of them is to leverage an out of box script named **'VariableUtil'**. The script is present in the global scope and contains an function named getDisplayValue aptly. This function seems to work on both the normal variable as well as multirow variableset. You just need to pass the sysid of the variable or the multirow variableset and its corresponding value. **new VariableUtil().getDisplayValue('sys_id of variable or MRVS','value of variable/MRVS');** ================================================ FILE: Client-Side Components/Catalog Client Script/Get Display Value of MRVS/mrvs.js ================================================ var script = new global.VariableUtil(); var gr = new GlideRecord("sc_req_item"); gr.addEncodedQuery("sys_id="); gr.query(); if (gr.next()) { gs.info(script.getDisplayValue('', gr.variables.MRVSName)); //MRVS Display Value } ================================================ FILE: Client-Side Components/Catalog Client Script/Get MRVS Values from Parent/README.md ================================================ # Get Multi-row Variable Set Values from parent form Sometimes you need to query the current set of values for a MRVS from the actual MRVS or another MRVS. This requires getting the data from the parent form, the method to retrieve and the format of the data is different when running on the platform or portal. This script gives a way of getting the values regardless of the platform in use. On the platform (backend) this could be moved to a global UI Script, but that is not available to portal scripts. Make sure the UI Type is All, and Isolate Script is false (unchecked). ================================================ FILE: Client-Side Components/Catalog Client Script/Get MRVS Values from Parent/onload.js ================================================ function onLoad() { /* * work out the parent form based on platform and return the given MRVS data * This could go in a global UI script but that wouldn't work for Portal, * would love anyone to suggest an alternative for portal :) */ function getMRVSDataFromParent(mrvsName) { var parent_g_form = g_service_catalog.parent; // default for backend/platform if (parent.angular) { // this is portal so get a different way var parentItem = parent.angular.element(parent.$('#sc_cat_item').find('sp-variable-layout')[0]).scope(); parent_g_form = parentItem.getGlideForm(); } var vmData = parent_g_form.getValue(mrvsName); // on portal we get back an empty string rather than an empty array so convert return vmData == '' ? [] : JSON.parse(vmData); } var vmJSONData = getMRVSDataFromParent('virtual_machine'); console.log("JSON " + JSON.stringify(vmJSONData, '', 3)); } ================================================ FILE: Client-Side Components/Catalog Client Script/Hide Variables of Catalog Item on Order Guide/Hide Variables.js ================================================ function onLoad(){ if(g_service_catalog.isOrderGuide()){ //variable_name1, varaible_name2 are the fields already present on the Order guide, hence hiding below fields on the catalog form when the catalog form is used through an order guide. g_form.setDisplay('varible_name1',false); g_form.setDisplay('varible_name2',false); } ================================================ FILE: Client-Side Components/Catalog Client Script/Hide Variables of Catalog Item on Order Guide/README.md ================================================ 1. The onLoad catalog client script can be used to hide the catalog varaibles on catalog form when the catalog item is being used on OrderGuide and cascade varaibles field is enabled on Order guide. 2. Cascading enables the transfer of values entered for variables in the initial order form to their corresponding variables in the catalog items that have been ordered. 3. Assume that the variables variable_name1, varaible_name2 are already present on the order guide form, hence hiding these variables on the catalog form when the catalog form is opened through an order guide using the function isOrderGuide(). ================================================ FILE: Client-Side Components/Catalog Client Script/Hide attachment icon/Hide Attachment icon.js ================================================ //Hide attachment icon on catalog item function onLoad() { var document = document || top.document; (jQuery || top.jQuery)("#sc_attachment_button, #catItemTop > div > div.wrapper-md.row.no-margin.ng-scope > label").hide(); } ================================================ FILE: Client-Side Components/Catalog Client Script/Hide attachment icon/README.md ================================================ # Hide Attachment Icon on Catalog Items ## Use Case / Requirement Hide the attachment icon on a specific catalog item when the end user should not submit supporting documents. This can reduce confusion and prevent oversized uploads. ## Solution Use an onLoad catalog client script to target the attachment button rendered on the Service Portal form and hide it with jQuery. The snippet works for both classic and Service Portal experiences. ## Implementation 1. Create a new catalog client script with Type set to onLoad. 2. Copy the contents of Hide Attachment icon.js into the script field. 3. Adjust the selector if your catalog item uses a custom portal or markup. ## Notes - Requires jQuery, which is available on standard Service Portal forms. - The DOM can change between releases; retest after theme or layout updates. - Remove the script if the catalog item later requires attachments. ================================================ FILE: Client-Side Components/Catalog Client Script/Incident Sentiment Detector (Using Simple Word Matching, No AI)/SentimentAnalyzer.js ================================================ var SentimentAnalyzer = Class.create(); SentimentAnalyzer.prototype = Object.extendsObject(AbstractAjaxProcessor, { getSentiment: function() { var text = (this.getParameter('sysparm_text') || '').toLowerCase(); var positive = ['thanks', 'great', 'resolved', 'appreciate']; var negative = ['issue', 'error', 'not working', 'fail', 'problem']; var score = 0; positive.forEach(function(word) { if (text.includes(word)) score++; }); negative.forEach(function(word) { if (text.includes(word)) score--; }); if (score > 0) return 'Positive'; if (score < 0) return 'Negative'; return 'Neutral'; } }); ================================================ FILE: Client-Side Components/Catalog Client Script/Incident Sentiment Detector (Using Simple Word Matching, No AI)/onChangeClientscript.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || !newValue) return; var ga = new GlideAjax('SentimentAnalyzer'); ga.addParam('sysparm_name', 'getSentiment'); ga.addParam('sysparm_text', newValue); ga.getXMLAnswer(function(sentiment) { g_form.addInfoMessage('Sentiment: ' + sentiment); g_form.setValue('u_sentiment', sentiment); }); } ================================================ FILE: Client-Side Components/Catalog Client Script/Incident Sentiment Detector (Using Simple Word Matching, No AI)/readme.md ================================================ Incident Sentiment Detector (No AI, Pure JavaScript) A lightweight ServiceNow utility that detects sentiment (Positive / Negative / Neutral) of an Incident’s short description or comments using simple keyword matching — no AI APIs or external libraries required. Useful for support teams to auto-tag sentiment and analyze user frustration or satisfaction trends without expensive integrations. 🚀 Features ✅ Detects sentiment directly inside ServiceNow ✅ Works without external APIs or ML models ✅ Instant classification on form update ✅ Adds detected sentiment to a custom field (u_sentiment) ✅ Simple to extend — just add more positive/negative keywords 🧩 Architecture Overview The solution consists of two main scripts: Component Type Purpose SentimentAnalyzer Script Include Processes text and returns sentiment Client Script (onChange) Client Script Calls SentimentAnalyzer via GlideAjax on short description change 🧱 Setup Instructions 1️⃣ Create Custom Field Create a new field on the Incident table: Name: u_sentiment Type: Choice Choices: Positive, Neutral, Negative ================================================ FILE: Client-Side Components/Catalog Client Script/MRVS Email Validation with Mutation Observer/EmailValidationOnCatalogUI.js ================================================ /** * This is a client script that validates the email address in the MRVS field. * It uses MutationObserver to observe the changes in the MRVS field. * UseCase: Validate the email address in the catalog whenever user is going to add another email, * and show an error message if a duplicate email address is found. */ function onLoad() { var document = document || top.document; var duplicateErrorMessageStatus = false; //adding this as corner case as observer will be called multiple times setTimeout(function () { var tbody = document.querySelector("#user_details > div > tbody"); if (tbody) { var observer = new MutationObserver(function (m, o) { var users = g_form.getValue('user_details'); var hasDuplicateEmails = validateUserDetails(); if (!users || hasDuplicateEmails) { if (hasDuplicateEmails && !duplicateErrorMessageStatus) { g_form.addErrorMessage('Duplicate email address found'); duplicateErrorMessageStatus = true; } //disable the submit button } else { duplicateErrorMessageStatus = false; g_form.clearMessages(); //enable the submit button } }); observer.observe(tbody, { attributes: true, childList: true, subtree: true }); } }, 3000); } // MRVS contains the user details in the form of JSON function validateUserDetails() { var userDetailsMRVS = g_form.getValue('user_details'); if (userDetailsMRVS) { var multiRowData = JSON.parse(userDetailsMRVS); var emailSet = new Set(); for (var i = 0; i < multiRowData.length; i++) { var row = multiRowData[i]; var email = row.email.trim().toLowerCase(); if (emailSet.has(email)) { return true; } emailSet.add(email); } } return false; } ================================================ FILE: Client-Side Components/Catalog Client Script/MRVS Email Validation with Mutation Observer/README.md ================================================ # Email Validation on Catalog UI This file contains a client script that validates email addresses in the MRVS (Multi-Row Variable Set) field using a MutationObserver. The script ensures that duplicate email addresses are not allowed in the catalog. ## File Structure - `/code-snippets/Catalog Client Script/MRVS Email Validation with Mutation Observer/EmailValidationOnCatalogUI.js` ## Description The script observes changes in the MRVS field and validates the email addresses whenever a user attempts to add another email. If a duplicate email address is found, an error message is displayed, and the submit button is disabled. ## Usage 1. **onLoad Function**: This function initializes the MutationObserver to watch for changes in the MRVS field. 2. **validateUserDetails Function**: This function checks for duplicate email addresses in the MRVS field. ## How It Works 1. The `onLoad` function sets up a MutationObserver on the MRVS field. 2. When changes are detected, the `validateUserDetails` function is called to check for duplicate email addresses. 3. If duplicates are found, an error message is displayed, and the submit button is disabled. ================================================ FILE: Client-Side Components/Catalog Client Script/MRVS Interact With Parent Form/Portal onLoad.js ================================================ //Using this Catalog Client Script that Applies to the Catalog Item will enable g_form methods like setValue and clearValue which affect Catalog Item variables from a Catalog Client Script that applies to the multi-row variable set function onLoad() { if (this) {//we only need to do this for Service Portal //We need to make the g_form object for the parent item available from the MRVS window this.cat_g_form = g_form; } } ================================================ FILE: Client-Side Components/Catalog Client Script/MRVS Interact With Parent Form/README.md ================================================ Use this code snippet to interact with the parent form (i.e the main Cataloge Item) from within a Catalog Client Script that applies to a multi-row variable set. The g_service_catalog object allows for accessing the "parent" GlideForm (g_form) object for getValue only (for now?) To affect a variable in the main catalogue item using setValue, clearValue, etc. parent.g_form... is available in the native UI, and this.cat_g_form... can be used for Service Portal, etc if an additional Catalog Client Script is used, this one onLoad that Applies to the Catalog Item (not the MRVS). ================================================ FILE: Client-Side Components/Catalog Client Script/MRVS Interact With Parent Form/Write to Parent Form.js ================================================ //These methods can be used onLoad, onChange, or onSubmit in a script that Applies to the MRVS //Retrieve a variable value from the parent form - works in native UI as well as Service Portal, etc. g_service_catalog.parent.getValue('variable_name'); //With this approach, you can set a variable value on the parent form - use similar code for other g_form methods like clearValue //Service Portal method requires an additional Catalog Client Script onLoad that Applies to the Catalog Item if (this) { //Service Portal method this.cat_g_form.clearValue('variable_name'); } else { //native UI method parent.g_form.clearValue('variable_name'); } ================================================ FILE: Client-Side Components/Catalog Client Script/MRVS Loop Rows/README.md ================================================ Use this to loop through a Multi Row Variable Set and create an array of objects with the variables in it. ================================================ FILE: Client-Side Components/Catalog Client Script/MRVS Loop Rows/loopRows.js ================================================ var mrvsObj = []; var multiRow = variable.mrvsName; //replace with the actual name of the mrvs if (multiRow.getRowCount()) { //if there are any entries on the MRVS loop through it var eachRow = multiRow.getRowCount(); for (var i = 0; i < eachRow; i++) { var row = multiRow.getRow(i); var rowVars = {}; rowVars.var1 = row.var1; //replace with the variables in the mrvs rowVars.var2 = row.var2; rowVars.var3 = row.var3; // ... add rows as appropriate for your mrvs mrvsObj.push(rowVars); // creates an array of objects of the above fields from the MRVS } } ================================================ FILE: Client-Side Components/Catalog Client Script/MRVS Reference Qualifier from Catalog Item Variable/Catalog Client Script.js ================================================ function onLoad() { var mgr = g_service_catalog.parent.getValue('v_manager'); //if using this script onLoad of the MRVS //var mgr = newValue; // if using this script onChange of the Catalog Item variable var filterString = 'active=true^manager=' + mgr; //Reference qualifier following the 'javascript:' //alternate method for Service Portal only // if (window == null){ //Service Portal method // var setfilter = g_list.get('v_employee'); // setfilter.setQuery(filterString); // } else { //native UI method var ga = new GlideAjax('refQualUtils'); //Client callable Script Include Name ga.addParam('sysparm_name', 'setSysProp'); //Function in Script Include ga.addParam('sysparm_sys_prop_name', 'sr.mrvs.ref_qual.emp'); //System Property Name used in MRVS variable Reference qualifier ga.addParam('sysparm_sys_prop_value', filterString); ga.getXML(getResponse); function getResponse(response) { //to avoid Service Portal 'There is a JavaScript error in your browser console' var answer = response.responseXML.documentElement.getAttribute("answer"); } //} } ================================================ FILE: Client-Side Components/Catalog Client Script/MRVS Reference Qualifier from Catalog Item Variable/README.md ================================================ On a reference variable in a multi-row variable set, sometimes you want the reference qualifier to include the value of a variable that is part of the Catalog Item, not within the MRVS. In this simplified example, I have a Manager (v_manager) reference variable (sys_user table) that belongs to the Catalog Item. In the MRVS, I have an Employee (v_employee) reference variable (sys_user table). I only want to be able to select user records that are active, and the Manager is the user I selected on the Catalog Item variable. 1) Set the advanced reference qualifier on the MRVS variable to javascript: gs.getProperty("sr.mrvs.ref_qual.emp"); using a System Property Name of your choice 2) Use the included onLoad Catalog Client Script that applies to the Variable set, or you can also use this onChange of the Catalog Item variable in a script that applies to the Catalog Item. 3) Add the included Client callable Script Include for the Catalog Client Script to call via GlideAjax. This Script Include uses parameters for the System Property Name and Value, so it can be re-used in every instance of this solution. This solution works in both the Native UI and Service Portal. The script contains an alternate Service Portal only approach in the commented if block that can be used in conjunction with the native UI approach in the else block. This alternate Service Portal solution was developed in collaboration with Chris Perry. ================================================ FILE: Client-Side Components/Catalog Client Script/MRVS Reference Qualifier from Catalog Item Variable/Script Include.js ================================================ var refQualUtils = Class.create(); refQualUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, { setSysProp: function(){ var propertyName = this.getParameter('sysparm_sys_prop_name'); var propertyValue = this.getParameter('sysparm_sys_prop_value'); var property = gs.getProperty(propertyName); gs.setProperty(propertyName, propertyValue); return; }, type: 'refQualUtils' }); ================================================ FILE: Client-Side Components/Catalog Client Script/MRVS dependent ref qual 1st row/AccountUtils.js ================================================ var AccountUtils = Class.create(); AccountUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, { //Populate the department name from the account in the session data for the reference qualifier to use: setSessionData: function() { var acct = this.getParameter('sysparm_account'); var dept = ''; var acctGR = new GlideRecord('customer_account'); //reference table for Account variable if (acctGR.get(acct)) { dept = '^dept_name=' + acctGR.dept_name; //department field name on account table } var session = gs.getSession().putClientData('selected_dept', dept); return; }, type: 'AccountUtils' }); ================================================ FILE: Client-Side Components/Catalog Client Script/MRVS dependent ref qual 1st row/README.md ================================================ This Catalog Client Script and Script Include are used with a reference qualifier similar to javascript: 'disable=false' + session.getClientData('selected_dept'); The scenario is a MRVS with a reference variable to the customer account table. When an (active) account is selected in the first row, subsequent rows should only be able to select active accounts in the same department as the first account. The Catalog Client Script will pass the first selected account (if there is one) to a Script Include each time a MRVS row is added or edited. The Script Include will pass the department name to the reference qualifier. ================================================ FILE: Client-Side Components/Catalog Client Script/MRVS dependent ref qual 1st row/onLoad.js ================================================ function onLoad() { //applies to MRVS, not Catalog Item. This will pass the first selected account (if there is one) to a Script Include each time a MRVS row is added or edited var mrvs = g_service_catalog.parent.getValue('my_mrvs'); //MRVS internal name var acct = ''; if (mrvs.length > 2) { //MRVS is not empty var obj = JSON.parse(mrvs); acct = obj[0].account_mrvs; } var ga = new GlideAjax('AccountUtils'); ga.addParam('sysparm_name', 'setSessionData'); ga.addParam('sysparm_account', acct); ga.getXMLAnswer(getResponse); } function getResponse(response) { //do nothing } ================================================ FILE: Client-Side Components/Catalog Client Script/Make OOB Attachment Mandatory/README.md ================================================ # Make out of the box attahcment mandatory onChange of a field in Catalog Item This scripts makes the out of the box attachments to mandatory on the Service Portal for any Catalog Item. Usage: Step #1 - Create the UI Script * [Click here for script](UI Scripts/Make OOB Attachment Mandatory/setAttachmentMandatory.js) Step #2 - Include the UI Script in the Portal Theme under JS Includes Step #3 - Create a Catalog Client Script for the respective Catalog Item where you want to make the OOB attachment mandatory for certain criteria * [Click here for script](Catalog Client Script/Make OOB Attachment Mandatory/onChange.js) ================================================ FILE: Client-Side Components/Catalog Client Script/Make OOB Attachment Mandatory/onChange.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue == 'No') { setAttachmentMandatory(false); return; } if (newValue == 'Yes') setAttachmentMandatory(true); } ================================================ FILE: Client-Side Components/Catalog Client Script/Mandatory Attachments with 'n' numbers/README.md ================================================ # Enable Mandatory Attachments Count wtih 'n' numbers on Service Catalog item **Problem:** Check if exact 'n' number of attachments are added or not when user submit a request through service catalog from Portal/Platform UI. **For example:** We need to ensure there are exact 3 attachments added before submission **Note:** Ensure Isolate Script field is set to False for this Catalog Client Script to ensure DOM manipulation works * [Click here for script](onSubmitClientScript.js) ================================================ FILE: Client-Side Components/Catalog Client Script/Mandatory Attachments with 'n' numbers/onSubmitClientScript.js ================================================ function onSubmit() { //Type appropriate comment here, and begin script below var count = 3; //Pass the number to ensure given number of attachments are added var alertMsg="You must add "+count+" attachments before submitting this request."; if(window == null){ // Service portal validation, Make sure Isolate Script is set to False if(this.document.getElementsByClassName('get-attachment').length != count) { spModal.alert(alertMsg); return false; } } else{ // Platform View var length = $j("li.attachment_list_items").find("span").length; if(length != count){ alertWindow(alertMsg); return false; } } } function alertWindow(message) { var modal = new GlideModal("glide_warn"); modal.setTitle("Attachment issue"); modal.setPreference("title", message); modal.render(); } ================================================ FILE: Client-Side Components/Catalog Client Script/Multi-User Collaboration/Readme.md ================================================ This project introduces a collaboration feature for Service Catalog requests, allowing multiple users to contribute to a single request. It’s ideal for scenarios like team onboarding, shared resource provisioning, or cross-functional workflows. ================================================ FILE: Client-Side Components/Catalog Client Script/Multi-User Collaboration/Script include.JS ================================================ var CollaboratorHandler = Class.create(); CollaboratorHandler.prototype = Object.extendsObject(AbstractAjaxProcessor, { addCollaborators: function() { var requestId = this.getParameter('sysparm_request_id'); var users = this.getParameter('sysparm_users').split(','); users.forEach(function(userId) { var gr = new GlideRecord('x_your_scope_collaborators'); gr.initialize(); gr.request = requestId; gr.collaborator = userId; gr.status = 'Pending'; gr.insert(); }); return 'Collaborators added successfully'; } }); ================================================ FILE: Client-Side Components/Catalog Client Script/Multi-User Collaboration/client script.JS ================================================ function onSubmit() { var collaborators = g_form.getValue('collaborators'); // Multi-user reference field if (collaborators) { var ga = new GlideAjax('CollaboratorHandler'); ga.addParam('sysparm_name', 'addCollaborators'); ga.addParam('sysparm_request_id', g_form.getUniqueValue()); ga.addParam('sysparm_users', collaborators); ga.getXMLAnswer(function(response) { alert('Collaborators added: ' + response); }); } return true; } ================================================ FILE: Client-Side Components/Catalog Client Script/Multi-User Collaboration/widget client controller.JS ================================================ function($scope, $http) { $scope.approve = function(sysId) { $http.post('/api/x_your_scope/collab_action', { action: 'approve', sys_id: sysId }).then(function(response) { $scope.server.update(); }); }; $scope.reject = function(sysId) { $http.post('/api/x_your_scope/collab_action', { action: 'reject', sys_id: sysId }).then(function(response) { $scope.server.update(); }); }; } ================================================ FILE: Client-Side Components/Catalog Client Script/Multi-User Collaboration/widget server script.JS ================================================ (function() { var collabs = []; var gr = new GlideRecord('x_your_scope_collaborators'); gr.addQuery('request', $sp.getParameter('request_id')); gr.query(); while (gr.next()) { collabs.push({ sys_id: gr.getUniqueValue(), name: gr.collaborator.name.toString(), status: gr.status.toString(), comments: gr.comments.toString() }); } data.collaborators = collabs; })(); ================================================ FILE: Client-Side Components/Catalog Client Script/Normalise and Reset a MRVS based on Variable Changes/README.md ================================================ # MRVS - Normalise and Reset Rows on Change ## What this solves When a controlling variable changes (for example, Environment), existing MRVS rows may no longer be valid. This client script: - Clears or normalises specific MRVS columns - Deduplicates rows - Optionally sorts rows for a cleaner UX - Works entirely client-side using MRVS JSON ## Where to use Catalog Item → OnChange client script on your controlling variable. ## How it works - Reads the MRVS value as JSON via `g_form.getValue('my_mrvs')` - Applies transforms (clear columns, unique by key, sort) - Writes back the JSON with `g_form.setValue('my_mrvs', JSON.stringify(rows))` ## Setup 1. Replace `CONTROLLING_VARIABLE` with your variable name. 2. Replace `MY_MRVS` with your MRVS variable name. 3. Adjust `COLUMNS_TO_CLEAR`, `UNIQUE_KEY`, and `SORT_BY` as needed. ## Notes - To clear the MRVS entirely, set `rows = []` before `setValue`. - Works with Catalog Client Scripts; no server call required. ## References - GlideForm API (client): `getValue`, `setValue`, `clearValue` https://www.servicenow.com/docs/bundle/zurich-api-reference/page/app-store/dev_portal/API_reference/GlideForm/concept/c_GlideFormAPI.html - Working with MRVS values on the client (community examples) https://www.servicenow.com/community/developer-articles/accessing-multi-row-variable-set-value-outside-the-multi-row/ta-p/2308876 ================================================ FILE: Client-Side Components/Catalog Client Script/Normalise and Reset a MRVS based on Variable Changes/mrvs_normalise_onchange.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading) return; var MRVS_NAME = 'MY_MRVS'; // your MRVS variable name var COLUMNS_TO_CLEAR = ['env', 'owner']; // MRVS column names to clear var UNIQUE_KEY = 'hostname'; // MRVS column that should be unique var SORT_BY = 'hostname'; // MRVS column to sort by try { var raw = g_form.getValue(MRVS_NAME); var rows = raw ? JSON.parse(raw) : []; if (!Array.isArray(rows)) rows = []; // Clear specified columns rows.forEach(function(row) { COLUMNS_TO_CLEAR.forEach(function(col) { if (row.hasOwnProperty(col)) row[col] = ''; }); }); // Deduplicate by UNIQUE_KEY if (UNIQUE_KEY) { var seen = {}; rows = rows.filter(function(row) { var key = String(row[UNIQUE_KEY] || '').toLowerCase(); if (!key || seen[key]) return false; seen[key] = true; return true; }); } // Sort (case-insensitive) if (SORT_BY) { rows.sort(function(a, b) { var A = String(a[SORT_BY] || '').toLowerCase(); var B = String(b[SORT_BY] || '').toLowerCase(); if (A < B) return -1; if (A > B) return 1; return 0; }); } g_form.setValue(MRVS_NAME, JSON.stringify(rows)); } catch (e) { console.error('MRVS normalise failed', e); } } ================================================ FILE: Client-Side Components/Catalog Client Script/Normalise and Reset a MRVS based on Variable Changes/variant_reset_specific_columns.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading) return; var MRVS_NAME = 'MY_MRVS'; var COLUMNS_TO_CLEAR = ['env', 'region']; var rows = []; try { rows = JSON.parse(g_form.getValue(MRVS_NAME) || '[]'); } catch (e) {} if (!Array.isArray(rows)) rows = []; rows.forEach(function(row) { COLUMNS_TO_CLEAR.forEach(function(col) { if (row.hasOwnProperty(col)) row[col] = ''; }); }); g_form.setValue(MRVS_NAME, JSON.stringify(rows)); } ================================================ FILE: Client-Side Components/Catalog Client Script/Onsubmit validation/Readme.md ================================================ This project adds pre-validation for hardware availability in ServiceNow Catalog Items. Before submitting a request, the system checks if the requested hardware is available in inventory and blocks submission if stock is insufficient. we can easy to extend other validations (budget, licenses, etc.).Improves user experience by validating before approval.Prevents unnecessary approvals and fulfillment. ================================================ FILE: Client-Side Components/Catalog Client Script/Onsubmit validation/on submit scriptinclude.JS ================================================ var HardwareValidationUtils = Class.create(); HardwareValidationUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, { validateHardware: function() { var hardware = this.getParameter('sysparm_hardware'); var qty = parseInt(this.getParameter('sysparm_quantity'), 10); if (!hardware || isNaN(qty)) { return 'Invalid input!'; } var gr = new GlideRecord('u_hardware_inventory'); if (gr.get(hardware)) { var availableQty = parseInt(gr.getValue('available_quantity'), 10); if (availableQty >= qty) { return 'OK'; } else { return 'Not enough stock available!'; } } return 'Hardware not found!'; }, type: 'HardwareValidationUtils' }); ================================================ FILE: Client-Side Components/Catalog Client Script/Onsubmit validation/submit validation client script.js ================================================ function onSubmit() { var hardware = g_form.getValue('hardware_name'); var qty = g_form.getValue('quantity'); var ga = new GlideAjax('HardwareValidationUtils'); ga.addParam('sysparm_name', 'validateHardware'); ga.addParam('sysparm_hardware', hardware); ga.addParam('sysparm_quantity', qty); ga.getXMLAnswer(function(response) { if (response !== 'OK') { alert(response); g_form.addErrorMessage(response); // Optional inline error g_form.setSubmit(false); // Prevent submission in Service Portal } else { g_form.setSubmit(true); // Allow submission } }); return false; } ================================================ FILE: Client-Side Components/Catalog Client Script/Open modal widget in an Onsubmit/README.md ================================================ Code snippet to stop submission of a form in an Onsubmit Client Script, use an asynchronous call, and open a Widget in Modal view. In the script provided, there are two buttons in the modal. The first continues with the submission to create a new record and the second one cancels it. We can use a Script Include to get some value that we want and based on that open the modal or continue with submission. ================================================ FILE: Client-Side Components/Catalog Client Script/Open modal widget in an Onsubmit/openmodal.js ================================================ function onSubmit() { if (g_scratchpad.isFormValid) return true; //We can do some check using a Client Callable script include var getAnswer = new GlideAjax('example'); getAnswer.addParam('sysparm_name', 'checkFor'); getAnswer.addParam('sysparm_input1', g_user.userID); getAnswer.addParam('sysparm_input2', g_form.getUniqueValue()); getAnswer.getXML(parsing); function parsing(response) { var answer = response.responseXML.documentElement.getAttribute('answer'); if (answer) { var data = JSON.parse(answer); if (data == true) { spModal.open({ title: "Test title", widget: "mywidget", buttons: [{ label: 'Close', value: 'close' }, { label: 'Create New Record', value: 'create' } ], size: 'md' }).then(function(answer) { //if button pressed is "create" then submit the form if (answer.value == 'create') { g_scratchpad.isFormValid = true; g_form.submit(); } }); } else { g_scratchpad.isFormValid = true; g_form.submit(); } } } //Dont submit the form return false; } ================================================ FILE: Client-Side Components/Catalog Client Script/PAN Validation/PAN Validation.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } var panNumber = g_form.getValue("pan_number"); //Get the PAN card information var panRegex = /^[A-Z]{5}[0-9]{4}[A-Z]{1}$/; // Regex for the PAN Card if (panRegex.test(panNumber)) { g_form.showFieldMsg("pan_number", "Valid PAN card number.", true); //Valid PAN card enterd populates this message } else { g_form.showErrorBox("pan_number", "InValid PAN card number.", true); //In Valid PAN card details enterd populate this message } } ================================================ FILE: Client-Side Components/Catalog Client Script/PAN Validation/README.md ================================================ PAN is a ten-digit unique alphanumeric number issued by the Income Tax Department. Indian PAN (Permanent Account Number) card based on its standardized format. A PAN number is a unique 10-character alphanumeric identifier issued by the Indian government, and it follows a specific structure: First 5 characters: Uppercase English letters (A-Z). Next 4 characters: Numeric digits (0-9). Last character: A single uppercase English letter (A-Z). ![image](https://github.com/user-attachments/assets/37a6490b-912e-499c-9c41-c0cdf79491a5) When we provide the information in the mentioned format it validates as a valid PAN Card Eg: ABCDE1234F Result ![image](https://github.com/user-attachments/assets/779757b9-2d46-439b-bc8c-a468a5c0264a) Mentioned formate was not used it will give an error message to the field Eg:abcde1234f ![image](https://github.com/user-attachments/assets/1dd214b8-aaf5-405d-aefb-8e46b786be3c) Eg: 1234ABCDEF ![image](https://github.com/user-attachments/assets/08f538b7-d1ac-4d26-8579-8342ca757c8c) ================================================ FILE: Client-Side Components/Catalog Client Script/Passport Validation/README.md ================================================ This OnChange Catalog Client Script is for validating passport number, date of issue, and date of expiry. It follows the specified rules(As per indian passport):- - The passport number should be 8 characters long, with the first character as an uppercase letter, the second and third characters as numbers (1-9 for the first digit, 0-9 for the second digit). - The date of expiry should be calculated based on the date of issue: - 1. If the user is an adult (18 years or older), the expiry date should be exactly 5 years from the date of issue. 2. If the user is under 18, the expiry date should be exactly 10 years from the date of issue. Passport Number Validation: The passportPattern uses a regular expression: ^[A-Z] – The first character must be an uppercase letter. [1-9][0-9] – The second and third characters are numbers; the first is between 1-9, and the second between 0-9. [A-Z0-9]{5}$ – The last five characters are alphanumeric. If the passport number does not match this pattern, the script displays an error message and clears the field. Date of Expiry Calculation: - After the date of issue and age are provided, the script calculates the expiry date by adding 5 years for adults (18 or older) or 10 years for minors (under 18). - The calculated expiry date is automatically set in the date_of_expiry field in the yyyy-MM-dd format. - Prompts are displayed if necessary fields like date_of_issue or age are missing before attempting the expiry date calculation. This Client Script will ensure that the entered passport information and expiry date meet the requirements, providing a seamless and guided experience for the user. ================================================ FILE: Client-Side Components/Catalog Client Script/Passport Validation/passportvalidity.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading) return; var passportNumber = g_form.getValue('passport_number'); var dateOfIssue = g_form.getValue('date_of_issue'); var age = parseInt(g_form.getValue('age'), 10); var dateOfExpiry = g_form.getValue('date_of_expiry'); // Passport Number Validation var passportPattern = /^[A-Z][1-9][0-9][A-Z0-9]{5}$/; if (passportNumber && !passportPattern.test(passportNumber)) { g_form.showFieldMsg('passport_number', "The entered number is invalid passport number format. It must be 8 characters long, start with an uppercase letter, followed by a number between 1-9, then 0-9, and the rest alphanumeric.", "error"); g_form.clearValue('passport_number'); } else { g_form.hideFieldMsg('passport_number'); } // Date of Expiry Calculation based on date of issue if (dateOfIssue && age) { var issueDate = new GlideDate(); issueDate.setValue(dateOfIssue); var expiryDate = new GlideDate(); expiryDate.setValue(issueDate); if (age >= 18) { expiryDate.addYears(5); // Adult - add 5 years } else { expiryDate.addYears(10); // Under 18 - add 10 years } g_form.setValue('date_of_expiry', expiryDate.getByFormat('yyyy-MM-dd')); // Set expiry date in correct format g_form.hideFieldMsg('date_of_expiry'); } else if (!dateOfIssue) { g_form.showFieldMsg('date_of_issue', "Please enter the Date of Issue first.", "info"); } else if (!age) { g_form.showFieldMsg('age', "Please enter your age first.", "info"); } } ================================================ FILE: Client-Side Components/Catalog Client Script/Password Validation Script/README.md ================================================ Description of the Combined Password Validation Script Purpose The script validates the password entered by the user in a ServiceNow catalog item form. It ensures that the password meets strong security criteria and does not include the user's first or last name, enhancing overall security. Validation Criteria Strong Password Requirements: At least 8 characters long. Contains at least one uppercase letter. Contains at least one lowercase letter. Contains at least one digit. Contains at least one special character (e.g., @$!%*?&). First and Last Name Restrictions: The password cannot contain the user's first name or last name. ================================================ FILE: Client-Side Components/Catalog Client Script/Password Validation Script/Script.js ================================================ function onSubmit() { // Get the password value from the field var password = g_form.getValue('password'); // Change 'password' to your field name // Get the first and last name values from the fields var firstName = g_form.getValue('first_name'); // Change 'first_name' to your field name var lastName = g_form.getValue('last_name'); // Change 'last_name' to your field name // Define the regex pattern for a strong password var passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/; // Check if the password contains the first or last name if (password.includes(firstName) || password.includes(lastName)) { // Display an error message if validation fails g_form.showFieldMsg('password', 'Password cannot contain your first or last name.', 'error'); return false; // Prevent form submission } // Validate the password against the pattern if (!passwordPattern.test(password)) { // Display an error message if validation fails g_form.showFieldMsg('password', 'Password must be at least 8 characters long, contain at least one uppercase letter, one lowercase letter, one digit, and one special character.', 'error'); return false; // Prevent form submission } return true; // Allow form submission if all validations pass } ================================================ FILE: Client-Side Components/Catalog Client Script/Percentage Symbol/readme.md ================================================ Sets the field value to the formatted percentage string (e.g., 45 becomes 45.00%). Retrieves the current value of the field. Removes any existing % symbol to avoid duplication. Validates the input to ensure it's a number. Converts the value to a float. Formats it to two decimal places and appends a % symbol. ================================================ FILE: Client-Side Components/Catalog Client Script/Percentage Symbol/script.js ================================================ /*Sets the field value to the formatted percentage string (e.g., 45 becomes 45.00%). Retrieves the current value of the field. Removes any existing % symbol to avoid duplication. Validates the input to ensure it's a number. Converts the value to a float. Formats it to two decimal places and appends a % symbol. */ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue == '') { return; } function formatPercent(value) { if (value === null || value === undefined || value === '' || isNaN(value)) { return ""; } var num = parseFloat(value); if (isNaN(num)) return ""; return num.toFixed(2) + "%"; } var des_amount = g_form.getValue("").replace("%",""); g_form.setValue("", formatPercent(des_amount)); } ================================================ FILE: Client-Side Components/Catalog Client Script/PopulateDropdown/README.md ================================================ ## Load / Populate the options for the second dropdown field (select box) based on what user chooses in the first dropdown ![alt text](demo_catalog.png) ### Use case You need to dynamically populate the options for a dropdown (select box) field following another field. For example, you need to create a catalog that has 2 fields: country and city, when the user chooses a country, the city field will only show the cities of that country. This will especially come in handy when you have hundreds of options to populate, and easier to maintain than using catalog UI policies 👍 ### Set up - On the catalog, create 2 select box fields, for example: `city` and `country` - Add the question choices for the 1st dropdown - In the 2nd dropdwon, add any value for the question choices to bypass the mandatory - Create new client script, on change, for the FIRST dropdown. Only applies on Catalog Item view. It is recommended to set the variables readonly on the RITM and SCTASK view. ================================================ FILE: Client-Side Components/Catalog Client Script/PopulateDropdown/script.js ================================================ // Please check readme for the set up var data = { Japan: ["Tokyo", "Osaka"], USA: ["New York", "Chicago", "San Diego"], }; function onChange(control, oldValue, newValue, isLoading) { var secondDropdown = "city"; g_form.clearOptions(secondDropdown); // Remove all the options of the 2nd dropdown // When user switch to 'None' in country or when the form is loaded if (newValue == "" || isLoading) { return; } var cities = data[newValue]; if (cities && cities.length) { cities.forEach(function (value) { g_form.addOption(secondDropdown, value, value); }); } } ================================================ FILE: Client-Side Components/Catalog Client Script/Prevent duplicate records on MRVS/README.md ================================================ # Prevent duplicate records to be selected in Multi row variable set We have many ways to do this but this is bit unique compare to what you find in community, you can do it in one single script within the Variable set. Of course ServiceNow introduce a new feature in Quebec and have Unique checkbox field introuduced but to have a custom info message you need to go with the custom script. [ServiceNow Docs to disallow duplicate values](https://docs.servicenow.com/bundle/quebec-servicenow-platform/page/product/service-catalog-management/task/t_CreateAVariableForACatalogItem.html) [Click here for the script](script.js) ================================================ FILE: Client-Side Components/Catalog Client Script/Prevent duplicate records on MRVS/script.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue == '') { return; } var MRVS_FIELD = "field_name_here"; //Multi row variable set name var MRVS = (g_service_catalog.parent.getValue(MRVS_FIELD).length != 0) ? JSON.parse(g_service_catalog.parent.getValue(MRVS_FIELD)) : []; //If the MRVS is empty - exit if(MRVS.length == 0)return; var valueExists = MRVS.some(function(obj){ return obj.Variable_Name == newValue; // Reference variable name which needs to be unique }); if(valueExists){ g_form.showFieldMsg(MRVS_FIELD , "Field must be unique"); } } ================================================ FILE: Client-Side Components/Catalog Client Script/Real time count of letters/Count letters.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading) { return; } var maxChars = 100;//count of charaters var currentLength = newValue.length; // Clear previous messages g_form.clearMessages(); // Show info message g_form.addInfoMessage('Character count: ' + currentLength + ' / ' + maxChars); if (currentLength > maxChars) { // Show error message g_form.addErrorMessage('Character limit exceeded! Please shorten your text.'); g_form.showFieldMsg('short_description', 'Too many characters!', 'error'); // Make field mandatory to block submission g_form.setMandatory('short_description', true); } else { // Remove mandatory if valid g_form.setMandatory('short_description', false); } } ================================================ FILE: Client-Side Components/Catalog Client Script/Real time count of letters/readme.md ================================================ This onChange Catalog Client Script displays the current character count for a text field and enforces a maximum limit by showing error messages and making the field mandatory to prevent form submission when exceeded. ================================================ FILE: Client-Side Components/Catalog Client Script/Remove reference icon from portal/README.md ================================================ # Remove reference icon from portal using Catalog Client script of a Catalog Item This catalog client script is design to remove the reference icon from the Portal for any reference field record. This needs to be configured for each Catalog Item where the reference field is being used. * [Click here for script](remove-reference-icon-from-portal-onLoad.js) ================================================ FILE: Client-Side Components/Catalog Client Script/Remove reference icon from portal/remove-reference-icon-from-portal-onLoad.js ================================================ function onLoad() { setTimeout(function() { var referenceElement = top.document.getElementsByClassName('btn btn-default bg-white lookup')[0]; if (referenceElement != undefined || referenceElement != null) referenceElement.remove(); }, 500); } ================================================ FILE: Client-Side Components/Catalog Client Script/Restrict Number of rows in Multi Row Variable/README.md ================================================ ## restrict_multi_row.js Use this to restrict multi row variable set rows to 1. this value can be changed to any number of rows as requirement. ================================================ FILE: Client-Side Components/Catalog Client Script/Restrict Number of rows in Multi Row Variable/restrict_multi_row.js ================================================ function onLoad() { var field = g_form.getField("mrvs_variable_set_name"); if (field != null) { field.max_rows_size = 1; } } ================================================ FILE: Client-Side Components/Catalog Client Script/Return Date Validation/README.md ================================================ This piece of code was written as a part of an usecase where the return date value validation was expected to be after start date as well as within 6 months from the start date. This code runs in an OnChange catalog client script for the field 'u_return_date' and validates the return date(u_return_date) in a ServiceNow Catalog item form to ensure: 1. A start date(u_start_date) is entered before setting a return date(u_return_date). 2. The return date is within 6 months after the start date. 3. Return date must be after the start date, it can't be same as it or before it. Let’s say with an example: a) u_start_date = 2025-10-01 You enter u_return_date = 2026-04-15 Steps: a)Difference = 196 days → More than 180 days b)Result: u_return_date is cleared and error shown: “Select Return Date within 6 months from Start Date” b) u_start_date = 2025-10-02 You enter u_return_date = 2025-10-01 Steps: a)Difference = -1 day → Return date put as 1 day before start date b)Result: u_return_date is cleared and error shown: “Select Return Date in future than Start Date” ================================================ FILE: Client-Side Components/Catalog Client Script/Return Date Validation/validateReturndate.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue == '') { return; } var u_start_date = g_form.getValue('u_start_date'); //start date validation to check to see whether filled or not if (!u_start_date) { g_form.clearValue('u_return_date'); g_form.showFieldMsg('u_return_date', 'Please enter start date', 'error'); } else { var startTime = getDateFromFormat(u_start_date, g_user_date_format); //converting to js date object var returnTime = getDateFromFormat(newValue, g_user_date_format); var selectedStartDate = new Date(startTime); var returnDate = new Date(returnTime); var returnDateDifference = (returnDate - selectedStartDate) / 86400000; //converting the diff between the dates to days by dividing by 86400000 if (returnDateDifference > 180) { g_form.clearValue('u_return_date'); g_form.showFieldMsg('u_return_date', 'Select Return Date within 6 months from Start Date', 'error'); } else if (returnDateDifference < 1) { g_form.clearValue('u_return_date'); g_form.showFieldMsg('u_return_date', 'Select Return Date in future than Start Date', 'error'); } } } ================================================ FILE: Client-Side Components/Catalog Client Script/Reusable GlideAjax Client Script/DynamicTableQueryUtil.js ================================================ var DynamicTableQueryUtil = Class.create(); DynamicTableQueryUtil.prototype = Object.extendsObject(AbstractAjaxProcessor, { getTableRow: function() { var tableName = this.getParameter('sysparm_table_name'); var keyField = this.getParameter('sysparm_key_field'); var keyValue = this.getParameter('sysparm_key_value'); var fieldsParam = this.getParameter('sysparm_fields'); var limitFields = !JSUtil.nil(fieldsParam); var desiredFields = limitFields ? fieldsParam.split(',') : []; var result = {}; var tableObj = {}; var gr = new GlideRecord(tableName); // Use addQuery for non-sys_id fields if (keyField === 'sys_id') { if (!gr.get(keyValue)) { return null; } } else { gr.addQuery(keyField, keyValue); gr.query(); if (!gr.next()) { return null; } } // Handle variables (if present) if (gr.variables) { for (var key in gr.variables) { if (!JSUtil.nil(gr.variables[key])) { var variableObj = gr.variables[key]; tableObj['variables.' + key] = { fieldDisplayVal: variableObj.getDisplayValue() || String(variableObj), fieldVal: String(variableObj) }; } } } // Handle standard fields var fields = gr.getFields(); for (var i = 0; i < fields.size(); i++) { var field = fields.get(i); var fieldName = field.getName(); tableObj[fieldName] = { fieldDisplayVal: field.getDisplayValue() || String(field), fieldVal: String(field) }; } // Add sys_id explicitly tableObj['sys_id'] = { fieldDisplayVal: 'Sys ID', fieldVal: gr.getUniqueValue() }; // Filter fields if requested if (limitFields) { desiredFields.forEach(function(field) { field = field.trim(); if (tableObj[field]) { result[field] = tableObj[field]; } }); } else { result = tableObj; } return new JSON().encode(result); } }); ================================================ FILE: Client-Side Components/Catalog Client Script/Reusable GlideAjax Client Script/Readme.md ================================================ This solution provides a generic and reusable GlideAjax-based client-server interaction in ServiceNow that allows querying any table by passing: Table name Key field and value Desired fields to retrieve It dynamically returns field values from the server and populates them on the form, making it ideal for use cases like CMDB enrichment, entitlement lookups, or dynamic form population. 1. Client Script (onChange) Triggers on field change. Sends parameters to the Script Include via GlideAjax. Receives JSON response and sets target field value. Parameters: sysparm_table_name: Table to query (e.g., sys_user) sysparm_key_field: Field to match (e.g., sys_id) sysparm_key_value: Value to match sysparm_fields: Comma-separated list of fields to retrieve 2. Script Include: DynamicTableQueryUtil Processes incoming parameters. Queries the specified table and retrieves requested fields. Supports both standard fields and catalog item variables. Returns a JSON object with field values and display values. ================================================ FILE: Client-Side Components/Catalog Client Script/Reusable GlideAjax Client Script/clientscript.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue === '') { return; } // Define parameters dynamically var tableName = 'sys_user'; // Change as needed var keyField = 'sys_id'; // Change as needed var fieldsToFetch = 'email'; // Comma-separated list var targetField = 'user'; // Field to populate var ga = new GlideAjax('DynamicTableQueryUtil'); ga.addParam('sysparm_name', 'getTableRow'); ga.addParam('sysparm_table_name', tableName); ga.addParam('sysparm_key_field', keyField); ga.addParam('sysparm_key_value', newValue); ga.addParam('sysparm_fields', fieldsToFetch); ga.getXML(function(response) { var answer = response.responseXML.documentElement.getAttribute("answer"); if (!answer) { alert('No response from Script Include'); return; } var parsedAnswer = JSON.parse(answer); if (parsedAnswer[fieldsToFetch]) { g_form.setValue(targetField, parsedAnswer[fieldsToFetch]['fieldVal']); } else { alert('error'); } }); } ================================================ FILE: Client-Side Components/Catalog Client Script/Rounding Money or Price Field/README.md ================================================ This script is OnChange, and will automatically round the selected field to [x] value. The script can either be applied to a Catalog Item, or a specific Variable in a Var Set. For example, if prompting a user for a cost, and said cost should only be in multiples of $5. Any instance of `[VAR NAME]` in these files should be replaced with the name of the variable that the script is being applied to. More information and a full write-up available on [Community](https://community.servicenow.com/community?id=community_article&sys_id=75ab01271bac5d10c17111751a4bcb40). ================================================ FILE: Client-Side Components/Catalog Client Script/Rounding Money or Price Field/catalog_client_script.js ================================================ function onChange(control, oldValue, newValue, isLoading) { //Value for increment rounding var roundTo = [ROUNDING INCREMENT]; //Standard onChange code + if the value is now empty, return if (isLoading || newValue == '') return; //Get the existing value, and remove the $, if it exists var existingVal = newValue.replace('$', ''); //Make sure the field contains a valid numerical value if(!Number.isInteger(parseInt(existingVal))) return; //Round up if not a multiple of the increment if(existingVal % roundTo !=0) { //Calculate the rounded value var newVal = (Math.ceil(existingVal/roundTo)*roundTo); //Set the value of the field to the new, rounded value g_form.setValue('[VAR_NAME]', "$"+ newVal); //Show a message beneath the field indicating it was rounded, and the new value g_form.showFieldMsg('[VAR_NAME]', "Rounded $" + existingVal + " to $" + newVal); } } ================================================ FILE: Client-Side Components/Catalog Client Script/Rounding Money or Price Field/catalog_client_script_config.md ================================================ Name: Round [Field] to nearest [Rounding Amount] UI Type: All Type: onChange Variable name: [VAR NAME] Script: see `catalog_client_script.js` for code. ================================================ FILE: Client-Side Components/Catalog Client Script/Schedule Request/Readme.JS ================================================ This project allows users to schedule a Service Catalog request for a future date and time. Instead of submitting immediately, the request is stored and automatically submitted later using a Scheduled Job ================================================ FILE: Client-Side Components/Catalog Client Script/Schedule Request/scheduled client script.js ================================================ function onSubmit() { var scheduledTime = g_form.getValue('scheduled_time'); var currentTime = new Date().toISOString(); if (scheduledTime > currentTime) { var ga = new GlideAjax('ScheduledRequestHelper'); ga.addParam('sysparm_name', 'storeScheduledRequest'); ga.addParam('sysparm_item', g_form.getUniqueValue()); ga.addParam('sysparm_time', scheduledTime); ga.getXMLAnswer(function(response) { alert('Your request has been scheduled for: ' + scheduledTime); }); return false; // Prevent immediate submission } return true; // Submit immediately if time is now or past } ================================================ FILE: Client-Side Components/Catalog Client Script/Schedule Request/scheduled scriptinclude.JS ================================================ var ScheduledRequestHelper = Class.create(); ScheduledRequestHelper.prototype = Object.extendsObject(AbstractAjaxProcessor, { storeScheduledRequest: function() { var itemID = this.getParameter('sysparm_item'); var scheduledTime = this.getParameter('sysparm_time'); var record = new GlideRecord('x_snc_scheduled_requests'); // Custom table record.initialize(); record.catalog_item = itemID; record.scheduled_time = scheduledTime; record.insert(); return 'Scheduled successfully'; } }); ================================================ FILE: Client-Side Components/Catalog Client Script/Schedule Request/scheduled_job.JS ================================================ var now = new GlideDateTime(); var gr = new GlideRecord('x_snc_scheduled_requests'); gr.addQuery('scheduled_time', '<=', now); gr.query(); while (gr.next()) { var request = new GlideRecord('sc_request'); request.initialize(); request.requested_for = gr.requested_for; request.cat_item = gr.catalog_item; request.insert(); gr.deleteRecord(); // Remove after submission } ================================================ FILE: Client-Side Components/Catalog Client Script/Set User Field Values on Load/README.md ================================================ On Load Catalog client script is created to auto set the field values and make that field read only - Navigate to your instance -> App Navigator > Open Catalog CLient Script [catalog_script_client] - Set following field Values - Name: xyz - Applies to: A Catalog item - Type: onLoad - Catalog item: Select your Catalog item - UI Type: All - Isolated script: Checked - Create the script as per script.js file. ================================================ FILE: Client-Side Components/Catalog Client Script/Set User Field Values on Load/script.js ================================================ /* Set user field value on load using catalog cleint script and make the field readonly */ function onLoad() { var user_id = g_user.userID; g_form.setValue('field_name', user_id); g_form.setReadOnly('field_name', true); } ================================================ FILE: Client-Side Components/Catalog Client Script/Set and Lock Variable by Group/README.md ================================================ **Set and Lock Variable by Group** This solution provides a secure and dynamic way to control data entry on a Service Catalog form based on the user's group membership. It is typically used to pre-fill and lock certain justification or approval bypass fields for authorized users (like managers or executive staff), improving their efficiency while maintaining an accurate audit trail. This functionality requires a combined Client-side (Catalog Client Script) and Server-side (Script Include) approach to ensure the group check is done securely. ================================================ FILE: Client-Side Components/Catalog Client Script/Set and Lock Variable by Group/set_lock_variable_by_grp.js ================================================ // onload Catalog Client Script with Catalog Name function onLoad() { var variableName = 'bypass_approval_reason'; var targetGroupName = 'ServiceNow Support'; // The group authorized to skip this step var ga = new GlideAjax('UserUtils'); ga.addParam('sysparm_name', 'isMemberOf'); ga.addParam('sysparm_group_name', targetGroupName); ga.getXMLAnswer(checkAndLockVariable); function checkAndLockVariable(response) { var isMember = response; if (isMember == 'true') { var message = 'Value set and locked due to your ' + targetGroupName + ' membership.'; var setValue = 'Bypassed by authorized ' + targetGroupName + ' member.'; g_form.setValue(variableName, setValue); g_form.setReadOnly(variableName, true); g_form.showFieldMsg(variableName, message, 'info'); } else { g_form.setReadOnly(variableName, false); } } } //Script Include var UserUtils = Class.create(); UserUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, { isMemberOf: function() { var groupName = this.getParameter('sysparm_group_name'); var isMember = gs.getUser().isMemberOf(groupName); return isMember.toString(); }, type: 'UserUtils' }); ================================================ FILE: Client-Side Components/Catalog Client Script/Set fields from URL Parameter 2/CatalogClientScript.js ================================================ function onLoad() { var taskId = getParameterValue("taskid"); if (taskId != "" && taskId != null && taskId != undefined) { console.log('=== CAMACHO Task id: ' + taskId); var gaGetTaskNumber = new GlideAjax('UtilsAjax'); gaGetTaskNumber.addParam('sysparm_name', 'getTaskNumber'); gaGetTaskNumber.addParam('sysparm_task_id', taskId); gaGetTaskNumber.getXMLAnswer(setmyFormValue); } } function setmyFormValue(answer) { //console.log('=== CAMACHO Entered setmyFormValue'); if (answer) { var obj = JSON.parse(answer); var numero = obj.number.toString(); console.log(numero); g_form.setValue('task_number', numero); } } function getParameterValue(name) { var url = top.location.href; var value = new URLSearchParams(url).get(name); if (value) { return value; } else { return null; } } ================================================ FILE: Client-Side Components/Catalog Client Script/Set fields from URL Parameter 2/KMXOUtils.js ================================================ var KMXOUtils = Class.create(); KMXOUtils.prototype = { initialize: function() { }, /* * Receives a um sys_id and returns a Task table field value * * @param {String} - taskId * @return {Object} */ getTaskNumber: function(taskId) { if (taskId != "" && taskId != null && taskId != undefined) { var grTask = new GlideRecord('x_770214_consultor_rwd_activity'); if (grTask.get(taskId)) { var number = grTask.getValue('number'); var obj = {}; obj["number"] = number; return obj; } else { return {}; } } }, type: 'KMXOUtils' }; ================================================ FILE: Client-Side Components/Catalog Client Script/Set fields from URL Parameter 2/README.md ================================================ # Set fields on a catalog item from URL parameters. The mission was to get a sys_id from the URL, query a record and return a value to the front-end. In the front-end we have a Record Producer (RP) with a String field. On the onLoad event, we want to populate the field in order to show the value retrieved from the back-end. The Use Case is defined so let's go to the step by step process. 1. Create a Util class in the back-end Our Utils class will be a Script Include that receives a sys_id and returns a String. [KMXOUtils](KMXOUtils.js) 2. Create a class to provide access for the Front-End 2.1. To provide access in this class, the parameter Client callable should be True (checked) [UtilsAjax](UtilsAjax.js) 3. I'll suppose that you have a String field called task_number in your RP 3.1. Create a Catalog Client Script (Type: OnLoad) to get the URL parameter and call the back-end class: [CatalogClientScript](CatalogClientScript.js) ================================================ FILE: Client-Side Components/Catalog Client Script/Set fields from URL Parameter 2/UtilsAjax.js ================================================ var UtilsAjax = Class.create(); UtilsAjax.prototype = Object.extendsObject(global.AbstractAjaxProcessor, { getTaskNumber: function() { var taskId = this.getParameter('sysparm_task_id'); gs.debug('=== Camacho UtilsAjax = Received the sys_id ' + taskId); return JSON.stringify(new KMXOUtils().getTaskNumber(taskId)); }, type: 'UtilsAjax' }); ================================================ FILE: Client-Side Components/Catalog Client Script/Set fields from URL Parameters/README.md ================================================ # Set fields on a catalog item from URL parameters. This only works on both the classic ui and service portal. To use this you must provide the technical name as a url parameter and then provide the value you would like set. This script is also console logging the techcnical names if you don't have them handy. Reference fields require using the sys_id. ### Example For the OOTB Password Reset catalog item it has a field "Whose password needs to be reset?" with a technical value of "caller_id" after adding this script you could use the below url parameter to auto populate the form with Abel Tuter. /sp?id=sc_cat_item&sys_id=29a39e830a0a0b27007d1e200ad52253&**caller_id=62826bf03710200044e0bfc8bcbe5df1** ================================================ FILE: Client-Side Components/Catalog Client Script/Set fields from URL Parameters/script.js ================================================ function onLoad() { try{ // Classic UI var pFields = g_form.nameMap; console.log(pFields); pFields.forEach(function(field){ if(getParam(field.prettyName)){ g_form.setValue(field.prettyName, getParam(field.prettyName)); } }); }catch(e){ // Service Portal or Mobile var fields = g_form.getEditableFields(); console.log(fields); fields.forEach(function(field){ if(getParam(field)){ g_form.setValue(field, getParam(field)); } }); } } function getParam(name){ var url = new URL(top.location); return url.searchParams.get(name); } ================================================ FILE: Client-Side Components/Catalog Client Script/Validate a Credit Card Number/README.md ================================================ **Description of the Credit Card Number Validation Script** Purpose The script validates a credit card number entered by the user in a ServiceNow form. It checks if the number is a valid 16-digit credit card number using a combination of a regular expression and the Luhn algorithm for basic validation. **Validation Criteria** Format: The credit card number must consist of exactly 16 digits. **Luhn Algorithm:** The script implements the Luhn algorithm to determine if the credit card number is potentially valid. This algorithm helps catch common errors in credit card numbers, such as transposed digits. ================================================ FILE: Client-Side Components/Catalog Client Script/Validate a Credit Card Number/Script.js ================================================ function onSubmit() { var cardNumber = g_form.getValue('credit_card'); // Change 'credit_card' to your field name var cardPattern = /^\d{16}$/; // Simple pattern for 16-digit cards if (!cardPattern.test(cardNumber) || !isValidCardNumber(cardNumber)) { g_form.showFieldMsg('credit_card', 'Please enter a valid 16-digit credit card number.', 'error'); return false; } return true; } function isValidCardNumber(number) { var sum = 0; var alternate = false; for (var i = number.length - 1; i >= 0; i--) { var n = parseInt(number.charAt(i), 10); if (alternate) { n *= 2; if (n > 9) n -= 9; } sum += n; alternate = !alternate; } return sum % 10 === 0; } ================================================ FILE: Client-Side Components/Catalog Client Script/catalog Draft/Readm.md ================================================ This project implements an Auto Save Draft feature for ServiceNow Catalog Items. It automatically saves the user’s progress (form variables) every few minutes to prevent data loss if the session times out or the browser closes. it Prevents data loss during long form filling. features Auto-save catalog form data every 2 minutes. Stores draft data in a custom table. Restores saved data when the user reopens the catalog item. Works in Service Portal ================================================ FILE: Client-Side Components/Catalog Client Script/catalog Draft/Script include.JS ================================================ var CatalogDraftUtils = Class.create(); CatalogDraftUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, { saveDraft: function() { var userId = gs.getUserID(); var catalogItem = this.getParameter('sysparm_catalog_item'); var draftData = this.getParameter('sysparm_draft_data'); var gr = new GlideRecord('u_catalog_draft'); gr.addQuery('user', userId); gr.addQuery('catalog_item', catalogItem); gr.query(); if (gr.next()) { gr.variables_json = draftData; gr.last_saved = new GlideDateTime(); gr.update(); } else { gr.initialize(); gr.user = userId; gr.catalog_item = catalogItem; gr.variables_json = draftData; gr.last_saved = new GlideDateTime(); gr.insert(); } return 'Draft saved successfully'; }, type: 'CatalogDraftUtils' }); ================================================ FILE: Client-Side Components/Catalog Client Script/catalog Draft/client script.js ================================================ function onLoad() { console.log('Auto Save Draft initialized'); setInterval(function() { var draftData = { hardware_name: g_form.getValue('hardware_name'), quantity: g_form.getValue('quantity') }; var ga = new GlideAjax('CatalogDraftUtils'); ga.addParam('sysparm_name', 'saveDraft'); ga.addParam('sysparm_catalog_item', g_form.getValue('sys_id')); // Catalog item sys_id ga.addParam('sysparm_draft_data', JSON.stringify(draftData)); ga.getXMLAnswer(function(response) { console.log('Draft saved: ' + response); }); }, 120000); // Every 2 minutes } ================================================ FILE: Client-Side Components/Catalog Client Script/onCellEdit Catalog Task State Change Restriction/README.md ================================================ # Catalog Task state should not be closed from list edit The onCellEdit Client Script type is used for lists rather than forms and here it is being used so that the task should not be closed from the list edit without opening the form and filling other mandatory details like worknotes etc. ================================================ FILE: Client-Side Components/Catalog Client Script/onCellEdit Catalog Task State Change Restriction/script.js ================================================ function onCellEdit(sysIDs, table, oldValues, newValue, callback) { var saveAndClose = true; //Type appropriate comment here, and begin script below // here the values are 7|closed skipped, 3|closed complete and 4|closed incomplete if(newValue == 7 || newValue == 3 || newValue == 4) { saveAndClose = false; alert('you cannot update from list'); } callback(saveAndClose); } ================================================ FILE: Client-Side Components/Catalog Client Script/previous Request/Readme.MD ================================================ Show previous request ON requested for selection This feature enhances the Service Catalog experience by displaying previous requests for the selected Requested For user. When a user selects the Requested For variable in a catalog item form, a confirmation message appears showing the last few requests created for that user. ================================================ FILE: Client-Side Components/Catalog Client Script/previous Request/previous request client script.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue == '') return; var ga = new GlideAjax('PreviousRequestsUtils'); ga.addParam('sysparm_name', 'getPreviousRequests'); ga.addParam('sysparm_requested_for', newValue); ga.getXMLAnswer(function(response) { var requests = JSON.parse(response); if (requests.length === 0) { alert('No previous requests found for this user.'); } else { var message = 'Previous Requests:\n\n'; requests.forEach(function(req) { message += 'Number: ' + req.number + ' | Item: ' + req.item + ' | Date: ' + req.date + '\n'; }); if (confirm(message + '\nDo you want to continue?')) { // User clicked OK } else { // User clicked Cancel g_form.setValue('requested_for', oldValue); } } }); } ================================================ FILE: Client-Side Components/Catalog Client Script/previous Request/previous request script include.js ================================================ var PreviousRequestsUtils = Class.create(); PreviousRequestsUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, { getPreviousRequests: function() { var requestedFor = this.getParameter('sysparm_requested_for'); var result = []; var gr = new GlideRecord('sc_req_item'); gr.addQuery('requested_for', requestedFor); gr.orderByDesc('sys_created_on'); gr.setLimit(5); // Show last 5 requests gr.query(); while (gr.next()) { result.push({ number: gr.number.toString(), item: gr.cat_item.getDisplayValue(), date: gr.sys_created_on.getDisplayValue() }); } return JSON.stringify(result); } }); ================================================ FILE: Client-Side Components/Catalog Client Script/spModal for Sweet Alerts/readme.md ================================================ In ServiceNow, Open catalog client Scripts [catalog_script_client] and paste the code snippet of [spModalSweetAlerts.js] file. Setup: 1. A catalog item having variable name Rewards[rewards] of type 'Select Box'(include none as true) and 2 choices(Yes and No) 2. A Single line type field named 'Reward Selected' [reward_selected] which will hold the value selected by user from the spModal popup. 3. The onLoad catalog client script setup as below: 4. Type: onChange 5. Variable: rewards (as per step 1) 6. Script: [[spModalSweetAlerts.js]] Screenshots: image Rewards selected as 'Yes' image From the spModal popup select anyone of the reward, it should be populated in the Reward Selected field. Along with that a message shows the same of the selection. image ================================================ FILE: Client-Side Components/Catalog Client Script/spModal for Sweet Alerts/spModalSweetAlerts.js ================================================ function onChange(control, oldValue, newValue) { if (newValue == 'Yes') { spModal.open({ title: "Reward Type", message: "Please select the category of Reward", buttons: [{ label: "Star Performer", value: "Star Performer" }, { label: "Emerging Player", value: "Emerging Player" }, { label: "High Five Award", value: "High Five Award" }, { label: "Rising Star", value: "Rising Star" } ] }).then(function(choice) { if (choice && choice.value) { g_form.addInfoMessage('Selected Reward: '+ choice.label); g_form.setValue('reward_selected', choice.value); } }); } else { g_form.clearValue('reward_selected'); } } ================================================ FILE: Client-Side Components/Client Scripts/Abort action when description is empty/Code.js ================================================ function onSubmit() { //Type appropriate comment here, and begin script below var description = g_form.getValue('description'); var state = g_form.getValue('state'); if ((!description) && (state == 'completed')) { g_form.addErrorMessage('Please provide Description Value, Description Cannot be empty'); return false; } } ================================================ FILE: Client-Side Components/Client Scripts/Abort action when description is empty/ReadMe.md ================================================ When an Incident record is being closed, the system should validate that the Description field is not empty and state is completed. If the Description field is blank and state is completed, the record submission (update) should be aborted, and the user should be prompted to provide a description before closing the incident. ================================================ FILE: Client-Side Components/Client Scripts/Abort direct incident closure without Resolve State/readme.md ================================================ 📘 README — Incident State Validation (Client Script) Overview This Client Script enforces the correct ITIL incident lifecycle by preventing users from directly closing an incident without first transitioning it through the Resolved state. If a user attempts to move the state directly to Closed Complete, the system reverts the change and displays a notification. What This Code Does Monitors changes to the state field on Incident form Checks if new selection is trying to skip the Resolved step Reverts state to its previous value when the rule is violated Alerts the user with a clear and guided message Refreshes the form to maintain data consistency Usage Instructions Create a Client Script on the Incident table Type: onChange Field Name: state Paste the script under the script section Test by trying to directly move an Incident to Closed Complete S cript Requirements State values must be configured as: 6 → Resolved 7 → Closed Complete Script runs only on Incident records Must be active in applicable ITIL views Notes for Developers This code supports clean transition handling for ITSM workflows Helps enforce process compliance without server-side overhead Recommended for environments requiring strict closure governance ================================================ FILE: Client-Side Components/Client Scripts/Abort direct incident closure without Resolve State/script.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { // Prevent execution during form load or when value is empty if (isLoading || newValue === '') { return; } // Dummy values for state comparison // Assuming: // 6 = Resolved // 7 = Closed Complete var RESOLVED = 6; var CLOSED = 7; // Prevent direct move to Closed without passing through Resolved if (newValue == CLOSED && oldValue != RESOLVED) { // Reset to previous state g_form.setValue('state', oldValue); // Show validation warning g_form.addErrorMessage( 'Direct closure is not allowed. Please first move the incident to Resolved state.' ); // Reload page after showing error setTimeout(function() { location.reload(); }, 3000); } } ================================================ FILE: Client-Side Components/Client Scripts/Add Field Decoration/Add Field Decoration.js ================================================ function onLoad() { // Adding Field Decorators g_form.addDecoration("caller_id", "icon-user-profile", "The Requester"); } ================================================ FILE: Client-Side Components/Client Scripts/Add Field Decoration/README.md ================================================ # Client Script - Add Field Decoration A client script that added field decoration. ## Usage - Create a new OnLoad script - Copy this script into it - Change the field name, icon and the message ================================================ FILE: Client-Side Components/Client Scripts/Add Image to Field Based on Company/AddImageToFieldBasedOnCompany.js ================================================ function onLoad() { //Type appropriate comment here, and begin script below //Get element of the field var requestedForLabel = $('element.sc_req_item.request.requested_for'); var company = g_scratchpad.company; //Set the Position of the image var bgPosition = "5% 45%"; var image = ''; //Set the image based on the Company switch (company) { case 'company 1': image = 'url(company1.png)'; break; case 'company 2': image = 'url(company2.png)'; break; case 'company 3': image = 'url(company3.png)'; break; default: image = ''; } //Update the Field Label requestedForLabel.down('label').setStyle({ backgroundImage: image, backgroundRepeat: "no-repeat", backgroundPosition: bgPosition, paddingLeft: '30px' }); } ================================================ FILE: Client-Side Components/Client Scripts/Add Image to Field Based on Company/README.md ================================================ # Add Image to Field Based on Company From the User Profile get the Company associated to the User using a Display Business Rule and putting the value in the scratchpad. After that use the scratchpad value in the Client Script to check the company name and then associate the image accordingly. You would need to add all your images to the ServiceNow instance first with the correct names. ================================================ FILE: Client-Side Components/Client Scripts/Add Image to Field Based on Company/SetCompanyScratchPadValue.js ================================================ (function executeRule(current, previous /*null when async*/) { // Set the Scratchpad value for Company which would be used in the Client Script g_scratchpad.company = current.request.requested_for.company.getDisplayValue(); })(current, previous); ================================================ FILE: Client-Side Components/Client Scripts/Adding Placeholder on Resolution Notes/README.md ================================================ # Adding Placeholder Text in Resolution Notes To maintain consistency and ensure specific information is captured in resolution notes, process owners may require fulfillers to follow a predefined format when resolving tickets. By adding **placeholder** text in the resolution notes, fulfillers are reminded of the required information(e.g., Root cause, Steps taken, Resolution provided), reducing the risk of missing important details. The placeholder disappears as soon as the fulfiller begins entering their notes, ensuring it doesn't interfere with their input. ## How It Works ### When? - The placeholder text is automatically added when the state of the ticket changes to Resolved (6). ### What Happens? - A placeholder text appears in the resolution notes field to guide the fulfiller. - As soon as the fulfiller starts typing, the placeholder disappears. - This ensures consistency and alignment with the process requirements. ================================================ FILE: Client-Side Components/Client Scripts/Adding Placeholder on Resolution Notes/script.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } var res = g_form.getElement('close_notes'); if (newValue == 6) res.placeholder = "1. Root Cause" + "\n" + "2. Steps taken" + "\n" + "3. Resolution provided"; //Placeholder text as required else res.placeholder = ""; } ================================================ FILE: Client-Side Components/Client Scripts/Auto Update Priority based on Impact and Urgency/readme.md ================================================ 🧩 Readme : Client Script: Auto Priority Update Based on Impact and Urgency 📘 Overview This client script automatically updates the Priority field on the Incident form whenever the Impact or Urgency value changes. It follows the ITIL standard mapping to ensure the correct priority is always set automatically, improving data accuracy and efficiency for service desk agents. ⚙️ Script Details Field Value Name Auto Priority Update based on Impact and Urgency Type onChange Applies to Table Incident Applies on Fields impact, urgency UI Type All (Classic, Mobile, Workspace) Active ✅ Yes Condition Leave blank 💻 Script Code // ========================================================================== // Script Name: Auto Priority Update based on Impact and Urgency // Table: Incident // Type: onChange | Fields: impact, urgency // UI Type: All // Version: 2025 Production Ready // ========================================================================== function onChange(control, oldValue, newValue, isLoading, isTemplate) { // Skip execution if form is loading or field is empty if (isLoading || newValue == '') { return; } // Get Impact and Urgency values var impact = g_form.getValue('impact'); var urgency = g_form.getValue('urgency'); // Define Priority Matrix (ITIL standard) var priorityMatrix = { '1': { '1': '1', '2': '2', '3': '3' }, '2': { '1': '2', '2': '3', '3': '4' }, '3': { '1': '3', '2': '4', '3': '5' } }; // Find the new Priority var newPriority = priorityMatrix[impact]?.[urgency]; // Update the Priority field if valid if (newPriority) { if (g_form.getValue('priority') != newPriority) { g_form.setValue('priority', newPriority); g_form.showFieldMsg('priority', 'Priority auto-updated based on Impact and Urgency', 'info'); } } else { // Optional: Clear Priority if invalid combination is selected g_form.clearValue('priority'); g_form.showFieldMsg('priority', 'Invalid Impact/Urgency combination — priority cleared', 'error'); } } 🧠 How It Works The script runs automatically when Impact or Urgency changes. It checks the ITIL-based matrix to determine the correct Priority. If a valid combination is found, the Priority field updates automatically. A small info message appears to confirm the update. 🔢 ITIL Mapping Table Impact Urgency Resulting Priority 1 (High) 1 (High) 1 (Critical) 1 2 2 1 3 3 2 1 2 2 2 3 2 3 4 3 1 3 3 2 4 3 3 5 ✅ Benefits Automatically enforces ITIL priority standards Reduces manual effort and user errors Ensures consistency in priority calculation Compatible with Classic UI, Next Experience, and Agent Workspace ================================================ FILE: Client-Side Components/Client Scripts/Auto Update Priority based on Impact and Urgency/script.js ================================================ //Auto Priority Update based on Impact and Urgency // ========================================================================== // Script Name: Auto Priority Update based on Impact and Urgency // Table: Incident (or any Task-based table) // Type: onChange | Fields: impact, urgency // UI Type: All // ========================================================================== function onChange(control, oldValue, newValue, isLoading, isTemplate) { // Prevent the script from running when the form loads or when the field is empty if (isLoading || newValue == '') { return; } // ---------------------------------------------------------------------- // Step 1: Fetch field values from the form // ---------------------------------------------------------------------- var impact = g_form.getValue('impact'); // e.g., 1 - High, 2 - Medium, 3 - Low var urgency = g_form.getValue('urgency'); // e.g., 1 - High, 2 - Medium, 3 - Low // ---------------------------------------------------------------------- // Step 2: Define the ITIL-based Priority Matrix // ---------------------------------------------------------------------- // Each row represents "Impact", and each column represents "Urgency" // The resulting value sets the "Priority" var priorityMatrix = { '1': { '1': '1', '2': '2', '3': '3' }, // Impact = High '2': { '1': '2', '2': '3', '3': '4' }, // Impact = Medium '3': { '1': '3', '2': '4', '3': '5' } // Impact = Low }; // ---------------------------------------------------------------------- // Step 3: Determine the new priority based on selected Impact/Urgency // ---------------------------------------------------------------------- var newPriority = priorityMatrix[impact]?.[urgency]; // optional chaining prevents errors // ---------------------------------------------------------------------- // Step 4: Update the Priority field and inform the user // ---------------------------------------------------------------------- if (newPriority) { // Only update if priority is different from current value if (g_form.getValue('priority') != newPriority) { g_form.setValue('priority', newPriority); // Show message (works in both Classic UI and Next Experience) g_form.showFieldMsg('priority', 'Priority auto-updated based on Impact and Urgency', 'info'); } } else { // Optional: clear priority if invalid combination is selected g_form.clearValue('priority'); g_form.showFieldMsg('priority', 'Invalid Impact/Urgency combination — priority cleared', 'error'); } // ---------------------------------------------------------------------- // End of Script // ---------------------------------------------------------------------- } ================================================ FILE: Client-Side Components/Client Scripts/Auto-Populate Planned End Date/README.md ================================================ An onChange client script that calls the script includes one that adds the specified number of business days to the planned start date and autopopulates the planned end date based on the type. For the client callable script include refer to the README file in the Add Business Days Script include folder. Link: [Add Business Days Script Include](/Server-Side%20Components/Script%20Includes/Add%20Business%20Days/README.md) ================================================ FILE: Client-Side Components/Client Scripts/Auto-Populate Planned End Date/autoPopulatePlannedEndDate.js ================================================ //Client script //Table: Change Request //UI Type: All //Type: onChange //Field: Planned Start Date function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } var daysToAdd; if(g_form.getValue('type') =='standard' || g_form.getValue('type') =='normal') daysToAdd = 3; else if(g_form.getValue('type') =='emergency') daysToAdd = 1; var ga = new GlideAjax("addBusinessDays"); //Calling the add business days script include, which is in the Server-Side Components/Script Includes/Add Business Days/addBusinessDays.js ga.addParam('sysparm_name', 'addDays'); ga.addParam('sysparm_days', daysToAdd); ga.addParam('sysparm_date', newValue); ga.getXML(processResponse); function processResponse(response) { var answer = response.responseXML.documentElement.getAttribute("answer").toString(); g_form.setValue('end_date', answer); } } ================================================ FILE: Client-Side Components/Client Scripts/Auto-Populate Short Discription/Auto populate short description.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } // Define category-to-short description mapping var categoryToShortDescription = { 'hardware': 'Hardware Issue - ', 'software': 'Software Issue - ', 'network': 'Network Issue - ', 'inquiry': 'Inquiry/Help - ', 'database': 'Database - ' }; // Convert the selected value to lowercase for matching var selectedCategory = newValue.toLowerCase(); // If category exists in mapping, update the short description if (categoryToShortDescription.hasOwnProperty(selectedCategory)) { var existingDesc = g_form.getValue('short_description') || ''; var prefix = categoryToShortDescription[selectedCategory]; // Only add prefix if it's not already there if (!existingDesc.startsWith(prefix)) { g_form.setValue('short_description', prefix + existingDesc); g_form.showFieldMsg('short_description', 'Short Description auto-updated based on category.', 'info'); } } } ================================================ FILE: Client-Side Components/Client Scripts/Auto-Populate Short Discription/README.md ================================================ # Auto-Populate Short Description Client Script ## Overview This client script is designed to enhance the user experience in ServiceNow by automatically populating the 'Short Description' field when a user selects a category for an incident or request. It simplifies the data entry process, ensures consistency in short descriptions, and saves time for users. ## How It Works When a user selects a category from the provided options, the script appends a predefined prefix to the existing 'Short Description' value, creating a more informative short description. ### Configuration To configure and use this client script in your ServiceNow instance, follow these steps: 1. **Create a Client Script:** - Log in to your ServiceNow instance as an admin or developer. - Navigate to "System Definition" > "Client Scripts." - Create a new client script and give it a name (e.g., "Auto-Populate Short Description"). 2. **Copy and Paste the Script:** - Copy the JavaScript code provided in this README. - Paste the code into your newly created client script. 3. **Attach to 'category' Field:** - Save the client script and ensure it's active. - Attach the client script to the 'category' field of the relevant table (e.g., Incident, Request) where you want to enable this functionality. 4. **Define Your Category-to-Short-Description Mappings:** - Modify the script to define your own mappings for categories and their corresponding short description prefixes. Customize the `categoryToShortDescription` object to match your requirements. 5. **Testing:** - Test the functionality by creating or editing an incident or request record. - Select a category, and observe how the 'Short Description' field is automatically populated based on your mappings. ## Example Mapping Here's an example of how you can define category-to-short-description mappings in the script: ```javascript var categoryToShortDescription = { 'Hardware': 'Hardware Issue - ', 'Software': 'Software Issue - ', 'Network': 'Network Issue - ', 'Other': 'Other Issue - ' }; ``` You can customize these mappings to align with your organization's specific categories and short description conventions. ## Benefits - Streamlines data entry for users. - Ensures consistent and informative short descriptions. - Saves time and reduces the risk of human error. - Enhances user experience in ServiceNow. ================================================ FILE: Client-Side Components/Client Scripts/Auto-Populate Short Discription/Readme.md ================================================ # Auto-Populate Short Description (Client Script) ## Overview This client script automatically updates the Short Description field in ServiceNow whenever a user selects a category on the form. It improves data consistency, saves time, and ensures that short descriptions remain meaningful and standardized. ## How It Works When a user selects a category such as Hardware, Software, or Network, the script automatically prefixes the existing short description with a corresponding label (for example, “Hardware Issue –”). This makes incident records easier to identify and improves the quality of data entry. ## Configuration Steps 1. Log in to your ServiceNow instance with admin or developer access. 2. Navigate to System Definition → Client Scripts. 3. Create a new Client Script with the following details: ``` Table: Incident Type: onChange Field name: category Copy and paste the provided script into the Script field. Save the record and make sure the script is active. ``` ## Testing 1. Open any existing or new Incident record. 2. Select a category such as Hardware or Software. 3. The Short Description field will automatically update with the corresponding prefix. ## Benefits Speeds up data entry for users. Maintains consistency in short descriptions. Reduces manual effort and human errors. Enhances clarity in incident listings and reports. ================================================ FILE: Client-Side Components/Client Scripts/Auto-Populate Short Discription/autoPopulateSD.js ================================================ // Client Script to Auto-Populate Short Description based on Category function onChangeCategory() { var categoryField = g_form.getValue('category'); // Get the selected category var shortDescriptionField = g_form.getValue('short_description'); // Get the Short Description field // Define mappings for categories and corresponding short descriptions var categoryToShortDescription = { 'Hardware': 'Hardware Issue - ', 'Software': 'Software Issue - ', 'Network': 'Network Issue - ', 'Other': 'Other Issue - ' }; // Update Short Description based on the selected category if (categoryToShortDescription.hasOwnProperty(categoryField)) { var newShortDescription = categoryToShortDescription[categoryField] + shortDescriptionField; g_form.setValue('short_description', newShortDescription); } } // Attach the onChangeCategory function to the 'category' field g_form.observe('change', 'category', onChangeCategory); ================================================ FILE: Client-Side Components/Client Scripts/Auto-populate watch_list based on company/README.md ================================================ Table: Incident Type: onChange field: caller_id Sometimes customers have a requirement to add users or distribution lists to all tickets that are raised for their company. This script allows to automatically add a list of users or external email addresses to the watch_list field. ================================================ FILE: Client-Side Components/Client Scripts/Auto-populate watch_list based on company/script.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading) { return; } var callerID = g_form.getReference('caller_id', getUserCompany); } function getUserCompany(callerID) { switch (callerID.company) { case 'company_1_sys_id': g_form.setValue('watch_list', 'PEOPLE_TO_ADD'); //PEOPLE_TO_ADD is a comma separated list of user sys_id //and external email addresses break; case 'company_2_sys_id': g_form.setValue('watch_list', 'PEOPLE_TO_ADD'); break; default: break; } } ================================================ FILE: Client-Side Components/Client Scripts/Call SI to recover User data/README.md ================================================ # Call Script Include to recover User data In this onChange Client Script, the scenario is this: - In a form, we have a reference field to the User table to allow our user to select any user within the platform. - Once a user is selected, we are passing that Sys ID to recover more information regarding the selected record. ================================================ FILE: Client-Side Components/Client Scripts/Call SI to recover User data/script.js ================================================ /* Type: onChange Field: a reference to the User table */ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } var test = newValue; //The Script Include called here can be found in: //Server-Side Components / Script Includes / Get User Data by Id //It is in Global scope var ga = new GlideAjax('GetUserData');//script include name ga.addParam('sysparm_name', 'GetUserBy_id'); //method to be called ga.addParam('sysparm_userid', test); //send a parameter to the method. ga.getXMLAnswer(userInfoParse); function userInfoParse(response) { if (response == '') { g_form.addErrorMessage('User not found with the informed sys_id.'); } else { //alert(response); var obj = JSON.parse(response); g_form.setValue('u_first_name', obj.first_name.toString()); g_form.setValue('u_last_name', obj.last_name.toString()); g_form.setValue('u_email', obj.email.toString()); } } } ================================================ FILE: Client-Side Components/Client Scripts/Change Label of Field/Change Label of Field.js ================================================ function onLoad() { //Type appropriate comment here, and begin script below if (g_form.getValue('priority') == '1') { g_form.setLabelOf('description', 'Please Explain Briefly'); } } ================================================ FILE: Client-Side Components/Client Scripts/Change Label of Field/README.md ================================================ # Change Label of Field • Modify field labels dynamically in the ServiceNow platform. • Change labels through the UI form designer or through client/server-side scripts. • Implement Business Rules to change labels based on conditions. • Adjust field labels dynamically using Client Scripts based on user interactions. ================================================ FILE: Client-Side Components/Client Scripts/Change incident Number label color based on priority/Script.js ================================================ function onLoad () { var aPriority = g_form.getValue('priority'); var label = g_form.getLable('number'); label.style.backgroundColor = "lightblue"; if(aPriority==1) { label.style.color = "red"; } else if (aPriority==2) { label.style.color = "yellow"; } else { label.style.color = "blue"; } } ================================================ FILE: Client-Side Components/Client Scripts/Change incident Number label color based on priority/readme.md ================================================ Write Client Script Change Incident Number Lable color based on priority ================================================ FILE: Client-Side Components/Client Scripts/Check all mandatory fields using mandatoryCheck()/README.md ================================================ # Mandatory Field Check on Form Change This client script demonstrates how to use `g_form.mandatoryCheck()` to validate whether all mandatory fields on a form are filled. It is typically used in an **onChange** catalog client script to trigger validation when a specific field changes. If mandatory fields are missing, the script: - Displays an info message listing the missing fields. Note: the red message is the existing functionality that will give you an error message. - Visually highlights each missing field using a flashing effect to guide the user. This approach improves user experience by clearly indicating which fields require attention. **Example configuration** image **Example execution** image ================================================ FILE: Client-Side Components/Client Scripts/Check all mandatory fields using mandatoryCheck()/script.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } // Check if all mandatory fields are filled if (!g_form.mandatoryCheck()) { //if not get all missing fields var missing = g_form.getMissingFields(); //Info message displaying the fields that are missing g_form.addInfoMessage("Please complete the following mandatory fields: " + missing.join(", ")); //go through missing fields and flash them missing.forEach(function (fieldName) { g_form.flash(fieldName, "#FFFACD",0); // Flash to draw attention }); } } ================================================ FILE: Client-Side Components/Client Scripts/Client Validation of Attachments by File Type and Count/README.md ================================================ This ServiceNow client script is designed to validate file attachments on a form before submission. It's most likely used as a "Client Script" or "Client Script in a Catalog Item" that runs in the browser when a user tries to submit a form. This client script runs when a form is submitted in ServiceNow. It checks if the user has: Attached at least one file (shows an error if none). Attached no more than three files (shows an error if more). Only uploaded files of type PDF or PNG (shows an error for other types). If any of these checks fail, the form submission is blocked and an appropriate error message is displayed. ================================================ FILE: Client-Side Components/Client Scripts/Client Validation of Attachments by File Type and Count/code.js ================================================ (function executeRule(gForm, gUser, gSNC) { var attachments = gForm.getAttachments(); if (!attachments || attachments.length === 0) { gForm.addErrorMessage("You must attach at least one file."); return false; } if (attachments.length > 3) { gForm.addErrorMessage("You can only upload up to 3 files."); return false; } var allowedTypes = ['pdf', 'png']; for (var i = 0; i < attachments.length; i++) { var fileName = attachments[i].file_name.toLowerCase(); var ext = fileName.split('.').pop(); if (!allowedTypes.includes(ext)) { gForm.addErrorMessage("Only PDF and PNG files are allowed."); return false; } } return true; })(gForm, gUser, gSNC); ================================================ FILE: Client-Side Components/Client Scripts/Client script using getMessage() function without filling Messages field/README.md ================================================ # Client scripts using getMessage() function without filling Messages field **Problem scenario :** Developers while writing a client script uses getMessage() function but there are no messages preloaded in the 'Messages' field in client script form **Solution :** Write an onSubmit client script to check the above scenario and prevent client script submission with an error message on top of the form. Check the file `script.js` file for example. Note : As a best practice for this scenario use 'Checks' in the'Instance scan' feature of serviceNow. If Instance scan is not configured in your instance yet or if you want to avoid the problem during development itself then use this client script. ================================================ FILE: Client-Side Components/Client Scripts/Client script using getMessage() function without filling Messages field/script.js ================================================ function onSubmit() { var messages = g_form.getValue('messages'); var script = g_form.getValue('script'); var count = 0; if(messages == '' && script.includes('getMessage')) { g_form.addErrorMessage("Please add any message in the Messages field before using getMessage() function"); return false; } } ================================================ FILE: Client-Side Components/Client Scripts/Color-coded Priority field for improved UX/README.md ================================================ # Field Color-Coding Based on Choice Values ## Purpose Dynamically change the background color of any choice field on a form based on the selected backend value. ## How to Use 1. Create an OnChange client script on the desired choice field. 2. Replace `'your_field_name'` in the script with your actual field name. 3. Update the `colorMap` with relevant backend choice values and colors. 4. Save and test on the form. ## Key Points - Works with any choice field - Uses backend values of choices for mapping colors. ## Demo image ================================================ FILE: Client-Side Components/Client Scripts/Color-coded Priority field for improved UX/setColor.js ================================================ function onChange(control, oldValue, newValue, isLoading) { var colorMap = { '1': '#e74c3c', // Critical - strong red '2': '#e67e22', // High - bright orange '3': '#f1c40f', // Moderate - yellow '4': '#3498db', // Low - blue '5': '#27ae60' // Planning - green }; var priorityField = g_form.getControl('priority'); if (!priorityField) return; priorityField.style.backgroundColor = colorMap[newValue] || ''; } ================================================ FILE: Client-Side Components/Client Scripts/Conditional Auto-Routing and Dynamic Mandatory Fields/Conditional_AutoRouting_Dynamic_Mandatory_Fields.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading) return; if (newValue === 'hardware') { g_form.setMandatory('asset_tag', true); g_form.setDisplay('asset_tag', true); g_form.setValue('assignment_group', 'Hardware Support Group'); } else if (newValue === 'software') { g_form.setMandatory('asset_tag', false); g_form.setDisplay('asset_tag', false); g_form.setValue('assignment_group', 'Software Support Group'); } else { g_form.setMandatory('asset_tag', false); g_form.setDisplay('asset_tag', true); } } ================================================ FILE: Client-Side Components/Client Scripts/Conditional Auto-Routing and Dynamic Mandatory Fields/Readme.md ================================================ If an Incident Category = Hardware, make Asset Tag mandatory and automatically assign to Hardware Support Group. If Software, assign to Software Support Group and hide Asset Tag. ================================================ FILE: Client-Side Components/Client Scripts/Conditional Form Section based on Role/README.md ================================================ # Conditional Form Sections Based on User Role ## Overview This client script dynamically shows or hides specific sections of a form based on the logged-in user’s role. It ensures that only authorized users, such as managers, can view and interact with sensitive sections (e.g., budget approvals). ## Use Case - Managers: Can see the Budget Approval section. - Other Users: The section remains hidden. ## How It Works - The script runs onLoad of the form. - It checks if the logged-in user has the specified role (manager in this example). - If the user has the role, the Budget Approval section is shown. If not, it remains hidden. ================================================ FILE: Client-Side Components/Client Scripts/Conditional Form Section based on Role/script.js ================================================ function onLoad() { var userHasRole = g_user.hasRole('case_manager'); // Check if user has specific role if (userHasRole) { g_form.setSectionDisplay('budget_approval', true); // Show section if user has specific role } else { g_form.setSectionDisplay('budget_approval', false); } } ================================================ FILE: Client-Side Components/Client Scripts/Control Form Behaviour from Reference Lookup/README.md ================================================ # Use Case Clicking the lookup icon on a reference field opens the list view for the referenced table. On clicking the 'New' button to create new records on the reference table, 'Default' form view for that table is displayed. There might be a requirement to control the form's behaviour when the new record form is opened from a designated field on a specific table. # Usage Write a client script/scripted UI policy on the reference table and add the code in ```script.js``` file. # Explanation The URL parameters contains the necessary information about the originating table and field from where the lookup icon is clicked. These parameters can be extracted using the client-side class ```GlideURL```. Key parameters of interest here: - ```sysparm_nameofstack: "reflist"``` ==> Will always be reflist when form has originated from a reference lookup icon click - ```sysparm_target: "change_request.cmdb_ci"``` ==> Will be in the format . ================================================ FILE: Client-Side Components/Client Scripts/Control Form Behaviour from Reference Lookup/script.js ================================================ var url = new GlideURL(); url.setFromCurrent(); var source = url.getParam('sysparm_nameofstack'); // Always 'reflist' when opening from Reference Lookup Icon var field = url.getParam('sysparm_target'); //Dot-walked path to the field (Example: cmdb_ci field on change_request form - change_request.cmdb_ci) if (source === 'reflist' && field === 'change_request.cmdb_ci') { // Add form control logic here g_form.setMandatory('name', true); } ================================================ FILE: Client-Side Components/Client Scripts/Count Assigned To Field/README.md ================================================ Count Assigned To Field 1. Write a Client Script name as getAssignedToCount 2. Glide the Incident Table 3. Use onChange Client Script 4. Use the Field name as "assigned_to" field 5. Glide the Script Include using "GlideAjax". 6. Call the function "getCount" from Script Include 7. Add the parameter for the newValue. 8. Use the getXML for asynchronous response. 9. Get the answer using the callback function 10. Use the logic for the more than how many tickets that error needs to populate 11. Use the addErrorMessage for marking the error message 12. Use the setValue for the "assigned_to" field. ================================================ FILE: Client-Side Components/Client Scripts/Count Assigned To Field/getAssignedToCount.js ================================================ function onChange(control,oldValue,newValue,isLoading,isTemplate) { if(isLoading || newValue === '') { return; } var ga = new GlideAjax('countAssignedUtil'); ga.addParam('sysparm','getCount'); ga.addParam('sysparm_assignedto', newValue); ga.getXML(callback); function callback(response){ var answer = response.responseXML.documentElement.getAttribute("answer"); if(answer >=5){ g_form.addErrorMessage("Please select another person to work on this Incident, selected user is already having 5 tickets in his/her Queue"); g_form.setValue("assigned_to", ""); } } } ================================================ FILE: Client-Side Components/Client Scripts/Date Range Validation (Within 30 Days)/README.md ================================================ Date Range Validation (Within 30 Days) in Client Side This ServiceNow client script provides real-time date validation for form fields, ensuring users can only select dates within a specific 30-day window from today's date. The script runs automatically when a user changes a date field value, providing immediate feedback and preventing invalid date submissions. The script validates that any date entered in a form field meets these criteria: Minimum Date: Today's date (no past dates allowed) Maximum Date: 30 days from today's date Real-time Validation: Instant feedback as users type or select dates User-friendly Errors: Clear error messages explaining the valid date range Automatic Field Clearing: Invalid dates are automatically cleared to prevent submission ================================================ FILE: Client-Side Components/Client Scripts/Date Range Validation (Within 30 Days)/dateRangeValidation.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } var todayDate = new Date(); var futureDate = new Date(); futureDate.setDate(futureDate.getDate() + 30); var todayDateStr = formatDate(todayDate, g_user_date_format); var futureDateStr = formatDate(futureDate, g_user_date_format); var selectedDateNum = getDateFromFormat(newValue, g_user_date_format); var todayDateNum = getDateFromFormat(todayDateStr, g_user_date_format); var futureDateNum = getDateFromFormat(futureDateStr, g_user_date_format); if (selectedDateNum < todayDateNum || selectedDateNum > futureDateNum) { g_form.showFieldMsg(control, 'Date must be between today and 30 days from today', 'error'); g_form.clearValue(control); } else { g_form.hideFieldMsg(control); } } ================================================ FILE: Client-Side Components/Client Scripts/Detect oldValue newValue and Operation in Glide List Type Fields/detectOldValuenewValueOperation.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading) { return; } var prevValue; if (g_scratchpad.prevValue == undefined) prevValue = oldValue; else { prevValue = g_scratchpad.prevValue; } g_scratchpad.prevValue = newValue; var oldGlideValue = prevValue.split(','); var newGlideValue = newValue.split(','); var operation; if (oldGlideValue.length > newGlideValue.length || newValue == '') { operation = 'remove'; } else if (oldGlideValue.length < newGlideValue.length || oldGlideValue.length == newGlideValue.length) { operation = 'add'; } else { operation = ''; } var ajaxGetNames = new GlideAjax('watchListCandidatesUtil'); ajaxGetNames.addParam('sysparm_name', 'getWatchListUsers'); ajaxGetNames.addParam('sysparm_old_values', oldGlideValue); ajaxGetNames.addParam('sysparm_new_values', newGlideValue); ajaxGetNames.getXMLAnswer(function(response) { var result = JSON.parse(response); g_form.clearMessages(); g_form.addSuccessMessage('Operation Performed : ' + operation); g_form.addSuccessMessage('OldValue : ' + result.oldU); g_form.addSuccessMessage('NewValue : ' + result.newU); }); } ================================================ FILE: Client-Side Components/Client Scripts/Detect oldValue newValue and Operation in Glide List Type Fields/readme.md ================================================ In Client Scripts, oldValue will display the value of last value/record which is stored in that field. For new records, it is generally empty and for existing records it displays the value which is stored after load. If we will try to change the value in that field, it will still show oldValue the same value which was there during the load of form. So, In order to identify the oldValue on change(as it does in business rule(previous)), This script comes handy and also it will detect the action performed. This onChange Client script comes handy when dealing with Glide List type fields where we have to detect whether the value was added or removed and returns the display name of users who were added/removed along with name of operation performed. Setup details: onChange client Script on [incident] table Field Name: [watch_list] Script: Check [detectOldValuenewValueOperation.js] file Script Include Name: watchListCandidatesUtil (client callable/GlideAjax Enabled - true), Check [watchListCandidatesUtil.js] file for script Output: Currently there is no one in the watchlist field: image Adding [Bryan Rovell], It shows the operation was addition, oldValue as 'No record found' as there was no value earlier.New Value shows the name of the user (Bryan Rovell) image Adding 2 users one by one: image Removing 2 at once: image ================================================ FILE: Client-Side Components/Client Scripts/Detect oldValue newValue and Operation in Glide List Type Fields/watchListCandidatesUtil.js ================================================ var watchListCandidatesUtil = Class.create(); watchListCandidatesUtil.prototype = Object.extendsObject(AbstractAjaxProcessor, { getWatchListUsers: function() { var oldUsers = this.getParameter('sysparm_old_values'); var newUsers = this.getParameter('sysparm_new_values'); var result = { oldU: this._getUserNames(oldUsers), newU: this._getUserNames(newUsers) }; return JSON.stringify(result); }, _getUserNames: function(userList) { var names = []; var grUserTab = new GlideRecord('sys_user'); grUserTab.addQuery('sys_id', 'IN', userList); grUserTab.query(); if (grUserTab.hasNext()) { while (grUserTab.next()) { names.push(grUserTab.getDisplayValue('name')); } return names.toString(); } else { return 'No record found'; } }, type: 'watchListCandidatesUtil' }); ================================================ FILE: Client-Side Components/Client Scripts/Display Custom Field Based on Incident Channel Field and populate with Caller Information/GlideAjaxCallerInfo.js ================================================ var CallerInfoHelper = Class.create(); CallerInfoHelper.prototype = Object.extendsObject(AbstractAjaxProcessor, { getCallerInfo: function() { var callerSysId = this.getParameter('sysparm_caller'); if (!callerSysId) return JSON.stringify({ email: '', mobile: '' }); var userGR = new GlideRecord('sys_user'); if (!userGR.get(callerSysId)) return JSON.stringify({ email: '', mobile: '' }); var userObj = { email: userGR.email.toString(), mobile: userGR.mobile_phone.toString() }; return JSON.stringify(userObj); }, type: 'CallerInfoHelper' }); ================================================ FILE: Client-Side Components/Client Scripts/Display Custom Field Based on Incident Channel Field and populate with Caller Information/README.md ================================================ # Display Custom Email/Phone Field Based on Incident Channel Field and Populate those Field with Caller Information Displays either the **Email** or **Phone** field on the **Incident** form based on the selected **Channel** value (Email or Phone) and populate the fields with the caller’s details. ### Use Case - When **Channel = Email**, the **Email** field becomes visible and is auto-populated with the caller’s email address - When **Channel = Phone**, the **Phone** field becomes visible and is auto-populated with the caller’s mobile number - Both details fetched from the caller’s record from **sys_user** table. - The custom Email and Phone fields may also serve as placeholder to update if details differ from the caller record ### Prerequisites - Create Two custom fields on Incident Table - **u_email** which captures store the caller’s email address - **u_phone** which capture caller’s mobile number - Create **Two UI Policies** which hides the u_email and u_phone field unless channel choice is phone or email - Create an onChange Client Script that calls a GlideAjax Script to fetch the caller’s contact details and populate the custom Email or Phone field on the Incident form - To further enhance usecase Regex used on Phone field. Refer (https://github.com/ServiceNowDevProgram/code-snippets/pull/2375) --- ### Incident Record when channel choice is other than Email or Phone ![Display_CustomField_Autopopulate_Caller_3](Display_CustomField_Autopopulate_Caller_3.png) --- ### Incident Record when Channel choice is email and populate Email Field by caller's Email ![Display_CustomField_Autopopulate_Caller_1](Display_CustomField_Autopopulate_Caller_1.png) --- ### Incident Record when channel choice is phone and populate Phone Field by caller's Phone Number ![Display_CustomField_Autopopulate_Caller_2](Display_CustomField_Autopopulate_Caller_2.png) --- ================================================ FILE: Client-Side Components/Client Scripts/Display Custom Field Based on Incident Channel Field and populate with Caller Information/clientScriptOnChangeCaller.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue === '') return; var ga = new GlideAjax('CallerInfoHelper'); ga.addParam('sysparm_name', 'getCallerInfo'); ga.addParam('sysparm_caller', newValue); ga.getXMLAnswer(function(answer) { // Confirm what you’re actually receiving console.log("GlideAjax raw answer:", answer); if (!answer) return; var info; try { info = JSON.parse(answer); } catch (e) { console.log("Error parsing JSON:", e); return; } g_form.setValue('u_email', info.email || ''); g_form.setValue('u_phone', info.mobile || ''); }); } ================================================ FILE: Client-Side Components/Client Scripts/Display Incident Count of Assigned-To User When Field Changes/README.md ================================================ ## Display Info Message of Incident Count of Assigned-To User When Field Assigned-To Changes Displays a message showing the count of **open incidents** assigned to a user whenever the **Assigned To** field changes on the Incident form. - Helps assess the assignee’s **current workload** by fetching and displaying active incident counts (excluding *Resolved*, *Closed*, and *Canceled* states) - Shows an **info message** with the count of the assignee's assigned incidents - Uses an **onChange Client Script** on the **Assigned To** field and a **GlideAjax Script Include** called from the client script to fetch the count dynamically --- ### Info Message Example 1 ![Incident_Count_message_1](Incident_Count_message_1.png) ### Info Message Example 2 ![Incident_Count_message_2](Incident_Count_message_2.png) --- ================================================ FILE: Client-Side Components/Client Scripts/Display Incident Count of Assigned-To User When Field Changes/clientScript.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') return; // Clear any previous messages g_form.clearMessages(); // Create GlideAjax object to call the Script Include var ga = new GlideAjax('IncidentAssignmentCheck'); ga.addParam('sysparm_name', 'getIncidentCount'); ga.addParam('sysparm_user', newValue); ga.getXMLAnswer(function(response) { var count = parseInt(response, 10); if (isNaN(count)) { g_form.addErrorMessage("Could not retrieve open incident count."); return; } var userName = g_form.getDisplayValue('assigned_to'); var msg = userName + " currently has " + count + " incidents assigned "; if (count >= 5) { g_form.addInfoMessage(msg + " Please review workload before assigning more incidents"); } else { g_form.addInfoMessage(msg); } }); } ================================================ FILE: Client-Side Components/Client Scripts/Display Incident Count of Assigned-To User When Field Changes/glideAjaxIncidentCount.js ================================================ var IncidentAssignmentCheck = Class.create(); IncidentAssignmentCheck.prototype = Object.extendsObject(AbstractAjaxProcessor, { getIncidentCount: function() { var user = this.getParameter('sysparm_user'); var count = 0; if (user) { var gr = new GlideAggregate('incident'); gr.addQuery('assigned_to', user); gr.addQuery('state', 'NOT IN', '6,7,8'); gr.addAggregate('COUNT');a gr.query(); if (gr.next()) { count = gr.getAggregate('COUNT'); } } return count; }, type: 'IncidentAssignmentCheck' }); ================================================ FILE: Client-Side Components/Client Scripts/Display Section on State/README.md ================================================ # Display Section Based on State ## Overview This client script dynamically displays specific sections on a form based on the selected State field value. It improves user experience by hiding or showing relevant sections as needed. ## How It Works - When the State field changes, the script checks the new value. - For Example, if the state is changed to "Resolved", the Resolution Section becomes visible. - For other states, the section remains hidden. ================================================ FILE: Client-Side Components/Client Scripts/Display Section on State/script.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } if (newValue == 6) //State on which section needs to be displayed g_form.setSectionDisplay('resolution_information', true); //Section which needs to be display else g_form.setSectionDisplay('resolution_information', false); } ================================================ FILE: Client-Side Components/Client Scripts/Display a Live Word Count for Description Field/README.md ================================================ The script enhances a form field (specifically the description field) by: -Adding a live word counter below the field. -Visually warning the user if the word count exceeds 150 words. This client-side script, intended for use in a ServiceNow form (e.g., catalog item or incident form), dynamically appends a custom `
` element below the `description` field to display a real-time word count. It leverages the `g_form.getControl()` API to access the field's DOM element and attaches an `input` event listener to monitor user input. The script calculates the word count by splitting the input text using a regular expression (`\s+`) and updates the counter accordingly. It applies conditional styling to the counter (`green` if ≤150 words, `red` if >150), providing immediate visual feedback to the user to enforce input constraints. ================================================ FILE: Client-Side Components/Client Scripts/Display a Live Word Count for Description Field/code.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === oldValue) { return; } var wordCount = newValue.trim().split(/\s+/).length; var message = 'Word Count: ' + (newValue ? wordCount : 0); var messageType = (wordCount > 150) ? 'error' : 'info'; g_form.showFieldMsg('description', message, messageType); } ================================================ FILE: Client-Side Components/Client Scripts/Dynamic Field Dependencies with GlideAjax/README.md ================================================ # Dynamic Field Dependencies with GlideAjax This folder contains advanced Client Script examples demonstrating real-time field dependencies using GlideAjax for server-side data retrieval, cascading dropdowns, and dynamic form behavior. ## Overview Modern ServiceNow forms often require dynamic behavior based on user selections. This example demonstrates: - **Real-time field updates** using GlideAjax for server-side queries - **Cascading dropdown menus** that filter based on parent selections - **Conditional field visibility** based on complex business logic - **Debouncing** to prevent excessive server calls - **Loading indicators** for better user experience - **Error handling** for failed AJAX calls - **Performance optimization** with caching and batching ## Script Descriptions - **dynamic_category_subcategory.js**: Client Script implementing cascading category/subcategory dropdowns with real-time filtering. - **conditional_field_loader.js**: Advanced example showing conditional field loading based on multiple dependencies. - **ajax_script_include.js**: Server-side Script Include (Client Callable) that provides data for the client scripts. - **debounced_field_validator.js**: Real-time field validation with debouncing to reduce server load. ## Key Features ### 1. GlideAjax Communication Efficient client-server communication: ```javascript var ga = new GlideAjax('MyScriptInclude'); ga.addParam('sysparm_name', 'getSubcategories'); ga.addParam('sysparm_category', categoryValue); ga.getXMLAnswer(callback); ``` ### 2. Cascading Dropdowns Dynamic filtering of child fields: - Parent field change triggers child field update - Child options filtered based on parent selection - Multiple levels of cascading supported - Maintains previously selected values when possible ### 3. Debouncing Prevents excessive server calls: - Waits for user to stop typing before making request - Configurable delay (typically 300-500ms) - Cancels pending requests when new input received - Improves performance and user experience ### 4. Loading Indicators Visual feedback during AJAX calls: - Shows loading spinner or message - Disables fields during data fetch - Clears loading state on completion or error - Prevents duplicate submissions ## Use Cases - **Category/Subcategory Selection**: Filter subcategories based on selected category - **Location-Based Fields**: Update city/state/country fields dynamically - **Product Configuration**: Show/hide fields based on product type - **Assignment Rules**: Dynamically populate assignment groups based on category - **Cost Estimation**: Calculate and display costs based on selections - **Availability Checking**: Real-time validation of resource availability - **Dynamic Pricing**: Update pricing fields based on quantity/options ## Implementation Requirements ### Client Script Configuration - **Type**: onChange (for field changes) or onLoad (for initial setup) - **Table**: Target table (e.g., incident, sc_req_item) - **Field**: Trigger field (e.g., category) - **Active**: true - **Global**: false (table-specific for better performance) ### Script Include Configuration - **Name**: Descriptive name (e.g., CategoryAjaxUtils) - **Client callable**: true (REQUIRED for GlideAjax) - **Active**: true - **Access**: public or specific roles ### Required Fields Ensure dependent fields exist on the form: - Add fields to form layout - Configure field properties (mandatory, read-only, etc.) - Set up choice lists for dropdown fields ## Performance Considerations ### Optimization Techniques 1. **Cache responses**: Store frequently accessed data client-side 2. **Batch requests**: Combine multiple queries into single AJAX call 3. **Minimize payload**: Return only required fields 4. **Use indexed queries**: Ensure server-side queries use indexed fields 5. **Debounce input**: Wait for user to finish typing 6. **Lazy loading**: Load data only when needed ### Best Practices - Keep Script Includes focused and single-purpose - Validate input parameters server-side - Handle errors gracefully with user-friendly messages - Test with large datasets to ensure performance - Use browser developer tools to monitor network calls - Implement timeout handling for slow connections ## Security Considerations ### Input Validation Always validate parameters server-side: ```javascript // BAD: No validation var category = this.getParameter('sysparm_category'); var gr = new GlideRecord('cmdb_ci_category'); gr.addQuery('parent', category); // SQL injection risk // GOOD: Validate and sanitize var category = this.getParameter('sysparm_category'); if (!category || !this._isValidSysId(category)) { return '[]'; } ``` ### Access Control - Respect ACLs in Script Includes - Use GlideRecordSecure when appropriate - Don't expose sensitive data to client - Implement role-based filtering ### XSS Prevention - Sanitize data before displaying - Use g_form.setValue() instead of innerHTML - Validate choice list values - Escape special characters ## Error Handling ### Client-Side ```javascript ga.getXMLAnswer(function(response) { if (!response || response === 'error') { g_form.addErrorMessage('Failed to load data. Please try again.'); return; } // Process response }); ``` ### Server-Side ```javascript try { // Query logic } catch (ex) { gs.error('Error in getSubcategories: ' + ex.message); return 'error'; } ``` ## Testing Checklist - [ ] Test with empty/null values - [ ] Test with invalid input - [ ] Test with large datasets (1000+ records) - [ ] Test on slow network connections - [ ] Test concurrent user interactions - [ ] Test browser compatibility (Chrome, Firefox, Safari, Edge) - [ ] Test mobile responsiveness - [ ] Verify ACL enforcement - [ ] Check for console errors - [ ] Monitor network tab for performance ## Browser Compatibility Tested and compatible with: - Chrome 90+ - Firefox 88+ - Safari 14+ - Edge 90+ - ServiceNow Mobile App ## Related APIs - **GlideAjax**: Client-server communication - **GlideForm (g_form)**: Form manipulation - **GlideUser (g_user)**: User context - **GlideRecord**: Server-side queries - **JSON**: Data serialization ## Additional Resources - ServiceNow Client Script Best Practices - GlideAjax Documentation - Client-Side Scripting API Reference - Performance Optimization Guide ================================================ FILE: Client-Side Components/Client Scripts/Dynamic Field Dependencies with GlideAjax/ajax_script_include.js ================================================ /** * CategoryAjaxUtils Script Include * * Name: CategoryAjaxUtils * Client callable: true (REQUIRED) * Active: true * * Description: Server-side Script Include providing data for dynamic field dependencies * Supports multiple AJAX methods for category/subcategory and related field operations */ var CategoryAjaxUtils = Class.create(); CategoryAjaxUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, { /** * Get subcategories for a given category * Parameters: sysparm_category, sysparm_table * Returns: JSON array of {value, label} objects */ getSubcategories: function() { var category = this.getParameter('sysparm_category'); var table = this.getParameter('sysparm_table') || 'incident'; // Validate input if (!category) { return JSON.stringify([]); } var subcategories = []; try { // Query subcategory choices var gr = new GlideRecord('sys_choice'); gr.addQuery('name', table); gr.addQuery('element', 'subcategory'); gr.addQuery('dependent_value', category); gr.addQuery('inactive', false); gr.orderBy('sequence'); gr.orderBy('label'); gr.query(); while (gr.next()) { subcategories.push({ value: gr.getValue('value'), label: gr.getValue('label') }); } // If no dependent choices found, get all subcategories if (subcategories.length === 0) { gr = new GlideRecord('sys_choice'); gr.addQuery('name', table); gr.addQuery('element', 'subcategory'); gr.addQuery('inactive', false); gr.orderBy('sequence'); gr.orderBy('label'); gr.setLimit(100); // Limit to prevent performance issues gr.query(); while (gr.next()) { subcategories.push({ value: gr.getValue('value'), label: gr.getValue('label') }); } } } catch (ex) { gs.error('[CategoryAjaxUtils] Error in getSubcategories: ' + ex.message); return 'error'; } return JSON.stringify(subcategories); }, /** * Get all dependent field data in a single call (performance optimization) * Parameters: sysparm_category, sysparm_priority, sysparm_assignment_group, sysparm_table * Returns: JSON object with multiple field data */ getDependentFields: function() { var category = this.getParameter('sysparm_category'); var priority = this.getParameter('sysparm_priority'); var assignmentGroup = this.getParameter('sysparm_assignment_group'); var table = this.getParameter('sysparm_table') || 'incident'; var result = { subcategories: [], suggested_assignment_group: null, sla_info: null, estimated_cost: null, recommendations: [] }; try { // Get subcategories result.subcategories = this._getSubcategoriesArray(category, table); // Get suggested assignment group based on category result.suggested_assignment_group = this._getSuggestedAssignmentGroup(category); // Get SLA information result.sla_info = this._getSLAInfo(category, priority); // Get estimated cost (if applicable) result.estimated_cost = this._getEstimatedCost(category, priority); // Get recommendations result.recommendations = this._getRecommendations(category, priority); } catch (ex) { gs.error('[CategoryAjaxUtils] Error in getDependentFields: ' + ex.message); return 'error'; } return JSON.stringify(result); }, /** * Validate field dependencies * Parameters: sysparm_category, sysparm_subcategory * Returns: JSON object with validation result */ validateDependencies: function() { var category = this.getParameter('sysparm_category'); var subcategory = this.getParameter('sysparm_subcategory'); var result = { valid: false, message: '' }; try { // Check if subcategory is valid for the category var gr = new GlideRecord('sys_choice'); gr.addQuery('name', 'incident'); gr.addQuery('element', 'subcategory'); gr.addQuery('value', subcategory); gr.addQuery('dependent_value', category); gr.query(); if (gr.hasNext()) { result.valid = true; result.message = 'Valid combination'; } else { result.valid = false; result.message = 'Invalid subcategory for selected category'; } } catch (ex) { gs.error('[CategoryAjaxUtils] Error in validateDependencies: ' + ex.message); result.valid = false; result.message = 'Validation error: ' + ex.message; } return JSON.stringify(result); }, // ======================================== // Private Helper Methods // ======================================== /** * Get subcategories as array (internal method) * @private */ _getSubcategoriesArray: function(category, table) { var subcategories = []; var gr = new GlideRecord('sys_choice'); gr.addQuery('name', table); gr.addQuery('element', 'subcategory'); gr.addQuery('dependent_value', category); gr.addQuery('inactive', false); gr.orderBy('sequence'); gr.orderBy('label'); gr.query(); while (gr.next()) { subcategories.push({ value: gr.getValue('value'), label: gr.getValue('label') }); } return subcategories; }, /** * Get suggested assignment group based on category * @private */ _getSuggestedAssignmentGroup: function(category) { // This could be a lookup table or business rule // For demonstration, using a simple mapping var categoryGroupMap = { 'hardware': 'Hardware Support', 'software': 'Application Support', 'network': 'Network Operations', 'database': 'Database Team', 'inquiry': 'Service Desk' }; var groupName = categoryGroupMap[category]; if (!groupName) { return null; } // Look up the actual group sys_id var gr = new GlideRecord('sys_user_group'); gr.addQuery('name', groupName); gr.addQuery('active', true); gr.query(); if (gr.next()) { return gr.getUniqueValue(); } return null; }, /** * Get SLA information based on category and priority * @private */ _getSLAInfo: function(category, priority) { // Simplified SLA calculation // In production, this would query actual SLA definitions var resolutionHours = 24; // Default if (priority == '1') { resolutionHours = 4; } else if (priority == '2') { resolutionHours = 8; } else if (priority == '3') { resolutionHours = 24; } else if (priority == '4') { resolutionHours = 72; } // Adjust based on category if (category === 'hardware') { resolutionHours *= 1.5; // Hardware takes longer } return { resolution_time: resolutionHours, response_time: resolutionHours / 4 }; }, /** * Get estimated cost based on category and priority * @private */ _getEstimatedCost: function(category, priority) { // Simplified cost estimation var baseCost = 100; var categoryMultiplier = { 'hardware': 2.0, 'software': 1.5, 'network': 1.8, 'database': 1.7, 'inquiry': 0.5 }; var priorityMultiplier = { '1': 3.0, '2': 2.0, '3': 1.0, '4': 0.5, '5': 0.3 }; var catMult = categoryMultiplier[category] || 1.0; var priMult = priorityMultiplier[priority] || 1.0; return Math.round(baseCost * catMult * priMult); }, /** * Get recommendations based on category and priority * @private */ _getRecommendations: function(category, priority) { var recommendations = []; // Add category-specific recommendations if (category === 'hardware') { recommendations.push('Attach hardware diagnostic logs'); recommendations.push('Include serial number and model'); } else if (category === 'software') { recommendations.push('Include application version'); recommendations.push('Attach error screenshots'); } else if (category === 'network') { recommendations.push('Run network diagnostics'); recommendations.push('Include IP address and subnet'); } // Add priority-specific recommendations if (priority == '1' || priority == '2') { recommendations.push('Consider escalating to manager'); recommendations.push('Notify stakeholders immediately'); } return recommendations; }, /** * Validate if a string is a valid sys_id * @private */ _isValidSysId: function(value) { if (!value || typeof value !== 'string') { return false; } // sys_id is 32 character hex string return /^[0-9a-f]{32}$/.test(value); }, type: 'CategoryAjaxUtils' }); ================================================ FILE: Client-Side Components/Client Scripts/Dynamic Field Dependencies with GlideAjax/conditional_field_loader.js ================================================ /** * Advanced Conditional Field Loader with Multiple Dependencies * * Table: incident * Type: onChange * Field: category, priority, assignment_group (multiple scripts or combined) * * Description: Shows/hides and populates fields based on multiple conditions * Demonstrates debouncing, caching, and complex business logic */ // Global cache object (persists across onChange calls) if (typeof window.fieldDependencyCache === 'undefined') { window.fieldDependencyCache = {}; window.fieldDependencyTimers = {}; } /** * Main onChange handler for category field */ function onChangeCategoryWithDependencies(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading) { return; } var priority = g_form.getValue('priority'); var assignmentGroup = g_form.getValue('assignment_group'); // Update field visibility based on category updateFieldVisibility(newValue); // Load dependent data with debouncing loadDependentFields(newValue, priority, assignmentGroup); } /** * Update field visibility based on category selection */ function updateFieldVisibility(category) { // Example: Show additional fields for specific categories var showAdvancedFields = false; var showHardwareFields = false; // Check cache first var cacheKey = 'visibility_' + category; if (window.fieldDependencyCache[cacheKey]) { var cached = window.fieldDependencyCache[cacheKey]; showAdvancedFields = cached.advanced; showHardwareFields = cached.hardware; } else { // Determine visibility (this could also be an AJAX call) if (category === 'hardware' || category === 'network') { showHardwareFields = true; } if (category === 'software' || category === 'database') { showAdvancedFields = true; } // Cache the result window.fieldDependencyCache[cacheKey] = { advanced: showAdvancedFields, hardware: showHardwareFields }; } // Show/hide fields with animation if (showHardwareFields) { g_form.setSectionDisplay('hardware_details', true); g_form.setDisplay('cmdb_ci', true); g_form.setDisplay('hardware_model', true); g_form.setMandatory('cmdb_ci', true); } else { g_form.setSectionDisplay('hardware_details', false); g_form.setDisplay('cmdb_ci', false); g_form.setDisplay('hardware_model', false); g_form.setMandatory('cmdb_ci', false); g_form.clearValue('cmdb_ci'); g_form.clearValue('hardware_model'); } if (showAdvancedFields) { g_form.setDisplay('application', true); g_form.setDisplay('version', true); g_form.setMandatory('application', true); } else { g_form.setDisplay('application', false); g_form.setDisplay('version', false); g_form.setMandatory('application', false); g_form.clearValue('application'); g_form.clearValue('version'); } } /** * Load dependent fields with debouncing to prevent excessive AJAX calls */ function loadDependentFields(category, priority, assignmentGroup) { // Clear existing timer if (window.fieldDependencyTimers.loadFields) { clearTimeout(window.fieldDependencyTimers.loadFields); } // Set new timer (debounce for 300ms) window.fieldDependencyTimers.loadFields = setTimeout(function() { executeDependentFieldLoad(category, priority, assignmentGroup); }, 300); } /** * Execute the actual AJAX call to load dependent data */ function executeDependentFieldLoad(category, priority, assignmentGroup) { // Check cache first var cacheKey = 'fields_' + category + '_' + priority + '_' + assignmentGroup; if (window.fieldDependencyCache[cacheKey]) { applyDependentFieldData(window.fieldDependencyCache[cacheKey]); return; } // Show loading indicators showLoadingIndicators(['subcategory', 'assignment_group', 'assigned_to']); // Make AJAX call var ga = new GlideAjax('CategoryAjaxUtils'); ga.addParam('sysparm_name', 'getDependentFields'); ga.addParam('sysparm_category', category); ga.addParam('sysparm_priority', priority); ga.addParam('sysparm_assignment_group', assignmentGroup); ga.addParam('sysparm_table', g_form.getTableName()); ga.getXMLAnswer(function(response) { // Hide loading indicators hideLoadingIndicators(['subcategory', 'assignment_group', 'assigned_to']); if (!response || response === 'error') { g_form.addErrorMessage('Failed to load dependent fields. Please refresh and try again.'); return; } try { var data = JSON.parse(response); // Cache the response window.fieldDependencyCache[cacheKey] = data; // Apply the data applyDependentFieldData(data); } catch (ex) { g_form.addErrorMessage('Error processing field dependencies: ' + ex.message); console.error('Field dependency error:', ex); } }); } /** * Apply dependent field data to the form */ function applyDependentFieldData(data) { // Update subcategories if (data.subcategories) { g_form.clearOptions('subcategory'); for (var i = 0; i < data.subcategories.length; i++) { var sub = data.subcategories[i]; g_form.addOption('subcategory', sub.value, sub.label); } } // Update suggested assignment group if (data.suggested_assignment_group) { g_form.setValue('assignment_group', data.suggested_assignment_group); g_form.showFieldMsg('assignment_group', 'Auto-populated based on category', 'info', 3000); } // Update SLA information if (data.sla_info) { var slaMsg = 'Expected resolution time: ' + data.sla_info.resolution_time + ' hours'; g_form.showFieldMsg('priority', slaMsg, 'info'); } // Update estimated cost (if applicable) if (data.estimated_cost) { g_form.setValue('estimated_cost', data.estimated_cost); } // Show recommendations if (data.recommendations && data.recommendations.length > 0) { var recMsg = 'Recommendations: ' + data.recommendations.join(', '); g_form.addInfoMessage(recMsg); } } /** * Show loading indicators on multiple fields */ function showLoadingIndicators(fields) { for (var i = 0; i < fields.length; i++) { var field = fields[i]; g_form.showFieldMsg(field, 'Loading...', 'info'); g_form.setReadOnly(field, true); } } /** * Hide loading indicators on multiple fields */ function hideLoadingIndicators(fields) { for (var i = 0; i < fields.length; i++) { var field = fields[i]; g_form.hideFieldMsg(field); g_form.setReadOnly(field, false); } } /** * Clear cache (useful for testing or when data changes) */ function clearFieldDependencyCache() { window.fieldDependencyCache = {}; console.log('Field dependency cache cleared'); } /** * onLoad script to initialize form * Type: onLoad */ function onLoadInitializeDependencies() { // Initialize cache if (typeof window.fieldDependencyCache === 'undefined') { window.fieldDependencyCache = {}; window.fieldDependencyTimers = {}; } // Load initial dependencies if category is already set var category = g_form.getValue('category'); if (category) { updateFieldVisibility(category); } // Add cache clear button for admins (optional) if (g_user.hasRole('admin')) { console.log('Field dependency cache available. Use clearFieldDependencyCache() to clear.'); } } ================================================ FILE: Client-Side Components/Client Scripts/Dynamic Field Dependencies with GlideAjax/dynamic_category_subcategory.js ================================================ /** * Dynamic Category/Subcategory Client Script * * Table: incident (or any table with category/subcategory fields) * Type: onChange * Field: category * * Description: Dynamically populates subcategory field based on selected category * using GlideAjax to fetch filtered options from server */ function onChange(control, oldValue, newValue, isLoading, isTemplate) { // Don't run during form load or if value hasn't changed if (isLoading || newValue === '') { return; } // Clear subcategory when category changes g_form.clearValue('subcategory'); // Show loading indicator g_form.showFieldMsg('subcategory', 'Loading subcategories...', 'info'); g_form.setReadOnly('subcategory', true); // Make AJAX call to get filtered subcategories var ga = new GlideAjax('CategoryAjaxUtils'); ga.addParam('sysparm_name', 'getSubcategories'); ga.addParam('sysparm_category', newValue); ga.addParam('sysparm_table', g_form.getTableName()); ga.getXMLAnswer(function(response) { // Clear loading indicator g_form.hideFieldMsg('subcategory'); g_form.setReadOnly('subcategory', false); // Handle error response if (!response || response === 'error') { g_form.addErrorMessage('Failed to load subcategories. Please try again.'); return; } // Parse response try { var subcategories = JSON.parse(response); // Clear existing options (except empty option) g_form.clearOptions('subcategory'); // Add new options if (subcategories.length === 0) { g_form.addInfoMessage('No subcategories available for this category.'); g_form.setReadOnly('subcategory', true); } else { // Add each subcategory as an option for (var i = 0; i < subcategories.length; i++) { var sub = subcategories[i]; g_form.addOption('subcategory', sub.value, sub.label); } // Auto-select if only one option if (subcategories.length === 1) { g_form.setValue('subcategory', subcategories[0].value); } // Show success message g_form.showFieldMsg('subcategory', subcategories.length + ' subcategories loaded', 'info', 2000); } } catch (ex) { g_form.addErrorMessage('Error parsing subcategory data: ' + ex.message); console.error('Subcategory parsing error:', ex); } }); } ================================================ FILE: Client-Side Components/Client Scripts/Dynamic Location Validation Approach/Readme.md ================================================ **User Location Validator** This script restricts form submissions based on the physical location of the user. The current location is obtained using the browser’s geolocation API (latitude and longitude), and is then compared against the user's assigned business location stored in ServiceNow. **How It Works** - The **server-side Script Include**(UserLocationUtils.js) fetches the assigned business location’s latitude, longitude, and name for the logged-in user. - The **client-side script**(User Location Validator.js) uses the browser API to obtain the current latitude and longitude of the user at form submission. - It calculates the distance between these two points using the **Haversine formula**, which accounts for the spherical shape of the Earth. - The key constant `earthRadiusKm = 6371` defines the Earth's radius in kilometers and is essential for accurate distance calculation. - If the user’s current location is outside the predefined radius (default 10 km), the form submission is blocked with an error message showing the distance and allowed location. - If the user is within range, a confirmation info message is displayed and the submission proceeds. **Sample Output** - **Success:** "Location validated successfully within range of Headquarters." - **Failure:** "You are 15.23 km away from your registered location: Headquarters." **Usage Notes** - Requires user consent for geolocation access in the browser. - The script uses descriptive variable names for clarity and maintainability. - Suitable for scenarios requiring geo-fencing compliance or location-based workflow restrictions. ================================================ FILE: Client-Side Components/Client Scripts/Dynamic Location Validation Approach/User Location Validator.js ================================================ function onSubmit() { // Check if the browser supports geolocation if ("geolocation" in navigator) { // Request current user position navigator.geolocation.getCurrentPosition(function(position) { var currentLatitude = position.coords.latitude; // Current user latitude var currentLongitude = position.coords.longitude; // Current user longitude // Allowed business location coordinates fetched from server var allowedLatitude = locData.latitude; var allowedLongitude = locData.longitude; var locationName = locData.name; // Earth's radius in kilometers - constant used in distance calculation formula var earthRadiusKm = 6371; // Convert degree differences to radians var deltaLatitude = (currentLatitude - allowedLatitude) * Math.PI / 180; var deltaLongitude = (currentLongitude - allowedLongitude) * Math.PI / 180; // Haversine formula components var a = Math.sin(deltaLatitude / 2) * Math.sin(deltaLatitude / 2) + Math.cos(allowedLatitude * Math.PI / 180) * Math.cos(currentLatitude * Math.PI / 180) * Math.sin(deltaLongitude / 2) * Math.sin(deltaLongitude / 2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); // Calculate distance in kilometers between current and allowed locations var distanceKm = earthRadiusKm * c; // Check if user's current distance exceeds tolerance (e.g., 10 km) if (distanceKm > 10) { alert("You are " + distanceKm.toFixed(2) + " km away from your registered location: " + locationName); g_form.addErrorMessage("Location validation failed: Submission outside the allowed radius."); return false; // Cancel form submission } else { g_form.addInfoMessage("Location validated successfully within range of " + locationName); return true; // Allow form submission } }, function(error) { alert("Geolocation error: " + error.message); return false; // Stop submission if geolocation fails }); // Prevent form submission while waiting for async geolocation result return false; } else { g_form.addErrorMessage("Geolocation is not supported by your browser."); return false; // Block if geolocation API unsupported } } ================================================ FILE: Client-Side Components/Client Scripts/Dynamic Location Validation Approach/UserLocationUtils.js ================================================ var UserLocationUtils = Class.create(); UserLocationUtils.prototype = { initialize: function() { }, getUserLocationCoords: function() { var user = gs.getUser(); var loc = user.getRecord().location; if (loc) { var locGR = new GlideRecord('cmn_location'); if (locGR.get(loc)) return { latitude: parseFloat(locGR.latitude), longitude: parseFloat(locGR.longitude), name: locGR.name.toString() }; } return null; }, type: 'UserLocationUtils' }; ================================================ FILE: Client-Side Components/Client Scripts/Dynamic Reference Qualifier with Filtering/README.md ================================================ **Dynamic Reference Qualifier with Filtering** This Client Script provides a solution for dynamically updating the available options in a Reference Field based on the value selected in another field on the same form. This technique is essential for ensuring data quality and improving the user experience (UX). A typical use case is filtering the Assignment Group field to show only groups relevant to the selected Service, Category, or Location. ================================================ FILE: Client-Side Components/Client Scripts/Dynamic Reference Qualifier with Filtering/reference_qual_dynamic.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } var controlledField = 'assignment_group'; var controllingField = 'u_service'; var serviceSysId = g_form.getValue(controllingField); var encodedQuery = 'typeLIKEITIL^u_related_service=' + serviceSysId; g_form.setQuery(controlledField, encodedQuery); var currentGroupSysId = g_form.getValue(controlledField); if (currentGroupSysId && oldValue !== '' && currentGroupSysId !== '') { g_form.setValue(controlledField, ''); } } ================================================ FILE: Client-Side Components/Client Scripts/Dynamic UI Actions/README.md ================================================ # Dynamic Visible UI Actions This code snippet can be used to dynamically change the visibility of an UI action via a Client Script or UI Policy. As this happens on the client, it happens without the need of saving/reloading the form like you'd have to do if controlled via the UI Action's condition field (server-side). As this uses DOM manipulation you might have to uncheck the "Isolate Script" field on the Client Script or UI Policy to make it work or make sure you have a system property "glide.script.block.client.globals" with a value of false if you're in a scoped application. **Note**: DOM manipulation should be used with caution. ================================================ FILE: Client-Side Components/Client Scripts/Dynamic UI Actions/script.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } // Adjust condition to your needs if (newValue === "true") { $$('button[data-action-name^=action_name').each(function(e) { // replace action_name with the action name of the UI Action e.hide(); }); } else { $$('button[data-action-name^=action_name').each(function(e) { // replace action_name with the action name of the UI Action e.show(); }); } } ================================================ FILE: Client-Side Components/Client Scripts/Dynamic script to make fields read only/reame.md ================================================ Dynamic script to make fields read only It runs when the field value changes.On change client script If the new value equals '7', it retrieves all editable fields using g_form.getEditableFields(). Then it loops through each field and sets it to read-only using g_form.setReadOnly(). ================================================ FILE: Client-Side Components/Client Scripts/Dynamic script to make fields read only/script.js ================================================ /* It runs when the field value changes. If the new value equals '7', it retrieves all editable fields using g_form.getEditableFields(). Then it loops through each field and sets it to read-only using g_form.setReadOnly(). */ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } if (newValue == '7') { // update condition as required var fields = g_form.getEditableFields(); for (var i = 0; i < fields.length; i++) { g_form.setReadOnly(fields[i].getName(), true); } ================================================ FILE: Client-Side Components/Client Scripts/Dynamically Switch Form View Based on Field Value/README.md ================================================ ## Dynamically Switch Form View Based on Field Value This client script demonstrates how to **automatically switch form views** based on the value of a field. **Use case:** For example, if the **Category** field is set to *Hardware*, the form view switches to **ess**. You can extend this by updating the mapping object to support additional fields and values (e.g., *Software → itil*, *Network → support*). **Benefit:** Improves user experience by guiding users to the **most relevant form view**, ensuring the right fields are shown for the right scenario. **Test:** - Change the **Category** field to *Hardware* → Form view should switch to **ess**. - Update mapping to add new conditions (e.g., *Software → itil*) and verify the view switches accordingly. **How to Use:** 1. **Modify the table name** in the `switchView` function to match your target table: ```javascript switchView("section", "", targetView); 2. **Modify the view mapping** ================================================ FILE: Client-Side Components/Client Scripts/Dynamically Switch Form View Based on Field Value/dynamic-form-view-onchange.js ================================================ /** * dynamic-form-view-onchange.js * * Dynamically switches the form view automatically depending on the value of a specific field. * Example: If Category = Hardware → switch to ess view. * Extendable by modifying the mapping object for different fields/values. * */ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || !newValue) { return; } // Field value → view name mapping var viewMapping = { "hardware": "ess", "software": "itil", "network": "support" }; var fieldValue = newValue.toLowerCase(); var targetView = viewMapping[fieldValue]; if (targetView) { try { // Here for example the table name is incident switchView("section", "incident", targetView); } catch (e) { console.error("View switch failed: ", e); } } } ================================================ FILE: Client-Side Components/Client Scripts/Enable VIP and Senior VIP Checkboxes and read only/README.md ================================================ This script is to enable The VIP and Senior VIP check boxes on the user form to be visible but read-only for any user who is not a system administrator. ths script helps to better manage the VIP users and better administration of the Platform. You need to run this client script on the sys_user table and UI Type as Desktop and Type as "On load" and make this as Global. ================================================ FILE: Client-Side Components/Client Scripts/Enable VIP and Senior VIP Checkboxes and read only/ScriptToEnableVIP_superVIP users ================================================ function onLoad() { //Type appropriate comment here, and begin script below var myRole= g_user.hasRole("admin"); if(myRole!=true){ g_form.setReadOnly("vip",true); }else{ g_form.setReadOnly("vip",false); } } ================================================ FILE: Client-Side Components/Client Scripts/End Date can't be before Start Date/README.md ================================================ This script is for an onChange client script This is using an example where you have two date variables and need to ensure the user does not choose an end date that's before the start date 1. replace 'start_date' in the script with your actual start date field name 2. replace 'end_date' in the script with yoru actual start date field name 3. replace showFieldMsg and showErrorBox messages with your own message, if applicable This script works for both the standard (desktop) UI and Service Portal ================================================ FILE: Client-Side Components/Client Scripts/End Date can't be before Start Date/code.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue == '') { return; } var sDate = g_form.getValue('start_date'); //this gets the value from your start date field if (sDate == '') { //if the start date hasn't been populated then clear the end-date and inform user to please choose a stating date first g_form.clearValue('end_date'); g_form.showFieldMsg('end_date', "Please choose a starting date first"); } if (newValue < sDate) { //if the end date is before the start date, clear the field value, and inform the user that the end date cannot be before the start date g_form.clearValue('end_date'); g_form.showErrorBox('end_date', "End date cannot be before the Start date"); } } ================================================ FILE: Client-Side Components/Client Scripts/Expanding Info Message/README.md ================================================ This is an expanding info message. It can even run a script when an element is clicked. ================================================ FILE: Client-Side Components/Client Scripts/Expanding Info Message/script.js ================================================ function onLoad() { var message = ''; message += 'This is an expanding info message. It can even run code! Click "Show more" to see!'; message += '
'; message += '

Show more

'; message += '
'; message += '
    '; message += '
  • This is the expanded info in the message.
  • '; message += '
  • You can include any details you like here, including info retreived from a script like the sys_id of the current record: ' + g_form.getUniqueValue() + '
  • '; message += '
'; message += '

You can even run a script when an element is clicked like this. You just have to escape your code in the HTML.

'; message += '
'; message += '
'; g_form.addInfoMessage(message); } ================================================ FILE: Client-Side Components/Client Scripts/Field Completion Counter/README.md ================================================ # Field Completion Counter ## Use Case / Requirement Display a simple message showing how many fields are completed vs total fields on a form. This helps users track their progress while filling out forms. ## Solution A simple onLoad client script that: - Counts filled vs empty fields - Shows completion status in an info message - Updates when fields are modified ## Implementation Add this as an **onLoad** client script on any form. ## Notes - Excludes system fields and read-only fields - Updates in real-time as users fill fields - Simple and lightweight solution ================================================ FILE: Client-Side Components/Client Scripts/Field Completion Counter/script.js ================================================ function onLoad() { // Display field completion counter showFieldProgress(); // Set up listener for field changes setupProgressUpdater(); function showFieldProgress() { var allFields = g_form.getFieldNames(); var visibleFields = []; var filledFields = 0; // Count visible, editable fields for (var i = 0; i < allFields.length; i++) { var fieldName = allFields[i]; // Skip system fields and hidden/readonly fields if (fieldName.indexOf('sys_') === 0 || !g_form.isVisible(fieldName) || g_form.isReadOnly(fieldName)) { continue; } visibleFields.push(fieldName); // Check if field has value if (g_form.getValue(fieldName)) { filledFields++; } } var totalFields = visibleFields.length; var percentage = totalFields > 0 ? Math.round((filledFields / totalFields) * 100) : 0; g_form.addInfoMessage('Form Progress: ' + filledFields + '/' + totalFields + ' fields completed (' + percentage + '%)'); } function setupProgressUpdater() { // Simple debounced update var updateTimer; function updateProgress() { clearTimeout(updateTimer); updateTimer = setTimeout(function() { g_form.clearMessages(); showFieldProgress(); }, 500); } // Listen for any field change var allFields = g_form.getFieldNames(); for (var i = 0; i < allFields.length; i++) { g_form.addElementChangeListener(allFields[i], updateProgress); } } } ================================================ FILE: Client-Side Components/Client Scripts/Field Placeholder/README.md ================================================ Hint that appears inside an input field to indicate the type of data the user should enter. It is typically shown in a lighter font colour and disappears once the user starts typing. This helps guide users by providing an example or context without taking up additional space on the form. For example, a field placeholder in an short description input might read: "Please give the issue details here" ![image](https://github.com/user-attachments/assets/600d8ba7-2322-4b7f-b769-41f464082341) ================================================ FILE: Client-Side Components/Client Scripts/Field Placeholder/fieldplaceholder.js ================================================ function onLoad() { //Type appropriate comment here, and begin script below var shortDescription = g_form.getControl("short_description"); shortDescription.placeholder = "Please give the issue details here"; } ================================================ FILE: Client-Side Components/Client Scripts/Field Validations/README.md ================================================ An `onLoad` client script that validates required fields in specific ServiceNow form views. This ServiceNow client script provides automatic validation of required form fields when users access specific form views. The script runs immediately when a form loads and checks that critical fields are populated, displaying user-friendly error messages for any missing required information. This ensures data completeness and improves form submission success rates by catching validation issues early in the user workflow. What This Script Does: The onLoad client script performs comprehensive field validation with these key capabilities: View-Specific Validation: Only triggers validation when accessing a designated form view Multiple Field Support: Validates multiple required fields simultaneously in a single operation Smart Field Detection: Uses field labels (not technical names) in error messages for better user experience Consolidated Error Display: Shows all missing required fields in a single, clear error message Immediate Feedback: Provides instant validation results as soon as the form loads Non-Intrusive Design: Displays informational errors without blocking form interaction ================================================ FILE: Client-Side Components/Client Scripts/Field Validations/fieldValidation.js ================================================ function onLoad(){ var targetViewName = 'your_target_view_name'; var requiredFields = ['field1', 'field2', 'field3']; var currentViewName = g_form.getViewName(); if (currentViewName === targetViewName) { var emptyFields = []; for (var i = 0; i < requiredFields.length; i++) { var fieldValue = g_form.getValue(requiredFields[i]); if (!fieldValue || fieldValue.trim() === '') { emptyFields.push(g_form.getLabelOf(requiredFields[i])); } } if (emptyFields.length > 0) { var errorMessage = "The following required fields cannot be empty: " + emptyFields.join(', '); g_form.addErrorMessage(errorMessage); } } } ================================================ FILE: Client-Side Components/Client Scripts/Form Dirty State Prevention/README.md ================================================ # Form Dirty State Prevention ## Overview Detects form changes and warns users before navigating away or closing the form, preventing accidental data loss. ## What It Does - Tracks form field changes (dirty state) - Warns user before leaving unsaved form - Allows user to cancel navigation - Works with all form fields - Prevents accidental data loss - Clean, reusable pattern ## Use Cases - Complex multi-field forms - Long data entry forms - Forms with expensive operations - Critical data entry (financial, medical) - Any form where accidental exit would cause issues ## Files - `form_dirty_state_manager.js` - Client Script to manage form state ## How to Use ### Step 1: Create Client Script 1. Go to **System Definition > Scripts** (any table) 2. Create new Client Script 3. Set **Type** to "onChange" 4. Copy code from `form_dirty_state_manager.js` 5. Set to run on any field ### Step 2: Add Navigation Handler 1. Add to form's onLoad script: ```javascript // Initialize dirty state tracking var formStateManager = new FormDirtyStateManager(); ``` ### Step 3: Test 1. Open form and make changes 2. Try to navigate away without saving 3. User sees warning dialog 4. User can choose to stay or leave ## Example Usage ```javascript // Automatically tracks all field changes // When user tries to close/navigate: // "You have unsaved changes. Do you want to leave?" // - Leave (discard changes) // - Stay (return to form) ``` ## Key Features - ✅ Detects any field change - ✅ Persistent across form interactions - ✅ Works with new records and updates - ✅ Ignores read-only fields - ✅ Resets after save - ✅ No performance impact ## Output Examples ``` User opens form and changes a field → Form marked as "dirty" User clicks close/back button → Warning dialog appears: "You have unsaved changes" User clicks Leave → Form closes, changes discarded User clicks Save then navigates → No warning (form is clean) ``` ## Customization ```javascript // Customize warning message var warningMessage = "Warning: You have unsaved changes!"; // Add specific field tracking g_form.addOnFieldChange('priority', myCustomHandler); // Reset dirty flag after save g_form.save(); // Automatically triggers cleanup ``` ## Requirements - ServiceNow instance - Client Script access - Any table form ## Browser Support - Chrome, Firefox, Safari, Edge (all modern browsers) - Works with ServiceNow classic and modern UI ## Related APIs - [g_form API](https://docs.servicenow.com/bundle/sandiego-application-development/page/app-store/dev_apps/concept/c_FormAPI.html) - [Client Script Events](https://docs.servicenow.com/bundle/sandiego-application-development/page/app-store/dev_apps/concept/c_ClientScriptEvents.html) - [Form Field Changes](https://docs.servicenow.com/bundle/sandiego-application-development/page/app-store/dev_apps/concept/c_FieldChanges.html) ## Best Practices - Apply to important data entry forms - Test with real users - Consider accessibility for screen readers - Use with save shortcuts (Ctrl+S) - Combine with auto-save patterns ================================================ FILE: Client-Side Components/Client Scripts/Form Dirty State Prevention/form_dirty_state_manager.js ================================================ // Client Script: Form Dirty State Manager // Purpose: Track form changes and warn user before leaving with unsaved changes var FormDirtyStateManager = Class.create(); FormDirtyStateManager.prototype = { initialize: function() { this.isDirty = false; this.setupFieldChangeListeners(); this.setupNavigationWarning(); }, // Mark form as changed when any field is modified setupFieldChangeListeners: function() { var self = this; g_form.addOnFieldChange('*', function(control, oldValue, newValue, isLoading) { // Ignore system updates and form loads if (isLoading || oldValue === newValue) { return; } self.setDirty(true); }); }, // Warn user before navigating away with unsaved changes setupNavigationWarning: function() { var self = this; // Warn on form close attempt window.addEventListener('beforeunload', function(e) { if (self.isDirty && !g_form.isNewRecord()) { e.preventDefault(); e.returnValue = 'You have unsaved changes. Do you want to leave?'; return e.returnValue; } }); // Warn on GlideForm navigation g_form.addOnSave(function() { // Reset dirty flag after successful save self.setDirty(false); return true; }); }, setDirty: function(isDirty) { this.isDirty = isDirty; if (isDirty) { // Optional: Show visual indicator document.title = '* ' + document.title.replace(/^\* /, ''); gs.info('[Form State] Unsaved changes detected'); } }, isDirtyState: function() { return this.isDirty; } }; // Initialize on form load var formDirtyState = new FormDirtyStateManager(); ================================================ FILE: Client-Side Components/Client Scripts/Get Field Value From List on Client/README.md ================================================ # Use Case Let;s consider a scenario. Some validation logic needs to be executed on click of a list banner button. The field values to be validated are present right on the screen, so why bother writing a GlideAjax code + client-callable Script Include to call the server and run the same validation on the server. Client-side ```GlideList``` API provides the ```getCell()``` method to fetch visible cell values from the list. # Usage Write a client-side code on the UI Action and add the code in ```script.js``` file. ```g_list.getCell(String recSysId, String fieldName)``` # Explanation Values of visible fields/cells can be extracted using the ```getCell(String recSysId, String fieldName)``` method of client-side class ```GlideList (g_list)```. Required parameters: - ```String recSysId``` - SysID of the record/row whose value is to be fetched - ```String fieldName``` - Name of the field whose value is to be fetched **Returns:** ```HTMLElement (HTMLTableCellElement)```, string content can be extracted using ```.innerText``` property ================================================ FILE: Client-Side Components/Client Scripts/Get Field Value From List on Client/script.js ================================================ /* g_list.getCell(rowSysId, ) - Accepts two parameters * recordSysID (to identify the row) - For example: 23d7584c977a611056e8b3e6f053af6b * field_name - name of the field whose value is to be fetched */ var recSysId = g_list.getChecked(); // 23d7584c977a611056e8b3e6f053af6b - Can be modified to pass Sys ID by other means var fieldName = 'short_description'; // Modify as per requirement var cellVal = g_list.getCell(recSysId, fieldName).innerText; g_GlideUI.addOutputMessage({ msg: cellVal, type: 'success', preventDuplicates: true }); // Using this fancy notification trick because g_form is not supported on list view ================================================ FILE: Client-Side Components/Client Scripts/Get Form Elements/README.MD ================================================ This script is a Client Script (onLoad) for ServiceNow which retrieves all field names present on the current form and displays them in an alert message. Example: Hi Sai, please find the elements of the form short_description, description, number, etc. ================================================ FILE: Client-Side Components/Client Scripts/Get Form Elements/getFormElements.js ================================================ function onLoad() { //Type appropriate comment here, and begin script below var arr = []; for (var i = 0; i < g_form.elements.length; i++) { arr.push(g_form.elements[i].fieldName); } alert("Hi Sai, please find the form elements: " + arr.join(",")); } ================================================ FILE: Client-Side Components/Client Scripts/Get Logged in User Information/README.md ================================================ # The Glide User (g_user) is a global object available within the client side. It provides information about the logged-in user. Property Description g_user.userID Sys ID of the currently logged-in user g_user.name User's Full name g_user.firstName User's First name g_user.lastName User's Last name # It also has some methods available within the client side. Method Description g_user.hasRole() Determine whether the logged-in user has a specific role g_user.hasRoleExactly() Do not consider the admin role while evaluating the method g_user.hasRoles() You can pass two or more roles in a single method ================================================ FILE: Client-Side Components/Client Scripts/Get Logged in User Information/script.js ================================================ if (g_user.hasRole('admin') || g_user.hasRole('itil')) { // User has at least one of the roles g_form.setDisplay('internal_notes', true); } if (g_user.hasRole('admin') && g_user.hasRole('itil')) { // User must have both roles g_form.setDisplay('advanced_settings', true); } //Using the parameters to set a field value g_form.setValue('short_description', g_user.firstName); g_form.setValue('short_description', g_user.lastName); g_form.setValue('short_description', g_user.name); g_form.setValue('short_description', g_user.userID); ================================================ FILE: Client-Side Components/Client Scripts/Get URL Parameters/README.md ================================================ # Get URL Parameters in Global,Scoped Application for Record & Catalog Item Many times there is a need to grab parameters from URL. This could be required at table form load or catalog item load and either in Global scope or custom scope application when redirection happened. Given script will help you in achieving this: [Click here for the script](script.js) ================================================ FILE: Client-Side Components/Client Scripts/Get URL Parameters/script.js ================================================ function onLoad() { //Type appropriate comment here, and begin script below if (typeof spModal != "undefined") { // For Service Portal var url = top.location.href; var value = new URLSearchParams(url).get("sys_id"); //provide the parameter name console.log(value); } else { //For Native UI var glideURL = new GlideURL(); glideURL.setFromCurrent(); var id = glideURL.getParam("sysparm_id"); // provide the parameter name console.log(id); } } ================================================ FILE: Client-Side Components/Client Scripts/Get Value from URL Parameter/README.md ================================================ Script that can be used in Client scripts to get URL parameter value. ** Isolate script should be **false** ================================================ FILE: Client-Side Components/Client Scripts/Get Value from URL Parameter/script.js ================================================ //Isolate Script should be false function onLoad() { var getUrlParameter = function(url, parameterName) { return new URLSearchParams(url).get(); }; //should not use top.location, in UI16 this will "break out" of the iFrame and return the "nav_to.do?uri=xyz" URL. var winURL = window.location.href; console.log(getUrlParameter(winURL, "")); } ================================================ FILE: Client-Side Components/Client Scripts/Health Scan Prevent Insert Update in Before BRs/README.md ================================================ **Client script Deatils** 1. Table: sys_script 2. Type: onSubmit 3. UI Type: Desktop **Benefits** 1. This client script will prevent admin users to do insert/update operation in onBefore business rules. 2. It will help to avoid HealthScan findings thus increasing healthscan score. 3. Will prevent recursive calls. Link to ServiceNow Business Rules best Practise : https://developer.servicenow.com/dev.do#!/guide/orlando/now-platform/tpb-guide/business_rules_technical_best_practices ================================================ FILE: Client-Side Components/Client Scripts/Health Scan Prevent Insert Update in Before BRs/script.js ================================================ function onSubmit() { /* This client script will prevent insert and update operation in onBefore business rules. Table: sys_script Type: onSubmit Ui Type: Desktop */ var whenCond = g_form.getValue('when'); // when condition of business rule var scriptVal = g_form.getValue('script'); // script value of business rule. if (whenCond == 'before' && (scriptVal.indexOf('insert()') > -1 || scriptVal.indexOf('update()')) > -1) { alert("As per ServiceNow best Practise insert and update should be avoided in onBefore BRs. If you still want tp proceed try deactivating client script : " + g_form.getUniqueValue()); return false; } } ================================================ FILE: Client-Side Components/Client Scripts/Hide Dependent Choice field if there no dependent choices/HideDepnedentField.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { var fieldToHide = 'subcategory'; // I have taken subcategory as an example if (newValue === '') { g_form.setDisplay(fieldToHide, false); return; } var ga = new GlideAjax('NumberOfDependentChoices'); ga.addParam('sysparm_name', 'getCountOfDependentChoices'); ga.addParam('sysparm_tableName', g_form.getTableName()); ga.addParam('sysparm_element', fieldToHide); ga.addParam('sysparm_choiceName', newValue); ga.getXMLAnswer(callBack); function callBack(answer) { g_form.setDisplay(fieldToHide, parseInt(answer) > 0 ? true : false); } } ================================================ FILE: Client-Side Components/Client Scripts/Hide Dependent Choice field if there no dependent choices/NumberOfDependentChoices.js ================================================ var NumberOfDependentChoices = Class.create(); NumberOfDependentChoices.prototype = Object.extendsObject(AbstractAjaxProcessor, { getCountOfDependentChoices: function() { var dependentChoiceCount = 0; var choiceName = this.getParameter('sysparm_choiceName'); var tableName = this.getParameter('sysparm_tableName'); var element = this.getParameter('sysparm_element'); var choiceCountGa = new GlideAggregate('sys_choice'); choiceCountGa.addAggregate('COUNT'); choiceCountGa.addQuery('dependent_value',choiceName); choiceCountGa.addQuery('inactive','false'); choiceCountGa.addQuery('name',tableName); choiceCountGa.addQuery('element',element); choiceCountGa.query(); while(choiceCountGa.next()){ dependentChoiceCount = choiceCountGa.getAggregate('COUNT'); } return dependentChoiceCount; }, type: 'NumberOfDependentChoices' }); ================================================ FILE: Client-Side Components/Client Scripts/Hide Dependent Choice field if there no dependent choices/README.md ================================================ Hide the dependent choice field when there are no available options for the selected parent choice. For example, if a selected category on the incident form has no subcategories, then the subcategory field should be hidden. The file NumberOfDependentChoices.js is a client callable script include file which has a method which returns number of dependent choices for a selected choice of parent choice field. HideDepnedentField.js is client script which hides the dependent choice field(ex:subcategory field on incident form) if there are no active choices to show for a selected choices of it's dependent field (example: category on incident form) ================================================ FILE: Client-Side Components/Client Scripts/Hide Work Notes section/README.md ================================================ **Client Script** Client Script for hiding work notes section on incident form. In this example it is configured to hide work notes section, when incident is in state new, but you can adjust condition to fit your requirements. **Example configuration of Client Script** ![Coniguration](ScreenShot_1.PNG) **Example effect of execution** When incident is in state NEW (Work notes section not visible): ![Not Visible](ScreenShot_2.PNG) When incident is not in state NEW (Work notes section visible): ![Visible](ScreenShot_3.PNG) ================================================ FILE: Client-Side Components/Client Scripts/Hide Work Notes section/script.js ================================================ function onLoad() { //Script to hide work notes section, when incident is in state NEW //Get incident state var state = g_form.getValue('state'); //Check if incident is in state NEW (value = 1) if (state == 1) { //Hide work notes section g_form.setSectionDisplay('notes', false); } } ================================================ FILE: Client-Side Components/Client Scripts/How to adjust the Date format within a client script to align with the User Date format/README.md ================================================ # When getting a date from another table, it's usually in the format (YYYY-MM-DD). To display it in the user's preferred format on the client side, use the method below. If date is fetched from a query(like GlideAjax), date returned from query pass that date object into "new Date()" # Example ``` var user_date = formatDate(new Date(), g_user_date_format) g_form.setValue('',user_date); ``` ================================================ FILE: Client-Side Components/Client Scripts/How to adjust the Date format within a client script to align with the User Date format/code.js ================================================ var currentDateObj = new Date(); var currentDateUsr = formatDate(currentDateObj, g_user_date_format); ================================================ FILE: Client-Side Components/Client Scripts/Incident Count of Selected CI with Clickable Link to Related Incidents/README.md ================================================ # Incident Count of Selected Configuration Item with Info Message and Link to its Related Incident Displays a message showing the count of open incidents associated with a selected **Configuration Item (CI)** whenever the **Configuration Item** field changes on the Incident form. - Helps quickly identify whether the selected CI has existing incident by fetching and displaying active incident counts (excluding *Resolved*, *Closed*, and *Canceled* states). - Shows an **info message** with a **clickable link** that opens a filtered list of related incidents for that CI - If more than five incidents are linked, a **warning message** appears suggesting Problem investigation for frequent or repeated CI issues. - Uses an **onChange Client Script** on the *Configuration Item* field and a **GlideAjax Script Include** called from the client script to fetch the incident count --- ## Warning Message displayed on form when CI has 5 or more incidents ![CI_Incident_Message_Count_1](CI_Incident_Message_Count_1.png) --- ## Info Message displayed on form when CI has no incidents ![CI_Incident_Message_Count_2](CI_Incident_Message_Count_2.png) --- ## Info Message displayed on form when CI has incidents less than 5 ![CI_Incident_Message_Count_3](CI_Incident_Message_Count_3.png) --- ## Upon clicking the url link filter list opens with incidents associated with CI ![CI_Incident_Message_Count_4](CI_Incident_Message_Count_4.png) --- ================================================ FILE: Client-Side Components/Client Scripts/Incident Count of Selected CI with Clickable Link to Related Incidents/clientScriptCiIncident.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') return; g_form.clearMessages(); // Call Script Include to count incidents by CI var ga = new GlideAjax('ConfigurationIncidentCheck'); ga.addParam('sysparm_name', 'getIncidentCount'); ga.addParam('sysparm_ci', newValue); ga.getXMLAnswer(function(response) { var count = parseInt(response, 10); if (isNaN(count)) { g_form.addErrorMessage("Could not retrieve incident count for this CI."); return; } var ciName = g_form.getDisplayValue('cmdb_ci'); var url = '/incident_list.do?sysparm_query=cmdb_ci=' + newValue + '^stateNOT IN6,7,8'; var msg = 'Configuration Item ' + ciName + ' has ' + count + ' related incident(s).'; if (count === 0) { g_form.addInfoMessage( 'Configuration Item ' + ciName + ' has no incidents associated with it.' ); } else { if (count >= 5) { g_form.addWarningMessage( msg + ' consider Problem investigation.' ); } else { g_form.addInfoMessage(msg); } } }); } ================================================ FILE: Client-Side Components/Client Scripts/Incident Count of Selected CI with Clickable Link to Related Incidents/glideAjaxIncidentCiCount.js ================================================ var ConfigurationIncidentCheck = Class.create(); ConfigurationIncidentCheck.prototype = Object.extendsObject(AbstractAjaxProcessor, { getIncidentCount: function() { var ci = this.getParameter('sysparm_ci'); if (!ci) return 0; var gr = new GlideAggregate('incident'); gr.addQuery('cmdb_ci', ci); gr.addQuery('state', 'NOT IN', '6,7,8'); gr.addAggregate('COUNT'); gr.query(); return gr.next() ? gr.getAggregate('COUNT') : 0; }, type: 'ConfigurationIncidentCheck' }); ================================================ FILE: Client-Side Components/Client Scripts/Live Character Counter and Validator/README.md ================================================ This solution dynamically provides users with real-time feedback on the length of a text input field (like short_description or a single-line text variable). It immediately displays a character count beneath the field and uses visual cues to indicate when a pre-defined character limit has been reached or exceeded. This is a vital User Experience (UX) enhancement that helps agents and users write concise, actionable information, leading to improved data quality and better integration reliability. Name Live_Character_Counter_ShortDesc_OnLoad Table : Custom Table or Incident Type onChange Field : Description UI Type All Isolate Script false ================================================ FILE: Client-Side Components/Client Scripts/Live Character Counter and Validator/character_counter.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { var FIELD_NAME = 'short_description'; var MAX_CHARS = 100; var currentLength = newValue.length; var counterId = FIELD_NAME + '_counter_label'; if (typeof g_form.getControl(FIELD_NAME) !== 'undefined' && !document.getElementById(counterId)) { var controlElement = g_form.getControl(FIELD_NAME); var counterLabel = document.createElement('div'); counterLabel.setAttribute('id', counterId); counterLabel.style.fontSize = '85%'; counterLabel.style.marginTop = '2px'; controlElement.parentNode.insertBefore(counterLabel, controlElement.nextSibling); } var counterElement = document.getElementById(counterId); if (counterElement) { var remaining = MAX_CHARS - currentLength; counterElement.innerHTML = 'Characters remaining: ' + remaining + ' (Max: ' + MAX_CHARS + ')'; // Apply red color if the limit is exceeded if (remaining < 0) { counterElement.style.color = 'red'; } else { // Revert color if back within limits counterElement.style.color = 'inherit'; } } } ================================================ FILE: Client-Side Components/Client Scripts/MRVS variables validations/README.md ================================================ Multi Row Variable Set (MRVS) variables validations which are based on variables out of MRVS for this we need to use "g_service_catalog" client side API of ServiceNow Example: Requirement is like there is an adaptation leave form, where the user can take 30 leaves in batches not all at once. Users can enter the last working date which is a normal variable in catalog item and batched leaves in MRVS where the Extended leave start and end dates will populate. We are restricting here only to enter extended leave start date after the last working date. ![image](https://user-images.githubusercontent.com/46869542/193416763-27fb52c9-e15b-48b5-99fd-6c146a6819c3.png) ================================================ FILE: Client-Side Components/Client Scripts/MRVS variables validations/script.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue == '') { return; } //Type appropriate comment here, and begin script below if (g_form.getValue('extended_leave_start') <= g_service_catalog.parent.getValue("last_working_date")) { // extended leave start date is earlier or equal to last working date g_form.clearValue('extended_leave_start'); g_form.showFieldMsg('extended_leave_start', "Entended leave start can't be earlier or equal to last working day", 'error'); } } ================================================ FILE: Client-Side Components/Client Scripts/Major Incident Proposal/Client_Script.js ================================================ function onSubmit() { var priority = g_form.getValue('priority'); if (priority == '1') { // is already a Major Incident var gaCheck = new GlideAjax('CreateMajorIncident'); gaCheck.addParam('sysparm_name', 'isAlreadyMajorIncident'); gaCheck.addParam('sysparm_sysid', g_form.getUniqueValue()); var response = gaCheck.getXMLWait(); var isMajorIncident = response.documentElement.getAttribute('answer'); // not yet a Major Incident if (isMajorIncident == 'false') { var resp = confirm(getMessage('Please confirm that you would like to propose a Major Incident Candidate?')); if (resp) { // propose the Major Incident var ga = new GlideAjax('CreateMajorIncident'); ga.addParam('sysparm_name', 'majorIncCreate'); ga.addParam('sysparm_sysid', g_form.getUniqueValue()); var majorIncidentResponse = ga.getXMLWait(); var incidentNumber = majorIncidentResponse.documentElement.getAttribute('answer'); alert("Incident " + incidentNumber + " has been proposed as a Major Incident candidate."); } else { // cancels return false; } } } return true; // Allow if priority is not 1 or after processing } ================================================ FILE: Client-Side Components/Client Scripts/Major Incident Proposal/README.md ================================================

Major Incident Proposal - Client Script & Script Include

This solution asks the users to propose an incident as a Major Incident candidate. The process is initiated from the Incident form when the priority is set to 1 (Critical), and the system checks if the incident has already been proposed as a Major Incident. If not, the user is prompted to confirm whether they wish to propose the incident. Upon confirmation, the incident is updated, and the Major Incident status is assigned.

This solution consists of a Client Script and a Script Include, which handle the user interaction and the back-end logic, respectively.

Functionality

1. Client Script - onSubmit()

The Client Script is triggered when the user submits an Incident form with a priority of 1 (Critical). It performs the following actions:

  • Checks if the incident is already marked as a Major Incident by calling the Script Include via GlideAjax.
  • If the incident is not already marked as a Major Incident, the user is prompted to confirm whether they wish to propose it as a Major Incident.
  • Upon confirmation, the incident is proposed as a Major Incident candidate, and a success message is displayed with the incident number.

Key Features:

  • Priority Check: The script only runs when the priority is set to 1 (Critical).
  • GlideAjax Call: Uses GlideAjax to communicate with the server-side Script Include to check and propose the Major Incident.
  • User Confirmation: The user is prompted to confirm the proposal of the Major Incident before proceeding.
  • Synchronous Execution: The script waits for a response from the server using getXMLWait() to ensure the process completes before submitting the form.

2. Script Include - CreateMajorIncident

The Script Include provides the back-end logic for:

  • Checking whether the incident is already proposed as a Major Incident.
  • Proposing the incident as a Major Incident by updating its major_incident_state, proposed_by, and proposed_on fields, and adding work notes.

Usage Example:

  1. When the priority of an incident is set to 1 (Critical), the client script checks whether the incident is already a Major Incident.
  2. If not, the user is prompted to confirm the Major Incident proposal.
  3. Upon confirmation, the CreateMajorIncident Script Include updates the incident record to reflect its proposed Major Incident status and returns the incident number.

Customization

You can easily customize this functionality by:

  • Adding more validation rules to the Script Include.
  • Modifying the client script to handle different priorities or additional fields.
  • Updating the work notes or other fields when proposing the incident.
================================================ FILE: Client-Side Components/Client Scripts/Major Incident Proposal/Script_Include.js ================================================ var CreateMajorIncident = Class.create(); CreateMajorIncident.prototype = Object.extendsObject(AbstractAjaxProcessor, { majorIncCreate: function() { var incSysId = this.getParameter('sysparm_sysid'); var ginc = new GlideRecord('incident'); if (ginc.get(incSysId)) { ginc.major_incident_state = 'proposed'; ginc.proposed_by = gs.getUserID(); ginc.proposed_on = new GlideDateTime(); ginc.work_notes = "Hello World! " + new GlideDateTime(); ginc.update(); return ginc.number.toString(); } return 'false'; }, isAlreadyMajorIncident: function() { var incSysId = this.getParameter('sysparm_sysid'); var ginc = new GlideRecord('incident'); if (ginc.get(incSysId)) { return ginc.major_incident_state == 'proposed' ? 'true' : 'false'; } return 'false'; }, type: 'CreateMajorIncident' }); ================================================ FILE: Client-Side Components/Client Scripts/Make Variable Editor Read Only for Catalog Items containing MRVS/CheckMRVSDetails.js ================================================ var CheckMRVSDetails = Class.create(); CheckMRVSDetails.prototype = Object.extendsObject(AbstractAjaxProcessor, { checkMRVS: function(){ var itemID = this.getParameter('sysparm_itemID'); var grMultiRow = new GlideRecord('io_set_item'); grMultiRow.addEncodedQuery('variable_set.type=one_to_many'); // Variable Set is MRVS grMultiRow.addQuery('sc_cat_item',itemID); grMultiRow.setLimit(1); grMultiRow.query(); if(grMultiRow.next()){ return true; } return false; }, type: 'CheckMRVSDetails' }); ================================================ FILE: Client-Side Components/Client Scripts/Make Variable Editor Read Only for Catalog Items containing MRVS/MakeVariableSetReadOnly.js ================================================ function onLoad() { //Type appropriate comment here, and begin script below var catItem = g_form.getValue('cat_item'); //Sys Id of the Catalog Item var ga = new GlideAjax('CheckMRVSDetails'); // SI Name ga.addParam('sysparm_name', 'checkMRVS'); // Function Name ga.addParam('sysparm_itemID', catItem); ga.getXML(checkMRVS); function checkMRVS(response) { var answer = response.responseXML.documentElement.getAttribute("answer"); if (answer == 'true') { g_form.setVariablesReadOnly(true); } } } ================================================ FILE: Client-Side Components/Client Scripts/Make Variable Editor Read Only for Catalog Items containing MRVS/README.md ================================================ # Make Variable Editor Read Only for Catalog Items which have a Multi Row Variable Set Create an onLoad Client Script which would call a Script Include and pass in the Catalog Item sys_id to check if the Catalog Item contains a MRVS. The script has been tailored to work with the Requested Item table. To make it work for any other table which has the Variable Editor replace the field "cat_item" with the field containing the details of the Catalog Item ================================================ FILE: Client-Side Components/Client Scripts/Make all fields read only/README.md ================================================ Use this script to make all fields readonly via client script. **Tested in Global scope **You can't make mandatory fields as readonly **Best Practice is to use ACLs ================================================ FILE: Client-Side Components/Client Scripts/Make all fields read only/script.js ================================================ function onLoad() { var fields = g_form.getEditableFields(); var skippedFields = [ 'sys_created_on', 'sys_created_by', 'sys_updated_on', 'sys_updated_by', ]; for (var i = 0; i < fields.length; i++) { var field = fields[i]; // Skip fields in the designated array if (skippedFields.indexOf(field) !== -1) { continue; } g_form.setMandatory(fields[i], false); g_form.setReadOnly(fields[i], true); } } ================================================ FILE: Client-Side Components/Client Scripts/Make fields read only in specific states/Make fields read only in specific state.md ================================================ # Make editable fields read only on load in specific state for example state is On Hold. ================================================ FILE: Client-Side Components/Client Scripts/Make fields read only in specific states/code.js ================================================ function onLoad() { var getStateValue = g_form.getValue('state'); if (getStateValue == '3') { var fields = g_form.getEditableFields(); for (var fieldLength = 0; fieldLength < fields.length; fieldLength++) { g_form.setReadOnly(fields[fieldLength], true); } } } ================================================ FILE: Client-Side Components/Client Scripts/Mandatory Field Highlighter/README.md ================================================ # Mandatory Field Highlighter ## Use Case Provides visual feedback for empty mandatory fields on ServiceNow forms by showing error messages when the form loads. Helps users quickly identify which required fields need to be completed. ## Requirements - ServiceNow instance - Client Script execution rights - Forms with mandatory fields ## Implementation 1. Create a new Client Script with Type "onLoad" 2. Copy the script code from script.js 3. **Customize the fieldsToCheck string** with your form's mandatory field names 4. Apply to desired table/form 5. Save and test on a form with mandatory fields ## Configuration Edit the `fieldsToCheck` variable to include your form's mandatory fields as a comma-separated string: ```javascript // Example configurations for different forms: // For Incident forms: var fieldsToCheck = 'short_description,priority,category,caller_id,assignment_group'; // For Request forms: var fieldsToCheck = 'short_description,requested_for,category,priority'; // For Change Request forms: var fieldsToCheck = 'short_description,category,priority,assignment_group,start_date,end_date'; // For Problem forms: var fieldsToCheck = 'short_description,category,priority,assignment_group'; // Custom fields (add as needed): var fieldsToCheck = 'short_description,priority,u_business_justification,u_cost_center'; ``` ## Features - Shows error messages under empty mandatory fields on form load - Easy configuration with comma-separated field names - Automatically skips fields that don't exist on the form - Only processes fields that are actually mandatory and visible - Uses proper ServiceNow client scripting APIs - No DOM manipulation or unsupported methods ## Common Field Names - `short_description` - Short Description - `priority` - Priority - `category` - Category - `caller_id` - Caller - `requested_for` - Requested For - `assignment_group` - Assignment Group - `assigned_to` - Assigned To - `state` - State - `urgency` - Urgency - `impact` - Impact - `start_date` - Start Date - `end_date` - End Date - `due_date` - Due Date - `location` - Location - `company` - Company - `department` - Department ## Notes - Uses g_form.showFieldMsg() method to display error messages - Uses g_form.hasField() to safely check field existence (built into the safety checks) - Only runs on form load - provides initial validation feedback - Easy to customize for different forms by modifying the field list - Compatible with all standard ServiceNow forms - Lightweight and focused on essential functionality ## Example Usage For a typical incident form, simply change the configuration line to: ```javascript var fieldsToCheck = 'short_description,priority,category,caller_id,assignment_group'; ``` Save the Client Script and test on an incident form to see error messages appear under empty mandatory fields. ================================================ FILE: Client-Side Components/Client Scripts/Mandatory Field Highlighter/script.js ================================================ function onLoad() { // USER CONFIGURATION: Add field names you want to check (comma-separated) var fieldsToCheck = 'short_description,priority,category,caller_id'; // Convert to array and process var fieldArray = fieldsToCheck.split(','); // Check each field for (var i = 0; i < fieldArray.length; i++) { var fieldName = fieldArray[i]; // Skip if field is not mandatory or not visible if (!g_form.isMandatory(fieldName) || !g_form.isVisible(fieldName)) { continue; } // Get current field value var value = g_form.getValue(fieldName); // Show error message if field is empty if (!value || value === '') { g_form.showFieldMsg(fieldName, 'This field is required', 'error'); } } } ================================================ FILE: Client-Side Components/Client Scripts/MultiSelect in Portal/README.md ================================================ The custom widget that enables you to select multiple incidents in the portal page. ================================================ FILE: Client-Side Components/Client Scripts/MultiSelect in Portal/script.js ================================================ //HTML code that displays the incidents to select multiple at once.

Complaints

{{c.getSelectedCount()}} complaints selected (across pages)
Run Date Case ID Policy ID
{{item.batchno}} {{item.caseid}} {{item.policyid}}
No incidents found.
Page {{c.pageNumber}} of {{c.totalPages}}
================================================ FILE: Client-Side Components/Client Scripts/On load Switch-Case Testing/README.md ================================================ This is code snippet of switch case, and it will easily help to understand the usage of switch case and how we can implement it into the environment as per our requirements. ================================================ FILE: Client-Side Components/Client Scripts/On load Switch-Case Testing/Switch-Case code snippet.js ================================================ function onLoad() { // Switch Case code snippet using in ServiceNow var category = g_form.getValue("category"); // It will capture the value of category field. switch(category){ case "hardware": // if the value would be 'hardware' in category field. g_form.addInfoMessage("Yes category is hardware") // This function will add a infomessage. break; //This keyword is used to stop the execution inside a switch block. case "software": // if the value would be 'software' in category field. g_form.addInfoMessage("Yes category is software") // This function will add a infomessage. break; case "network": // if the value would be 'Network' in category field. g_form.addInfoMessage("Yes category is Network") // This function will add a infomessage. break; case "database": // if the value would be 'Database' in category field. g_form.addInfoMessage("Yes category is Database") // This function will add a infomessage. break; default: // This will execute only if the category field have the value apart from mentioned cases. g_form.addInfoMessage("Oh! no category is something else") // This function will add a infomessage. } } ================================================ FILE: Client-Side Components/Client Scripts/Only number validation for input/OnChange.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue === '') { return; } // Change the name of the field to your field name var FIELD_NAME = ''; // allow number only var reg = new RegExp(/^\d+$/); if (!reg.test(newValue)) { g_form.hideFieldMsg(FIELD_NAME); g_form.showFieldMsg(FIELD_NAME, g_form.getLabelOf(FIELD_NAME) + ' may contain digits only.', 'error'); } else { g_form.hideFieldMsg(FIELD_NAME); } } ================================================ FILE: Client-Side Components/Client Scripts/Only number validation for input/README.md ================================================ # Client Script - Input is number only A client script that validates that the input entry is number only ## Usage - Create a new client script - Set the type to OnChange. Update the field in the client script to - Select a field field that you want to be number - Navigate to the form and set the field to a string - There will be a validation error close to the field ================================================ FILE: Client-Side Components/Client Scripts/Open Record in Agne Workspace Tab/README.md ================================================ This code helps to open a record in readonly mode irrespective of ACLS, UI Policies via client script in a Agent workspace tab. Here we are opening a story that is stored in the parent field on the incident record in the agent workspace. ================================================ FILE: Client-Side Components/Client Scripts/Open Record in Agne Workspace Tab/openrecawtab.js ================================================ // Opens a Story attached to a Incident record in the parent field in the readonly mode. function onLoad(g_form) { g_aw.openRecord('rm_story', g_form.getValue('parent'), {readOnlyForm: true}); } ================================================ FILE: Client-Side Components/Client Scripts/Populate Jelly Slushbucket with Values/ClientScript.js ================================================ //Called when the form loads addLoadEvent(function () { //Load the groups when the form loads slush.clear(); var ajax = new GlideAjax("example_ajax_call"); // Can use this to get values to fill the slushbucket ajax.addParam("sysparm_example", "example"); ajax.getXML(loadResponse); return false; }); //Called when we get a response from the 'addLoadEvent' function function loadResponse(response) { //Process the return XML document and add groups to the left select var xml = response.responseXML; var e = xml.documentElement; var items = xml.getElementsByTagName("item"); if (items.length == 0) return; //Loop through item elements and add each item to left slushbucket for (var i = 0; i < items.length; i++) { var item = items[i]; slush.addLeftChoice( item.getAttribute("example"), item.getAttribute("example_2") + ": " + item.getAttribute("example_3") + ": " + item.getAttribute("example_4") ); //This is what will be displayed in the left side of the slushbucket } } ================================================ FILE: Client-Side Components/Client Scripts/Populate Jelly Slushbucket with Values/README.md ================================================ This code is used to populate the Jelly tag within a UI Page. In the UI Page, must ensure that this tag is located in the HTML section. Then in the client script, include the code provided in the example. ================================================ FILE: Client-Side Components/Client Scripts/Prevent Rejection Without Comments/readme.md ================================================ 🧩 Readme: Prevent Rejection Without Comments – Client Script 📘 Overview This Client Script enforces that approvers must enter comments before rejecting a record in the Approval [sysapproval_approver] table. It ensures accountability, audit readiness, and clear justification for rejection decisions. 🧠 Use Case Field Details Table sysapproval_approver Type Client Script – onSubmit Purpose Prevent users from rejecting approvals without comments Business Value Ensures transparency and proper audit trail in approval workflows ⚙️ Configuration Steps Navigate to System Definition → Client Scripts. Click New. Fill the form as follows: Field Value Name Prevent Rejection Without Comments Table sysapproval_approver UI Type All Type onSubmit Active ✅ Applies on Update Paste the following script in the Script field. 💻 Script function onSubmit() { // Get the current state value of the approval record var state = g_form.getValue('state'); // Get the comments entered by the approver var comments = g_form.getValue('comments'); // Check if the approver is trying to REJECT the record // The out-of-box (OOB) value for rejection in sysapproval_approver is "rejected" // If state is 'rejected' and comments are empty, stop the submission if (state == 'rejected' && !comments) { // Display an error message to the user g_form.addErrorMessage('Please provide comments before rejecting the approval.'); // Prevent the form from being submitted (block save/update) return false; } // Allow the form submission if validation passes return true; } 🧪 Example Scenario Field Value Approver John Doe State Rejected Comments (empty) User Action: Clicks Update System Response: Shows error message — “Please provide comments before rejecting the approval.” Record submission is blocked until comments are provided. ✅ Expected Outcome 🚫 Prevents rejection without comments ⚠️ Displays user-friendly validation message 📝 Ensures that every rejection has a reason logged for compliance ================================================ FILE: Client-Side Components/Client Scripts/Prevent Rejection Without Comments/script.js ================================================ //Prevent Rejection Without Comments function onSubmit() { // Get the current state value of the approval record var state = g_form.getValue('state'); // Get the comments entered by the approver var comments = g_form.getValue('comments'); // Check if the approver is trying to REJECT the record // The out-of-box (OOB) value for rejection in sysapproval_approver is "rejected" // If state is 'rejected' and comments are empty, stop the submission if (state == 'rejected' && !comments) { // Display an error message to the user g_form.addErrorMessage('Please provide comments before rejecting the approval.'); // Prevent the form from being submitted (block save/update) return false; } // Allow the form submission if validation passes return true; } ================================================ FILE: Client-Side Components/Client Scripts/Price field restriction to one currency/README.md ================================================ # Client Script - Set Price type field to only one currency In a multi currecny enabled servicenow environment, if you have a requirement to enable only one currency choice for a particular table and field of type Price. ## Usage - Create a new client script - Set the type to OnLoad. - Copy the script to your client script. - Update the in the client script to 'Your field name' - Add your currency code and symbol in place of USD & $ - Save ================================================ FILE: Client-Side Components/Client Scripts/Price field restriction to one currency/price_field_restriction_to_one_currency.js ================================================ function onLoad(){ // Remove all currency options g_form.clearOptions('.currency_type'); // Add only one currency option (e.g., USD) g_form.addOption('.currency_type', 'USD', '$'); // Set the currency field to the only available option g_form.setValue('.currency_type', 'USD'); } ================================================ FILE: Client-Side Components/Client Scripts/Redact Sensitive Information/README.md ================================================ # Redact Sensitive Information When users create an incident or HR case via the self-service portal, they may occasionally enter sensitive information (e.g., personal identifiers, account numbers). To prevent misuse of such data, **fulfillers** can redact sensitive information from the short description or description fields. This ensures that confidential information is safeguarded and not accessible for unauthorized use or distribution. ## Prerequisites 1. Custom Field: Add a custom field to the form to hold the redacted text. Example: u_redact (Redact). 2. OnSubmit Client Script: Create an onsubmit client script to redact sensitive information. This script will update the **short description** and **description** field with custom value as required. **Note**: Data that has been redacted cannot be recovered. ================================================ FILE: Client-Side Components/Client Scripts/Redact Sensitive Information/script.js ================================================ function onSubmit() { var redact = g_form.getValue("u_redact"); //custom field on the form to redact information if (redact == true) { var answer = confirm(getMessage('Do you want to redact sensitive information')); //Confirm the user who wants to redact information if (answer) { g_form.setValue('short_description', 'Short Description is redacted as it contained sensitive information'); //Custom short_description post redacting g_form.setValue('description', 'Description is redacted as it contained sensitive information'); //Custom description post redacting g_form.setValue('work_notes', 'The Description and Short Description has been redacted.'); //Adding work notes to track who redacted the short_description and description g_form.setReadOnly('short_description', true); g_form.setReadOnly('description', true); g_form.setReadOnly('u_redact', true) } else { g_form.setValue('u_redact', false); return false; } } } ================================================ FILE: Client-Side Components/Client Scripts/Reinstate Error status/README.md ================================================ Table: Time Worked [task_time_worked] Type: onsubmit #Objective : Ensure that time entries (represented by the work_date field) are not submitted after 8:00 PM CST on two key dates: The 16th of the month and The last day of the month If a user tries to submit time for a current or past date after the cut-off time, the submission is blocked and a clear error message is displayed. #Business Scenario Imagine a consulting firm where employees log billable hours against customer cases. There are internal controls in place that lock the timekeeping system after a certain cut-off time to ensure accurate payroll and billing. The finance department requires that: On the 16th and last day of each month, submissions must be in before 8:00 PM CST. If employees miss the deadline, they can only log time for future dates (not today or the past). ================================================ FILE: Client-Side Components/Client Scripts/Reinstate Error status/script.js ================================================ function onSubmit() { // Cutoff time for submission in CST. var cutoffTime = "20:00:00"; // Get the current date and time in CST var currentDate = new Date(); var currentCSTDate = new Date( currentDate.toLocaleString("en-US", { timeZone: "America/Chicago" }) ); // Get time from current CST date var currentCSTTime = currentCSTDate.toTimeString().substring(0, 8); // Get last day of the month var dayOfMonth = currentCSTDate.getDate(); var lastDayOfMonth = new Date( currentCSTDate.getFullYear(), currentCSTDate.getMonth() + 1, 0 ).getDate(); if ((dayOfMonth === 16 || dayOfMonth === lastDayOfMonth) && currentCSTTime > cutoffTime) { var workDate = g_form.getValue("work_date"); if (workDate) { var formattedWorkDate = new Date(workDate + "T00:00:00"); // If work_date is on or before current date, block submission if (formattedWorkDate <= currentCSTDate) { g_form.addErrorMessage( "The time period closed for time submission at 8:00 PM CST. Time must be billed in the next time period." + ": " + lastDayOfMonth ); return false; } } } return true; } ================================================ FILE: Client-Side Components/Client Scripts/Remove Option from Choice List/README.md ================================================ **Purpose:** This onChange function automatically reacts when the "Category" field is changed. If the new category selected is "inquiry," the function removes the options for "Impact" and "Urgency" that have a value of 1. Whenever a user selects a new category, the script checks if it’s set to "inquiry." If so, it removes the specified options for "Impact" and "Urgency". **How to Use This Function** You can use this Onchange client script on any form and maanage your field choice options. ================================================ FILE: Client-Side Components/Client Scripts/Remove Option from Choice List/Remove Options from Choice List.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue == '') { return; } if (newValue == 'inquiry') { //Onchange of Category g_form.removeOption('impact', '1'); g_form.removeOption('urgency', '1'); } } ================================================ FILE: Client-Side Components/Client Scripts/Require comment onPriority change/README.md ================================================ Table: sn_customerservice_case Type: OnChange Field: Priority Use Case: Make additional comments mandatory on priority change for the case table. ================================================ FILE: Client-Side Components/Client Scripts/Require comment onPriority change/script.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } if ((!g_form.isNewRecord()) && (newValue != oldValue)) { g_form.setMandatory('comments', true); g_form.addErrorMessage('Additional comment required when changing Priority.'); } else { g_form.setMandatory('comments', false); } ================================================ FILE: Client-Side Components/Client Scripts/Restrict Creation of P1, P2 Incidents/README.md ================================================ The code snippet can be used to restrict the creation of priority P1, P2 incidents except for the admins and a particular group members. To achieve this requirement, I have created a onChange client script for the field name "Priority" and also created a script include to get the necessary data from the server side. ================================================ FILE: Client-Side Components/Client Scripts/Restrict Creation of P1, P2 Incidents/Restrict_Creation_Of_P1_P2_Incidents.js ================================================ //Client Script //Type onChnage, Field name: Priority // To restrict creation of priority P1, P2 incidents except for the admins and a particular group members var checkAjax = ''; // if logged in user is admin then skip the code execution if(!g_user.hasRole('admin')){ if(newValue == 1 || newValue == 2){ if(g_form.isNewRecord()){ checkAjax = new GlideAjax('checkMemberOfGroup'); checkAjax.addParm('sysparm_name', 'checkAccessNew'); checkAjax.getXMLWait(); var ans = checkAjax.getAnswer(); if(and == 'false'){ g_form.setValue('impact', 3); g_form.setValue('urgency', 3); g_form.addErrorMessage('Creation of P1, P2 incidents is restricted to Admins and IT ServiceDesk'); } else{ var incNumber = g_form.getValuye('number'); checkAjax = newGlideAjax('checkMemberOfGroup'); checkAjax.addParm('sysparm_name', 'checkAccess'); checkAjax.addParm('sysparm_number', incNumber); // passing the current incident number so that if the logged in user is an end user, then get the previous values of impact and urgency values. checkAjax.getXMLWait(); var ans = checkAjax.getAnswer(); ans = ans.split(","); // if value returned false, then logged in user is neither admin nor member of a particluar gorup, if(ans[2] == 'false'){ var imp = parseInt(ans[0]); var urg = parseInt(ans[1]); // setting back the impact and urgency values to their previous values if logged in user is not part of a particular group and not an admin. g_form.setValue('impact', imp); g_form.setValue('urgency', urg); g_form.addErrorMessage('Creation of P1, P2 incidents is restricted to Admins and IT ServiceDesk'); } } } } //Script Include var checkMemberOfGroup = Class.create(); checkMemberOfGroup.prototype = Object.extendsObject(AbstractAjaxProcessor, { // The below method is used to restrict the creation of P1, P2 incidents for existing incidents checkAccess: function(){ var arr = []; var number = this.getParameter('sysparm_number'); var glideInc = new GlideRecord('incident'); glideInc.addquery('number', number); glideInc.query(); if(glideInc.next()){ arr.push(glideInc.impact); arr.push(glideInc.urgency); } var checkGroupMember = gs.getUser().isMemberOf('Group_Name'); if(checkGroupMember){ arr.push('true'); } else{ arr.push('false'); } return arr.toString(); }, // The below function is used to restict the creation of priority P1, P2 incidents for new incidents checkAccessNew: function(){ var checkGroupMember = gs.getUser().isMemberOf('Group_Name'); if(checkGroupMember){ return true; } return false; }, type: 'checkMemberOfGroup }); ================================================ FILE: Client-Side Components/Client Scripts/Restrict Fields on Template/README.md ================================================ **Details** This is a on change client script on sys_template table. This script will restrict users to select defined fields while template creation. Type: OnChange Field: Template Table: sys_template **Use Case** There is an OOB functionality to restrict fields using "**save as template**" ACL, but it has below limitations: 1. If the requirement is to restrcit more number of fields (example: 20), 20 ACLs will have to be created. 2. The ACls will have instance wide effect, this script will just restrict on client side. ================================================ FILE: Client-Side Components/Client Scripts/Restrict Fields on Template/script.js ================================================ /* Type: onChnage Table: sys_template Field: Template */ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } if (g_form.getValue('table') == 'incident') { // table on which sys_template is being created. var fields = ['active', 'comments']; // array of fields to be restricted while template creation. for (var i = 0; i < fields.length; i++) { if (newValue.indexOf(fields[i]) > -1) { alert("You Cannot Add " + fields[i]); // alert if user selects the restricted field. var qry = newValue.split(fields[i]); g_form.setValue('template', qry[0] + 'EQ'); // set the template value to previous values (oldValue does not work in this case). } } } } ================================================ FILE: Client-Side Components/Client Scripts/Set Severity, state & assigned to/README.md ================================================ Use the script provided in script_include.js and script.js to set fetch multiple values from server to client side by passing an object from server to the client side and setting values on your form. This can be used to pass multiple parameters from server to client side. Use Case: Consider you have a reference field on your form referring to "sn_si_incident" and you need to set Severity, state and assigned to onChange of the reference field. Solution: Create a client callable script include as mentioned in script_include.js and pass the required values to your client script. Then use the onChange client script in script.js to set values on the form. ================================================ FILE: Client-Side Components/Client Scripts/Set Severity, state & assigned to/script.js ================================================ //onChange client script function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } //Type appropriate comment here, and begin script below var ga = new GlideAjax('getSIRDetails'); // calling script include ga.addParam('sysparm_name', 'getDetails'); ga.addParam('sysparm_sir', newValue); //passing newValue to the script include ga.getXMLAnswer(callBackFunction); function callBackFunction(response) { var ans = JSON.parse(response); g_form.setValue('severity', ans.severity); // setting values from the obj to appropriate fields g_form.setValue('soc_sir_state', ans.state); g_form.setValue('soc_sir_assigned_to', ans.assignedto); } } ================================================ FILE: Client-Side Components/Client Scripts/Set Severity, state & assigned to/script_include.js ================================================ //Client callable script include var getSIRDetails = Class.create(); getSIRDetails.prototype = Object.extendsObject(global.AbstractAjaxProcessor, { getDetails: function() { var sir = this.getParameter('sysparm_sir'); //getting the newValue of Security Inc from onChange client script var obj = {}; //declare an object var gr = new GlideRecord('sn_si_incident'); gr.addQuery('sys_id', sir); //Query to security incident table with the newValue gr.query(); if (gr.next()) { obj.severity = gr.severity.getDisplayValue(); //Setting values in the obj obj.state = gr.state.getDisplayValue(); obj.assignedto = gr.assigned_to.getDisplayValue(); } return JSON.stringify(obj); //passing the object to client script }, type: 'getSIRDetails' }); ================================================ FILE: Client-Side Components/Client Scripts/Set Urgency to High onChange of caller field/README.md ================================================ This is a client script that change urgency to high automatically when changing caller field with the caller name whose vip is true ================================================ FILE: Client-Side Components/Client Scripts/Set Urgency to High onChange of caller field/script.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } var vipalert = g_form.getReference('caller_id',vipFunction); function vipFunction(vipAlert){ if(vipAlert.vip == 'true'){ g_form.setValue('urgency','1'); } } } ================================================ FILE: Client-Side Components/Client Scripts/Set field style like font and background/OnLoad client script.js ================================================ // your condition to apply the style e.g. user is a VIP user var condition = true; // Set the condition as needed // Find the control var fieldToSetStyle = g_form.getControl('sys_display.incident.caller_id'); if (condition == true) { fieldToSetStyle.style.fontWeight = 'bold'; fieldToSetStyle.style.backgroundColor = 'red'; } else { fieldToSetStyle.style.fontWeight = 'normal'; fieldToSetStyle.style.backgroundColor = 'white'; } ================================================ FILE: Client-Side Components/Client Scripts/Set field style like font and background/README.md ================================================ # Client Script - Change field style A client script that changes field font, and background based on some condition And example would be if the currently raised incident is by a VIP user and hightlight the caller... ## Usage - Create a new OnLoad script - Copy this script into it - Set the condition and the field that requires to be changed ================================================ FILE: Client-Side Components/Client Scripts/Show Current Domain/DomainCheckUtil.js ================================================ var DomainCheckUtil = Class.create(); DomainCheckUtil.prototype = Object.extendsObject(global.AbstractAjaxProcessor, { //get current domain of user session getCurrentDomainName: function() { var sessionDomainId = gs.getSession().getCurrentDomainID(); var gr = new GlideRecord('domain'); if (gr.get(sessionDomainId)){ return gr.name; } //Return global domain name return 'Global'; }, type: 'DomainCheckUtil' }); ================================================ FILE: Client-Side Components/Client Scripts/Show Current Domain/Readme.md ================================================ Domain Separation Current Domain Display Overview This functionality provides real-time awareness to users about the current selected domain within ServiceNow's Domain Separation framework. It displays an informational message on form load indicating the active domain context, helping prevent accidental configuration or data entry in the wrong domain. Components Script Include: DomainCheckUtil Global, client-callable Script Include allowing client scripts to query the current domain name via GlideAjax. Methods: isCurrentDomain(domainSysId) — Checks if a given domain sys_id matches the current session domain. Client Script An onLoad client script configured globally on the Global table, set to true to load on all forms. Calls the Script Include via GlideAjax to retrieve current domain name asynchronously. Displays the domain name as an informational message (g_form.addInfoMessage) on the form header on every page load. Usage Upon loading any record form, users see a message stating: "You are currently working in Domain Separation domain: [domain_name]." ================================================ FILE: Client-Side Components/Client Scripts/Show Current Domain/Show Current Domain.js ================================================ function onLoad() { var ga = new GlideAjax('DomainCheckUtil'); ga.addParam('sysparm_name', 'getCurrentDomainName'); ga.getXMLAnswer(showDomainMessage); function showDomainMessage(response) { var message = 'You are currently working in Domain Separation domain: ' + response + '.'; g_form.addInfoMessage(message); } } ================================================ FILE: Client-Side Components/Client Scripts/Show Message On Both Form and List/README.md ================================================ # Use Case The OOB `GlideForm (g_form)` API has documentation on displaying messages of info, warning and error types on form view, but lack a success message. Moreover, this `g_form` API is not accessible on lists and hence makes it difficult to display list level messages. However, SN provides another client-side method `GlideUI.get().addOutputMessage({options})` that can be used to display messages in native UI irrespective of form or list views. Even the popular `g_form.addInfoMessage(params)` API actually leverages the same `addOutputMessage(options)` method to render messages. Custom icons can also be included, but the background colour is lost due to the way `.addOutputMessage()` method has been implemented. **Allowed icons (as per SN documentation):** > icon-user, icon-user-group, icon-lightbulb, icon-home, icon-mobile, icon-comment, icon-mail, icon-locked, icon-database, icon-book, icon-drawer, icon-folder, icon-catalog, icon-tab, icon-cards, icon-tree-right, icon-tree, icon-book-open, icon-paperclip, icon-edit, icon-trash, icon-image, icon-search, icon-power, icon-cog, icon-star, icon-star-empty, icon-new-ticket, icon-dashboard, icon-cart-full, icon-view, icon-label, icon-filter, icon-calendar, icon-script, icon-add, icon-delete, icon-help, icon-info, icon-check-circle, icon-alert, icon-sort-ascending, icon-console, icon-list, icon-form, and icon-livefeed # Output Screenshot ![image](https://github.com/annaydas/code-snippets/assets/29729050/7c93828c-d30a-4255-a97c-a2ee4f9126ac) # Usage ### Success Message ```javascript GlideUI.get().addOutputMessage({ msg: 'Success', type: 'success', preventDuplicates: true }); ``` ### Warning Message ```javascript GlideUI.get().addOutputMessage({ msg: 'Warning', type: 'warning', preventDuplicates: true }); ``` ### Error Message ```javascript GlideUI.get().addOutputMessage({ msg: 'Error', type: 'error', preventDuplicates: true }); ``` ### Info Message ```javascript GlideUI.get().addOutputMessage({ msg: 'Info', type: 'info', preventDuplicates: true }); ``` ### Custom Icon (but it loses the background colour) ```javascript GlideUI.get().addOutputMessage({ msg: 'Custom Icon, but styling is lost', icon: 'icon-lightbulb', type: 'custom-message', preventDuplicates: true }); ``` ================================================ FILE: Client-Side Components/Client Scripts/Show Message On Both Form and List/script.js ================================================ // Success Message GlideUI.get().addOutputMessage({ msg: 'Success', type: 'success', preventDuplicates: true }); // Warning Message GlideUI.get().addOutputMessage({ msg: 'Warning', type: 'warning', preventDuplicates: true }); // Error Message GlideUI.get().addOutputMessage({ msg: 'Error', type: 'error', preventDuplicates: true }); // Info Message GlideUI.get().addOutputMessage({ msg: 'Info', type: 'info', preventDuplicates: true }); ================================================ FILE: Client-Side Components/Client Scripts/Show field if x things are checked/README.md ================================================ Use this script to show a field after `n` checkboxes are checked and not before. **Tested in Global scope **You can't make mandatory fields as readonly **Best Practice is to use UI Policies **Sometimes you have a lot of check marks and that logic gets narly ================================================ FILE: Client-Side Components/Client Scripts/Show field if x things are checked/script.js ================================================ function onChange(control, oldValue, newValue, isLoading) { //Set the mandatory checkbox variable names and total mandatory count here var mandatoryVars = ['option1', 'option2', 'option3', 'option4', 'option5']; var variableToShow = 'someothervariable'; var requiredCount = 2; var actualCount = 0; for (var x = 0; x < mandatoryVars.length; x++) { if (g_form.getValue(mandatoryVars[x]) == 'true') { actualCount++; } } if (requiredCount <= actualCount) { g_form.setDisplay(variableToShow, true); } else { g_form.setDisplay(variableToShow, false); } } ================================================ FILE: Client-Side Components/Client Scripts/Smart-Field-Suggestions/README.md ================================================ # Smart Field Suggestions Based on Keywords ## Category Client-Side Components / Client Scripts ## Description This is an onChange Client Script designed for the Incident table that dynamically suggests and populates the Category, Subcategory, and Priority fields based on keywords detected in the Short Description field. By matching keywords, it prompts users to confirm applying suggestions aligned with backend choice values for seamless integration. ## Use Case During incident creation or update, manually categorizing tickets correctly is critical for IT operations efficiency. This snippet automates early triage by analyzing user-entered short descriptions, providing actionable suggestions to improve categorization accuracy, accelerate routing, and enhance resolution speed. ## How to Use - Add this script as an "onChange" client script on the Incident table's `short_description` field. - Ensure the Category, Subcategory, and Priority fields have choice lists aligned with backend values specified in the snippet. - Modify the keyword list to align with your organizational terminologies if needed. - The user will be prompted with suggestions and may confirm or dismiss them, allowing balanced automation and human control. ## Why This Use Case is Unique and Valuable - Dynamically assists in categorizing incidents early, improving routing and resolution time. - Uses only platform APIs (`g_form`) without custom backend code or external integrations, making it lightweight and maintainable. - Uses real backend choice values ensuring seamless compatibility with existing configurations, reducing errors. - Provides prompt suggestions with user confirmation, balancing automation and user control. - Easily adaptable for other fields, keywords, or use cases beyond Incident management. - Designed without fragile DOM manipulations, following ServiceNow best practices, tailored for real environments. ## Compatibility This client script is compatible with all standard ServiceNow instances without requiring ES2021 features. ## Files - `Smart Field Suggestions Based on Keyword.js` — the client script implementing the logic. ================================================ FILE: Client-Side Components/Client Scripts/Smart-Field-Suggestions/Smart Field Suggestions Based on Keyword.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || !newValue || newValue.length < 10) { return; } var keywords = [ { pattern: /password|login|access/i, category: 'inquiry | Help ', subcategory: 'antivirus', priority: '3', suggestion: 'This appears to be a Inquiry issue.' }, { pattern: /slow|performance|hanging/i, category: 'software', subcategory: 'email', priority: '2', suggestion: 'This appears to be a Software issue.' }, { pattern: /printer|print|printing/i, category: 'hardware', subcategory: 'monitor', priority: '3', suggestion: 'This appears to be a Hardware issue.' }, { pattern: /database|data/i, category: 'database', subcategory: 'db2', priority: '3', suggestion: 'This appears to be an Database issue.' }, { pattern: /network|internet|wifi|connection/i, category: 'network', subcategory: 'vpn', priority: '2', suggestion: 'This appears to be a network issue.' } ]; var lowerDesc = newValue.toLowerCase(); var matched = null; for (var i = 0; i < keywords.length; i++) { if (keywords[i].pattern.test(lowerDesc)) { matched = keywords[i]; break; } } g_form.hideFieldMsg('short_description', true); g_form.clearMessages(); if (matched) { g_form.showFieldMsg('short_description', matched.suggestion, 'info', false); if (confirm(matched.suggestion + "\n\nApply these suggestions?")) { g_form.setValue('category', matched.category); g_form.setValue('subcategory', matched.subcategory); // Make sure you use backend value for subcategory! g_form.setValue('priority', matched.priority); g_form.addInfoMessage('Suggestions applied automatically!'); } else { g_form.addInfoMessage('Suggestions dismissed.'); g_form.hideFieldMsg('short_description', true); } } else { g_form.addInfoMessage('No keywords matched in description.'); } } ================================================ FILE: Client-Side Components/Client Scripts/State changes to On Hold then worknotes should be mandatory/README.md ================================================ ## This is OnChange client Script # whenever the 'State' on incident table will change to 'on Hold' the 'work notes' will become mandatory While using this script the table should be selected as 'incident' and type should be 'onChange' and field should be selected as 'State'. then write the script. ================================================ FILE: Client-Side Components/Client Scripts/State changes to On Hold then worknotes should be mandatory/worknotes.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } if(newValue == '3') // here 3 is the value of On Hold { g_form.setMandatory('work_notes',true); } else { g_form.setMandatory('work_notes',false); } } ================================================ FILE: Client-Side Components/Client Scripts/Sync Ajax with no getXMLWait/README.md ================================================ # onSubmit Ajax validation with no getXMLWait Using getXMLWait() ensures the order of execution, but can cause the application to seem unresponsive, significantly degrading the user experience of any application that uses it. Also, the getXMLWait method is not available in scoped applications. This snippet simulates the behavior of the getXMLWait method. ================================================ FILE: Client-Side Components/Client Scripts/Sync Ajax with no getXMLWait/script.js ================================================ function onSubmit() { if (g_scratchpad.isFormValid) { return true; } var actionName = g_form.getActionName(); var ga = new GlideAjax('scriptIncludeName'); ga.addParam('sysparm_name', 'methodName'); ga.addParam('sysparm_additional_parm', 'parmValue'); ga.getXMLAnswer(function (answer) { if (answer == 'true') { g_scratchpad.isFormValid = true; // It will trigger the same UI action that was used to submit the form g_form.submit(actionName); } }); return false; } ================================================ FILE: Client-Side Components/Client Scripts/Toggle Annotation On Forms With Script/README.md ================================================ # Use Case This method can be used to show/hide/toggle form annotations through client-side script. # Limitation This script works only with form annotations of the following types: - Info Box Blue - Info Box Red - Section Details - Text # Usage ### Show form annotations ```javascript SN.formAnnotations.show(); ``` ### Hide form annotations ```javascript SN.formAnnotations.hide(); ``` ### Toggle form annotations ```javascript SN.formAnnotations.toggle(); ``` ================================================ FILE: Client-Side Components/Client Scripts/Toggle Annotation On Forms With Script/script.js ================================================ // Show form annotations SN.formAnnotations.show(); // Hide form annotations SN.formAnnotations.hide(); // Toggle form annotations SN.formAnnotations.toggle(); ================================================ FILE: Client-Side Components/Client Scripts/Toggle form section visibility/README.md ================================================ # Toggle Form Section Visibility Client Script ## Overview This client script enhances the user experience in ServiceNow by providing a dynamic way to toggle the visibility of a form section based on the state of a checkbox or switch field. It simplifies complex forms and allows users to control which sections they want to view, making the form more user-friendly. ## How It Works When a user interacts with the designated checkbox field, the corresponding form section is either displayed or hidden in real-time. This behavior improves form navigation and streamlines the user experience. ### Configuration To use this client script in your ServiceNow instance, follow these steps: 1. **Create a Client Script:** - Log in to your ServiceNow instance as an admin or developer. - Navigate to "System Definition" > "Client Scripts." - Create a new client script and provide it with a meaningful name (e.g., "Toggle Section Visibility"). 2. **Copy and Paste the Script:** - Copy the JavaScript code provided in this README. - Paste the code into your newly created client script. 3. **Customize Field and Section:** - Modify the script to specify the checkbox field that triggers the visibility toggle and the ID of the section you want to control. 4. **Activate and Test:** - Save and activate the client script. - Test the functionality by creating or editing a form with the designated checkbox and section. ## Example Usage Imagine you have a form with a checkbox labeled "Show Additional Details." When users check this box, the "Additional Details" section of the form becomes visible, and when unchecked, it is hidden. This feature simplifies long forms and allows users to focus on the information that matters to them. ## Benefits - Improves user experience by offering dynamic form sections. - Simplifies complex forms, making them more user-friendly. - Enhances form navigation and efficiency. - Reduces clutter on forms and improves user satisfaction. ## Code Explanation - The toggleFormSection function is defined to be executed when the checkbox field changes. - It retrieves the checkbox field's control and the section's HTML element by their respective IDs. - When the checkbox is checked (checkboxField.checked is true), it sets the section's display style property to 'block', making the section visible. - When the checkbox is unchecked, it sets the section's display property to 'none', hiding the section. - The g_form.observe method attaches the toggleFormSection function to the change event of the checkbox field, so it triggers whenever the checkbox state changes. ================================================ FILE: Client-Side Components/Client Scripts/Toggle form section visibility/toggleFormSection.js ================================================ // Client Script to Toggle Form Section Visibility function toggleFormSection() { var checkboxField = g_form.getControl('checkbox_field'); // Replace 'checkbox_field' with your field name var section = gel('section_id'); // Replace 'section_id' with the ID of the section to toggle if (checkboxField.checked) { section.style.display = 'block'; // Show the section when the checkbox is checked } else { section.style.display = 'none'; // Hide the section when the checkbox is unchecked } } // Attach the toggleFormSection function to the checkbox field's change event g_form.observe('change', 'checkbox_field', toggleFormSection); ================================================ FILE: Client-Side Components/Client Scripts/Translate Message/README.md ================================================ # Translate your messages in client script using getMessage method *[getMessage() code snippet](getMessage.js) *[Translate message in client script doc](https://docs.servicenow.com/bundle/rome-platform-administration/page/administer/localization/task/t_TranslateAClientScriptMessage.html) ================================================ FILE: Client-Side Components/Client Scripts/Translate Message/getMessage.js ================================================ /* Translate messages according to the logged in user's preferred language in client script using getMessage() method. Note: Make sure to add an entry under [sys_ui_message] table and add the key in your client script Message fied (Not available in form by default) for preventing an extra round trip to server for fetching the message. Code :- */ var msg = getMessage('message_key'); //message_key defined in [sys_ui_message] table and added to the Message field of the client script. Fetching and storing the translated message to msg variable. g_form.addInfoMessage(msg); // Showing the translated message as an info message. ================================================ FILE: Client-Side Components/Client Scripts/Update Category from Short Description Keywords/README.md ================================================ ## Client Script that looks for category keywords in the Short Description updates the category field * Name: Set Category From Desc Keyword * Table: Incident * Type: onChange * Field Name: Short Description ================================================ FILE: Client-Side Components/Client Scripts/Update Category from Short Description Keywords/script.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') return; //Query the short description field var x = g_form.getValue('short_description'); var shortDescription = x.toLowerCase(); //Define a mapping of keywords to assignment groups var keywordMapping = { "network": "network", "ip": "network", "software": "software", "adobe": "software", "outlook": "software", "hardware": "hardware", "laptop": "hardware", "printer": "hardware", "database": "database", "oracle": "database", "how": "inquiry", "support": "inquiry", }; //Loop through the keywords and check if they are present in the short description for (var keyword in keywordMapping) { if (shortDescription.indexOf(keyword) !== -1) { //Set the Category based on the matching keyword g_form.setValue('category', keywordMapping[keyword]); break; } } } ================================================ FILE: Client-Side Components/Client Scripts/Use case of addOption() and removeOption()/README.md ================================================ # onChange client script for table 'change_request' where field is 'priority' if priority is critical, impact can be high and medium i.e, low will be removed from choice list using removeOption() and for other priority ,impact can be high, medium and low i.e, low option will be added, using addOption() ================================================ FILE: Client-Side Components/Client Scripts/Use case of addOption() and removeOption()/script.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } if(newValue == 1) { g_form.removeOption('impact',3); // 3 is the value for impact 'low' } else { g_form.addOption('impact',3,'3 - Low'); } } ================================================ FILE: Client-Side Components/Client Scripts/Validate Interaction record for FCR(First Call Resolution)/readme.md ================================================ README — Client Script: Validate Interaction Resolution 📌 Purpose This Client Script ensures proper validation when resolving an Interaction record in ServiceNow. It prevents a user from marking an Interaction as Closed Complete without proper justification. 🎯 What It Does When a user attempts to submit the form: ✔ Allows submission only if: Interaction Type is "walkup" And Related Task Boolean is true OR ✔ If work notes are provided for First Contact Resolution (FCR) ❌ Prevents submission if: State = Closed Complete Work Notes are empty And no related task condition is met 🧠 Validations Performed Field Condition Action state closed_complete Trigger validation type walkup AND u_boolean_no_related_task = true Submission allowed ✅ work_notes Must not be empty Show error & stop submission ❌ 🔔 User Feedback If work notes are missing: Displays inline field message Shows popup alert: "Provide Worknotes for FCR Interaction" 📍 Script Location Client Script → Type: onSubmit() Applicable to Interaction table (interaction) 📌 Script Code //Client Script to validate an Interaction record is resolved with out any related record created. function onSubmit() { var relatedTask = g_form.getValue('u_boolean_no_related_task'); var state = g_form.getValue('state'); var type = g_form.getValue('type'); var workNotes = g_form.getValue('work_notes'); // Get the value of work notes // Clear previous field messages g_form.clearMessages(); // Check if state is changing to 'Closed Complete' if (state == 'closed_complete') { // Check additional conditions if (type == 'walkup' && relatedTask == 'true') { return true; // Allow form submission } else if (!workNotes) { // Check if work notes is empty g_form.showFieldMsg('work_notes', 'Provide Worknotes for FCR Interaction', 'error'); alert('Provide Worknotes for FCR Interaction'); return false; // Prevent form submission } } return true; // Allow form submission for other states } ✅ Benefits Maintains consistent resolution standards Ensures justification/documentation for FCR interactions Reduces incorrect closure of requests without related actions ================================================ FILE: Client-Side Components/Client Scripts/Validate Interaction record for FCR(First Call Resolution)/script.js ================================================ //Client Script to validate an Interaction record is resolved with out any related record created. function onSubmit() { var relatedTask = g_form.getValue('u_boolean_no_related_task'); var state = g_form.getValue('state'); var type = g_form.getValue('type'); var workNotes = g_form.getValue('work_notes'); // Get the value of work notes // Clear previous field messages g_form.clearMessages(); // Check if state is changing to 'Closed Complete' if (state == 'closed_complete') { // Check additional conditions if (type == 'walkup' && relatedTask == 'true') { return true; // Allow form submission } else if (!workNotes) { // Check if work notes is empty g_form.showFieldMsg('work_notes', 'Provide Worknotes for FCR Interaction', 'error'); alert('Provide Worknotes for FCR Interaction'); return false; // Prevent form submission } } return true; // Allow form submission for other states } ================================================ FILE: Client-Side Components/Client Scripts/Validate Short Description/README.md ================================================ This Client Script validates the "Short Description" field before the form is submitted. If the description is more than 100 characters, it displays an alert and prevents submission. This helps maintain data quality by ensuring adequate information is provided. ================================================ FILE: Client-Side Components/Client Scripts/Validate Short Description/ShortDescriptionLength.js ================================================ function onSubmit() { var shortDescription = g_form.getValue('short_description'); if (shortDescription.length > 100) { alert('Short Description must be not be more than 100 characters long.'); return false; // Prevent form submission } return true; // Allow form submission } ================================================ FILE: Client-Side Components/Client Scripts/Validate Short Description/validShortDescription.js ================================================ function onSubmit() { var shortDescription = g_form.getValue('short_description'); if (shortDescription.length < 10) { alert('Short Description must be at least 10 characters long.'); return false; // Prevent form submission } return true; // Allow form submission } ================================================ FILE: Client-Side Components/Client Scripts/Validate Short Description/validateSpecialChar.js ================================================ // Client Script to Validate Special Charecters function onSubmit() { var shortDescription = g_form.getValue('short_description'); var specialCharsRegex = /[^a-zA-Z0-9\s]/g; var specialChars = description.match(specialCharsRegex); if (specialChars) { alert('Description contains invalid characters: ' + specialChars.join(', ')); return false; } else { return true; } ================================================ FILE: Client-Side Components/Client Scripts/Validate date is in future without GlideAjax/OnChange Client Script.js ================================================ /* Client script that validates a date is in future without the need of a GlideAjax and Script Include */ function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } var fieldToValidate = '' var currentDate = formatDate(new Date(), g_user_date_format); var currentDateInMs = getDateFromFormat(currentDate, g_user_date_format); var dateToValidateInMs = getDateFromFormat(g_form.getValue(fieldToValidate), g_user_date_format); if (dateToValidateInMs <= currentDateInMs) { g_form.showFieldMsg(fieldToValidate, "Enter a valid future date.", 'error'); return false; } } ================================================ FILE: Client-Side Components/Client Scripts/Validate date is in future without GlideAjax/README.md ================================================ # Client Script - Date in future A client script that validates that a specified date is in future without the need for a GlideAjax and Script Include ## Usage - Create a new client script - Set the type to OnChange - Select a date field that you want to validate - Navigate to the form and set the date field to the past - There will be a validation error close to the field ================================================ FILE: Client-Side Components/Client Scripts/Verify if e-mail already exists with Ajax call/README.md ================================================ **Client Script** Client script for verification if changed e-mail adders on user record is not already existing in sys_user table (real-time information about duplicated e-mail). Check is performed using asynchronous Ajax call and processed in callback function. In case e-mail already exists in sys_user table, message is displayed under email field with information which has have that e-mail. **How to use** You need to prepare both Script Include (which is processing check on backed) and Client Script (which is sending Ajax call after change on email field and display message). Example Script Include configuration (code in [scriptInclude.js](scriptInclude.js)): ![Coniguration_SI](ScreenShot_2.PNG) Example Client Script configuration (code in [clientScript.js](clientScript.js)): ![Coniguration_CS](ScreenShot_1.PNG) **Example effect of execution** ![Coniguration_SI](ScreenShot_3.PNG) ================================================ FILE: Client-Side Components/Client Scripts/Verify if e-mail already exists with Ajax call/clientScript.js ================================================ function onChange(control, oldValue, newValue, isLoading, isTemplate) { //Return if page is loading or new value of e-mail is empty if (isLoading || newValue === '') { return; } //Make Ajax call to check if e-mail already exists in sys_user table var ga = new GlideAjax('user_CheckEmail'); //user_CheckEmail - Script Include name ga.addParam('sysparm_name', 'validateEmail'); //sysparm_name - Parameter with function name in Script Include ga.addParam('sysparm_emailString', newValue); //sysparm_emailString - Parameter with new value of e-mail ga.getXMLAnswer(verifyDuplicates); //verifyDuplicates - Name of asynchronous callback function //Asynchronous callback function to process response function verifyDuplicates(response) { //If repsonse is not null (in case if e-mail was not find) if (response) { //Parse response and show message about found duplicate var data = JSON.parse(response); g_form.showFieldMsg('email', 'User with that e-mail already exists: ' + data.name + '(' + data.sys_id + ')', 'error'); } } } ================================================ FILE: Client-Side Components/Client Scripts/Verify if e-mail already exists with Ajax call/scriptInclude.js ================================================ var user_CheckEmail = Class.create(); user_CheckEmail.prototype = Object.extendsObject(AbstractAjaxProcessor, { validateEmail: function() { //Get new value of e-mail field var emailString = this.getParameter('sysparm_emailString'); //Query user table to verify if new e-mail already exists var user = new GlideRecord('sys_user'); user.addQuery('email', emailString); user.query(); //If e-mail already exists, return user name and sys_id if (user.next()) { var results = { "sys_id": user.getValue("sys_id"), "name": user.getValue("name") }; return JSON.stringify(results); //If e-mail not exists, return null } else { return null; } } }); ================================================ FILE: Client-Side Components/Client Scripts/Verify whether a date falls within a hour range/README.md ================================================ A code snippet that verifies whether a date falls within a specific hour range. ================================================ FILE: Client-Side Components/Client Scripts/Verify whether a date falls within a hour range/verifyWhetherADateFallsWithinAHourRange.js ================================================ /** * Verify whether a date falls within a hour range */ function onChange() { var dateNow = new Date(); var dateOpenedAt = new Date(g_form.getValue('opened_at')); var differenceInMilliseconds = dateOpenedAt.getTime() - dateNow.getTime(); var differenceInHours = diffInMs / (1000 * 60 * 60); if (differenceInHours < 24) { g_form.showFieldMsg('opened_at', 'Please choose a date and time that is at least 24 hours in the future.'); return false; } return true; } ================================================ FILE: Client-Side Components/Client Scripts/Whitespace Validation/README.md ================================================ This Client Script will validate whether the field contains any whitespace. ================================================ FILE: Client-Side Components/Client Scripts/Whitespace Validation/whitespaceValidation.js ================================================ // Client Script to Validate Whitespaces var reg = /\s/; var value = g_form.getValue('field_name'); var k = reg.test(value); if (k == true) { alert('Field Name cannot have spaces!'); // Alert if field contains whitespace g_form.setValue('field_name', ''); // Empty field for any whitespaces } ================================================ FILE: Client-Side Components/Client Scripts/Zurich - Upgraded info messages/README.md ================================================ **This feature will be used in the instance of Zurich++ release** Demonstrate different messages that has been introduced as part of Zurich release. Use Case: Display different information messages based on priority of the incident that will be showed on load and state is not Closed, Resolved or Cancelled. ================================================ FILE: Client-Side Components/Client Scripts/Zurich - Upgraded info messages/infoMessages.js ================================================ function onLoad() { var state = g_form.getValue('state'); //Get value of 'state' field if (state != '6' && state != '7' && state != '8') { var priority = g_form.getValue('priority'); // Get value of 'priority' field switch (priority) { case '1': g_form.addErrorMessage('Critical Incident'); break; case '2': g_form.addHighMessage('High Priority Incident'); // addHighMessage() method will display message in orange color break; case '3': g_form.addModerateMessage('Medium Priority Incident'); // addModerateMessage() method will display message in purple color break; case '4': g_form.addLowMessage('Low Priority Incident'); // addLowMessage() method will display message in grey color break; } } else if (state == '6' || state == '7') { g_form.addSuccessMessage('Incident closed'); // addSuccessMessage() method will display message in green color } } ================================================ FILE: Client-Side Components/Client Scripts/field-character-counter/README.md ================================================ # Field Character Counter ## Use Case Provides real-time character count feedback for text fields in ServiceNow forms. Shows remaining characters with visual indicators to help users stay within field limits. ## Requirements - ServiceNow instance - Client Script execution rights - Text fields with character limits ## Implementation 1. Create a new Client Script with Type "onChange" 2. Copy the script code from `script.js` 3. Configure the field name and character limit in the script 4. Apply to desired table/form 5. Save and test ## Configuration Edit these variables in the script: ```javascript var fieldName = 'short_description'; // Your field name var maxLength = 160; // Your character limit ``` ## Features - Real-time character counting as user types - Visual indicators: info (blue), warning (yellow), error (red) - Shows "X characters remaining" or "Exceeds limit by X characters" - Automatically clears previous messages ## Common Examples ```javascript // Short Description (160 chars) var fieldName = 'short_description'; var maxLength = 160; // Description (4000 chars) var fieldName = 'description'; var maxLength = 4000; // Work Notes (4000 chars) var fieldName = 'work_notes'; var maxLength = 4000; ``` ## Message Thresholds - **50+ remaining**: Info message (blue) - **1-20 remaining**: Warning message (yellow) - **Over limit**: Error message (red) ## Notes - Uses standard ServiceNow APIs: `g_form.showFieldMsg()` and `g_form.hideFieldMsg()` - Create separate Client Scripts for multiple fields - Works with all text fields and text areas - Character count includes all characters (spaces, punctuation, etc.) ================================================ FILE: Client-Side Components/Client Scripts/field-character-counter/script.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading) return; // USER CONFIGURATION: Set field name and character limit var fieldName = 'Description'; // Change to your field name var maxLength = 80; // Change to your character limit var currentLength = newValue ? newValue.length : 0; var remaining = maxLength - currentLength; // Clear any existing messages g_form.hideFieldMsg(fieldName); // Show appropriate message based on remaining characters if (remaining < 0) { g_form.showFieldMsg(fieldName, 'Exceeds limit by ' + Math.abs(remaining) + ' characters', 'error'); } else if (remaining <= 20) { g_form.showFieldMsg(fieldName, remaining + ' characters remaining', 'warning'); } else if (remaining <= 50) { g_form.showFieldMsg(fieldName, remaining + ' characters remaining', 'info'); } } ================================================ FILE: Client-Side Components/Client Scripts/g_form console access in workspace/README.md ================================================ # Access g_form instance inside Agent Workspace from DevTools Console When developing forms in ServiceNow it can be useful to try stuff out directly in the DevTools Console. In UI16 this was pretty straightforward because g_form was available globally, Agent Workspace makes this a little bit more complicated. So this script provides access to the g_form object of the currently active tab in a Workspace. Just copy the Script in the DevTools Console and run `var g_form = getGlideFormAW()` now you should be able to do stuff like `g_form.setValue("short_description", "Lorem ipsum")` ================================================ FILE: Client-Side Components/Client Scripts/g_form console access in workspace/script.js ================================================ function getGlideFormAW() { document.getElementsByTagName("sn-workspace-content")[0].shadowRoot.querySelectorAll("now-record-form-connected")[0] var firstContentChild = document.getElementsByTagName("sn-workspace-content")[0].shadowRoot .querySelectorAll(".chrome-tab-panel.is-active")[0].firstChild; var snWorkspaceFormEl; if (firstContentChild.tagName == "NOW-RECORD-FORM-CONNECTED") { snWorkspaceFormEl = firstContentChild.shadowRoot.querySelectorAll(".sn-workspace-form")[0]; } else { snWorkspaceFormEl = firstContentChild.shadowRoot.querySelectorAll("now-record-form-connected")[0] .shadowRoot.querySelectorAll(".sn-workspace-form")[0]; } if (!snWorkspaceFormEl) throw "Couldn't find sn-workspace-form"; var reactInternalInstanceKey = Object.keys(snWorkspaceFormEl).find(function (objKey) { if (objKey.indexOf("__reactInternalInstance$") >= 0) { return true; } return false; }); return snWorkspaceFormEl[reactInternalInstanceKey].return.stateNode.props.glideEnvironment._gForm; } ================================================ FILE: Client-Side Components/Client Scripts/onfocus and onblur/README.md ================================================ Using "onfocus" & "onblur" Show/Hide field messages while updating a field. Example:On load Client script on the incident form ![image](https://user-images.githubusercontent.com/42912180/195825979-e69e5798-a241-4fe8-8f49-7f70f1f3ae6e.png) **Quick video how it works:** https://user-images.githubusercontent.com/42912180/195825799-aff13ca5-0b85-4660-98a3-ea1af8b61974.mp4 ================================================ FILE: Client-Side Components/Client Scripts/onfocus and onblur/script.js ================================================ g_form.getElement("Field Name").onfocus = focus; g_form.getElement("Field Name").onblur = blur; } //function definition function focus() { g_form.showFieldMsg("Field Name", "Message you want to display"); } function blur() { g_form.hideFieldMsg("Field Name"); } ================================================ FILE: Client-Side Components/Client Scripts/state-edit-for-grpmem/README.md ================================================ This code will make state field editable only for group members, with the help of scratchpad variable that returns true or false from display business rule. ================================================ FILE: Client-Side Components/Client Scripts/state-edit-for-grpmem/grpmemstateedit.js ================================================ //Create a Display Business Rule with the following code and then the following code on "On-Load" client script //Display Business Rule Code: /***** g_scratchpad.grpmember = gs.getUser().isMemberOf(current.assignment_group); //This returns true or false . If user is part of the group it returns true, if user is not part of the group it returns false and assign it to scratchpad variable. *****/ if(g_scratchpad.grpmember == false) { g_form.setReadonly("state", true); } ================================================ FILE: Client-Side Components/Client Scripts/validate phone number/README.md ================================================ Purpose: Ensures that users enter their phone numbers in a specific format. Phone number must be in format(123) 456-7890. The regex:- 1) ^\(\d{3}\) \d{3}-\d{4}$ validates phone numbers in the format: 2) An area code enclosed in parentheses (e.g., (123)), 3) Followed by a space, 4) Then three digits, 5) A hyphen, 6) And finally four digits. Functionality: This script is triggered when the phone field changes. It uses a regular expression to validate the format. If the format is incorrect, it displays an error message and clears the field. ================================================ FILE: Client-Side Components/Client Scripts/validate phone number/Readme.md ================================================ Phone Number Validation — Client Script Overview This Client Script validates that users enter their phone numbers in the strict format: (123) 456-7890. It is triggered whenever the Phone field changes on a sys_user record. If the input does not match the required format, the script: Displays an inline error message directly below the field. Clears the invalid input so the user can re-enter the correct value. This script is designed to be dynamic, simple, and user-friendly. Features Ensures phone numbers follow the exact format (123) 456-7890. Provides immediate feedback via field-level error messages. Clears invalid entries automatically to prevent submission errors. Works on Classic UI forms and provides clear messaging to the user. Usage Instructions 1. Create the Client Script Navigate to System Definition → Client Scripts. Click New to create a client script. 2. Configure the Script Name: Phone Number Validation Table: sys_user Type: onChange Field: phone ================================================ FILE: Client-Side Components/Client Scripts/validate phone number/validate phone number.js ================================================ // Client Script: Validate Phone Number // Table: sys_user // Type: onChange // Field: phone function onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue === '') return; var phoneRegex = /^\(\d{3}\) \d{3}-\d{4}$/; // Format: (123) 456-7890 if (!phoneRegex.test(newValue)) { g_form.showFieldMsg('phone', 'Phone number must be in the format (123) 456-7890', 'error'); g_form.setValue('phone', ''); } } ================================================ FILE: Client-Side Components/Client Scripts/validate phone number/validate_phone_format_(123)_456-7890_no_regex.js.js ================================================ function onChange(control, oldValue, newValue, isLoading) { if (isLoading || !newValue) return; var fieldName = control.name; // Split the string var area = newValue.substring(1, 4); var firstThree = newValue.substring(6, 9); var lastFour = newValue.substring(10, 14); if ( newValue[0] !== '(' || newValue[4] !== ')' || newValue[5] !== ' ' || newValue[9] !== '-' || isNaN(parseInt(area)) || isNaN(parseInt(firstThree)) || isNaN(parseInt(lastFour)) ) { g_form.showFieldMsg( fieldName, 'Phone Number must be in the format (123) 456-7890', 'error', false ); g_form.setValue(fieldName, ''); } } ================================================ FILE: Client-Side Components/UI Actions/Add Loggedin user as Incident assigned to/ReadMe.md ================================================ >**UI Action** When a new Incident record is created, user can come to incident ticket and assigned to themself. Once they click on UI Action. ================================================ FILE: Client-Side Components/UI Actions/Add Loggedin user as Incident assigned to/code.js ================================================ var currentUser = gs.getUserID(); //Getting loggedIn User Id //Checing wheather user is available or not in Assignee field if(current.assigned_to == ""){ //checking assigned to is there or not current.assigned_to = currentUser; //Setting the current loggedIn user current.update(); //updating the record. gs.addInfoMessage("Incident has been assigned to You."); action.setRedirectURL(current); } else { gs.addErrorMessage("Incident is already assigned"); action.setRedirectURL(current); } ================================================ FILE: Client-Side Components/UI Actions/Add Show Workflow Related link/README.md ================================================ There are many cases where,we would have workflows on custom tables or non task tables , where we would like to see the "Show Workflow" Related Link for ease of accessibility to the workflow. The shared code will help show this related link on any rable record that has a workflow associated with it. Below has to be set for this to work on UI action form: • Table: Select the table that you would like this UI action to be available on (preferably table with workflows) • Onclick : showWorkflow() • Active : True • Show Update : True • Client : True • Form Link : True • Condition : new global.Workflow().hasWorkflow(current) ================================================ FILE: Client-Side Components/UI Actions/Add Show Workflow Related link/script.js ================================================ // ----------------------------- // Open workflow in a new window // ----------------------------- function showWorkflow() { var url = new GlideURL('/context_workflow.do'); url.addParam('sysparm_stack', 'no'); url.addParam('sysparm_document', g_form.getUniqueValue()); url.addParam('sysparm_table', g_form.getTableName()); g_navigation.open(url.getURL(), "_blank"); } ================================================ FILE: Client-Side Components/UI Actions/Add collapsible element in knowledge article/README.md ================================================ This code snippet will allow you to use collapsible element within knowledge atricle which will make articles clean, organized and effective. ![Demo](https://github.com/abhrajyotikanrar/code-snippets/assets/25823899/e3ad356e-a5c5-4f2d-aafa-20f89b0da248) Please check out the above demo on how this code-snippet can be used. ================================================ FILE: Client-Side Components/UI Actions/Add collapsible element in knowledge article/UI_Action.js ================================================ /* This script should be placed in the UI action on the table kb_knowledge form view. This UI action should be marked as client. Use addCollapsible() function in the Onclick field. */ function addCollapsible() { var gm = new GlideModal("add_collapsible"); gm.setTitle('Add collapsible'); gm.setWidth(1000); gm.render(); } ================================================ FILE: Client-Side Components/UI Actions/Add collapsible element in knowledge article/add_collapsible.js ================================================ ******HTML*****


**************** **Client Script** function updateForm() { $j("#alert").removeClass("show"); $j("#alert").addClass("hide"); var title = $j("#collapsibleTitle").val(); var content = $j("#collapsibleContent").val(); if (title.trim() == "" || content.trim() == "") { $j("#alert").removeClass("hide"); $j("#alert").addClass("show"); return; } var articleText = g_form.getValue("text"); var collapsibleElement = "
" + title.trim() + "
" + content.trim() + "
"; g_form.setValue("text", articleText + collapsibleElement); GlideDialogWindow.get().destroy() } ================================================ FILE: Client-Side Components/UI Actions/Call Subflow/README.md ================================================ # Call Subflow from UI Action This UI Action enables calling a subflow from ServiceNow Flow Designer using the FlowAPI. ### Instruction The call can either be done synchronously or asynchronously, depending on the requirement. ```javascript //Synchronous Call: sn_fd.FlowAPI.getRunner().subflow('global.subflow_name').inForeground().withInputs(inputs).run(); //Asynchronous Call: sn_fd.FlowAPI.getRunner().subflow('global.subflow_name').inBackground().withInputs(inputs).run(); ``` The API call requires the subflow internal name and scope. The internal name is shown as a second column in the subflow overview in ServiceNow Flow Designer, while the scope is shown in the third column. In the above example the subflow internal name is **subflow_name** and it was created in the **global** scope. ### Benefits - Enable Citizen Developers to create complex UI Actions with low code Flow Designer capabilities instead of scripting - Run complex server side UI Actions asynchronously via Flow Designer for better user experience (avoids long loading times in the current user session) - Re-use already created subflows and actions as well as provided spokes in UI actions ================================================ FILE: Client-Side Components/UI Actions/Call Subflow/script.js ================================================ (function() { try { // Provide subflow inputs var inputs = {}; inputs['incident'] = current; //the current incident record can be provided as input to the subflow //The subflow can either be executed synchronously (running in foreground) or asynchronously (running in background) //If the flow runs asynchronously the FlowAPI will not provide the outputs of the subflow //Asynchronous call // sn_fd.FlowAPI.getRunner().subflow('global.subflow_name').inBackground().withInputs(inputs).run(); //Synchronous call // sn_fd.FlowAPI.getRunner().subflow('global.subflow_name').inForeground().withInputs(inputs).run(); //In this case we are calling the subflow global.create_problem_from_incident synchronously and then access the subflow outputs var result = sn_fd.FlowAPI.getRunner().subflow('global.create_problem_from_incident').inForeground().withInputs(inputs).run(); var outputs = result.getOutputs(); // Get subflow outputs: var problem_number = outputs['problem_number']; var assignment_group = outputs['assignment_group']; } catch (ex) { var message = ex.getMessage(); gs.error(message); } })(); ================================================ FILE: Client-Side Components/UI Actions/CallingPopUpBoxInListView/README.md ================================================ Overview This document explains how to implement a custom UI action that triggers a UI page from the list view of a table in ServiceNow. Specifically, it demonstrates how to open a modal dialog when multiple items are selected in the list view. The modal dialog will display a UI page and pass the selected record sys_ids as parameters. This allows users to update multiple records simultaneously through the UI page, streamlining processes such as mass updates. This approach enhances user efficiency by enabling the execution of actions on multiple records at once, reducing manual effort and improving workflow automation Purpose of the UI Action The purpose of this UI action is to allow users to select multiple records from the list view and trigger a modal popup that displays a custom UI page. The user can interact with the popup to perform actions to update field value by slecting field values from the pop up. The selected records are passed to the UI page as parameters, ensuring the action is applied to all the checked items. Document: Using UI Action to Call a Custom UI Page in List View Overview This document explains how to implement a custom UI action that calls a UI page from the list view of a table in ServiceNow. Specifically, it demonstrates how to open a modal dialog when multiple items are selected in the list view. The modal dialog will display a UI page and pass selected record sys_ids as parameters. This can be useful for tasks like requesting an exception, remediation, or other custom actions that require user interaction. Purpose of the UI Action The purpose of this UI action is to allow users to select multiple records from the list view and trigger a modal popup that displays a custom UI page. The user can interact with the popup to perform actions such as remediation, request handling, or exception requests, depending on the implementation of the UI page. The selected records are passed to the UI page as parameters, ensuring the action is applied to the correct items. How It Works UI Action Creation: A UI action is created in ServiceNow, configured to be available in the list view of a specific table. In this example, the UI action is configured on the Incident table. JavaScript Function: The function showExceptiondialog() is triggered when the UI action button is clicked. This function does the following: Check for selected records: The script checks if any records have been selected in the list view. If no records are selected, an error message is shown to the user. Open Modal Popup: If records are selected, the function opens a modal dialog using either GlideModal or GlideDialogWindow, depending on the environment. The dialog displays a UI page specified by name (incident_pop_up in the example). Pass Selected Records: The selected record sys_ids are passed as a parameter (sysparm_sys_id) to the UI page, allowing the page to perform actions on those records. UI Action Configuration Table: The UI action is configured for a specific table, such as the Incident table (incident). List Context: The UI action should be available in the list view, where multiple records can be selected. OnClick Event: The UI action calls the JavaScript function showExceptiondialog() when clicked. Testing Navigate to List View: Go to the list view of the table where the UI action is configured (e.g., Incident table). Select Records: Check one or more records in the list view. Click the UI Action Button: Click the UI action button that triggers the showExceptiondialog() function. If no records are selected, an error message will appear. If records are selected, the modal dialog will open and display the custom UI page. Verify Modal: Ensure that the modal popup displays the correct UI page and that the selected records’ sys_ids are passed as parameters. Benefits User-Friendly: The modal popup provides an intuitive interface for users to perform actions on multiple selected records without leaving the list view. Customizable: The UI page can be customized to handle a variety of actions based on the selected records. For example, it can be used for remediation tasks, exception requests, approvals, or other workflows. Efficiency: Users can quickly perform actions on multiple records at once, reducing the need for repetitive manual operations. Error Handling: The script includes error handling to ensure users are prompted if they attempt to perform an action without selecting records. ================================================ FILE: Client-Side Components/UI Actions/CallingPopUpBoxInListView/calling_pop_up_box_in_list_view.js ================================================ function showExceptiondialog() { // Alert to display the checked records alert(g_list.getChecked()); // Check if any records are selected in the list if ((g_list.getChecked()).length > 0) { var title = getMessage("Remediation Task"); var dialogClass = GlideModal || GlideDialogWindow; // Initialize the modal dialog with a custom UI page var dialog = new dialogClass("incident_pop_up", true, 750); dialog.setTitle(title); // Pass selected record sys_ids as parameters to the UI page dialog.setPreference("sysparm_sys_id", g_list.getChecked()); // Render the modal dialog dialog.render(); } else { // Show error messages if no records are selected g_form.addErrorMessage(getMessage('Please Select Vulnerable Items before creating remediation')); alert('Please Select Vulnerable Items before creating remediation'); } } ================================================ FILE: Client-Side Components/UI Actions/Cancel Flow Executions/README.md ================================================ # CancelFlow UI Action A ServiceNow utility that dynamically cancels flows associated with the current record, ensuring seamless process management. ## Challenge Managing running flows in ServiceNow can be challenging, particularly when multiple flows are tied to a single record. This utility streamlines the process by offering a dynamic solution to identify and cancel running flows, minimizing manual intervention and ensuring seamless operations. This tool is especially useful in scenarios where you need to halt the current execution and initiate a new flow or process. Additionally, it can be leveraged to forcefully terminate the automation lifecycle when necessary, providing greater control over flow management. ## Description This UI Action is designed to identify and cancel all running flows associated with the current record in a ServiceNow instance. It provides a user-friendly interface for administrators and developers to manage flow cancellations efficiently. This utility is particularly useful in scenarios where flows need to be terminated to prevent conflicts or errors during record updates. ## Functionality The CancelFlow UI Action provides the following capabilities: - Dynamically identifies running flows for the current record. - Cancels the identified flows programmatically. - Displays success or error messages to the user for better visibility. - Ensures smooth handling of flow cancellations without manual intervention. ## Usage Instructions ### UI Action Script Add the given script to your UI Action: ### Example Usage 1. Open the record where you want to cancel the associated flows. 2. Click on the **Cancel Flow** UI Action button. 3. The system will identify and cancel all running flows for the current record. 4. The same can be used in Business rules as well based on trigger conditions ### Visibility for UI Action In certain scenarios, it may be necessary to restrict the visibility of this operation to specific user groups. For example, only HR administrators or members of a designated group (e.g., "X Group") should have access to this functionality. These requirements can be addressed by configuring the **Condition** field in the UI Action. You can tailor the conditions to align with your specific use case, ensuring that only authorized users can execute this operation. One edge case about not having an active flow execution can also be handled in the condition which will restrict the visibility if no active flow execution is present. ## Dependencies - `sn_fd.FlowAPI` ## Category Client-Side Components / UI Actions ================================================ FILE: Client-Side Components/UI Actions/Cancel Flow Executions/cancelFlow.js ================================================ function cancelRunningFlows() { try { var grFlowExecution = new GlideRecord("sys_flow_context"); grFlowExecution.addQuery("source_record", current.sys_id); grFlowExecution.query(); while (grFlowExecution.next()) { sn_fd.FlowAPI.cancel(grFlowExecution.getUniqueValue(), "Canceling Flows"); } } catch (error) { gs.error("Error cancelling flows: " + error.message); } } ================================================ FILE: Client-Side Components/UI Actions/Cancel Incident/README.md ================================================ # Cancel Incident UI Action A UI Action in ServiceNow is a script that defines an action or button within the platform's user interface. It enables users to perform specific operations on forms and lists, such as creating, updating, or deleting records, or executing custom scripts. UI Actions enhance the user experience by providing functional buttons, links, or context menus. ## Overview This UI Action allows users to cancel incidents directly from the incident form. It provides a confirmation dialog to prevent accidental cancellations and updates the incident state to "Cancelled" (state value 8) when confirmed. ## Features - **Confirmation Dialog**: Uses GlideModal to display a confirmation prompt before cancelling - **State Management**: Updates incident state to "Cancelled" (value 8) - **Client-Side Validation**: Runs client-side for better user experience - **Conditional Display**: Only shows when incident state is "New" (state value 1) ## Configuration Create a UI Action with the following field values: **Name**: Cancel Incident **Action Name**: cancel_incident **Table**: Incident [incident] **Client**: checked (true) **Onclick**: cancelIncident(); **Condition**: current.state == '1' **Script**: Use the provided script.js file ## Usage 1. Navigate to an incident record in "New" state 2. Click the "Cancel Incident" button 3. Confirm the action in the modal dialog 4. The incident state will be updated to "Cancelled" ## Technical Details - **Client-Side Function**: `cancelIncident()` - Displays confirmation modal - **Server-Side Function**: `serverCancel()` - Updates the incident state - **Modal Configuration**: Uses `glide_ask_standard` modal with custom title - **State Value**: Sets incident state to '8' (Cancelled) ## Prerequisites - User must have write access to the incident table - Incident must be in "New" state (state = 1) for the UI Action to be visible ## Notes - This UI Action only appears on incident forms when the state is "New" - The confirmation dialog helps prevent accidental cancellations - The server-side script executes only after user confirmation ================================================ FILE: Client-Side Components/UI Actions/Cancel Incident/SETUP.md ================================================ # Setup Instructions for Cancel Incident UI Action This document provides detailed step-by-step instructions for implementing the Cancel Incident UI Action in your ServiceNow instance. ## Prerequisites - Administrative access to ServiceNow instance - Access to System Definition > UI Actions module - Understanding of ServiceNow UI Actions and client-server scripting ## Step-by-Step Setup ### 1. Navigate to UI Actions 1. In ServiceNow, go to **System Definition > UI Actions** 2. Click **New** to create a new UI Action ### 2. Configure Basic Settings Fill in the following fields: | Field | Value | Description | |-------|-------|-------------| | **Name** | Cancel Incident | Display name for the UI Action | | **Table** | Incident [incident] | Target table for the UI Action | | **Action name** | cancel_incident | Unique identifier for the action | | **Active** | ✓ (checked) | Enables the UI Action | ### 3. Configure Display Settings | Field | Value | Description | |-------|-------|-------------| | **Form button** | ✓ (checked) | Shows button on form view | | **Form link** | ☐ (unchecked) | Optional: Show as link instead | | **List banner button** | ☐ (unchecked) | Not needed for this action | | **List choice** | ☐ (unchecked) | Not needed for this action | ### 4. Configure Client Settings | Field | Value | Description | |-------|-------|-------------| | **Client** | ✓ (checked) | Enables client-side execution | | **Onclick** | `cancelIncident();` | Client-side function to call | ### 5. Configure Conditions | Field | Value | Description | |-------|-------|-------------| | **Condition** | `current.state == '1'` | Only show for "New" incidents | ### 6. Add the Script Copy the entire content from `script.js` and paste it into the **Script** field of the UI Action. ### 7. Configure Advanced Settings (Optional) | Field | Value | Description | |-------|-------|-------------| | **Order** | 100 | Display order (adjust as needed) | | **Hint** | Cancel this incident | Tooltip text | | **Comments** | UI Action to cancel incidents in New state | Internal documentation | ## Verification Steps ### 1. Test the UI Action 1. Navigate to an incident in "New" state 2. Verify the "Cancel Incident" button appears 3. Click the button and confirm the modal appears 4. Test both "OK" and "Cancel" in the confirmation dialog ### 2. Verify State Changes 1. After confirming cancellation, check that: - Incident state changes to "Cancelled" - Work notes are added with cancellation details - Success message appears ### 3. Test Edge Cases 1. Try accessing the UI Action on incidents in other states (should not appear) 2. Test with different user roles to ensure proper permissions 3. Verify error handling works correctly ## Troubleshooting ### Common Issues **UI Action doesn't appear:** - Check that the incident is in "New" state (state = 1) - Verify the condition field: `current.state == '1'` - Ensure the UI Action is marked as Active **Script errors:** - Check browser console for JavaScript errors - Verify the script is properly copied from `script.js` - Ensure proper syntax and formatting **Permission issues:** - Verify user has write access to incident table - Check ACL rules for incident cancellation - Ensure proper role assignments ### Debug Mode To enable debug logging, add this line at the beginning of the `serverCancel()` function: ```javascript gs.info('Debug: Starting incident cancellation for ' + current.number); ``` ## Security Considerations - The UI Action respects existing ACL rules - Only users with incident write permissions can cancel incidents - All cancellations are logged for audit purposes - Work notes provide cancellation history ## Customization Options ### Modify Confirmation Message Edit line 33 in the script to customize the confirmation dialog: ```javascript gm.setPreference("question", "Your custom message here"); ``` ### Change Cancellation Reason Modify the work note in the `serverCancel()` function (line 79): ```javascript var workNote = 'Custom cancellation reason: ' + gs.getUserDisplayName() + ' on ' + gs.nowDateTime(); ``` ### Add Additional Validations Add custom validation logic in the `cancelIncident()` function before showing the modal. ## Support For issues or questions: 1. Check ServiceNow system logs 2. Review browser console for client-side errors 3. Test in a development instance first 4. Consult ServiceNow documentation for UI Actions ================================================ FILE: Client-Side Components/UI Actions/Cancel Incident/script.js ================================================ /** * Client-side function to initiate incident cancellation * Displays a confirmation modal before proceeding with the cancellation */ function cancelIncident() { try { // Validate that we have a valid form and record if (!g_form || !g_form.getUniqueValue()) { alert('Error: Unable to access incident record. Please refresh the page and try again.'); return; } // Check if incident is in the correct state for cancellation var currentState = g_form.getValue('state'); if (currentState !== '1') { alert('Error: This incident cannot be cancelled. Only incidents in "New" state can be cancelled.'); return; } // Create confirmation modal with improved messaging var gm = new GlideModal("glide_ask_standard", false, 600); gm.setPreference("title", "Cancel Incident Confirmation"); gm.setPreference("warning", true); gm.setPreference("onPromptComplete", function() { // Show loading message g_form.addInfoMessage('Cancelling incident...'); // Submit the form to trigger server-side processing gsftSubmit(null, g_form.getFormElement(), 'cancel_incident'); }); // Set the confirmation message gm.setPreference("question", "Are you sure you want to cancel this incident?\n\nThis action will change the incident state to 'Cancelled' and cannot be easily undone."); // Render the modal gm.render(); } catch (error) { // Handle any unexpected errors console.error('Error in cancelIncident function:', error); alert('An unexpected error occurred. Please contact your system administrator.'); } } /** * Server-side execution block * This code runs on the server when the UI Action is submitted */ if (typeof window == 'undefined') { serverCancel(); } /** * Server-side function to cancel the incident * Updates the incident state to 'Cancelled' and adds a work note */ function serverCancel() { try { // Validate that we have a current record if (!current || !current.isValidRecord()) { gs.addErrorMessage('Error: Invalid incident record.'); return; } // Double-check the current state before cancelling if (current.state.toString() !== '1') { gs.addErrorMessage('Error: This incident cannot be cancelled. Only incidents in "New" state can be cancelled.'); return; } // Store original values for logging var incidentNumber = current.number.toString(); var originalState = current.state.getDisplayValue(); // Update the incident state to 'Cancelled' (state value 8) current.state = '8'; // Add a work note to document the cancellation var workNote = 'Incident cancelled by ' + gs.getUserDisplayName() + ' on ' + gs.nowDateTime(); if (current.work_notes.nil()) { current.work_notes = workNote; } else { current.work_notes = current.work_notes + '\n\n' + workNote; } // Update the record current.update(); // Log the action for audit purposes gs.info('Incident ' + incidentNumber + ' cancelled by user ' + gs.getUserName() + '. State changed from "' + originalState + '" to "Cancelled"'); // Provide user feedback gs.addInfoMessage('Incident ' + incidentNumber + ' has been successfully cancelled.'); } catch (error) { // Handle server-side errors gs.error('Error cancelling incident: ' + error.message); gs.addErrorMessage('An error occurred while cancelling the incident. Please contact your system administrator.'); } } ================================================ FILE: Client-Side Components/UI Actions/Clone incident on Agent Workspace/README.md ================================================ Agent can use this UI Action on incident form to clone/copy any existing incident. This UI Action will create a copy of incident once agent confirm the action. Caller field will not be copeied to newly created incident, only basic information of ticket like Company, Short Description, Category, Sub-Category Create an UI Action with below field values: Name - Clone Incident Action Name - clone_incident Table - Incident Client - checked (true) Onclick - cloneIncident(); Workspace Form Button - checked (true) Script - use clone_incident.js Workspace Client script - use workspace_client_script.js ================================================ FILE: Client-Side Components/UI Actions/Clone incident on Agent Workspace/clone_incident.js ================================================ function cloneIncident() { var answer = confirm(getMessage("Are you sure you want to Clone this Incident?")); if (answer) gsftSubmit(null, g_form.getFormElement(), 'clone_incident'); else return false; } if (typeof window == 'undefined') { //Clone Incident var grInc = new GlideRecord('incident'); grInc.initialize(); grInc.company = current.company; grInc.short_description = current.short_description; grInc.description = current.description; grInc.contact_type = "Self-service"; grInc.category = current.category; grInc.subcategory = current.subcategory; grInc.setDisplayValue('assignment_group', "Assignment Group Name"); // or use grInc.assignment_group = current.assignment_group.toString(); /* uncomment this code if comments need to be copied //Remove Timestamp from Comments var getComments = current.comments.getJournalEntry(1); var regex = new RegExp("\n"); var returnComments = getComments; var getRegex = getComments.search(regex); if (getRegex > 0) { returnComments = getComments.substring(getRegex + 1, getComments.length); } gr.comments = returnComments; */ grInc.insert(); action.setRedirectURL(grInc); } ================================================ FILE: Client-Side Components/UI Actions/Clone incident on Agent Workspace/workspace_client_script.js ================================================ function onClick() { getMessage("Are you sure you want to Clone this Incident?", function (msg) { g_modal.confirm(getMessage("Confirmation"), msg, function (confirmed) { if (confirmed) { g_form.submit('clone_incident'); } }); }); } ================================================ FILE: Client-Side Components/UI Actions/Close Related HR cases & HR tasks/README.md ================================================ Scenario:- Table: HR Case Create a form button named "Check related item and Close Complete" feature and list down the related child HR cases and HR tasks in the pop-up message. Upon confirmation, it will close the current case and other listed items. This will help in reducing the manual effort of closing items manually. Scripts: Client UI script to handle the confirmation popup and state of current case. GlideAJAX enabled script include to fetch the data and close the related items. ================================================ FILE: Client-Side Components/UI Actions/Close Related HR cases & HR tasks/Script Include.js ================================================ var close_item = Class.create(); close_item.prototype = Object.extendsObject(global.AbstractAjaxProcessor, { getRelatedItems: function() { var caseId = this.getParameter('sysparm_case_id'); var results = []; // Get child HR cases var childCases = new GlideRecord('sn_hr_core_case'); childCases.addQuery('parent', caseId); childCases.query(); while (childCases.next()) { results.push({ type: 'HR Case', number: childCases.getValue('number') }); } // Get tasks var tasks = new GlideRecord('sn_hr_core_task'); tasks.addQuery('hr_case', caseId); tasks.query(); while (tasks.next()) { results.push({ type: 'HR Task', number: tasks.getValue('number') }); } return JSON.stringify(results); }, closeRelatedItems: function() { var caseId = this.getParameter('sysparm_case_id'); // Close child cases var childCases = new GlideRecord('sn_hr_core_case'); childCases.addQuery('parent', caseId); childCases.query(); while (childCases.next()) { childCases.setValue('state', '3'); childCases.update(); } // Close tasks var tasks = new GlideRecord('sn_hr_core_task'); tasks.addQuery('hr_case', caseId); tasks.query(); while (tasks.next()) { tasks.setValue('state', '3'); tasks.update(); } return "done"; }, type: 'close_task' }); ================================================ FILE: Client-Side Components/UI Actions/Close Related HR cases & HR tasks/UI Action script.js ================================================ // Demo- OnClick function to execute function demo() { var ga = new GlideAjax('sn_hr_core.close_items'); ga.addParam('sysparm_name', 'getRelatedItems'); ga.addParam('sysparm_case_id', g_form.getUniqueValue()); ga.getXMLAnswer(function(response) { // If there exist related items var items = JSON.parse(response); if (items.length > 0) { var msg = "This case has related items:\n"; items.forEach(function(item) { msg += "- " + item.type + ": " + item.number + "\n"; }); msg += "\nDo you want to close them as well?"; if (confirm(msg)) { // close current HR case g_form.setValue('state', '3'); g_form.save(); } } else { // If no related item is associated if (confirm("No related items found. Close this case?")) { g_form.setValue('state', '3'); g_form.save(); } } }); } ================================================ FILE: Client-Side Components/UI Actions/Close child incident/README.md ================================================ This is a ui actions that close the child incident directly from the parent incident Two actions for this : 1. Client side ui action from which button is shown and onClick of that button server side action will be performed 2. Server side ui action that update the state of that child incident to closed ### update Updated client script to replace JavaScript function confirm() with GlideModal() API. To complete a on issue #745 (Close child incident UI Action) ================================================ FILE: Client-Side Components/UI Actions/Close child incident/clientScript.js ================================================ function popUpClientScript(){ var actionCallbackOK = function() { gsftSubmit(null,g_form.getFormElement(),'sys_demoaction'); // calling server ui action to update state }; var actionCallbackCancel = function() { //do nothing }; var gm = new GlideModal('glide_confirm_basic',false); //UI page with logic to confirm gm.setTitle("Are you sure you want to close the attached child incident"); // confirm message to ask for confirmation gm.setPreference('onPromptComplete', actionCallbackOK.bind(this)); //bind to local function to take action when selected Ok gm.setPreference('onPromptCancel', actionCallbackCancel.bind(this)); //bind to local function to take action when selected Cancel gm.render(); } ================================================ FILE: Client-Side Components/UI Actions/Close child incident/serverScript.js ================================================ var gr = new GlideRecord('incident'); gr.addQuery('parent_incident',current.sys_id); //querying over particular parent incident gr.query(); while(gr.next()){ gr.state = '7'; //updating the state of the child incident to closed gr.update(); } ================================================ FILE: Client-Side Components/UI Actions/CloseChildCases/CloseChildCases.js ================================================ (function executeAction() { var grCase = new GlideRecord('sn_customerservice_case'); grCase.addQuery('parent', current.sys_id); grCase.query(); var counter = 0; while (grCase.next()) { if (grCase.state != 3) { // 3 = Closed grCase.resolution_code = '16'; grCase.close_notes = 'This case was auto closed from the parent case.'; grCase.state = 3; grCase.update(); counter++; } } // Show info message only if any cases were closed if (counter > 0) { gs.addInfoMessage(counter + ' child case(s) have been closed.'); } action.setRedirectURL(current); })(); ================================================ FILE: Client-Side Components/UI Actions/CloseChildCases/README.md ================================================ Name: Close all Child Case Table:sn_customerservice_case Condition: (gs.hasRole('sn_customerservice_agent') || gs.hasRole('admin') ) && (new GlideRecord('sn_customerservice_case').addQuery('parent', current.sys_id).query().hasNext()) Use Case: Provide UI action button to close all the associated child cases from the parent Case. ================================================ FILE: Client-Side Components/UI Actions/Convert Request to Incident/README.md ================================================ This is a UI Action that creates an Incident using the field values of the current Request and closes the Request as "Closed Skipped". It also compliles all the worknotes and comments into a single worknote on the Incident. This action has an OnClick function as well as a server-side function that runs using: if (typeof current != 'undefined') The OnClick function opens a confirmation window to protect against misclicks. Setting up the UI Action: ![alt text](https://github.com/ezratkim/code-snippets/blob/main/UI%20Actions/Convert%20Request%20to%20Incident/UIActionScreenshot.png) ================================================ FILE: Client-Side Components/UI Actions/Convert Request to Incident/script.js ================================================ //Prompts confirmation window on click function ReqWarning() { var answer = confirm("Please confirm Request to Incident Action. \n This will set the current Request to 'Closed Skipped' and create a new Incident."); if (answer == false) { return false; } gsftSubmit(null, g_form.getFormElement(), 'create_inc_cancel_req'); } // Ensure this runs on the server side if (typeof current != 'undefined') { // Create a new incident record var inc = new GlideRecord('incident'); inc.initialize(); // Map fields from the task to the incident inc.short_description = current.short_description; inc.description = current.special_instructions; inc.caller_id = current.requested_for; inc.watch_list = current.watch_list; inc.assigned_to = current.assigned_to; inc.state = 1; // Sets state to 'New' inc.contact_type = 'self-service'; inc.assignment_group = 'group_sys_id'; // Assign to your preferred assignment group's sys_id // Construct the initial work note for the Incident with a link back to the original var callerName = current.requested_for.getDisplayValue(); var currentLink = "[code]" + current.number + "[/code]"; var initialJournalEntry = gs.getMessage("This incident was converted from {0} on behalf of {1}" , [currentLink, callerName]); inc.work_notes = initialJournalEntry; // Initialize a variable to compile the work notes and comments var compiledEntries = "Compiled Work Notes and Comments:\n=================================\n\n"; // Query combined work notes and comments from the current task var journal = new GlideRecord('sys_journal_field'); journal.addQuery('element_id', current.sys_id); journal.addQuery('element', 'IN', 'work_notes,comments'); // Fetch work notes and comments journal.orderBy('sys_created_on'); // Ensures chronological order journal.query(); while (journal.next()) { var entryType = journal.element == 'work_notes' ? 'Work Note' : 'Comment'; var entryTimestamp = journal.sys_created_on.getDisplayValue(); var userRecord = new GlideRecord('sys_user'); var entryCreatedBy = 'Unknown User'; // Default value in case user is not found // Query the sys_user table based on the user_name stored in sys_created_by userRecord.addQuery('user_name', journal.sys_created_by); userRecord.query(); if (userRecord.next()) { var firstName = userRecord.first_name; var lastName = userRecord.last_name; // Concatenate first name and last name to form the full name entryCreatedBy = firstName + ' ' + lastName; } var entryText = journal.value; // Format the entry with structured and visually separated format compiledEntries += entryType + " - " + entryTimestamp + " - " + entryCreatedBy + ":\n" + "--------------------------------------------------------\n" + "\"" + entryText + "\"\n" + "--------------------------------------------------------\n\n"; } // Add the compiled entries as a work note to the new incident if (compiledEntries != "Compiled Work Notes and Comments:\n=================================\n\n") { inc.work_notes = compiledEntries; } // Insert the new incident record to get a sys_id for work notes and comments transfer var incSysId = inc.insert(); // Check if successful incident record creation, set closing fields, and inform user if (incSysId) { // Set fields on current record current.request_state = 'closed_cancelled'; current.state = 7; // Sets state to 'Closed Skipped' var incLink = "[code]" + inc.number + "[/code]"; current.work_notes = gs.getMessage("Converted to Incident: " + incLink); current.update(); gs.addInfoMessage("Incident created from Request: " + inc.number); // Redirect to the newly created incident action.setRedirectURL(inc); } else { gs.addErrorMessage("Failed to convert"); } } ================================================ FILE: Client-Side Components/UI Actions/Copy Bulk SysIDs/Copy Bulk Sysids.js ================================================ var sysIds = g_list.getChecked(); copyToClipboard(sysIds); ================================================ FILE: Client-Side Components/UI Actions/Copy Bulk SysIDs/README.md ================================================ # Copy SysIDs in Bulk — ServiceNow Utility > Simplify copying checked sys_ids from a list view with a one-click UI Action. --- ## Purpose / Use Case Often, you may need to extract sys_ids from records listed in a ServiceNow list view (for scripting, validations, data workflows, etc.). Instead of exporting CSVs or manually gathering IDs, this utility enables direct copying of the selected records’ sys_ids (comma-separated) from the list itself. --- ## How It Works It adds a global UI Action (on lists) that, when clicked, collects the sys_ids of checked records and copies them to the clipboard using a small client-side script. --- ## Installation Steps 1. Navigate to **System Definition > UI Actions**. 2. Create a **new UI Action** with these settings: - **Name**: e.g. `Copy Bulk SysIDs` - **Table**: `Global` (so it works on every list) - **Check** the **Client** and **List** checkboxes (so it appears in list context on client side) 3. In the **Onclick / Client script** field, paste: ```javascript var sysIds = g_list.getChecked(); copyToClipboard(sysIds); ## Result image ================================================ FILE: Client-Side Components/UI Actions/Copy Variable Set/README.md ================================================ This UI action will help create a copy of the Variable set, including the Catalog Client Script, Catalog UI actions and Variable. Below Configurations need to be performed on the UI action form on creation Table : Variable Set Active: True Show Update : True Client : True Action name : copyQuestionSet On Click : clientConfirm() ### update To complete a task on issue #745 Replace JavaScript function confirm() with GlideModal() API. ================================================ FILE: Client-Side Components/UI Actions/Copy Variable Set/scripts.js ================================================ /****************Client Code****************/ function clientConfirm() { var actionCallbackOK = function() { gsftSubmit(null, g_form.getFormElement(), 'copyQuestionSet'); }; var actionCallbackCancel = function() { return false; }; var gm = new GlideModal('glide_confirm_basic',false); //UI page with logic to confirm gm.setTitle("This will create a copy of this variable set including all variables, choices, UI policies, UI policy actions and client scripts. Do you want to proceed?"); // confirm message to ask for confirmation gm.setPreference('onPromptComplete', actionCallbackOK.bind(this)); //bind to local function to take action when selected Ok gm.setPreference('onPromptCancel', actionCallbackCancel.bind(this)); //bind to local function to take action when selected Cancel gm.render(); } /****************Server Code****************/ //set some new default values var name = current.title; current.title = 'Copy of ' + name; //insert a copy of the variable set var oldid = current.sys_id.toString(); var newid = current.insert(); var allVars = {}; if (typeof window == 'undefined') { main(oldid, newid); } function main(oldid, newid) { createVariables(oldid, newid); createCatalogClientScript(oldid, newid); createCatalogUiPolicy(oldid, newid); } //creates a copy of the variables and associates them to the new variable set function createVariables(oldid, newid) { var vars = new GlideRecord('item_option_new'); vars.addQuery('variable_set', oldid); vars.query(); while (vars.next()) { var varoldid = vars.sys_id.toString(); vars.variable_set = newid; var varnewid = vars.insert(); allVars['IO:' + varoldid] = 'IO:' + varnewid.toString(); var qc = new GlideRecord('question_choice'); qc.addQuery('question', varoldid); qc.query(); while (qc.next()) { qc.question = varnewid; qc.insert(); } } } //creates a copy of the client scripts and associates to the variable set. function createCatalogClientScript(oldid, newid) { var ccs = new GlideRecord('catalog_script_client'); ccs.addQuery('variable_set', oldid); ccs.query(); while (ccs.next()) { if (ccs.type == 'onChange') { var cv = ccs.cat_variable; ccs.cat_variable = allVars[cv]; } ccs.variable_set = newid; ccs.insert(); } } //creates a copy of the UI Policies and associates them to the new variable set function createCatalogUiPolicy(oldid, newid) { var cup = new GlideRecord('catalog_ui_policy'); cup.addQuery('variable_set', oldid); cup.query(); while (cup.next()) { var uipoldid = cup.sys_id.toString(); cup.variable_set = newid; var newuip = cup.insert(); var cupa = new GlideRecord('catalog_ui_policy_action'); cupa.addQuery('ui_policy', uipoldid); cupa.query(); while (cupa.next()) { cupa.ui_policy = newuip; cupa.variable_set = newid; var cv = cupa.catalog_variable; cupa.catalog_variable = allVars[cv]; cupa.insert(); } } } //Return the user to the new variable set record action.setRedirectURL(current); ================================================ FILE: Client-Side Components/UI Actions/Copy incident details and create a child incident/README.md ================================================ # Copy incident details and create a child incident Use case : A button on the header of incident form to copy details(fields) of current incident and create a child incident with those copied details. Solution : Added a code snippet for UI action script that copies few fields of current incident and creates a child for the current incident with those details. ================================================ FILE: Client-Side Components/UI Actions/Copy incident details and create a child incident/ui_action_script.js ================================================ var gr = new GlideRecord('incident'); gr.initialize(); gr.short_description = current.short_description; //copy short description field gr.caller_id = current.caller_id; //copy caller id field //you can copy few more fields as per requirement gr.parent_incident = current.sys_id; gr.work_notes = "This incident is a child and copy of " + current.number; //you can customize work notes if needed gr.insert(); action.setRedirectURL(gr); //use this line if you want to redirect to newly created child incident after execution action.setRedirectURL(current) //use this line if you want to stay in parent incident after execution. ================================================ FILE: Client-Side Components/UI Actions/Create Incident from Record - Open in both Platform and Workspace/README.md ================================================ Script that can be used in the Script section of a UI Action to create an Incident, or any record type, from another record and will open the record that was just created. This script will work both in Platform UI and Workspace UI, as long as one of the Workspace Form/Menu buttons are checked. This reduced the need to create a script for both Platform and Workspace within one UI Action. For more information: https://www.ashleysn.com/post/action-opengliderecord ================================================ FILE: Client-Side Components/UI Actions/Create Incident from Record - Open in both Platform and Workspace/script.js ================================================ //Placed in the Script field of the UI Action, in order to work on Workspace the Workspace Form Action button/or menu must be checked var incGr = new GlideRecord('incident'); incGr.newRecord(); incGr.setValue('caller_id', current.getValue('contact')); //This can be whatever field your record is housing the user in, this example is from the Case record. incGr.setValue('short_description', current.short_description); incGr.insert(); action.openGlideRecord(incGr); ================================================ FILE: Client-Side Components/UI Actions/Create New blank incident from the incident/README.md ================================================ A UI Action in ServiceNow is a script that defines an action or button within the platform's user interface. It enables users to perform specific operations on forms and lists, such as creating, updating, or deleting records, or executing custom scripts. UI Actions enhance the user experience by providing functional buttons, links, or context menus. In this UI action script when clicked Creates New blank incident form, from the incident. action.setRedirectURL() is a method used in server-side scripting within UI Actions to redirect users to a specific URL after a UI action is performed. It is commonly used to navigate users to different records, forms, or list views after they have completed an action. Syntax - action.setRedirectURL(URL); Parameters: URL: The URL to which the user will be redirected. This can be a string representing a GlideURL object or a hardcoded URL. It must point to a valid ServiceNow page (record, list, form, etc.). Return: None. It performs a redirection after the script completes. GlideURL is a class in ServiceNow used for constructing URLs dynamically in server-side scripts. It allows developers to programmatically create and manipulate URLs to redirect users, perform navigation, or link to specific ServiceNow resources (e.g., forms, lists, reports). ================================================ FILE: Client-Side Components/UI Actions/Create New blank incident from the incident/script.js ================================================ //UI Action - Create New blank incident from the incident. var newFormURL = new GlideURL('incident.do'); newFormURL.addParam('sys_id', '-1'); // Open a new blank form action.setRedirectURL(newFormURL.toString()); ================================================ FILE: Client-Side Components/UI Actions/Create Problem Record from any Table/CreateProblemRecord.js ================================================ //This UI action helps in generating a problem record from a chnage or an incident form var createPrb = new GlideRecord("problem"); // Gliding the problem table createPrb.initialize(); createPrb.short_description = current.short_description; // taking current records short description as problem short description(problem statement) createPrb.first_reported_by_task = current.getUniqueValue(); createPrb.cmdb_ci = current.cmdb_ci; //taking the affected in configuration item createPrb.insert(); //inserting the record into the problem table gs.addInfoMessage("problem number" + createPrb.number.getDisplayValue()); // informing the user with the created problem record number for easy reference. action.setRedirectURL(current); ================================================ FILE: Client-Side Components/UI Actions/Create Problem Record from any Table/README.md ================================================ This UI action helps in creating a problem record from an incident or even from a change table, and we can modify this code by changing the table name we can use it for any sort of table to create a problem record. ================================================ FILE: Client-Side Components/UI Actions/Create Problem Task from the Problem/README.md ================================================ UI Action Create Problem Task from the Problem A UI Action in ServiceNow is a script that defines an action or button within the platform's user interface. It enables users to perform specific operations on forms and lists, such as creating, updating, or deleting records, or executing custom scripts. UI Actions enhance the user experience by providing functional buttons, links, or context menus. Using this UI action script we can create a Problem task from a Problem and associate it to the current problem. UI Action will be available as Form Button on the Problem Form. When Clicked Problem Task will be Created and associated to the current Problem. Short description of the problem task is "Problem Task Created for problem " + current.number Same will be added to the Description as well. sys_id of the current problem record will be added to the problem field of the Problem Task. This will create relation between Problem and Problem Task. Problem Task Type will be General ================================================ FILE: Client-Side Components/UI Actions/Create Problem Task from the Problem/script.js ================================================ //UI Action - Create a Problem task from a Problem. Problem Task Type is General var gr = new GlideRecord('problem_task'); gr.initialize(); gr.short_description = "Problem Task Created for problem " + current.number; gr.description = current.short_description; gr.problem = current.sys_id; gr.problem_task_type = 'general'; gr.insert(); ================================================ FILE: Client-Side Components/UI Actions/Create Update Set on DEV/README.md ================================================ # Create Update Set on DEV A client UI Action for the Story form that opens up a new browser window with the Create New Update Set form on a specified DEV instance with Update Set name pre-filled with the Story number and short description. Helps reducing copy/paste work and to keep up with Update Set naming standards. ================================================ FILE: Client-Side Components/UI Actions/Create Update Set on DEV/script.js ================================================ function openDevUpdateSetForm() { // Name of the DEV instance where Update sets should be created: var dev_instance_name = 'my_org_dev_instance'; // Update set name format: var update_set_name = g_form.getValue('number') + ' ' + g_form.getValue('short_description'); var instanceURL = 'https://' + dev_instance_name + '.service-now.com/nav_to.do?uri='; var updatesetURL = '/sys_update_set.do?sys_id=-1&sysparm_query=name=' + update_set_name; var encodedUpdateSetURL = encodeURIComponent(updatesetURL); var gotoURL = instanceURL + encodedUpdateSetURL; g_navigation.open(gotoURL, '_blank'); } ================================================ FILE: Client-Side Components/UI Actions/Create incident task and relate to incident/README.md ================================================ This UI Action loads a modal for to create a new incident task that is linked to the incident that you generated it from. Suggested values: Name: Create Incident Task Table: Incident Client: true List v2: true Form Link: true Onclick: createIncidentTask() Condition: current.state != 7 ================================================ FILE: Client-Side Components/UI Actions/Create incident task and relate to incident/script.js ================================================ function createIncidentTask() { var sysID = g_form.getUniqueValue(); var gm = new GlideModalForm('Create Incident Task', 'incident_task'); gm.setPreference('focusTrap', true); gm.setPreference('table', 'incident_task'); gm.setPreference('sysparm_query', 'incident='+sysID); gm.setWidth(650); //Opens the dialog gm.render(); } ================================================ FILE: Client-Side Components/UI Actions/Create story/Create story from other task.js ================================================ createStory(); function createStory() { // (1) Copy item fields into a new story var story = new GlideRecord("rm_story"); story.priority = current.priority; story.short_description = current.short_description; story.assignment_group = current.assignment_group; story.assigned_to = current.assigned_to; story.description = current.description; story.work_notes = current.work_notes; story.type="Development"; story.opened = current.opened; story.opened_by = current.opened_by; story.product = null; story.state = -6; //default to draft story.original_task = current.sys_id; var storySysID = story.insert(); current.agile_story = storySysID; current.update(); // (2) Redirect webpage to the new story (Ensure story displayed in scrum view) gs.addInfoMessage(gs.getMessage("Story {0} created", story.number)); action.setRedirectURL(story); var redirectURL = action.getRedirectURL(); redirectURL = redirectURL.replace("sysparm_view=", "sysparm_view=scrum"); action.setRedirectURL(redirectURL); action.setReturnURL(current); } ================================================ FILE: Client-Side Components/UI Actions/Create story/README.md ================================================ Code Snippet for UI Action to create an Agile Story from another task. For example Incident -> Story, Task -> Story, Requested Item -> Story ================================================ FILE: Client-Side Components/UI Actions/Display a 2-choice confirmation dialog/README.md ================================================ # Display a 2-choice confirmation dialog When you press the button on the Form screen, a two-choice dialog is displayed. Click the Complete button to execute Serverside processing. ## Please set the following values. * UI Action * Name: Example Dialog * Table: incident (anything) * Action name: example_dialog * Client: ture * Form button: ture * Onclick: onClickExampleDialog() * Script ```javascript function onClickExampleDialog() { var dialogClass = typeof GlideModal != 'undefined' ? GlideModal : GlideDialogWindow; var dialog = new dialogClass('glide_modal_confirm'); dialog.setTitle('Dialog title'); dialog.setPreference("focusTrap", true); // Restrict focus from moving out of Dialog dialog.setPreference('body', 'Approve this change?'); dialog.setPreference('buttonLabelCancel', 'Cancel'); // Cancel button label dialog.setPreference('buttonLabelComplete', 'Complete'); // Complete button label dialog.setPreference('buttonClassComplete', 'btn btn-destructive'); // Complete button CSS dialog.setPreference('onPromptComplete', dialogComplete.bind(this)); // Complete button function dialog.setPreference('onPromptCancel', dialogCancel.bind(this)); // Cancel button function dialog.render(); return true; } // Complete button function function dialogComplete() { //Press Submit Button and call UIAction(Server side 'example_dialog') again. gsftSubmit(null, g_form.getFormElement(), 'example_dialog'); } // Cancel button function function dialogCancel() { //alert('Dialog Cancel'); } //Judge Server side if (typeof window == 'undefined') { serversideTask(); } // Server side function function serversideTask() { current.update(); gs.info('Serverside Task'); action.setRedirectURL(current); } ``` ================================================ FILE: Client-Side Components/UI Actions/Display a 2-choice confirmation dialog/choice_dialog.js ================================================ // Display a 2-choice confirmation dialog. // When you press the button on the Form screen, a two-choice dialog is displayed. // Click the Complete button to execute Serverside processing. // Please set the following values. // * UI Action // * Name: Example Dialog // * Table: incident (anything) // * Action name: example_dialog // * Client: ture // * Form button: ture // * Onclick: onClickExampleDialog() // * Script function onClickExampleDialog() { var dialogClass = typeof GlideModal != 'undefined' ? GlideModal : GlideDialogWindow; var dialog = new dialogClass('glide_modal_confirm'); dialog.setTitle('Dialog title'); dialog.setPreference("focusTrap", true); // Restrict focus from moving out of Dialog dialog.setPreference('body', 'Approve this change?'); dialog.setPreference('buttonLabelCancel', 'Cancel'); // Cancel button label dialog.setPreference('buttonLabelComplete', 'Complete'); // Complete button label dialog.setPreference('buttonClassComplete', 'btn btn-destructive'); // Complete button CSS dialog.setPreference('onPromptComplete', dialogComplete.bind(this)); // Complete button function dialog.setPreference('onPromptCancel', dialogCancel.bind(this)); // Cancel button function dialog.render(); return true; } // Complete button function function dialogComplete() { //Press Submit Button and call UIAction(Server side 'example_dialog') again. gsftSubmit(null, g_form.getFormElement(), 'example_dialog'); } // Cancel button function function dialogCancel() { //alert('Dialog Cancel'); } //Judge Server side if (typeof window == 'undefined') { serversideTask(); } // Server side function function serversideTask() { current.update(); gs.info('Serverside Task'); action.setRedirectURL(current); } ================================================ FILE: Client-Side Components/UI Actions/Email Watermark Utility/GenericEmailUtility.js ================================================ var GenericEmailUtility = Class.create(); GenericEmailUtility.prototype = { initialize: function() {}, // Generate an Outlook (mailto) link with watermark tracking get_Outlook_link: function() { try { const email_payload = JSON.stringify({ "REQUESTOR_ID": "", "TITLE": "", "BODY": "", "REQUEST_ID": "", "TABLE_ID": "" }); var mailtoLink = false; const raw_data = this.getParameter("sysparm_email_body") || email_payload; if (global.JSUtil.notNil(raw_data)) { var email_data = JSON.parse(raw_data); const to = this.getEmail(email_data.REQUESTOR_ID); const cc = gs.getProperty("instanceEmailAddress"); // instance default CC const subject = email_data.TITLE || ''; const body = email_data.BODY || ''; const watermark = this.getWatermark(email_data.REQUEST_ID, email_data.TABLE_ID); // Construct mailto link mailtoLink = 'mailto:' + to + '?cc=' + cc; if (subject) mailtoLink += '&subject=' + encodeURIComponent(subject); if (body) mailtoLink += '&body=' + encodeURIComponent(body); if (watermark) mailtoLink += encodeURIComponent("\n\nRef: " + watermark); } return mailtoLink; } catch (ex) { gs.error("Error in get_Outlook_link(): " + ex.message); return false; } }, // Fetch watermark ID (creates one if missing) getWatermark: function(record_id, table_name) { var wm = new GlideRecord('sys_watermark'); wm.addQuery('source_id', record_id); wm.orderByDesc('sys_created_on'); wm.query(); if (wm.next()) { return wm.getValue('number'); } wm.initialize(); wm.source_id = record_id; wm.source_table = table_name; wm.insert(); return wm.getValue('number'); }, // Retrieve user’s email address getEmail: function(user_id) { if (global.JSUtil.notNil(user_id)) { var user = new GlideRecordSecure('sys_user'); if (user.get(user_id)) return user.email.toString(); } return ''; }, type: 'GenericEmailUtility' }; ================================================ FILE: Client-Side Components/UI Actions/Email Watermark Utility/README.md ================================================ # Outlook Email Watermark Utility for ServiceNow # Overview This reusable utility allows users to send emails **outside ServiceNow** (e.g., using Outlook or any default mail client) while still maintaining the conversation within ServiceNow. By embedding a unique watermark reference, any replies to the email will automatically append to the original record's activity feed. This helps teams collaborate externally without losing internal record visibility — ideal for customers or vendors who communicate via Outlook. --- # Objective - Enable ServiceNow users to send Outlook emails directly from a record. - Maintain conversation history in ServiceNow using watermark tracking. - Make the solution **generic**, reusable across tables (Incident, Change, Request, etc.). - Prevent dependency on outbound mail scripts or custom integrations. # Components ## 1. Script Include: GenericEmailUtility Handles the logic for: - Constructing the mailto: link. - Fetching recipient and instance email addresses. - Generating or retrieving the watermark ID. - Returning a formatted Outlook link to the client script. ## Key Methods 1. get_Outlook_link() - Builds the full Outlook mail link with subject, body, and watermark. 2. getWatermark(record_id, table_name) - Ensures a watermark exists for the record. 3. getEmail(user_id) - Fetches the email address for the target user. ## 2. UI Action (Client Script) Executes on the record form when the button/link is clicked. It gathers record data, constructs a payload, calls the Script Include using GlideAjax, and opens Outlook. ## Key Steps 1. Collect field data like requestor, short description, and description. 2. Pass record details to the Script Include (GenericEmailUtility). 3. Receive a ready-to-use Outlook link. 4. Open the mail client with prefilled details and watermark reference. ## How It Works 1. User clicks "Send Outlook Email" UI Action on a record. 2. Script gathers record data and passes it to GenericEmailUtility. 3. The utility builds a 'mailto:' link including the watermark. 4. Outlook (or default mail client) opens with pre-filled To, CC, Subject, and Body fields. 5. When the recipient replies, ServiceNow uses the watermark to append comments to the correct record. ## Example Usage **User clicks “Send Outlook Email”** on a Request record: Outlook opens prefilled like this: image image ================================================ FILE: Client-Side Components/UI Actions/Email Watermark Utility/Send Email UI Action.js ================================================ function onClick(g_form) { var separator = "\n--------------------------------\n"; var email_body = "Record URL:\n" + g_form.getDisplayValue('number') + separator; email_body += "Short Description:\n" + g_form.getValue('short_description') + separator; email_body += "Description:\n" + g_form.getValue('description') + separator; var email_data = {}; email_data.REQUESTOR_ID = g_form.getValue('caller_id') || g_form.getValue('opened_by') || g_form.getValue('requested_for'); email_data.TITLE = g_form.getValue('short_description') || 'ServiceNow Communication'; email_data.BODY = email_body; email_data.REQUEST_ID = g_form.getUniqueValue(); email_data.TABLE_ID = g_form.getTableName(); var ga = new GlideAjax('GenericEmailUtility'); ga.addParam('sysparm_name', 'get_Outlook_link'); ga.addParam('sysparm_email_body', JSON.stringify(email_data)); ga.getXMLAnswer(function(response) { var mailto_link = response; if (mailto_link && mailto_link != 'false') { window.open(mailto_link); } else { g_form.addErrorMessage('Unable to generate Outlook link.'); } }); } ================================================ FILE: Client-Side Components/UI Actions/Expire Timer in Flows/README.md ================================================ This UI Action adds an admin-only button to Flow Context records in ServiceNow. It is designed to expire active timers within a flow, allowing developers and testers to bypass waiting stages during sub-production testing. This helps accelerate flow validation and debugging during development cycles, especially useful during events like Hacktoberfest. Flows in ServiceNow often include Wait for Condition or Wait for Duration steps that can delay testing. This UI Action provides a quick way to expire those timers, enabling the flow to proceed immediately without waiting for the configured duration or condition. Features Adds a button labeled "Expire Timers" to Flow Context records. Visible only to users with the admin role. Executes a script to expire all active timers associated with the selected Flow Context. Ideal for sub-production environments (e.g., development, test, or staging). Speeds up flow development and validation. image ================================================ FILE: Client-Side Components/UI Actions/Expire Timer in Flows/script.js ================================================ /* This script should be placed in the UI action on the table sys_flow_context form view. This UI action should be marked as client. Use runClientCode() function in the Onclick field. */ function runClientCode() { if (confirm('Are you sure you want to Expire the Timer activity ?\n\nThis Action Cannot Be Undone!')) { //Call the UI Action and skip the 'onclick' function gsftSubmit(null, g_form.getFormElement(), 'ExpireTimer'); //MUST call the 'Action name' set in this UI Action } else { return false; } } if (typeof window == 'undefined') { ExpireTimer(); } function ExpireTimer() { var grTrigger = new GlideRecord('sys_trigger'); grTrigger.addQuery('name', 'flow.fire'); grTrigger.addQuery('script', 'CONTAINS', current.sys_id); grTrigger.addQuery('state', 0); grTrigger.setLimit(1); grTrigger.query(); if (grTrigger.next()) { var grEvent = new GlideRecord('sysevent'); grEvent.initialize(); grEvent.setNewGuid(); grEvent.setValue('name', 'flow.fire'); grEvent.setValue('queue', 'flow_engine'); grEvent.setValue('parm1', grTrigger.getValue('job_context').toString().slice(6)); grEvent.setValue('parm2', ''); grEvent.setValue('instance', current.sys_id); grEvent.setValue('table', 'sys_flow_context'); grEvent.setValue('state', 'ready'); grEvent.setValue('process_on', new GlideDateTime().getValue()); //aka run immediately grEvent.insert(); grTrigger.deleteRecord(); gs.addInfoMessage("You have chosen to end any timers related to this flow."); } action.setRedirectURL(current); } ================================================ FILE: Client-Side Components/UI Actions/Field Review of User Record when on form using action button/README.md ================================================ ## Field Review of User Record when on form using action button Displays informational messages suggesting improvements to field formatting on the User Table (**sys_user**) form when the **Fields Check** button is clicked. - Helps maintain consistency in user data by checking capitalization of names and titles, validating email format, ensuring phone numbers contain only digits, and preventing duplicate phone entries. - Also suggests users not to leave the **user_name** field empty. - Shows Info messages below each field highlighting fields that may need attention. - Simple Prerequisite is that: when form loads give Info message to check **Field Check** button to bring user's attention - Uses a Client-side UI Action (**Fields Check**) that to review entered data and display friendly suggestions - Name: Fields Check - Table: User (sys_user) - Client: true - Form button: true - Onclick: onClickCheckDetails() --- ### Grab user's attention on Field Check Button using Info message at top ![Field Review on User Table_1](Field_Review_userTable_1.png) --- ### After clicking Field Check Button where suggestions are displayed below fields ![Field Review on User Table_2](Field_Review_userTable_2.png) --- ### When user fixes the suggested issues and click the **Fields Check** button again, a message confirms that all fields are correctly formatted ![Field Review on User Table_3](Field_Review_userTable_3.png) --- ================================================ FILE: Client-Side Components/UI Actions/Field Review of User Record when on form using action button/actionButtonScript.js ================================================ function onClickCheckDetails() { // Friendly helper for field normalization guidance g_form.hideAllFieldMsgs(); g_form.clearMessages(); // --- Get Field values --- var firstName = g_form.getValue('first_name'); var lastName = g_form.getValue('last_name'); var title = g_form.getValue('title'); var userId = g_form.getValue('user_name'); var email = g_form.getValue('email'); var businessPhone = g_form.getValue('phone'); var mobilePhone = g_form.getValue('mobile_phone'); // --- Regex patterns --- var capitalRegex = /^[A-Z][a-zA-Z\s]*$/; // Names & titles start with a capital var emailRegex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/; var phoneRegex = /^\d+$/; var suggestions = []; if (firstName && !capitalRegex.test(firstName)) { g_form.showFieldMsg('first_name', 'Suggestion: Start the name with a capital letter.', 'info'); suggestions.push('First Name'); } if (lastName && !capitalRegex.test(lastName)) { g_form.showFieldMsg('last_name', 'Suggestion: Start the name with a capital letter.', 'info'); suggestions.push('Last Name'); } if (title && !capitalRegex.test(title)) { g_form.showFieldMsg('title', 'Suggestion: Titles usually start with a capital letter.', 'info'); suggestions.push('Title'); } if (!userId) { g_form.showFieldMsg('user_name', 'Suggestion: Do not keep the User ID empty.', 'info'); suggestions.push('User ID'); } if (email && !emailRegex.test(email)) { g_form.showFieldMsg('email', 'Suggestion: Please use a valid email format like name@example.com.', 'info'); suggestions.push('Email'); } if (businessPhone && !phoneRegex.test(businessPhone)) { g_form.showFieldMsg('phone', 'Suggestion: Use digits only avoid letters.', 'info'); suggestions.push('Business Phone'); } if (mobilePhone && !phoneRegex.test(mobilePhone)) { g_form.showFieldMsg('mobile_phone', 'Suggestion: Use digits only avoid letters.', 'info'); suggestions.push('Mobile Phone'); } / if (businessPhone && mobilePhone && businessPhone === mobilePhone) { g_form.showFieldMsg('phone', 'Work and mobile numbers appear identical, use different Numbers!', 'info'); suggestions.push('Phone Numbers'); } if (suggestions.length > 0) { g_form.addInfoMessage('Quick review complete! Please check: ' + suggestions.join(', ') + '.'); } else { g_form.addInfoMessage('looks good! Nicely formatted data.'); } } ================================================ FILE: Client-Side Components/UI Actions/Force to Update Set/README.md ================================================ # Introduction Manually Add a Record to an Update Set 1. Check to make sure the current table isn’t already recording updates 2. Push the current record into the currently-selected update set 3. Reload the form and add an info. message indicating the addition # Settings Name: Force to Update Set Table: Wherever you need it! Action name: force_update Form link: True Condition: gs.hasRole(‘admin’) Script: //Commit any changes to the record current.update(); //Check to make sure the table isn't synchronized already var tbl = current.getTableName(); if(tbl.startsWith('wf_') || tbl.startsWith('sys_ui_') || tbl == 'sys_choice' || current.getED().getBooleanAttribute('update_synch') || current.getED().getBooleanAttribute('update_synch_custom')){ gs.addErrorMessage('Updates are already being recorded for this table.'); action.setRedirectURL(current); } else{ //Push the update into the current update set var um = new GlideUpdateManager2(); um.saveRecord(current); //Query for the current update set to display info message var setID = gs.getPreference('sys_update_set'); var us = new GlideRecord('sys_update_set'); us.get(setID); //Display info message and reload the form gs.addInfoMessage('Record included in ' + us.name + ' update set.'); action.setRedirectURL(current); } ================================================ FILE: Client-Side Components/UI Actions/Force to Update Set/script.js ================================================ //Check to make sure the table isn't synchronized already var tbl = current.getTableName(); if(tbl.startsWith('wf_') || tbl.startsWith('sys_ui_') || tbl == 'sys_choice' || current.getED().getBooleanAttribute('update_synch') || current.getED().getBooleanAttribute('update_synch_custom')){ gs.addErrorMessage('Updates are already being recorded for this table.'); action.setRedirectURL(current); } else{ //Push the update into the current update set var um = new GlideUpdateManager2(); um.saveRecord(current); //Query for the current update set to display info message var setID = gs.getPreference('sys_update_set'); var us = new GlideRecord('sys_update_set'); us.get(setID); //Display info message and reload the form gs.addInfoMessage('Record included in ' + us.name + ' update set.'); action.setRedirectURL(current); } ================================================ FILE: Client-Side Components/UI Actions/Generate QR for Assets/ReadMe.md ================================================ # 🧩 ServiceNow Asset QR Code Generator (UI Action) This repository contains a **ServiceNow UI Action** script that generates and displays a QR Code for an Asset record from list view. When the user selects a record and clicks the UI Action, a modal window pops up showing a dynamically generated QR Code that links to asset details. A supporting **Script Include** (server-side) is required in your ServiceNow instance but **is not included** in this repository. At the bottom of file , a sample Script Include Code is given , check for the reference. --- ## 🚀 Features - Generates a QR Code for the selected Asset record. - Displays the QR Code inside a ServiceNow modal (`GlideModal`). - Uses **QrIckit API** for quick and free QR code generation. - Clean, modular client-side code that integrates seamlessly with UI Actions. - Includes a `qr-code-image` file showing example QR Code generated. --- ## 🧠 How It Works 1. The `onClickQR()` function is triggered when the user clicks a UI Action button. 2. It calls `generateQRCodeForAsset(sys_id)` and passes the record’s `sys_id`. 3. A `GlideAjax` request fetches asset data from a **Script Include** on the server. 4. That data is encoded and sent to the **QrIckit** API to generate a QR Code image. 5. A ServiceNow modal (`GlideModal`) displays the generated QR Code to the user. --- **Note :** 1) As the UI action calls a Script Include , in this folder no script include is present 2) You can modify script include part as required(i.e Which fields are to be shown when QR is scanned) 3) A sample Client Callable Script-Include is given here. ``` Script Include Code var GenerateAssetQR = Class.create(); GenerateAssetQR.prototype = Object.extendsObject(AbstractAjaxProcessor, { getAssetQRData: function() { var sys_id = this.getParameter('sysparm_sys_id'); var asset = new GlideRecord('alm_asset'); if (asset.get(sys_id)) { return 'Asset: ' + asset.name + ', Serial: ' + asset.serial_number; } return 'Invalid asset record.'; } }); ``` ================================================ FILE: Client-Side Components/UI Actions/Generate QR for Assets/ui-action-script.js ================================================ function onClickQR() { generateQRCodeForAsset(g_sysId);//get the sysid of selected record } function generateQRCodeForAsset(sys_id) { var ga = new GlideAjax('GenerateAssetQR');//Script Include which stores data to be presented when QR-Code is Scanned ga.addParam('sysparm_name', 'getAssetQRData'); ga.addParam('sysparm_sys_id', sys_id); ga.getXMLAnswer(function(response) { var qrData = response; var qrURL = 'https://qrickit.com/api/qr.php?d=' + encodeURIComponent(qrData) + '&addtext=Get Asset Data'; //QrIckit is a tool using which Customized QR-Codes can be generated var modalHTML = `
QR Code

Scan to view asset details

`; var gModal = new GlideModal("QR Code"); gModal.setTitle('Asset QR Code'); gModal.setWidth(500); gModal.renderWithContent(modalHTML); }); } ================================================ FILE: Client-Side Components/UI Actions/Generate a PDF/README.md ================================================ # Introduction Generating a PDF using PDFGenerationAPI Calling an OOTB convertToPDFWithHeaderFooter() method to generate a PDF with your header and footer. PDFGenerationAPI – convertToPDFWithHeaderFooter(String html, String targetTable, String targetTableSysId, String pdfName, Object headerFooterInfo, String fontFamilySysId, Object documentConfiguration) # Example: image ================================================ FILE: Client-Side Components/UI Actions/Generate a PDF/serverscript.js ================================================ //Table: Change Request // UI action button on the change form that exports all the related incidents into a PDF format. //ServiceNows PDFGenerationAPI allows you to customize the page size, header, footer, header image, page orientation, and more. var inc = new GlideRecord('incident'), incidentsList = [], v = new sn_pdfgeneratorutils.PDFGenerationAPI, html = '', hfInfo = new Object(), result; inc.addQuery('rfc', current.sys_id); inc.query(); while (inc.next()) { incidentsList.push(inc.number); incidentsList.push(inc.getDisplayValue('caller_id')); incidentsList.push(inc.getDisplayValue('category')); incidentsList.push(inc.getDisplayValue('subcategory')); incidentsList.push(inc.getValue('priority')); incidentsList.push(inc.getValue('short_description')); incidentsList.push(inc.getValue('description')); incidentsList.push(inc.getDisplayValue('assignment_group')); } var json = { incidents: incidentsList.toString() }; JSON.stringify(json); html = '

Incidents Related to the Change: ' + current.number + '


'; html += '

Incidents List  

'; html += '' + getIncidentsTable(json.incidents) + '
NumberCallerCategorySub CategoryPriorityShort DescriptionDescriptionAssignment Group
'; hfInfo["FooterTextAlignment"] = "BOTTOM_CENTER"; hfInfo["FooterText"] = "Incidents List"; hfInfo["PageOrientation"] = "LANDSCAPE"; hfInfo["GeneratePageNumber"] = "true"; result = v.convertToPDFWithHeaderFooter(html, 'change_request', current.sys_id, "Incidents Related to the Change:_" + current.number, hfInfo); action.setRedirectURL(current); function getIncidentsTable(incidents) { if (incidents == '') return ''; var table = '', i; incidents = incidents.split(','); for (i = 0; i < incidents.length; i += 8) table += '' + incidents[i] + '' + incidents[i + 1] + '' + incidents[i +2] + '' + incidents[i + 3] + '' + incidents[i + 4] + '' + incidents[i + 5] + '' + incidents[i + 6] + '' + incidents[i + 7] + ''; return table; } ================================================ FILE: Client-Side Components/UI Actions/GlideModalForm - Open New Record and Pass Query/README.md ================================================ This is an example of using the GlideModalForm API to open a brand new record on a specific table, passing along query parameters to it to assist with loading filling out the form Within the UI Action settings it's recommended to ensure: - Active is true - Either show insert and/or show update is true - Client is true - Your appropriate List v2 or V3 compatible checkbox is true - Onclick contains your function name that matches the function within your Script field -- the code related to this snipped would be: **functionName()** - Set true to however you wish to display this UI Action to the user; whether that's via Form button, Form context menu, Form link, etc. ================================================ FILE: Client-Side Components/UI Actions/GlideModalForm - Open New Record and Pass Query/code.js ================================================ function functionName() { //specify the function name you listed within the UI Action "onClick" field var tableName = "table_name"; //specify what table the new record should be created on var dialog = new GlideModalForm('modal_form_title', tableName); //set your modal form title here dialog.setSysID(-1); //sys_id -1 will open a brand new record dialog.addParm('sysparm_view', 'view_name'); //optional: you can specify a specific view name here dialog.addParm('sysparm_view_forced', 'true'); //optional: you can force the view so it overrides dialog.addParm('sysparm_form_only', 'true'); //optional: you can specify to show the form only, removing related lists from the screen var sDesc = g_form.getValue('short_description'); //example retrieving the short description on the current record var query = "short_description=" + sDesc; //example setting the query JavaScript variable to the sDesc JavaScript variable that contains our current record's short description dialog.addParm('sysparm_query', query); //sets the query to the JavaScript variable from the line above, this will populate the related field(s) on the new form with the values specified dialog.render(); //displays the modal form to the user } ================================================ FILE: Client-Side Components/UI Actions/GlideModalUiPagePopUp/README.md ================================================ Overview Modal alerts and confirmations in ServiceNow are interactive user interface elements that allow users to receive important messages or confirm actions before proceeding. These modals enhance the user experience by providing clear feedback during key interactions, ensuring that users are well-informed before performing potentially critical actions, such as closing a record or saving changes. Two key modal functions used in ServiceNow are: g_modal.alert(): Displays an informational message to the user. g_modal.confirm(): Requests user confirmation before proceeding with an action. Key Features: Clarity: Provides clear messaging for users, helping them understand the consequences of their actions. User Control: Allows users to confirm or cancel actions, reducing mistakes. Customizable: Modal messages can be tailored to different scenarios, ensuring contextual and relevant feedback. Testing To test the modal alerts and confirmations, follow these steps: 1. User Scenario Setup: Create a form with an assigned_to field and ensure that different users can access the form. Set up a button or UI action that triggers the onClick() function. 2. Test Case 1: Unauthorized User Log in as a user who is not assigned to the task. Click the button to trigger the action. Expected Result: The user should receive an alert stating "Only the assigned user can perform this action." The process should stop here. 3. Test Case 2: Authorized User Log in as the user who is assigned to the task. Click the button to trigger the action. Expected Result: A confirmation dialog should appear asking, "Are you sure you want to take this action?" If the user clicks "Cancel," no action should be taken. If the user clicks "OK," the task's state should be set to "Closed Complete," and the form should be saved. 4. Edge Cases: Test what happens if the task has no assigned user. Test if the modals display correctly on different devices (desktop, mobile, etc.). 5. Performance: Ensure that the modals load quickly and do not interfere with other form functions. How It Works The modal alert and confirmation functions are built on the ServiceNow UI framework and use JavaScript to control the interactions. Alert Modal (g_modal.alert()): This function is used to inform the user without requiring any further input. It takes three arguments: the title of the alert, the message to display, and an optional callback function that can execute code after the alert is closed. Once triggered, the user sees a message box with only an "OK" button. Confirmation Modal (g_modal.confirm()): This function prompts the user to confirm their action with two options: "OK" or "Cancel." It takes three arguments: the title of the confirmation modal, the message, and a callback function. The callback function receives a confirmed argument that is true if the user clicks "OK" and false if they click "Cancel." This is useful in scenarios where user confirmation is critical (e.g., deleting a record, submitting a form). Process Flow: The system checks if the logged-in user has permission to perform the action (e.g., checking if the user matches the assigned_to field). If the user is not authorized, an alert modal is displayed, and the process stops. If the user is authorized, a confirmation modal is displayed to ask for final approval. If confirmed, the system proceeds with the action (e.g., changing the record state and saving the form). Benefits 1. Improved User Experience: Modal alerts and confirmations provide immediate feedback to users. By using clear language in modals, users understand exactly what actions they can or cannot perform. Confirmation dialogs prevent accidental actions, which is especially useful in critical workflows (e.g., closing or deleting records). 2. Reduced Errors: Alerts help users understand why an action is restricted, while confirmations ensure that actions are intentional. This reduces the risk of accidental data changes or loss. 3. Increased Control: By requiring confirmation before a critical action is taken, users feel more in control of their tasks. They are prompted to reconsider their choices, which minimizes hasty decisions. 4. Customization: Modal alerts and confirmations can be easily customized for various forms and records, allowing tailored feedback depending on the context of the action. 5. Asynchronous Operations: Modals are asynchronous, meaning they don't block the user interface while waiting for input. This ensures that other parts of the application continue functioning smoothly. ================================================ FILE: Client-Side Components/UI Actions/GlideModalUiPagePopUp/glide_modal_ui_pop_up.js ================================================ function onClick(g_form) { // Check if the current user is the assigned user if (g_user.userID != g_form.getValue('assigned_to')) { // Alert if the user is not the assigned user g_modal.alert('Only the assigned user can perform this action.'); return; } // Confirmation message var msg = getMessage("Are you sure you want to take this action?"); // Confirmation modal before closing the task g_modal.confirm(getMessage("Confirmation"), msg, function (confirmed) { if (confirmed) { // If confirmed, close the task g_form.setValue('state', 'closed_complete'); g_form.save(); } }); return false; } ================================================ FILE: Client-Side Components/UI Actions/Go to Agent Workspace Home Page/README.md ================================================ ================================================ FILE: Client-Side Components/UI Actions/Go to Agent Workspace Home Page/ui_action_script.js ================================================ function onClick() { top.window.location ='now/workspace/agent/home'; } ================================================ FILE: Client-Side Components/UI Actions/Group Membership Admin Util/README.md ================================================ # Group Membership Utility The **Group Membership Utility** is a ServiceNow server-side tool designed to streamline the management of user membership in assignment groups. It provides administrators with two UI actions on the Assignment Group table to add or remove themselves from a group, ensuring efficient group membership management. Super helpful when doing group membership testing. ## Challenge Managing assignment group memberships manually can be time-consuming and frustrating when doing group change related testings. ## Features - **Add Me**: Adds the current user to the selected assignment group, ensuring quick inclusion. - **Remove Me**: Removes the current user from the selected assignment group, simplifying group updates. - **Admin-Only Visibility**: Both actions are restricted to users with administrative privileges i.e admin user role, ensuring controlled access. ## Functionality The Group Membership Utility provides the following capabilities: - Detects the current user's membership status in the selected group. - Dynamically enables or disables the **Add Me** and **Remove Me** actions based on the user's membership. - Ensures visibility of these actions only for users with administrative privileges. ## Visibility Add below condition script for the "Add Me" UI action ```javascript gs.hasRole('admin') && !gs.getUser().isMemberOf(current.sys_id); ``` Add below condition script for the "Remove Me" UI action ```javascript gs.hasRole('admin') && gs.getUser().isMemberOf(current.sys_id); ``` ## Usage Instructions 1. Navigate to the Assignment Group table. 2. Select a group. 3. Use the **Add Me** button to add yourself to the group if you're not already a member. 4. Use the **Remove Me** button to remove yourself from the group if you're already a member. ================================================ FILE: Client-Side Components/UI Actions/Group Membership Admin Util/addMeUIActionScript.js ================================================ try { var groupSysId = current.sys_id; var userSysId = gs.getUserID(); // Validate input if (!groupSysId || !userSysId) { throw new Error("Group Sys ID and User Sys ID are required."); } // Create a new record in the sys_user_grmember table var gr = new GlideRecord("sys_user_grmember"); gr.initialize(); gr.group = groupSysId; gr.user = userSysId; var sysId = gr.insert(); if (sysId) { gs.addInfoMessage( "User successfully added to the group. Record Sys ID: " + sysId ); } else { throw new Error("Failed to add user to the group."); } } catch (error) { gs.addErrorMessage("Error adding user to group: " + error.message); } ================================================ FILE: Client-Side Components/UI Actions/Group Membership Admin Util/removeMeUIActionScript.js ================================================ try { var groupSysId = current.sys_id; var userSysId = gs.getUserID(); // Validate input if (!groupSysId || !userSysId) { throw new Error("Group Sys ID and User Sys ID are required."); } // Query the sys_user_grmember table to find the record var gr = new GlideRecord("sys_user_grmember"); gr.addQuery("group", groupSysId); gr.addQuery("user", userSysId); gr.query(); if (gr.next()) { // Delete the record var deleted = gr.deleteRecord(); if (deleted) { gs.addInfoMessage("User successfully removed from the group."); } else { throw new Error("Failed to remove user from the group."); } } else { throw new Error("No matching record found for the user in the group."); } } catch (error) { gs.addErrorMessage("Error removing user from group: " + error.message); } ================================================ FILE: Client-Side Components/UI Actions/Group dependency/README.md ================================================ Easily assess where a user group is used across your ServiceNow instance — before you retire, modify, or repurpose it. This solution adds a UI Action to the sys_user_group form that opens a clean, dynamic UI Page showing all the dependencies across modules like Tasks, Script Includes, Business Rules, Workflows, Catalog Items, Reports, and more. Key Features: • One-click access to group dependency insights • Displays usage across 10+ key modules • Modular architecture using UI Page, Script Include, and client-side UI Action • Easily extensible to include custom tables or rules Use Cases: • Pre-deactivation impact checks for groups • Governance and cleanup tasks • Platform documentation and audit support • Extensible framework for users, catalog items, or roles ================================================ FILE: Client-Side Components/UI Actions/Group dependency/uiaction.js ================================================ /* This script should be placed in the UI action on the table sys_user_group form view. This UI action should be marked as client. Use popupDependency() function in the Onclick field. condition - gs.hasRole('admin') */ function popupDependency() { var groupSysId = gel('sys_uniqueValue').value; var gdw = new GlideDialogWindow('display_group_dependency_list'); gdw.setTitle('Group Dependency'); gdw.setPreference('sysparm_group', groupSysId); gdw.render(); } ================================================ FILE: Client-Side Components/UI Actions/Group dependency/uipage.js ================================================ var groupSysId = trim(jelly.sysparm_group); var groupName = ''; var taskRecords = 0; var reportRecords = 0; var workflowRecords = 0; var workflowVersions = ''; var scriptIncludeRecords = 0; var businessRulesRecords = 0; var clientScriptRecords = 0; var maintainItemRecords = 0; var businessServiceRecords=0; var systemPropertiesRecords=0; var avaiforGroupsRecords = 0; var grACLRecords = 0; var grGroupName = new GlideRecord('sys_user_group'); if (grGroupName.get('sys_id', groupSysId)) { groupName = grGroupName.name; } // TASK var grTask = new GlideRecord("task"); grTask.addEncodedQuery('sys_created_onRELATIVEGE@month@ago@9^assignment_group.sys_id='+groupSysId+'^active=true^GROUPBYsys_class_name^ORDERBYsys_created_on'); grTask.query(); taskRecords = grTask.getRowCount(); // REPORTS var grReport = new GlideRecord("sys_report"); grReport.addEncodedQuery('filterLIKE'+groupSysId+'^ORfilterLIKEE'+groupName); grReport.query(); reportRecords = grReport.getRowCount(); // WORKFLOWS var grWFActivity = new GlideRecord('wf_activity'); grWFActivity.addQuery('sys_id', 'IN', getWfActivities(groupSysId, groupName)); grWFActivity.addQuery('workflow_version.published', 'true'); grWFActivity.query(); while (grWFActivity.next()) { var grh = new GlideRecord("wf_workflow_version"); grh.addEncodedQuery("sys_id=" + grWFActivity.workflow_version.sys_id); grh.query(); while (grh.next()) { workflowVersions += grWFActivity.workflow_version.sys_id + ','; } } workflowRecords = getCount(workflowVersions); if(workflowRecords == 1){ var sci = sciWorkflow(grh.workflow.sys_id.toString()); if(sci){ workflowRecords =0; workflowVersions =''; } } function sciWorkflow(workflow){ var gr12 = new GlideRecord("sc_cat_item"); gr12.addEncodedQuery("active=false^workflow="+workflow); gr12.query(); if (gr12.next()) { return true; } } function getCount(sysid){ var gr = new GlideRecord('wf_workflow_version'); gr.addQuery('sys_id','IN',sysid); gr.query(); return gr.getRowCount(); } function getWfActivities(group_id, group_name) { var grVariables = new GlideRecord('sys_variable_value'); grVariables.addEncodedQuery('valueLIKE'+group_name+'^ORvalueLIKE'+group_id+'^document=wf_activity'); grVariables.query(); var results = []; while (grVariables.next()) { results.push(grVariables.document_key + ''); } return results; } // SCRIPT INCLUDES var grScriptInclude = new GlideRecord("sys_script_include"); grScriptInclude.addEncodedQuery('scriptLIKE'+groupSysId+'^ORscriptLIKE'+groupName+'^active=true'); grScriptInclude.query(); scriptIncludeRecords = grScriptInclude.getRowCount(); // BUSINESS RULES var grBusinessRules = new GlideRecord("sys_script"); grBusinessRules.addEncodedQuery('active=true^scriptLIKE'+groupName+'^ORscriptLIKE'+groupSysId); grBusinessRules.query(); businessRulesRecords = grBusinessRules.getRowCount(); // CLIENT SCRIPT var grClientScript = new GlideRecord("sys_script_client"); grClientScript.addEncodedQuery('sys_class_name=sys_script_client^active=true^scriptLIKE'+groupName+'^ORscriptLIKE'+groupSysId); grClientScript.query(); clientScriptRecords = grClientScript.getRowCount(); // MAINTAIN ITEMS (CATALOG ITEMS) var grMaintainItems = new GlideRecord("sc_cat_item"); grMaintainItems.addEncodedQuery('u_approval_group_1='+groupSysId+'^ORu_approval_group_2='+groupSysId+'^ORgroup='+groupSysId+'^ORu_fulfillment_group_2='+groupSysId+'^active=true'); grMaintainItems.query(); maintainItemRecords = grMaintainItems.getRowCount(); //CMDB CI's var grBusinessServices = new GlideRecord('cmdb_ci'); grBusinessServices.addEncodedQuery('install_status!=7^change_control='+groupSysId+'^ORsupport_group='+groupSysId) grBusinessServices.query(); businessServiceRecords= grBusinessServices.getRowCount(); //System Properties var grsysProperties = new GlideRecord('sys_properties'); grsysProperties.addEncodedQuery('valueLIKE'+groupSysId); grsysProperties.query(); systemPropertiesRecords = grsysProperties.getRowCount(); //Available for Groups var grAvaiForGroups = new GlideRecord("sc_cat_item_group_mtom"); grAvaiForGroups.addEncodedQuery('sc_cat_item.active=true^sc_avail_group='+ groupSysId); grAvaiForGroups.query(); avaiforGroupsRecords = grAvaiForGroups.getRowCount(); //Available for Notifications var grAvaiForNotifications = new GlideRecord("sysevent_email_action"); grAvaiForNotifications.addEncodedQuery('active=true^conditionLIKE'+ groupSysId +'^ORrecipient_groupsLIKE'+ groupSysId + '^ORadvanced_conditionLIKE'+ groupSysId); grAvaiForNotifications.query(); NotificationRecords = grAvaiForNotifications.getRowCount(); //ACL for Groups var grACL = new GlideRecord("sys_security_acl"); grACL.addEncodedQuery('scriptLIKE' + groupName + '^ORscriptLIKE' + groupSysId + '^ORconditionLIKE' + groupName + '^ORconditionLIKE' + groupSysId + '^active=true'); grACL.query(); grACLRecords = grACL.getRowCount();
Module Records Details
Workflows ${workflowRecords} View records
timer sys_id next action time left kill?
${jvar_timer.sys_id} ${jvar_timer.waitUntil} ${jvar_timer.timeLeft}
//Client script: // handler for clicking ok on modal. gathers the sys_ids for the timers that have checked checkbox function okDialog() { var c = gel('sysids'); var sysids = []; $j('input[type="checkbox"]:checked').each(function () { var checkboxId = $j(this).attr('id').replace("ni.", ""); sysids.push(checkboxId); }); c.value = sysids.toString(); return true; } //Processing script: // queries for timer jobs and sets the job and new flow.fire event to process instantly -> timer on flow completes var waitJob = new GlideRecord("sys_trigger"); waitJob.addQuery("sys_id", "IN", sysids); waitJob.query(); while (waitJob.next()) { var currentScript = waitJob.getValue("script"); var now = new GlideDateTime().getValue(); var replaceScript = currentScript.replace(/gr\.setValue\('process_on',\s*'[^']*'\)/, "gr.setValue('process_on','" + now + "')"); waitJob.setValue("script", replaceScript); waitJob.setValue("next_action", now); waitJob.update(); } //redirect back to bottom of nav stack var urlOnStack = GlideSession.get().getStack().bottom(); response.sendRedirect(urlOnStack); ================================================ FILE: Client-Side Components/UI Actions/Knowledge Link Validator/Readme.md ================================================ This utility script helps ServiceNow administrators and content managers ensure the integrity and usability of hyperlinks embedded within knowledge articles. It scans article content to identify and classify links pointing to catalog items and other knowledge articles, providing detailed insights into: Catalog Item Links: Detects and categorizes links as active, inactive, or not found. Knowledge Article Links: Flags outdated articles based on workflow state and expiration (valid_to). Non-Permalink KB Links: Identifies knowledge article links that do not follow the recommended permalink format (i.e., missing sysparm_article=KBxxxxxxx), even if they use kb_view.do. The solution includes a Jelly-based UI that displays categorized results with direct links to the affected records, enabling quick remediation. It's ideal for improving content quality, ensuring consistent user experience, and maintaining best practices in knowledge management. image ================================================ FILE: Client-Side Components/UI Actions/Knowledge Link Validator/uiaction.js ================================================ /* This script should be placed in the UI action on the table kb_knowledge form view. This UI action should be marked as client. Use validateLinksInArticle() function in the Onclick field. */ function validateLinksInArticle() { var articleSysId = g_form.getUniqueValue(); var gdw = new GlideDialogWindow('validate_links_dialog'); gdw.setTitle('Validate Article Links'); gdw.setPreference('sysparm_article_id', articleSysId); gdw.render(); } ================================================ FILE: Client-Side Components/UI Actions/Knowledge Link Validator/uipage.js ================================================ tags var regex = /]+href=["']([^"']+)["']/gi; var urls = []; var match; while ((match = regex.exec(content)) !== null) { urls.push(match[1]); } for (var i = 0; i < urls.length; i++) { var url = urls[i]; // --- 1. Check if link is a Catalog Item --- var sysId = extractSysId(url, 'sysparm_id') || extractSysId(url, 'sys_id'); if (sysId) { var grItem = new GlideRecord('sc_cat_item'); if (grItem.get(sysId)) { if (grItem.active){ activeIds.push(sysId); activeCount++; } else if(grItem.active == false){ inactiveIds.push(sysId); inActiveCount++; } } else { notFoundIds.push(sysId); notFoundCount++; } } // --- 2. Check if link is a Knowledge Article --- // --- 1. Check for outdated knowledge articles via permalink --- // --- 1. Check for outdated knowledge articles via permalink --- var decodedUrl = decodeURIComponent(url + ''); decodedUrl = decodedUrl.replace(/&amp;amp;amp;/g, '&'); // Extract KB number or sys_id var kbNumber = extractSysId(decodedUrl, 'sysparm_article'); var kbSysId = extractSysId(decodedUrl, 'sys_kb_id') || extractSysId(decodedUrl, 'sys_id'); var grKb = new GlideRecord('kb_knowledge'); if (kbNumber && grKb.get('number', kbNumber)) { var isOutdated = false; if (grKb.workflow_state != 'published') { isOutdated = true; } else if (grKb.valid_to && grKb.valid_to.getGlideObject()) { var now = new GlideDateTime(); if (grKb.valid_to.getGlideObject().compareTo(now) <= 0) { isOutdated = true; } } if (isOutdated) { outdatedArticles.push(grKb.sys_id.toString()); outdatedCount++; } } else if (kbSysId && grKb.get(kbSysId)) { var isOutdated = false; if (grKb.workflow_state != 'published') { isOutdated = true; } else if (grKb.valid_to && grKb.valid_to.getGlideObject()) { var now = new GlideDateTime(); if (grKb.valid_to.getGlideObject().compareTo(now) <= 0) { isOutdated = true; } } if (isOutdated) { outdatedArticles.push(grKb.sys_id.toString()); outdatedCount++; } } // --- 2. Check for non-permalink knowledge links --- if ( decodedUrl.indexOf('kb_knowledge.do?sys_id=') !== -1 || // form view ( decodedUrl.indexOf('/kb_view.do') !== -1 && decodedUrl.indexOf('sysparm_article=KB') === -1 // missing KB number ) ) { var kbSysId = extractSysId(decodedUrl, 'sys_kb_id') || extractSysId(decodedUrl, 'sys_id'); if (kbSysId) { var grBadKB = new GlideRecord('kb_knowledge'); if (grBadKB.get(kbSysId)) { badPermalinks.push(kbSysId); badPermalinkCount++; } } } } } } function extractSysId(url, param) { try { var decoded = decodeURIComponent(url + ''); decoded = decoded .replace(/&amp;amp;/g, '&') .replace(/&amp;/g, '&') .replace(/&/g, '&') .replace(/=/g, '=') .replace(/&#61;/g, '='); var parts = decoded.split(param + '='); if (parts.length > 1) { var id = parts[1].split('&')[0]; return id && id.length === 32 ? id : null; } } catch (e) { var parts = url.split(param + '='); if (parts.length > 1) { var id = parts[1].split('&')[0]; return id && id.length === 32 ? id : null; } } return null; } // Expose variables to Jelly inactiveQuery = "sys_idIN"+inactiveIds.join(','); activeQuery = "sys_idIN"+activeIds.join(','); notFoundQuery = "sys_idIN"+notFoundIds.join(','); outdatedQuery = "sys_idIN"+outdatedArticles.join(','); badPermalinkQuery = "sys_idIN"+badPermalinks.join(','); ]]>
Module Records Details
================================================ FILE: Client-Side Components/UI Actions/Mark Records Inactive - List Action/README.md ================================================ Above two scripts will help you to select records in list view and mark them inactive. You can create your UI action(list action) on any table and then you should be able to mark the records as inactive by calling the reusable script include. Process is pretty simple as shown below: 1. Create a List action - list banner button or list choice. 2. Check the client checkbox. Use the script and do any necessary modifications. 3. Keep your script include ready with the function to make records inactive and done. You can even modify the scrip include to change other fields too based on your requirements. And you do not need to pass any table name also. This is complete generic. **UPDATE:** _Replaced standard Javascript Window method 'alert' with GlideModal as per issue #745. This completes the task 'Mark Records Inactive UI Action'_ ================================================ FILE: Client-Side Components/UI Actions/Mark Records Inactive - List Action/listAction.js ================================================ // Create a List Action. Make it as List Banner Button or List choice. Keep the client checkbox checked and the use the below script. var selRecords; function markInactive() { selRecords = g_list.getChecked(); //Get the sysIds of selected records from list view var ga_inactive = new GlideAjax('MarkRecordsInactive'); // call the script include for the same ga_inactive.addParam('sysparm_name', 'markInactiveRecords'); ga_inactive.addParam('sysparm_ids', selRecords); ga_inactive.addParam('sysparm_table', g_list.getTableName()); ga_inactive.getXML(ResponseFunction); function ResponseFunction(response) { var answer = response.responseXML.documentElement.getAttribute("answer"); //alert(answer.toString()); // Commented above code and replaced it with GlideModal var gm = new GlideModal("glide_alert_standard", false, 600); gm.setTitle("Info Message"); gm.setPreference("title", answer.toString()); gm.setPreference("warning", "false"); gm.render(); } } ================================================ FILE: Client-Side Components/UI Actions/Mark Records Inactive - List Action/scriptInclude.js ================================================ var MarkRecordsInactive = Class.create(); MarkRecordsInactive.prototype = Object.extendsObject(AbstractAjaxProcessor, { markInactiveRecords: function() { var sysIds = this.getParameter('sysparm_ids'); var tableName = this.getParameter('sysparm_table'); var recs = new GlideRecordSecure(tableName); recs.addEncodedQuery('sys_idIN'+sysIds); recs.query(); while(recs.next()) { recs.active = false; recs.update(); } return 'All the selected records are marked Inactive'; }, type: 'MarkRecordsInactive' }); ================================================ FILE: Client-Side Components/UI Actions/Open Email Client using UI Action/README.md ================================================ When an agent needs a button on the form to open Email Client in a single click, This UI action can be used, which works similar to the out of box Email button. Some user may not prefer the out of box button because it is two clicks. To use this UI action, mark the UI action as Client and add the openEmailClient function in the onClick Field in the UI action. Then use the script provided in the script field. ================================================ FILE: Client-Side Components/UI Actions/Open Email Client using UI Action/open_email_client.js ================================================ // This is a Client UI action. In the onClick field, call the function openEmailClient() and use below script in the script section. function openEmailClient(){ emailClientOpenPop(''); } ================================================ FILE: Client-Side Components/UI Actions/Open LIST UI Action/README.md ================================================ This UI Action opens the LIST view for the current table in another tab. UI action will run on Onclick openinLIST() ================================================ FILE: Client-Side Components/UI Actions/Open LIST UI Action/UIActionscript.js ================================================ function openinLIST() { var taskTable = g_form.getTableName(); // Construct the hardcoded LIST URL var listURL = '/' + taskTable + '_list.do?sysparm_clear_stack=true'; // Open in new window var w = getTopWindow(); w.window.open(listURL, '_blank'); } ================================================ FILE: Client-Side Components/UI Actions/Open Record in Alternate Instance/README.md ================================================ This set of: - RESTMessage - Script Includes - Scripted REST API (SRAPI) - UI Action Will allow you to open a record in alternate corresponding instance(s) of ServiceNow. For example, from a record in a PROD instance, you can open the correlating record in your DEV instance. The 'Starting Instance', refers to the instance you are currently in, and the 'Target Instance' refers to the instance you want to open the record in. Any instance of `[Target Instance]` in these files will need to be replaced. - The RESTMessage will need to exist on the 'Starting' Instance - The Script Includes will need to exist on the 'Starting' Instance - The UI Action will need to exist on the 'Starting' Instance - The SRAPI will need to exist on the Target Instance Any file ending in `_config.md` relates to field configuration of the record. ================================================ FILE: Client-Side Components/UI Actions/Open Record in Alternate Instance/RESTMessageV2/sys_rest_message_config.md ================================================ Name: Cross-Instance Record Checking Endpoint: [An Instance Url]/api/[api subpath, i.e., riot]/recordapi/record_exists See `sys_rest_message_fn_config.md` for HTTP Method setup. ================================================ FILE: Client-Side Components/UI Actions/Open Record in Alternate Instance/RESTMessageV2/sys_rest_message_fn_config.md ================================================ Name: [Target Instance] Record Exists HTTP Method: PATCH Endpoint: [Target Instance Url]/api/[api subpath]/.../record_exists HTTP Request Parameters: { "table": "${table}", "sysId": "${sysId}" } ================================================ FILE: Client-Side Components/UI Actions/Open Record in Alternate Instance/Script Includes/README.md ================================================ !! The switch statement in CrossInstanceHelper.js will need to be reconfigured to match instance names, and RESTMessage naming. ================================================ FILE: Client-Side Components/UI Actions/Open Record in Alternate Instance/Script Includes/sys_script_include.js ================================================ var CrossInstanceHelper = Class.create(); CrossInstanceHelper.prototype = { initialize: function() { }, //Defines a filter so that tables that will never exist in other environments are not checked. _goodTable: function(table){ var disallowedTables = [ 'sys_update_set_source', 'sys_update_set', 'sys_email', 'syslog' ]; //If the table is disallowed, return false if(disallowedTables.indexOf(table) != -1) return false; //If the table extends task, or extends a table that extends task, return false return !(new TableUtils('task').getTableExtensions().indexOf(table) != -1 || new TableUtils(table).getAbsoluteBase() == 'task'); }, exists: function(instance, table, sys_id){ //If the table is disallowed, return if(!_goodTable(table)) return false; //Determine endpoint from instance var endpoint; instance = instance.toLowerCase(); /* These are example instance names and their corresponding endpoints. You will need to add your own instances and endpoints here. */ switch(instance){ case 'prod': endpoint = 'PROD Exists'; break; case 'test': endpoint = 'TEST Exists'; break; case 'dev': endpoint = 'DEV Exists'; break; } //Build the REST Message var r = new sn_ws.RESTMessageV2('Cross-Instance Record Checking', endpoint); r.setStringParameterNoEscape('table', table); r.setStringParameterNoEscape('sysId', sys_id); //Execute the request var response = r.execute(); //Return whether or not the record exists (true if it exists) return(response.getStatusCode().toString() == '200'); }, type: 'CrossInstanceHelper' }; ================================================ FILE: Client-Side Components/UI Actions/Open Record in Alternate Instance/Script Includes/sys_script_include_config.md ================================================ Name: CrossInstanceHelper Client callable: false Script: See `sys_script_include.js` for code. ================================================ FILE: Client-Side Components/UI Actions/Open Record in Alternate Instance/Scripted REST API/sys_ws_definition_config.md ================================================ General: Name: RecordAPI API ID: recordapi Content Negotiation: Supported request formats: application/json,application/xml,text/xml Supported response formats: application/json,application/xml,text/xml ================================================ FILE: Client-Side Components/UI Actions/Open Record in Alternate Instance/Scripted REST API/sys_ws_operation/sys_ws_operation.js ================================================ (function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) { function isNull(obj){ return(obj == '' || obj == undefined || obj == 'undefined' || obj == null || obj == 'null'); } //Get data from request var requestBody = request.body; var requestData = requestBody.data; //Get table name from request var tableName = requestData.table; //Get sys_id from request var sysId = requestData.sysId; //Make sure data exists if(isNull(tableName) || isNull(tableName.toString()) || isNull(sysId) || isNull(sysId.toString())){ response.setStatus(400); return; } //Start lookup var rGr = new GlideRecord(tableName); rGr.get('sys_id', sysId); var recordExists = !rGr.sys_id == ''; response.setBody(recordExists ? "Resource exists." : "Resource does not exist."); response.setStatus(recordExists ? 200 : 404); })(request, response); ================================================ FILE: Client-Side Components/UI Actions/Open Record in Alternate Instance/Scripted REST API/sys_ws_operation/sys_ws_operation_config.md ================================================ General: HTTP Method: PATCH Relative Path: /record_exists Content Negotiation: Request formats: application/json,application/xml,text/xml Response formats: application/json,application/xml,text/xml Script: see `sys_ws_operation.js` for code. ================================================ FILE: Client-Side Components/UI Actions/Open Record in Alternate Instance/UI Action/sys_ui_action.js ================================================ function openInInstance(){ //Since we are in a client action, get the current record utilizing the URL if(!top.location) return; var href = top.location.href.toString(); //Grab record from current URL var recordHref = href.split('.com/')[1]; //Open the record in the new instance window.open('https://[Target Instance].service-now.com/nav_to.do?uri=' + recordHref, '_blank'); } ================================================ FILE: Client-Side Components/UI Actions/Open Record in Alternate Instance/UI Action/sys_ui_action_config.md ================================================ !! If your instances use vanity urls, or otherwise do not end in `.com`, the `split()` in code.js will need to be edited Name: Open Record in [Target Instance] Table: Global [global] Action Name: Open Record in [Target Instance] Client: true List v2 Compatible: true List v3 Compatible: true Isolate script: false Onclick: openInInstance() Condition: gs.getProperty('[Target Instance]') != '[target instance name]' && !current.isNewRecord() && new CrossInstanceHelper().exists('[Target Instance]', current.getTableName(), current.sys_id.toString()) Script: See `sys_ui_action.js` for code. ================================================ FILE: Client-Side Components/UI Actions/Open Record producer from Form Button In Configurable workspace/OpenItem.js ================================================ /* UI Action Client - true action name - open_item show update - true ( As per your requirement) onClick - openItem(); Workspace Form button - true Format for Configurable Workspace - true */ //Workspace client script: function onClick() { var result = g_form.submit('open_item'); if (!result) { return; } result.then(function() { var params = {}; params.sysparm_parent_sys_id = g_form.getUniqueValue(); params.sysparm_shortDescription = g_form.getValue('short_description'); //add params as required, These params can be parsed and used in the record producer. g_service_catalog.openCatalogItem('sc_cat_item', 'recordproducer_sysid_here', params); //Use the record producer sys_id in second parameter. }); } ================================================ FILE: Client-Side Components/UI Actions/Open Record producer from Form Button In Configurable workspace/ParseUrl.js ================================================ /* Catalog Client script on the record producer/catalog item you want to open from ui action Name - ParseURL Type - Onload Applies on catalog item view - true */ function onLoad() { g_form.setValue('task',parseURL('sysparm_parent_sys_id')); g_form.setValue('description',parseURL('sysparm_description)); function parseURL(paramName) { var url = decodeURIComponent(top.location.href); //Get the URL and decode it var workspaceParams = url.split('extra-params/')[1]; //Split off the url on Extra params var allParams = workspaceParams.split('/'); //The params are split on slashes '/' //Search for the parameter requested for (var i=0; i< allParams.length; i++) { if(allParams[i] == paramName) { return allParams[i+1]; } } } } /* pass the parameter name which was used in the ui action to parse the value here. Example - parseURL('sysparm_description) as I am passing description value in the params from ui action. */ ================================================ FILE: Client-Side Components/UI Actions/Open Record producer from Form Button In Configurable workspace/README.md ================================================ When we want to open a catalog item with details from current record and map it to the opened catalog form then we can use this code. This UI Action and catalog client script Redirects you to the record producer or catalog item( based on the sys id provided) and auto-populates the fields from the parent record to the catalog item/record producer variables. 1. UI Action Client - true action name - open_item show update - true ( As per your requirement) onClick - openItem(); Workspace Form button - true Format for Configurable Workspace - true 2. Catalog Client script. Type - Onload Applies on catalog item view - true Name - ParseURL Note : Above UI Action works in Configurable workspace and opens the catalog item/record producer in workspace itself. ================================================ FILE: Client-Side Components/UI Actions/Open a new blank form/README.md ================================================ UI Action - This script is server side script add it to the UI action to create a new blank form. When clicked on it will create a New Blank form and will redirect user to this newly created blank form. incident.do is used as URL paramenter will open new blank form for incident , we can change this to any other table ( for example - problem.do will open new blank problem form) action.setRedirectURL() is a method used in server-side scripting within UI Actions to redirect users to a specific URL after a UI action is performed. It is commonly used to navigate users to different records, forms, or list views after they have completed an action. Syntax - action.setRedirectURL(URL); Parameters: URL: The URL to which the user will be redirected. This can be a string representing a GlideURL object or a hardcoded URL. It must point to a valid ServiceNow page (record, list, form, etc.). Return: None. It performs a redirection after the script completes. GlideURL is a class in ServiceNow used for constructing URLs dynamically in server-side scripts. It allows developers to programmatically create and manipulate URLs to redirect users, perform navigation, or link to specific ServiceNow resources (e.g., forms, lists, reports). ================================================ FILE: Client-Side Components/UI Actions/Open a new blank form/script.js ================================================ //Create New blank incident form //Server side Script var newFormURL = new GlideURL('incident.do'); newFormURL.addParam('sys_id', '-1'); // Open a new blank form action.setRedirectURL(newFormURL.toString()); ================================================ FILE: Client-Side Components/UI Actions/Open in Service Operations Workspace/README.md ================================================ # Open in Service Operation Workspace Create UI Action with: Table: `Task` Onclick: `openInServiceOperationWorkspace()` Will open any task record in SOW ================================================ FILE: Client-Side Components/UI Actions/Open in Service Operations Workspace/ui_action_script.js ================================================ function openInServiceOperationWorkspace() { var taskSysID = g_form.getUniqueValue(); var taskTable = g_form.getTableName(); // Construct the hardcoded Service Operation Workspace URL var workspaceURL = '/now/sow/record/' + taskTable + '/' + taskSysID; // Open in new window var w = getTopWindow(); w.window.open(workspaceURL, '_blank'); } ================================================ FILE: Client-Side Components/UI Actions/Populate Due Date based on Priority/Readme.md ================================================ **Calculate the due date based on the Priority** Script Type: UI Action, Table: incident, Form button: True, Show update: True, Condition: (current.due_date == '' && current.priority != '5'), OnClick: functionName() Script Type: Script Include, Glide AJAX enabled: False Schedule- Name: Holidays, Time Zone: GMT Schedule Entry - Name: New Year's Day, Type: Exclude, Show as: Busy, When: 31-12-2024, To: 01-01-2025 Schedule Entry - Name: Christmas Day, Type: Exclude, Show as: Busy, When: 24-12-2025, To: 25-12-2025 Schedule Entry - Name: Thanksgiving Day, Type: Exclude, Show as: Busy, When: 26-11-2025, To: 27-11-2025 Schedule Entry - Name: Diwali, Type: Exclude, Show as: Busy, When: 19-10-2025, To: 20-10-2025 **Goal:** To Calculate Due-Date based on Priority with some conditions. **Walk through of code:** So in this use case the UI Action is been used and then Script Include for server calculate is used.So the main to calculate the due-date by the user trigger. UI Action- So this button will check the priority and check the due date field is empty or not if not then will fetch the value of "Priority" and "Created date" and pass the data to the Script Include for calculation once it gets the response will populate the value to the due_date field in the incident table and then update it. Script Include- The role of this is to get the "Priority" and "Created date" based on prioriy this will calculate the time and date by using th GlidDateTime API and the will do some additional changes based on each priorit which is mentioned below and then return the response back to the UI Action, Schedule & Schedule Entry- It is used for the P3 and P4 Priority which is mentioned below for the use case.To exclude the Holidays. These are the use case which the above functionality works, **1-> P1** - add 4hrs to the Created date **2-> P2 **- add 4hrs to the Created date but if it's exceed the working hrs of of 5 PM the add to the next day or if the is before the working hours of 8 AM set 5 PM to the same Created date. **3-> P3 or P4** - Kind of low priority so add the due date to the next day but it should exclude the holidays and the weekend's and the populate the next business working day. **4-> P5 **- User manually will populate the due date based on the process. The UI Action on the Incident Form Button UI Action which will call the Script Include UI Action Script Include SI Schedules and Schedule Entry Schedules Schedule Entry Output Priority 1 Priority 2 Priority 4 ================================================ FILE: Client-Side Components/UI Actions/Populate Due Date based on Priority/ScriptInclude.js ================================================ /* Input 1. Created Date 2. Priority Output 1. Due Date Based on Priority equivalent due dates P1 - add 4hrs to the Created date P2 - add 4hrs to the Created date but if it's exceed the working hrs of of 5 PM the add to the next day or if the is before the working hours of 8 AM set 5 PM to the same Created date. P3 or P4 - Kind of low priority so add the due date to the next day but it should exclude the holidays and the weekend's and the populate the next business working day. */ // This SI findDueDate() function will help to calculate the duration based on the each priority. var CalculateDueDates = Class.create(); CalculateDueDates.prototype = { initialize: function() {}, findDueDate: function(priority, created) { var dueDateVal; // For the Priority 1 and adding 4 hours in reagrd less of 8-5 working hours and then holidays if (priority == 1) { var now = new GlideDateTime(created); now.addSeconds(60 * 60 * 4); // Add 4 hours dueDateVal = now; return dueDateVal; } // For the Priority 2 and adding the 4 hours if exceed the workin hours then add the next day before 5'o Clock else if (priority == 2) { var dueDate = new GlideDateTime(created); dueDate.addSeconds(60 * 60 * 4); // Add 4 hours dueDate = dueDate+''; var hours = Number((dueDate + '').slice(11, 13)); if (hours >= 0 && hours < 12) { gs.addInfoMessage('if Inside 8-5/7'); dueDateVal = dueDate.slice(0, 10) + " 17:00:00"; return dueDateVal; } else if (hours >= 17 && hours <= 23) { var nextDate = new GlideDateTime(created); nextDate.addDaysUTC(1); var newDue = new GlideDateTime(nextDate.getDate().getValue() + " 17:00:00"); dueDateVal = newDue; return dueDateVal; } else { dueDateVal = dueDate; return dueDateVal; } } // For the Priority 3 or 4 add the next day and then if the due date is holiday or weekend populate the next working day in a respective field else if (priority == 3 || priority == 4) { var schedule = new GlideSchedule(); // cmn_schedule for the Holidays var scheduleId = 'bd6d74b2c3fc72104f7371edd40131b7'; schedule.load(scheduleId); var nextDay = new GlideDateTime(created); nextDay.addDaysUTC(1); //Checking for weekends var dayOfWeek = nextDay.getDayOfWeekUTC(); var isWeekend = (dayOfWeek == 6 || dayOfWeek == 7); // Loop until next working day (weekdays excluding holidays) while (schedule.isInSchedule(nextDay) || isWeekend) { nextDay.addDaysUTC(1); dayOfWeek = nextDay.getDayOfWeekUTC(); isWeekend = (dayOfWeek == 6 || dayOfWeek == 7); } // Set to 12:00 PM on that valid day var validDate = new GlideDateTime(nextDay.getDate().getValue() + " 17:00:00"); return validDate; } }, type: 'CalculateDueDates' }; ================================================ FILE: Client-Side Components/UI Actions/Populate Due Date based on Priority/UI Action.js ================================================ /* Table - incident Show Update - True Form Button - True Condition - (current.due_date == '' && current.priority != '5') Input 1. Created Date 2. Priority Validation Will not appeare if the value is already there and the priority is 5 Output 1. Due Date */ // The function duedate is used to pass the priority and then created display value to the script include where the calculate of Due date is done will get the response and the set the value to the due_date field of incident. function duedate() { var priority = current.getValue('priority'); var created = current.getDisplayValue('sys_created_on'); var si = new CalculateDueDates(); var response = si.findDueDate(priority, created); var gdt = new GlideDateTime(); gdt.setDisplayValue(response); current.setValue('due_date', gdt); current.update(); action.setRedirectURL(current); } duedate(); ================================================ FILE: Client-Side Components/UI Actions/Preview context record during approval/README.md ================================================ While approving any request it was very hard until now to preview the record for which the approval was required. This UI action created on sysapproval_approver table will enable previewing the record before approval so that the approver can make an easy informed decision. ================================================ FILE: Client-Side Components/UI Actions/Preview context record during approval/TestScriptInclude.js ================================================ var TestScriptInclude = Class.create(); TestScriptInclude.prototype = Object.extendsObject(AbstractAjaxProcessor, { getDocumentClass: function(){ var sysId = this.getParameter("sysparm_sys_id"); var gr = new GlideRecord("sysapproval_approver"); if(gr.get(sysId)){ return JSON.stringify({ "table": gr.source_table.toString(), "document": gr.document_id.toString(), "title": gr.document_id.getDisplayValue() }); } }, type: 'TestScriptInclude' }); ================================================ FILE: Client-Side Components/UI Actions/Preview context record during approval/UI_Action.js ================================================ /* This script should be placed in the UI action on the table sysapproval_approver. This UI action should be marked as client callable script include. Use openContextRecord() function in the Onclick field. */ function openContextRecord() { var rec = g_form.getDisplayValue("document_id"); var gaSi = new GlideAjax("TestScriptInclude"); gaSi.addParam("sysparm_name", "getDocumentClass"); gaSi.addParam("sysparm_sys_id", g_form.getUniqueValue()); gaSi.getXMLAnswer(function(response) { var answer = JSON.parse(response); var gp = new GlideModalForm(answer.title, answer.table, function(){}); gp.addParm('sys_id', answer.document); gp.render(); }); } ================================================ FILE: Client-Side Components/UI Actions/Printer Friendly Version/README.md ================================================ ## Overview This code snippet UI Action will allow you to have a printer friendly version of whatever record you might are trying to print. This UI action uses the GlideNavigation API which you can find here [GlideNavigation API](https://developer.servicenow.com/dev.do#!/reference/api/zurich/client/c_GlideNavigationV3API#r_GNV3-openPopup_S_S_S_B) ================================================ FILE: Client-Side Components/UI Actions/Printer Friendly Version/printer_friendly_verison.js ================================================ printView() function printView() { var table = g_form.getTableName(); var recordID = g_form.getUniqueValue(); var view = {{Insert the view you want to print here}}; //You can pass in an empty string and it will still work var windowName = {{Insert the name you want your window to display}}; //You can pass in an empty string and it will still work var features = 'resizeable,scrollbar'; //You can pass in an empty string and it will still work var urlString = '/' + table + ".do?sys_id=" + recordID + "&sysparm_view=" + view + "&sysparm_media=print"; var noStack = true; //Flag that indicates whether to append sysparm_stack=no to the URL g_navigation.openPopup(urlString, windowName, features, noStack); } ================================================ FILE: Client-Side Components/UI Actions/Publish a Retired Knowledge Article again/README.md ================================================ **UI Action**: Publish a Retired Knowledge Article again after it is already retired. **How it works:** 1. The code finds existing articles in the Knowledge base that share the same Article ID but are not the current article and are 'retired'. 2. It updates the workflow state of these found articles to 'outdated'. 3. For the current article, it sets the workflow_state to 'pubblished', records the current date and updates the 'published' field with the date. 4. It also removes the pre-existing 'retired' date from the 'retired' field. 5. It redirects the user to the updated current record. ================================================ FILE: Client-Side Components/UI Actions/Publish a Retired Knowledge Article again/publishretiredkb.js ================================================ var kbArticle = new GlideRecord('kb_knowledge'); kbArticle.setWorkflow(false); kbArticle.addQuery('article_id', current.article_id); kbArticle.addQuery('sys_id', "!=", current.sys_id); //articles that are not the current one kbArticle.addQuery('workflow_state', 'retired'); kbArticle.query(); while (kbArticle.next()) { kbArticle.workflow_state = 'outdated'; //setting the articles as outdated kbArticle.update(); } current.workflow_state = 'published'; //publishing retired kb article again current.published = new GlideDate(); current.retired = ""; //clearing retired field value current.update(); action.setRedirectURL(current); ================================================ FILE: Client-Side Components/UI Actions/Select Random User From Group/README.md ================================================ # Random User Assignment This function allows you to select a random user from a specified group. Use case could be on all task-like records that need to be worked on by someone and you want to select the person randomly. ================================================ FILE: Client-Side Components/UI Actions/Select Random User From Group/script.js ================================================ /** * Get a random user from a specific user group * * @param {string} group - The sys_id of the group to select a user from * @returns {string|null} - The sys_id of a randomly selected user from the specified group or null if no user is found */ function getRandomUserFromGroup(group) { var users = []; var grMember = new GlideRecord("sys_user_grmember"); grMember.addNotNullQuery("user"); grMember.addQuery("group", group); grMember.query(); while (grMember.next()) { users.push(grMember.getValue("user")); } if (users.length > 0) { // Select a random user from the "users" array return users[Math.floor(Math.random() * users.length)]; } else { // Return null if no user is found in the specified group return null; } } ================================================ FILE: Client-Side Components/UI Actions/Send notification if the incident remains unassigned/README.md ================================================ This is a UI Action script that adds a button to the Incident form. When clicked, it will check if the incident has been unassigned for more than 5 days. If this condition is met, the button will trigger a notification to the manager of the incident's assignment group, informing them that the incident is still unassigned. Below are the conditions when UI action will be created: Table: Incident Show Insert: False Show Update: True Form Button: Checked (to add the button on the form) Condition: current.assigned_to.nil() && current.assignment_group The code contains below vaidations: - The script checks if the incident has been unassigned for more than 5 days by comparing the current date with the sys_created_on date using gs.daysAgo(). - It verifies that the incident has an assignment group. If it does not, it displays an error message. - Queries the sys_user_group table to get the assignment group’s manager. If a manager is found, it sets up a notification to send an email directly to the manager. - Provides feedback to the user if the notification was sent or if there were issues (like missing assignment group or manager). - Redirects the user back to the current incident form after the UI Action runs. ================================================ FILE: Client-Side Components/UI Actions/Send notification if the incident remains unassigned/script.js ================================================ // Check if the incident has been unassigned for more than 5 days var unassignedDuration = gs.daysAgo(current.sys_created_on); if (unassignedDuration < 5) { gs.addErrorMessage("The incident has been unassigned for less than 5 days."); action.setRedirectURL(current); return; } // Check if the incident has an assignment group if (current.assignment_group.nil()) { gs.addErrorMessage("No assignment group is set for this incident."); action.setRedirectURL(current); return; } // Get the assignment group's manager var assignmentGroup = new GlideRecord('sys_user_group'); if (assignmentGroup.get(current.assignment_group)) { var manager = assignmentGroup.getValue('manager'); if (manager) { // Create a notification var notification = new GlideEmailOutbound(); notification.setFrom('no-reply@xyz.com'); notification.setSubject("Alert! Incident " + current.number + " is still unassigned"); notification.setBody("The incident " + current.number + " has been unassigned for more than 5 days. Please assign it promptly."); notification.setTo(manager); // Send the email notification.send(); gs.addInfoMessage("Notification sent to the assignment group's manager."); } else { gs.addErrorMessage("The assignment group has no manager defined."); } } else { gs.addErrorMessage("Could not find the assignment group."); } action.setRedirectURL(current); ================================================ FILE: Client-Side Components/UI Actions/Send notification to the assigned user/README.md ================================================ Please note - Users should endeavor to use Flow, Notifications, etc., but UI Action option is also available. ------------------------------------------------------------------------------------------------------------------------------------------- A UI Action in ServiceNow is a script that defines an action or button within the platform's user interface. It enables users to perform specific operations on forms and lists, such as creating, updating, or deleting records, or executing custom scripts. UI Actions enhance the user experience by providing functional buttons, links, or context menus. In this case, the UI Action contains a server-side script that creates a record in the sys_email table, including the email body, subject, and recipient details. When the button on the incident form is clicked, an email notification will be sent to the user to whom the incident is assigned. -> If the incident is not assigned to any user, a message will be shown, and no email will be sent. -> If the assigned user's email address is missing, the email will not be sent, and an appropriate message will be displayed. -> However, if the incident is assigned to a user with a valid email address, the UI Action will successfully send the email to the assigned user. ================================================ FILE: Client-Side Components/UI Actions/Send notification to the assigned user/script.js ================================================ //Servver Side Script to send notification to the assigned to user var assignedToEmail = current.getValue('assigned_to'); // Fetches the sys_id of the assigned_to field if (assignedToEmail) { var userGR = new GlideRecord('sys_user'); // Access the sys_user table var txt_email = ""; if (userGR.get(assignedToEmail)) { txt_email = userGR.getValue('email'); // Retrieves the email field from the sys_user record } if (txt_email) { var gr_sys_email = new GlideRecord('sys_email'); gr_sys_email.initialize(); gr_sys_email.setValue('type', 'send-ready'); gr_sys_email.setValue('subject', 'UI Action Notification from Incident ' + current.number); gr_sys_email.setValue('recipients', txt_email); gr_sys_email.setValue('body', 'As the incident ' + current.number + ' is assigned to you, this UI Action Notification has been sent. Please review the incident.'); gr_sys_email.insert(); } else { gs.addInfoMessage("The email address for the user " + userGR.getValue('name') + " is missing. As a result, the email could not be sent."); } } else { gs.addInfoMessage("The incident " + current.number + " has not been assigned to any user. Therefore, the email could not be sent."); } ================================================ FILE: Client-Side Components/UI Actions/Set Incident Priority Critical/README.md ================================================ Set Incident Priority Critical This UI Action sets current incident record as priority as Critical. When clicked Below Conformation message will be displayed to the user as below "Are you sure you want to set priority as Critical?" If user selects cancel nothing will happen. If user selects confirm then a) It sets the Priority of the current Record as 1-Critical by setting Urgency and Impact as 1-High. b) It also sets 'assigned_to' field as 'logedin user'. c) It also upends the description as "Priority is set to Critical by 'logedin User'". ** Please note that it changes field values only on the form that is client side. Unless you submit or update the record, field values will not be updated in the database table records. You can use this as reference to set other field values as well. ================================================ FILE: Client-Side Components/UI Actions/Set Incident Priority Critical/script.js ================================================ // Call this function from OnClick field of UI Action form function ClientSideScript() { var answer = confirm("Are you sure you want to set priority as Critical?"); if (answer == true) { g_form.setValue('assigned_to', g_user.userID); g_form.setValue('impact', 1); g_form.setValue('urgency', 1); g_form.setValue('description', g_form.getValue('description') + "\nPriority is set to Critical by " + g_user.getFullName()); } ================================================ FILE: Client-Side Components/UI Actions/Show Today Emails Logs/README.md ================================================ # Show Today Emails Logs The UI Action simplifies the debugging of ServiceNow notifications by redirecting you from the notification definition to the email log list view with the predefined filter. Setting: - Name: Show Today Emails Logs - Table: sysevent_email_action - Show insert: false - Show update: true - Client: true - Form link: true - Onclick: openEmailLogList() - Condition: new GlideRecord('sys_email_log').canRead() ================================================ FILE: Client-Side Components/UI Actions/Show Today Emails Logs/script.js ================================================ function openEmailLogList() { var query = ''; query += 'sys_created_onONToday@javascript:gs.beginningOfToday()@javascript:gs.endOfToday()'; query += '^notification=' + g_form.getUniqueValue(); query += '^ORDERBYDESCsys_created_on'; var url = new GlideURL('sys_email_log_list.do'); url.addParam('sysparm_query' , query); g_navigation.open(url.getURL(), '_blank'); } ================================================ FILE: Client-Side Components/UI Actions/Smart Assign to available member/README.md ================================================ **Use-case:** The primary goal of this UI Action is load-balancing. It assigns tasks based on the fewest currently Active tasks assigned to a member in a group. **Example Scenario**: An assignment group has 10 members. Instead of assigning a new task to the whole group or any random member, the user/agent clicks on "Smart Assign" to find the member with the fewest currently Active tasks in the same group and assign the task to them. **UI Action Name:** Smart Assign **Condition**: !current.assignment_group.nil() && current.assigned_to.nil() **How it works:** 1. The code queries the Group members table to find every single user associated with the currently selected assignment group. If someone removes a previous assignement group and then clicks on Smart Assign button, they are shown an error message to choose an Assignment group. 2. There is a loop on the task table. This loop uses GlideAggregate to count how many active records are assigned to a specific user. 3. It tracks the user that has the lowest count of tasks assigned to them and assigns the current task to them. ================================================ FILE: Client-Side Components/UI Actions/Smart Assign to available member/smartAssigntoAvailablemember.js ================================================ var assignedToId = ''; var minOpenTasks = 77777; var targetGroup = current.assignment_group; if (!targetGroup) { gs.addErrorMessage('Please select an Assignment Group first.'); action.setRedirectURL(current); } //Finding all active members in the target group var member = new GlideRecord('sys_user_grmember'); member.addQuery('group', targetGroup); member.query(); while (member.next()) { var userId = member.user.toString(); //CountIng the number of active tasks currently assigned to the member var taskCountGR = new GlideAggregate('task'); taskCountGR.addQuery('assigned_to', userId); taskCountGR.addQuery('active', true); taskCountGR.addAggregate('COUNT'); taskCountGR.query(); var openTasks = 0; if (taskCountGR.next()) { openTasks = taskCountGR.getAggregate('COUNT'); } //Checking if this member has fewer tasks than the current minimum if (openTasks < minOpenTasks) { minOpenTasks = openTasks; assignedToId = userId; } } //Assigning the current record to the chosen user if (assignedToId) { current.assigned_to = assignedToId; current.work_notes = 'Assigned via Smart Assign to the user with the fewest active tasks (' + minOpenTasks + ' open tasks).'; current.update(); gs.addInfoMessage('Incident assigned to ' + current.assigned_to.getDisplayValue() + '.'); } else { gs.addErrorMessage('Could not find an active member in the group to assign the task.'); } action.setRedirectURL(current); ================================================ FILE: Client-Side Components/UI Actions/Test and Debug Scheduled Scripts/README.md ================================================

Test and Debug Scheduled Scripts using the Script Debugger

This UI Action will run the script in the current session, so that it can be run and debugged in the script debugger.

Steps to Add:

1. Create a new UI Action in Global scope.
2. Name = Test and Debug (Modify this as per your preference).
3. Table = "Scheduled Script Execution [sysauto_script]"
4. Form Button = selected
5. Add the given script.
6. New UI Action will be available on Scheduled Scripts in Studio.
7. Use break points in script to debug.

WARNING

This will run the script. So use it wisely. ================================================ FILE: Client-Side Components/UI Actions/Test and Debug Scheduled Scripts/script.js ================================================ try{ var evaluator = new GlideScopedEvaluator(); evaluator.evaluateScript(current, 'script'); action.setRedirectURL(current); } catch(e){ gs.addInfoMessage(e); } ================================================ FILE: Client-Side Components/UI Actions/Try Catalog item in Portal view/README.md ================================================ UI action code which will help developers to test the same catalog item in serviceportal view by Clicking on the action . OOB we have try it which will preview the catalog item in ITIl view but some time we may miss few configurations which needs to bes tested in service portal. While doping testng its take to test the each item in serviceportal uisng this UI action we can test in portal itself. Its always recommended way to test and experience end user view way while performing testing. Note : Please update the "portal " value as per your request. Open any catalog item --> Click on Try It Sp Ui action --> Catalog item redirect to sp view in seperate tab. Create UI action : ================== Open System defination --> UI action --> Create new Name : Try it in SP Table : Catalog Item [sc_cat_item] Order : 100 Action name : try_it_in_sp Client : true Form button : true Onclick : tryitinsp() Condition : current.active == true && current.canWrite()&&new CatalogItemTypeProcessor().canTryIt(current.getRecordClassName()) && !(current.getRecordClassName() == "sc_cat_item_content" && current.content_type == "external") Script : copy and paste the code from TryItInSp.js file ================================================ FILE: Client-Side Components/UI Actions/Try Catalog item in Portal view/TryItInSP.js ================================================ tryitinsp(); function tryitinsp() { var portal = "sp"; //the portal you want to use for testing var page = g_form.getTableName(); var gUrl = new GlideURL(portal); gUrl.addParam("id", page); gUrl.addParam("sys_id", g_form.getUniqueValue()); //add the sys_id of this Catalog Item to render //and then display the Catalog Item in a new tab/window g_navigation.open (gUrl.getURL(), "_blank"); } ================================================ FILE: Client-Side Components/UI Actions/UI Action to Mark Incident as Escalated/README.md ================================================ Description: This UI Action allows users to mark an incident as escalated, indicating that it requires immediate attention. By changing the incident's state to "Escalated," the action not only helps prioritize the incident but also notifies the relevant support group to take prompt action. This functionality is essential in incident management, ensuring that critical issues are addressed without delay. Key Features: Immediate Priority Update: Changes the incident's state to "Escalated" to reflect its urgent status. Notification: Sends an alert to the relevant support team, ensuring they are aware of the escalation. User-Friendly: Provides an easy way for users to escalate incidents directly from the incident form. ================================================ FILE: Client-Side Components/UI Actions/UI Action to Mark Incident as Escalated/Script.js ================================================ // UI Action: Mark as Escalated // Table: incident // Condition: current.state != 7 // Not Closed // Client: false (function executeAction(current) { current.state = 3; // Set to Escalated (assuming 3 represents Escalated state) current.update(); // Update the record // Optionally notify the support group var notification = new GlideRecord('sysevent_email'); notification.initialize(); notification.recipients = 'support_group_email@example.com'; notification.subject = 'Incident ' + current.number + ' has been escalated'; notification.body = 'The incident has been marked as escalated. Please address it promptly.'; notification.insert(); gs.addInfoMessage('Incident ' + current.number + ' has been marked as escalated.'); })(current); ================================================ FILE: Client-Side Components/UI Actions/Variable Ownership/Readme.md ================================================ The Variable Ownerships UI Action is a lightweight admin utility designed to help manage request item (RITM) variables more effectively. With just one click, it provides direct access to the variable values table, allowing administrators to quickly review, update, or remove sensitive data entered by mistake. This eliminates the need to navigate multiple related tables manually, saving time and reducing risk. Key Benefits: • Direct access to RITM variable values • Faster cleanup of sensitive information • Improves data hygiene and admin efficiency • Simple to implement and lightweight ================================================ FILE: Client-Side Components/UI Actions/Variable Ownership/script.js ================================================ /* This script should be placed in the UI action on the table sc_req_item form view. This UI action should be marked as client. Use viewMtom() function in the Onclick field. */ function viewMtom() { var url = 'sc_item_option_mtom_list.do?sysparm_query=request_item=' + g_form.getUniqueValue(); g_navigation.openPopup(url); } ================================================ FILE: Client-Side Components/UI Actions/View in Portal Page/README.md ================================================ code-snippet used in UI Action SCript to view the current record in Service Portal Page using a redirect Example: //to view a KB article in the Service Portal: function goToPortal(){ var url = 'sp?id=kb_article_view&sys_kb_id=' + g_form.getUniqueValue(); g_navigation.openPopup(url); //g_navigation.open(url); return false; } ================================================ FILE: Client-Side Components/UI Actions/View in Portal Page/View in Portal Page.js ================================================ function goToPortal(){ var url = '?='; g_navigation.openPopup(url); return false; } ================================================ FILE: Client-Side Components/UI Macros/Copy To Clipboard/README.md ================================================ # ServiceNow UI Macro: Copy Field to Clipboard This ServiceNow UI Macro allows you to easily copy field values to your clipboard by clicking on the button next to the field. It supports both standard fields and reference fields. ## Usage 1. Install the UI Macro: - Navigate to **System UI** > **UI Macros**. - Create a new UI Macro. - Paste the XML code of the UI Macro into the script field. - Save the UI Macro. 2. Add the UI Macro to a Form: - Navigate to the form where you want to add the copy functionality. - Right-Click on the field you want this UI macro to be attached to. - Add/Modify the following attribute 'ref_contributions=' - (OPTIONAL) Use semicolon to separate UI macros in field attributes like this 'ref_contributions=;'. 3. Copy Field Value: - When viewing a record with the UI Macro added, you'll see a "Copy to Clipboard" icon next to the field. - Click the "Copy to Clipboard" icon to copy the field value to Clipboard. ## Supported Fields - **Standard Fields**: You can use this UI Macro to copy values from standard fields on the form. - **Reference Fields**: This UI Macro also supports copying sys_ids of reference fields. ================================================ FILE: Client-Side Components/UI Macros/Copy To Clipboard/copy_field_to_clipboard.xml ================================================ ================================================ FILE: Client-Side Components/UI Macros/FormBackground/form_background.xml ================================================ ================================================ FILE: Client-Side Components/UI Macros/FormBackground/readme.md ================================================ # ServiceNow Form Background Macro > A lightweight UI Macro to style ServiceNow forms with a custom background and simple element theming (labels, buttons, sections). --- ## Features * Adds a full-cover background image to a form (supports cover, center positioning). * Makes table/form/section backgrounds transparent so the background shows through. * Easy to customize (image path, label styles, button styles, additional CSS selectors). ## Requirements * ServiceNow instance with admin access. * An image to set as background > ⚠️ Note: This macro uses Jelly/CSS that may not work as expected in some Next Experience workspaces or future UI updates. Test in a non-production instance first. ## Installation 1. **Upload the background image** * Navigate to **System UI > Images** and upload your background image (e.g., `formbg.png`). 2. **Create the UI Macro** * Go to **System UI > UI Macros** and create a new macro (e.g., `ui_form_background`). * Copy the example macro content below into the UI Macro. 3. **Create a UI Formatter** * Go to **System UI > Formatters**. Create a new formatter for the target table (for example, `incident` table). * In the *Formatter* field, reference the macro name you created (e.g., `ui_form_background.xml`). 4. **Add the Formatter to the Form Layout** * Open the form layout for the target table (Form Layout / Form Designer) and place the formatter region on the form. * Save and open a record to see the background applied. ## Compatibility * Tested on ServiceNow classic forms (UI16). May require tweaks for Next Experience, Service Portal, or Workspace. * If your instance uses strict Content Security Policy (CSP) or image hosting constraints, host the image in a supported location or adapt the implementation. ## Troubleshooting * If no background appears: * Confirm the image is uploaded and the filename matches. * Ensure the formatter is placed on the form layout and published. * Inspect (browser devtools) to confirm CSS selectors are applied. ## Result image ================================================ FILE: Client-Side Components/UI Macros/JSON Formatter and Viewer/README.md ================================================ This solution provides a significant User Experience (UX) enhancement for fields that store complex data in JSON format (e.g., integration payloads, configuration properties, or setup data). Instead of forcing users (developers, administrators) to read or edit raw, unformatted JSON in a plain text area, this macro adds a "JSON Viewer" button next to the field Name json_formatter_macro Active true Type XML Navigate to **System UI > UI** Macros and create a new record named json_formatter_macro , Use XML Attached File as Script ================================================ FILE: Client-Side Components/UI Macros/JSON Formatter and Viewer/json_formatter.xml ================================================ ================================================ FILE: Client-Side Components/UI Macros/Purchase Order Approval Summarizer/README.md ================================================ With the [Procurement plugin](https://docs.servicenow.com/bundle/rome-it-asset-management/page/product/procurement/concept/c_Procurement.html) activated, Asset managers can track vendor purchase orders for hardware and software assets. This application does not have approvals built-in by default, but that is a common addition. When these approvers are also fulfillers, they will likely be approving from the platform rather than via the portal. The included XML is to be used in a UI Macro to show critical Purchase Order data elements so that the approver can approve directly from the approval record without clicking into the PO. In order to use this script, create a new UI Macro named "approval_summarizer_proc_po" add the full script from the XML file into the XML field. The system will automatically know to now use this macro for Purchase Orders because of the naming convention. You can also create summarizers for other tables in a similar fashion by creating UI Macros start with "approval_summarizer_" and ending with the database table name. Below is an example of the Purchase Order details display on an approval recor. It is not the most beautiful, but follows the formatting from similar approval summarizers. ![Purchase Order Approval](approval_summarizer_proc_po.png) ================================================ FILE: Client-Side Components/UI Macros/Purchase Order Approval Summarizer/approval_summarizer_proc_po.xml ================================================ ${gs.getMessage('Summary of Purchase Order')}:
${proc_po.number.sys_meta.label}: ${proc_po.number.getDisplayValue()}
${proc_po.vendor.sys_meta.label}: ${proc_po.vendor.getDisplayValue()}
${proc_po.ship_to.sys_meta.label}: ${proc_po.ship_to.getDisplayValue()}
${proc_po.due_by.sys_meta.label}: ${proc_po.due_by.getDisplayValue()}
${proc_po.requested_for.sys_meta.label}: ${proc_po.requested_for.getDisplayValue()}
${proc_po.department.sys_meta.label}: ${proc_po.department.getDisplayValue()}
${proc_po.short_description.sys_meta.label}: ${proc_po.short_description.getDisplayValue()}
${proc_po.ship_rate.sys_meta.label}:
${proc_po.total_cost.sys_meta.label}:
${proc_po_labels.number.sys_meta.label} ${proc_po_labels.model.cmdb_model_category.sys_meta.label} ${proc_po_labels.model.sys_meta.label} ${proc_po_labels.cost.sys_meta.label} ${proc_po_labels.ordered_quantity.sys_meta.label} ${proc_po_labels.total_cost.sys_meta.label}
${gs.getMessage('uppercase_view')} ${SP}${SP}${SP} ${proc_po_item.number} ${proc_po_item.model.cmdb_model_category.getDisplayValue()} ${proc_po_item.model.getDisplayValue()} ${proc_po_item.ordered_quantity}
================================================ FILE: Client-Side Components/UI Macros/Show Open Incident of Caller/Readme.md ================================================ Show Open Incident of caller Script Type: UI Macro Goal: In Form view caller can see what are the open incident of that particular caller. Walk through of code: So for this use case a new macro will be added to the caller field, when it is triggered it will open a new popup window where it will show the list of particular caller which are all open incident.So for this a new UImacro have been used in that a new list icon have been rendered from the db_image table and inside that a showopentckts() function this will get the current caller and then add the query to filter out the list of open incident and then open a popup to show the list of that particular caller which are all open(other than Closed and Cancelled). Note: To inherite the UI Macro in that particular field (Caller) we need to add the attribute in the Dictionary Entry = ref_contributions=caller_inc_lists [ref_contributions="name of the macro"] UI Macro UIMacro Dictonary Entry in Attribute section UIMacroDictionary UI Macro in Incident Form near the Caller Field From UIMacro Result: UIMacro Result ================================================ FILE: Client-Side Components/UI Macros/Show Open Incident of Caller/macro.js ================================================ ================================================ FILE: Client-Side Components/UI Macros/Variable Copy Context Options/README.md ================================================ # Add "Copy Variable Name" to Context Menu Adds code to the element_context UI Macro, allowing for admins to be able to right click a variable name and choose "Copy Variable Name" to quickly get the column name to their clipboard ## where to add 1. In the sys_ui_macro (Macros) table, open the record `element_context` 2. Replace the existing `` block with the block in element context.xml ## full XML for element_context example This code is from an out-of-box instance with the necessary code, you can replace the existing element_context xml with this: ```xml ``` ================================================ FILE: Client-Side Components/UI Macros/Variable Copy Context Options/element context.xml ================================================ // Custom "Copy Field Name" gcm.addLine(); gcm.addHref("${JS:gs.getMessage('Copy Field Name')}", "copyToClipboard('${jvar_field_name}');"); gcm.addHref("${JS:gs.getMessage('Copy Field Value')}", "copyToClipboard(g_form.getValue('${jvar_field_name}'));"); gcm.addHref("${JS:gs.getMessage('Copy Field Display Value')}", "copyToClipboard(g_form.getDisplayBox('${jvar_field_name}').value);"); gcm.addLine(); gcm.addHref("${JS:gs.getMessage('Show')}" + " - '" + '${jvar_field_name}' + "'", "showDictionary('" + table + "', '" + id + "');"); count = 1; ================================================ FILE: Client-Side Components/UI Pages/Add Multiple Items to Order Guide/README.md ================================================ **Details** 1. This code will add multiple items in an order guide in single click 2. Order guide rule base creation will be automatic 3. This code will also add variable set to selected catalog items automatically. **How to use** 1. Go to "sc_cat_item" table and select the items to be added in list view. 2. Look for "Add to order guide" in list actions. 3. The list action will give you an option to select order guide and variable set to be added to catalog items **Components** 1. UI Action 2. UI Page 3. Script Include image ================================================ FILE: Client-Side Components/UI Pages/Add Multiple Items to Order Guide/Script Include.js ================================================ var AddtoOG = Class.create(); AddtoOG.prototype = Object.extendsObject(AbstractAjaxProcessor, { addToOrderGuide: function() { var msgArrNotAdded = []; // array to store not added catalog items. var msgArrAdded = []; // array to store added catalog items. var msg = ''; var item = this.getParameter('sysparm_itemList').toString().split(','); var order_guide = this.getParameter('sysparm_og'); var var_set = this.getParameter('sysparm_set'); for (var i = 0; i < item.length; i++) { var itemName = new GlideRecord('sc_cat_item'); itemName.get(item[i]); // get item name var itemBckName = itemName.name.toString().replace(/[^a-zA-Z0-9]/g, "_"); // check if item is present in order guide var checkStatus = new GlideRecord('sc_cat_item_guide_items'); checkStatus.addQuery('guide', order_guide); checkStatus.addQuery('item', item[i]); checkStatus.query(); if (checkStatus.next()) { msgArrNotAdded.push(itemName.name); } else { // Add variable set to all catalog items selected var set = new GlideRecord('io_set_item'); var orderVar = new GlideRecord('item_option_new'); set.initialize(); set.variable_set = var_set; set.sc_cat_item = item[i]; set.order = '200'; // set order as per your requirement set.insert(); // Add checkbox variable in order guide for each catalog item orderVar.initialize(); orderVar.setValue('type', 7); orderVar.setValue('cat_item', order_guide); orderVar.setValue('question_text', itemName.name); orderVar.setValue('name', itemBckName); orderVar.setValue('order', 1200); // set order as per your requirement orderVar.insert(); } // Add rule base to order guide var ruleBase = new GlideRecord('sc_cat_item_guide_items'); ruleBase.initialize(); ruleBase.setValue('item', item[i]); ruleBase.setValue('guide', order_guide); ruleBase.setValue('condition', 'IO:' + orderVar.sys_id + '=true^EQ'); ruleBase.insert(); msgArrAdded.push(itemName.name); } if (msgArrNotAdded.length > 0) { msg = "Not added item are " + msgArrNotAdded + ' Added Items are ' + msgArrAdded; // array of items which are not added } else msg = 'Added Items are ' + msgArrAdded; // array of added items return msg; }, type: 'AddtoOG' }); ================================================ FILE: Client-Side Components/UI Pages/Add Multiple Items to Order Guide/UI Action.js ================================================ /* onClick function name : addToOrderGuide The UI action will prompt UI page to select order guide and variable set. */ function addToOrderGuide() { var items = g_list.getChecked(); var dialog = new GlideDialogWindow("add_to_og"); // UI page name dialog.setTitle("Select Order Guide and Variable Set"); // Prompt title. dialog.setPreference("items", items); dialog.render(); } ================================================ FILE: Client-Side Components/UI Pages/Add Multiple Items to Order Guide/UI Page.js ================================================



//Client Script of UI page function addItems(catItems) { var og = document.getElementById("order_guide").value; var varSet = document.getElementById("var_set").value; var orderG = new GlideAjax('AddtoOG'); orderG.addParam('sysparm_name', 'addToOrderGuide'); orderG.addParam('sysparm_itemList', catItems); orderG.addParam('sysparm_og', og); orderG.addParam('sysparm_set', varSet); orderG.getXML(addOrderGuide); } function addOrderGuide(response) { var answer = response.responseXML.documentElement.getAttribute("answer"); alert(answer); GlideDialogWindow.get().destroy(); } ================================================ FILE: Client-Side Components/UI Pages/BulkUpdate Worknotes/Readme.md ================================================ Bulk Update Worknotes Script Type: UI Action, Table: incident, List banner button: True, Client: True, Show update: True, OnClick: functionName() Script Type: UI Page Category: General Goal: To update the worknotes for multiple tickets in a single view Walk through of code: So in the incident List view we do have a list banner button called "Bulk Updates" so that was the UI Action configured with the script which has used the GlideModel API to call the UI page which is responsible to get the multiple tickets and worknotes value and then update to the respective ticket and store in the worknotes in journal entry. For this the HTML part in the UI page is configured with two fields, one for the multiple list of tickets, and then the worknotes field, and one submit button to save that into the table. And the Processing Script is used to get each ticket number and then check the valid tickets and query the respective table, and then update the worknotes of each respective ticket if it is valid. Otherwise, it won't update. ================================================ FILE: Client-Side Components/UI Pages/BulkUpdate Worknotes/UI Action.js ================================================ // Table: incident, List banner button: True, Client: True, Show update: True, OnClick: bulkupdate() function bulkupdate() { var modalT = new GlideModal("BulkUpdate", false, 1200); modalT.setTitle("Bulk Update Worknotes"); modalT.render(); } ================================================ FILE: Client-Side Components/UI Pages/BulkUpdate Worknotes/UI Page_ClientScript.js ================================================ function bulkupdate() { document.getElementById("spinner-btn").style.display = "block"; document.getElementById("submit-btn").style.display = "none"; } ================================================ FILE: Client-Side Components/UI Pages/BulkUpdate Worknotes/UI Page_HTML.html ================================================
================================================ FILE: Client-Side Components/UI Pages/Edit Last WorkNotes/uipage_clientcode.js ================================================ fetchLastComment(); function closeDialog() { GlideDialogWindow.get().destroy(); return false; } function fetchLastComment() { var dialogWindow = GlideDialogWindow.get(); var incidentSysId = dialogWindow.getPreference('incid'); var glideAjax = new GlideAjax('UpdateINCworkNotes'); glideAjax.addParam('sysparm_name', 'getIncLastWorknotes'); glideAjax.addParam('sysparm_id', incidentSysId); glideAjax.getXMLAnswer(setCommentFieldValue); } function setCommentFieldValue(answer) { var commentField = document.getElementById('commenttext'); if (commentField) { commentField.value = answer || ''; } } function submitComment() { var dialogWindow = GlideDialogWindow.get(); var incidentSysId = dialogWindow.getPreference('incid'); var newCommentText = document.getElementById('commenttext').value; var glideAjax = new GlideAjax('UpdateINCworkNotes'); glideAjax.addParam('sysparm_name', 'updateCommentsLatest'); glideAjax.addParam('sysparm_id', incidentSysId); glideAjax.addParam('sysparm_newcomment', newCommentText); glideAjax.getXMLAnswer(handleSuccessfulSubmit); closeDialog(); } function handleSuccessfulSubmit(answer) { window.location.reload(); } ================================================ FILE: Client-Side Components/UI Pages/Export UI pages to word docx/Export to word.js ================================================ ********** HTML CODE ************

Heading

Heading 1

Heading 2

********* End of HTML ************* ********* Client Script ************ function exportHTML() { var header = "" + "Export HTML to Word Document with JavaScript"; var footer = ""; var sourceHTML = header + document.getElementById("source-html").innerHTML + footer; var source = 'data:application/vnd.ms-word;charset=utf-8,' + encodeURIComponent(sourceHTML); var fileDownload = document.createElement("a"); document.body.appendChild(fileDownload); fileDownload.href = source; fileDownload.download = 'document.doc'; fileDownload.click(); document.body.removeChild(fileDownload); } ********* End of Client Script ************ ================================================ FILE: Client-Side Components/UI Pages/Export UI pages to word docx/README.md ================================================ This code allows you to export the contents of a UI page into a word document Steps 1. Create a body and contents in the html part of the UI Page 2. Create a button and name it as 'Export to word' and call the function. 3. Now, paste the js code in the client script section of the ui page. 4. Click on try it and the contents of the html body will be exported as a word upon clicking on the 'export to word' button. ================================================ FILE: Client-Side Components/UI Pages/Fetch Table(Incident) fields in UI Page/Fetch incident fields.js ================================================ ********** UI Action Code ********** //Client callable : true //OnClick : fetchHTML(); //table : incident function fetchHTML() { var dialog = new GlideDialogWindow("my_page"); dialog.setTitle('Fetch table fields'); dialog.setSize('600', '600'); dialog.setPreference("sysparm_sys_id", g_form.getUniqueValue()); dialog.render(); } ********** End of UI Action code ********** ********** UI Page code ********** // Name of ui page : my_page var gr_inc = new GlideRecord('incident'); gr_inc.addQuery('sys_id', jelly.jvar_sysId); gr_inc.query(); gr_inc;

Fetch Incident fields in UI Page

Caller

The Caller is : ${gr_inc.caller_id.getDisplayValue()}

Incident Short Description

${gr_inc.short_description()}

Incident Description

${gr_inc.description()}

********** End of UI Page code ********** ================================================ FILE: Client-Side Components/UI Pages/Fetch Table(Incident) fields in UI Page/README.md ================================================ Fetch Incident fields(or any table fields) in a UI page via UI Action trigger Steps 1) Create a UI action and create a function. 2) Add the UI action script provided in the Script section. 3) This code helps to render a pop up window of 600x600 dimentions for the UI page and passes the current sys id to UI page. 4) Make sure to add the code in the UI Page : 5) Create a UI Page and add the HTML Code provided. 6) Trigger the UI Action from the Incident form and it should render the ui page with the incident fields data. 7) Add additional static or dynamic data as per the requirement. ================================================ FILE: Client-Side Components/UI Pages/Populate Glide List field/README.md ================================================ //The UI page includes a label prompting users to select an "SN Company," and utilizes the lightweight_glide_list2 macro to generate a dropdown list populated with active companies from the core_company table. The form also features "OK" and "Cancel" buttons in the modal footer, which trigger the onSubmit() and onCancel() functions, respectively, when clicked. This setup facilitates user interaction by enabling the selection of a company, which can be crucial for various processes within the ServiceNow platform.

//This code creates a label for the company selection field, indicating that users should select a company from the list. Company Selection Control: //This line invokes a macro called lightweight_glide_list2, which creates a dropdown or list control for selecting a company. The parameters specify that it should reference the core_company table, only show active companies (where u_active is true), and allow the user to write (make changes). //This section creates a footer for the modal dialog containing "OK" and "Cancel" buttons. The g:dialog_buttons_ok_cancel macro generates these buttons, calling the onSubmit() function to handle submission when the OK button is clicked and the onCancel() function to handle cancellation when the Cancel button is clicked. var splitted = ['0c441abbc6112275000025157c651c89', '820351a1c0a8018b67c73d51c074097c']; var displayText = ['3Com', 'Acer']; //Two arrays are defined: splitted contains unique identifiers (likely Sys IDs), and displayText contains the corresponding display names for each identifier. For example, the first identifier corresponds to "3Com" and the second to "Acer." Loop Through the Arrays: for (var z = 0; z < splitted.length; z++) { //A for loop is initiated to iterate through the elements of the splitted array. The loop runs as long as z is less than the length of the splitted array, ensuring that each item in both arrays is processed. Get the Dropdown Element: var selEl = document.getElementById('select_0comCollector'); //This line retrieves the HTML select element with the ID select_0comCollector. This element is where the new options will be added. Create a New Option Element: var optEl = document.createElement('option'); //A new