From 03fea9db80710db4f5096d4df822f4ad6b7659aa Mon Sep 17 00:00:00 2001 From: Deepak Negi <120473057+dvn-lazywinner@users.noreply.github.com> Date: Fri, 18 Oct 2024 12:32:27 +0530 Subject: [PATCH 01/11] Create findBrokenLinks.js Script to check broken/invalid/blind links in Knowledge articles --- findBrokenLinks.js | 139 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 findBrokenLinks.js diff --git a/findBrokenLinks.js b/findBrokenLinks.js new file mode 100644 index 0000000000..b93dc3458f --- /dev/null +++ b/findBrokenLinks.js @@ -0,0 +1,139 @@ +/* Script to check broken/blind/invalid links in the knowledge articles */ +// Choose start and end indexes +var indexOffset = 16; +var windowSize = 500; + +var startIndex = windowSize * indexOffset; +var endIndex = windowSize * indexOffset + windowSize - 1; + +// Define a new GlideRecord object for the Knowledge Article table +var article = new GlideRecord('kb_knowledge'); +// Add a query to find all published knowledge articles +article.addQuery('workflow_state', 'published'); +article.orderByDesc("number"); + + + +// Apply indexes +article.chooseWindow(startIndex, endIndex); +// Execute the query to find the knowledge articles +article.query(); + +// Iterate through the knowledge articles +var invalidArticles = []; +while (article.next()) { + + // Get the article body. If empty, continue + var body = article.getValue('text'); + if (!body) + continue; + + var arrayUtil = new ArrayUtil(); + var regex = /href=(["'])http(.*?)\1/g; + + // Obtain a list of all unique links found in the article + var links = body.match(regex); + if (!links) + continue; + + links = arrayUtil.unique(links); + + var articleNum = article.getValue('number'); + var articleSys = article.getUniqueValue(); + var articleOwnerSys = article.getValue("u_knowledge_owner"); + var articleOwner = article.getDisplayValue('u_knowledge_owner'); + var invalid = false; + var invalidLinks = []; + + // Validate each link + links.forEach(function(l) { + if (!l) + return; + + l = l.substring(6, l.length - 1); + + // Check if we've already recorded errors for this article. If so, continue + if (checkLinkAlreadyLogged(articleSys, l)) + return; + + if (l.indexOf('sys_kb_id') != -1) { + // Link is another knowledge article, determine if article is outdated + var sysRegex = /sys_kb_id(=|(%3d))([^\s]+)/gi; + var sysId = l.match(sysRegex)[0].substring(10, 42); + + // Check if the referenced knowledge article is unpublished + var unpublished = new GlideRecord("kb_knowledge"); + unpublished.addQuery("sys_id", sysId); + unpublished.addQuery("workflow_state", "!=", "published"); + unpublished.query(); + + // Article is unpublished, log broken link + if (unpublished.next()) { + invalid = true; + var reason = "Contains unpublished knowledge article link"; + if (l.indexOf('sysparm_article') == -1) + reason += " (without KB Article Number)"; + var il = { + "link": l, + "reason": reason + }; + invalidLinks.push(il); + addBrokenLinkRecord(articleSys, articleOwnerSys, l, reason, null); + } + } else { + // Link is to an external site. Send a REST Message and log result + try { + var request = new sn_ws.RESTMessageV2(); + request.setEndpoint(l); + request.setHttpMethod('GET'); + var response = request.execute(); + + var httpStatus = response.getStatusCode(); + + // HTTP Error returned, log result + if (httpStatus != 200) { + invalid = true; + var reason = "External link returns status code " + httpStatus; + var il = { + "link": l, + "reason": reason + }; + invalidLinks.push(il); + addBrokenLinkRecord(articleSys, articleOwnerSys, l, reason, httpStatus); + } + } catch(e) { + // Error occurred while attempting to send a REST Message + // Log a result + addBrokenLinkRecord(articleSys, articleOwnerSys, l, e, null); + } + } + }); + + if (invalid) { + invalidArticles.push({ + number: articleNum, + owner: articleOwner, + links: invalidLinks + }); + } +} + +function checkLinkAlreadyLogged(article, link) { + var gr = new GlideRecord("u_broken_knowledge_links"); + gr.addQuery("u_article", article); + gr.addQuery("u_link", link); + gr.query(); + + return gr.hasNext(); +} + +function addBrokenLinkRecord(article, owner, link, reason, httpError) { + var gr = new GlideRecord("u_broken_knowledge_links"); + gr.initialize(); + gr.u_article = article; + gr.u_owner = owner; + gr.u_link = link; + gr.u_reason = reason; + gr.u_http_error_code = httpError; + gr.insert(); +} From 4e6a78fa77fbd503e95629ae765942e05d36e96e Mon Sep 17 00:00:00 2001 From: Deepak Negi <120473057+dvn-lazywinner@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:51:07 +0530 Subject: [PATCH 02/11] Create CopyBulkIDs.js --- UI Actions/Copy Bulk SysIDs/CopyBulkIDs.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 UI Actions/Copy Bulk SysIDs/CopyBulkIDs.js diff --git a/UI Actions/Copy Bulk SysIDs/CopyBulkIDs.js b/UI Actions/Copy Bulk SysIDs/CopyBulkIDs.js new file mode 100644 index 0000000000..6b8f15dc49 --- /dev/null +++ b/UI Actions/Copy Bulk SysIDs/CopyBulkIDs.js @@ -0,0 +1,16 @@ +/* +//This action will be able to copy the sysids of multiselected records. + +Table - Global +List Choice - True +Client - True +onClick - copySysIDs() + +Result - All the sysids will be copied as comma-separated strings which you can further copy into a system property for validations + +*/ + +function copySysIDs(){ + var sysIds = g_list.getChecked(); + copyToClipboard(sysIds); +} From 4bd7838df8c75801aa10389b7f129692276ea69f Mon Sep 17 00:00:00 2001 From: Deepak Negi <120473057+dvn-lazywinner@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:53:38 +0530 Subject: [PATCH 03/11] Create readme.md Readme file for usage instructions --- UI Actions/Copy Bulk SysIDs/readme.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 UI Actions/Copy Bulk SysIDs/readme.md diff --git a/UI Actions/Copy Bulk SysIDs/readme.md b/UI Actions/Copy Bulk SysIDs/readme.md new file mode 100644 index 0000000000..b84669f492 --- /dev/null +++ b/UI Actions/Copy Bulk SysIDs/readme.md @@ -0,0 +1,19 @@ +Did you ever get any use case where you need to copy SysIDs in bulk from a list view? + +The use case can be: +There is some matrix that you need to validate in your script. +You need to store the sysids in a property. One option is to export the CSV with Sys id field using ?CSV&sysparm_default_export_fields=all method, +then convert in comma separated list. + +![image](https://github.com/user-attachments/assets/90228462-cc67-4a99-b4e0-b1295c46bd67) + +Created this small utility to fasten the process of copying bulk sysids + +1. Navigate to System Definitions > UI Actions > Create New +2. Give the Name of your choice e.g “Copy Bulk SysIDs” +3. Select Table as “Global” so it is available on every list. +4. Tick the Client and List choice field checkbox and call the function in Onclick field +5. Write below code inside the function in Script field. +**var sysIds = g_list.getChecked(); +copyToClipboard(sysIds);** + From ea4e0baa454ad1ba746bb95662dc3b59ca953f64 Mon Sep 17 00:00:00 2001 From: Deepak Negi <120473057+dvn-lazywinner@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:04:49 +0530 Subject: [PATCH 04/11] Create CopyVariableSet.js Create a UI Action to Copy a variable set with the configurations --- .../Copy Variable Set/CopyVariableSet.js | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 UI Actions/Copy Variable Set/CopyVariableSet.js diff --git a/UI Actions/Copy Variable Set/CopyVariableSet.js b/UI Actions/Copy Variable Set/CopyVariableSet.js new file mode 100644 index 0000000000..d866096776 --- /dev/null +++ b/UI Actions/Copy Variable Set/CopyVariableSet.js @@ -0,0 +1,71 @@ +/* Copy a full variable set with the Catalog UI Policies, Actions, Variables, Catalog Client Scripts*/ +//set some new default values + var setName = current.title; +current.title = 'Copy of ' + setName; +current.sys_scope = gs.getCurrentApplicationId(); +current.sys_policy = ""; / / insert a copy of the variable set +var oldid = current.sys_id.toString(); +var newid = current.insert(); + +if (newid) { + var allVars = {}; + 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.addActiveQuery(); + 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.addActiveQuery(); + ccs.query(); + while (ccs.next()) { + 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.addActiveQuery(); + 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); From 946020bd147b7233327138aeb6198fc282db4281 Mon Sep 17 00:00:00 2001 From: Deepak Negi <120473057+dvn-lazywinner@users.noreply.github.com> Date: Wed, 30 Oct 2024 23:26:35 +0530 Subject: [PATCH 05/11] Delete UI Actions/Copy Variable Set directory --- .../Copy Variable Set/CopyVariableSet.js | 71 ------------- UI Actions/Copy Variable Set/readme.md | 14 --- UI Actions/Copy Variable Set/scripts.js | 100 ------------------ 3 files changed, 185 deletions(-) delete mode 100644 UI Actions/Copy Variable Set/CopyVariableSet.js delete mode 100644 UI Actions/Copy Variable Set/readme.md delete mode 100644 UI Actions/Copy Variable Set/scripts.js diff --git a/UI Actions/Copy Variable Set/CopyVariableSet.js b/UI Actions/Copy Variable Set/CopyVariableSet.js deleted file mode 100644 index d866096776..0000000000 --- a/UI Actions/Copy Variable Set/CopyVariableSet.js +++ /dev/null @@ -1,71 +0,0 @@ -/* Copy a full variable set with the Catalog UI Policies, Actions, Variables, Catalog Client Scripts*/ -//set some new default values - var setName = current.title; -current.title = 'Copy of ' + setName; -current.sys_scope = gs.getCurrentApplicationId(); -current.sys_policy = ""; / / insert a copy of the variable set -var oldid = current.sys_id.toString(); -var newid = current.insert(); - -if (newid) { - var allVars = {}; - 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.addActiveQuery(); - 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.addActiveQuery(); - ccs.query(); - while (ccs.next()) { - 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.addActiveQuery(); - 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); diff --git a/UI Actions/Copy Variable Set/readme.md b/UI Actions/Copy Variable Set/readme.md deleted file mode 100644 index b959798309..0000000000 --- a/UI Actions/Copy Variable Set/readme.md +++ /dev/null @@ -1,14 +0,0 @@ -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. diff --git a/UI Actions/Copy Variable Set/scripts.js b/UI Actions/Copy Variable Set/scripts.js deleted file mode 100644 index 4df46456bc..0000000000 --- a/UI Actions/Copy Variable Set/scripts.js +++ /dev/null @@ -1,100 +0,0 @@ -/****************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); From a79b5088d6ddbe1f6936370e4bdcb33ef9b09290 Mon Sep 17 00:00:00 2001 From: Deepak Negi <120473057+dvn-lazywinner@users.noreply.github.com> Date: Wed, 30 Oct 2024 23:27:27 +0530 Subject: [PATCH 06/11] Delete findBrokenLinks.js --- findBrokenLinks.js | 139 --------------------------------------------- 1 file changed, 139 deletions(-) delete mode 100644 findBrokenLinks.js diff --git a/findBrokenLinks.js b/findBrokenLinks.js deleted file mode 100644 index b93dc3458f..0000000000 --- a/findBrokenLinks.js +++ /dev/null @@ -1,139 +0,0 @@ -/* Script to check broken/blind/invalid links in the knowledge articles */ -// Choose start and end indexes -var indexOffset = 16; -var windowSize = 500; - -var startIndex = windowSize * indexOffset; -var endIndex = windowSize * indexOffset + windowSize - 1; - -// Define a new GlideRecord object for the Knowledge Article table -var article = new GlideRecord('kb_knowledge'); -// Add a query to find all published knowledge articles -article.addQuery('workflow_state', 'published'); -article.orderByDesc("number"); - - - -// Apply indexes -article.chooseWindow(startIndex, endIndex); -// Execute the query to find the knowledge articles -article.query(); - -// Iterate through the knowledge articles -var invalidArticles = []; -while (article.next()) { - - // Get the article body. If empty, continue - var body = article.getValue('text'); - if (!body) - continue; - - var arrayUtil = new ArrayUtil(); - var regex = /href=(["'])http(.*?)\1/g; - - // Obtain a list of all unique links found in the article - var links = body.match(regex); - if (!links) - continue; - - links = arrayUtil.unique(links); - - var articleNum = article.getValue('number'); - var articleSys = article.getUniqueValue(); - var articleOwnerSys = article.getValue("u_knowledge_owner"); - var articleOwner = article.getDisplayValue('u_knowledge_owner'); - var invalid = false; - var invalidLinks = []; - - // Validate each link - links.forEach(function(l) { - if (!l) - return; - - l = l.substring(6, l.length - 1); - - // Check if we've already recorded errors for this article. If so, continue - if (checkLinkAlreadyLogged(articleSys, l)) - return; - - if (l.indexOf('sys_kb_id') != -1) { - // Link is another knowledge article, determine if article is outdated - var sysRegex = /sys_kb_id(=|(%3d))([^\s]+)/gi; - var sysId = l.match(sysRegex)[0].substring(10, 42); - - // Check if the referenced knowledge article is unpublished - var unpublished = new GlideRecord("kb_knowledge"); - unpublished.addQuery("sys_id", sysId); - unpublished.addQuery("workflow_state", "!=", "published"); - unpublished.query(); - - // Article is unpublished, log broken link - if (unpublished.next()) { - invalid = true; - var reason = "Contains unpublished knowledge article link"; - if (l.indexOf('sysparm_article') == -1) - reason += " (without KB Article Number)"; - var il = { - "link": l, - "reason": reason - }; - invalidLinks.push(il); - addBrokenLinkRecord(articleSys, articleOwnerSys, l, reason, null); - } - } else { - // Link is to an external site. Send a REST Message and log result - try { - var request = new sn_ws.RESTMessageV2(); - request.setEndpoint(l); - request.setHttpMethod('GET'); - var response = request.execute(); - - var httpStatus = response.getStatusCode(); - - // HTTP Error returned, log result - if (httpStatus != 200) { - invalid = true; - var reason = "External link returns status code " + httpStatus; - var il = { - "link": l, - "reason": reason - }; - invalidLinks.push(il); - addBrokenLinkRecord(articleSys, articleOwnerSys, l, reason, httpStatus); - } - } catch(e) { - // Error occurred while attempting to send a REST Message - // Log a result - addBrokenLinkRecord(articleSys, articleOwnerSys, l, e, null); - } - } - }); - - if (invalid) { - invalidArticles.push({ - number: articleNum, - owner: articleOwner, - links: invalidLinks - }); - } -} - -function checkLinkAlreadyLogged(article, link) { - var gr = new GlideRecord("u_broken_knowledge_links"); - gr.addQuery("u_article", article); - gr.addQuery("u_link", link); - gr.query(); - - return gr.hasNext(); -} - -function addBrokenLinkRecord(article, owner, link, reason, httpError) { - var gr = new GlideRecord("u_broken_knowledge_links"); - gr.initialize(); - gr.u_article = article; - gr.u_owner = owner; - gr.u_link = link; - gr.u_reason = reason; - gr.u_http_error_code = httpError; - gr.insert(); -} From 3de7a0ba8aab0a9a69e017827ddf1a1cb7e3a5d4 Mon Sep 17 00:00:00 2001 From: Deepak Negi <120473057+dvn-lazywinner@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:25:53 +0530 Subject: [PATCH 07/11] Create EscalateIncidents.js Escalate high priority incidents and automate notifications --- GlideRecord/EscalateInicidents/EscalateIncidents.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 GlideRecord/EscalateInicidents/EscalateIncidents.js diff --git a/GlideRecord/EscalateInicidents/EscalateIncidents.js b/GlideRecord/EscalateInicidents/EscalateIncidents.js new file mode 100644 index 0000000000..abfbad0f8c --- /dev/null +++ b/GlideRecord/EscalateInicidents/EscalateIncidents.js @@ -0,0 +1,13 @@ +// Hours - set hours in a system property, and call below function to escalate +// Support it with the events and notifications + +function escalateHighPriorityCases(hours) { + var gr = new GlideRecord('incident'); + gr.addQuery('priority', '1 - Critical'); + gr.addQuery('opened_at', '<=', gs.hoursAgo(hours)); + gr.query(); + + while (gr.next()) { + gs.eventQueue('incident.escalation', gr, gr.assigned_to, 'Escalation triggered after ' + hours + ' hours.'); + } +} From 4b2833da1f0b6907b7a6eeee72274aa0209b887b Mon Sep 17 00:00:00 2001 From: Deepak Negi <120473057+dvn-lazywinner@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:27:33 +0530 Subject: [PATCH 08/11] Create readme.md --- GlideRecord/EscalateInicidents/readme.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 GlideRecord/EscalateInicidents/readme.md diff --git a/GlideRecord/EscalateInicidents/readme.md b/GlideRecord/EscalateInicidents/readme.md new file mode 100644 index 0000000000..7ae5052f49 --- /dev/null +++ b/GlideRecord/EscalateInicidents/readme.md @@ -0,0 +1 @@ +Escalate High priority incidents with supported notifications and events From 6684812434db2b364e6afeb578411b129bb61d3e Mon Sep 17 00:00:00 2001 From: Deepak Negi <120473057+dvn-lazywinner@users.noreply.github.com> Date: Fri, 1 Nov 2024 16:42:16 +0530 Subject: [PATCH 09/11] Create findBrokenLinks.js Broken Links in articles --- Find Broken Links/findBrokenLinks.js | 157 +++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 Find Broken Links/findBrokenLinks.js diff --git a/Find Broken Links/findBrokenLinks.js b/Find Broken Links/findBrokenLinks.js new file mode 100644 index 0000000000..c848cd5b91 --- /dev/null +++ b/Find Broken Links/findBrokenLinks.js @@ -0,0 +1,157 @@ +// Choose start and end indexes +var indexOffset = 16; +var windowSize = 500; + +var startIndex = windowSize * indexOffset; +var endIndex = windowSize * indexOffset + windowSize - 1; + +// Define a new GlideRecord object for the Knowledge Article table +var article = new GlideRecord('kb_knowledge'); +// Add a query to find all published knowledge articles +article.addQuery('workflow_state', 'published'); +article.orderByDesc("number"); + + + +// Apply indexes +article.chooseWindow(startIndex, endIndex); +// Execute the query to find the knowledge articles +article.query(); + +// Iterate through the knowledge articles +var invalidArticles = []; +while (article.next()) { + + // Get the article body. If empty, continue + var body = article.getValue('text'); + if (!body) + continue; + + var arrayUtil = new ArrayUtil(); + var regex = /href=(["'])http(.*?)\1/g; + + // Obtain a list of all unique links found in the article + var links = body.match(regex); + if (!links) + continue; + + links = arrayUtil.unique(links); + + var articleNum = article.getValue('number'); + var articleSys = article.getUniqueValue(); + var articleOwnerSys = article.getValue("u_knowledge_owner"); + var articleOwner = article.getDisplayValue('u_knowledge_owner'); + var invalid = false; + var invalidLinks = []; + + // Validate each link + links.forEach(function(l) { + if (!l) + return; + + l = l.substring(6, l.length - 1); + + // Check if we've already recorded errors for this article. If so, continue + if (checkLinkAlreadyLogged(articleSys, l)) + return; + + if (l.indexOf('sys_kb_id') != -1) { + // Link is another knowledge article, determine if article is outdated + var sysRegex = /sys_kb_id(=|(%3d))([^\s]+)/gi; + var sysId = l.match(sysRegex)[0].substring(10, 42); + + // Check if the referenced knowledge article is unpublished + var unpublished = new GlideRecord("kb_knowledge"); + unpublished.addQuery("sys_id", sysId); + unpublished.addQuery("workflow_state", "!=", "published"); + unpublished.query(); + + // Article is unpublished, log broken link + if (unpublished.next()) { + invalid = true; + var reason = "Contains unpublished knowledge article link"; + if (l.indexOf('sysparm_article') == -1) + reason += " (without KB Article Number)"; + var il = { + "link": l, + "reason": reason + }; + invalidLinks.push(il); + addBrokenLinkRecord(articleSys, articleOwnerSys, l, reason, null); + } + } else { + // Link is to an external site. Send a REST Message and log result + try { + var request = new sn_ws.RESTMessageV2(); + request.setEndpoint(l); + request.setHttpMethod('GET'); + var response = request.execute(); + + var httpStatus = response.getStatusCode(); + + // HTTP Error returned, log result + if (httpStatus != 200) { + invalid = true; + var reason = "External link returns status code " + httpStatus; + var il = { + "link": l, + "reason": reason + }; + invalidLinks.push(il); + addBrokenLinkRecord(articleSys, articleOwnerSys, l, reason, httpStatus); + } + } catch(e) { + // Error occurred while attempting to send a REST Message + // Log a result + addBrokenLinkRecord(articleSys, articleOwnerSys, l, e, null); + } + } + }); + + if (invalid) { + invalidArticles.push({ + number: articleNum, + owner: articleOwner, + links: invalidLinks + }); + } +} + +gs.info("Completed reviewing articles " + startIndex + " - " + endIndex); + +// if (invalidArticles.length) { +// var str = "Articles with invalid links: " + invalidArticles.length + "\n"; + +// for (var i = 0; i < invalidArticles.length; i++) { +// str += "\nArticle: " + invalidArticles[i].number; +// str += "\nOwner: " + invalidArticles[i].owner; + +// for (var j = 0; j < invalidArticles[i].links.length; j++) { +// str += "\n\tInvalid link " + (j + 1) + ":"; +// str += "\n\t\tLink: " + invalidArticles[i].links[j].link; +// str += "\n\t\tReason: " + invalidArticles[i].links[j].reason; +// } +// } + +// gs.info(str); +// } + +function checkLinkAlreadyLogged(article, link) { + var gr = new GlideRecord("u_broken_knowledge_links"); + gr.addQuery("u_article", article); + gr.addQuery("u_link", link); + gr.query(); + + return gr.hasNext(); +} + +function addBrokenLinkRecord(article, owner, link, reason, httpError) { + var gr = new GlideRecord("u_broken_knowledge_links"); + gr.initialize(); + gr.u_article = article; + gr.u_owner = owner; + gr.u_link = link; + gr.u_reason = reason; + gr.u_http_error_code = httpError; + gr.insert(); +} From b2e62c7f33d0a80484d6f4229db7df1eab419eb8 Mon Sep 17 00:00:00 2001 From: Deepak Negi <120473057+dvn-lazywinner@users.noreply.github.com> Date: Fri, 1 Nov 2024 16:51:04 +0530 Subject: [PATCH 10/11] Delete UI Actions/Copy Bulk SysIDs directory --- UI Actions/Copy Bulk SysIDs/CopyBulkIDs.js | 16 ---------------- UI Actions/Copy Bulk SysIDs/readme.md | 19 ------------------- 2 files changed, 35 deletions(-) delete mode 100644 UI Actions/Copy Bulk SysIDs/CopyBulkIDs.js delete mode 100644 UI Actions/Copy Bulk SysIDs/readme.md diff --git a/UI Actions/Copy Bulk SysIDs/CopyBulkIDs.js b/UI Actions/Copy Bulk SysIDs/CopyBulkIDs.js deleted file mode 100644 index 6b8f15dc49..0000000000 --- a/UI Actions/Copy Bulk SysIDs/CopyBulkIDs.js +++ /dev/null @@ -1,16 +0,0 @@ -/* -//This action will be able to copy the sysids of multiselected records. - -Table - Global -List Choice - True -Client - True -onClick - copySysIDs() - -Result - All the sysids will be copied as comma-separated strings which you can further copy into a system property for validations - -*/ - -function copySysIDs(){ - var sysIds = g_list.getChecked(); - copyToClipboard(sysIds); -} diff --git a/UI Actions/Copy Bulk SysIDs/readme.md b/UI Actions/Copy Bulk SysIDs/readme.md deleted file mode 100644 index b84669f492..0000000000 --- a/UI Actions/Copy Bulk SysIDs/readme.md +++ /dev/null @@ -1,19 +0,0 @@ -Did you ever get any use case where you need to copy SysIDs in bulk from a list view? - -The use case can be: -There is some matrix that you need to validate in your script. -You need to store the sysids in a property. One option is to export the CSV with Sys id field using ?CSV&sysparm_default_export_fields=all method, -then convert in comma separated list. - -![image](https://github.com/user-attachments/assets/90228462-cc67-4a99-b4e0-b1295c46bd67) - -Created this small utility to fasten the process of copying bulk sysids - -1. Navigate to System Definitions > UI Actions > Create New -2. Give the Name of your choice e.g “Copy Bulk SysIDs” -3. Select Table as “Global” so it is available on every list. -4. Tick the Client and List choice field checkbox and call the function in Onclick field -5. Write below code inside the function in Script field. -**var sysIds = g_list.getChecked(); -copyToClipboard(sysIds);** - From 4a0b310edd39bcffd80772823ea281fd6c752246 Mon Sep 17 00:00:00 2001 From: Deepak Negi <120473057+dvn-lazywinner@users.noreply.github.com> Date: Fri, 1 Nov 2024 16:51:51 +0530 Subject: [PATCH 11/11] Delete GlideRecord/EscalateInicidents directory --- GlideRecord/EscalateInicidents/EscalateIncidents.js | 13 ------------- GlideRecord/EscalateInicidents/readme.md | 1 - 2 files changed, 14 deletions(-) delete mode 100644 GlideRecord/EscalateInicidents/EscalateIncidents.js delete mode 100644 GlideRecord/EscalateInicidents/readme.md diff --git a/GlideRecord/EscalateInicidents/EscalateIncidents.js b/GlideRecord/EscalateInicidents/EscalateIncidents.js deleted file mode 100644 index abfbad0f8c..0000000000 --- a/GlideRecord/EscalateInicidents/EscalateIncidents.js +++ /dev/null @@ -1,13 +0,0 @@ -// Hours - set hours in a system property, and call below function to escalate -// Support it with the events and notifications - -function escalateHighPriorityCases(hours) { - var gr = new GlideRecord('incident'); - gr.addQuery('priority', '1 - Critical'); - gr.addQuery('opened_at', '<=', gs.hoursAgo(hours)); - gr.query(); - - while (gr.next()) { - gs.eventQueue('incident.escalation', gr, gr.assigned_to, 'Escalation triggered after ' + hours + ' hours.'); - } -} diff --git a/GlideRecord/EscalateInicidents/readme.md b/GlideRecord/EscalateInicidents/readme.md deleted file mode 100644 index 7ae5052f49..0000000000 --- a/GlideRecord/EscalateInicidents/readme.md +++ /dev/null @@ -1 +0,0 @@ -Escalate High priority incidents with supported notifications and events