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(); +} 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);