Skip to content

Instantly share code, notes, and snippets.

@lucasvieites
Last active February 10, 2026 11:21
Show Gist options
  • Select an option

  • Save lucasvieites/49b318e354244cd3cef92ff5c40c713c to your computer and use it in GitHub Desktop.

Select an option

Save lucasvieites/49b318e354244cd3cef92ff5c40c713c to your computer and use it in GitHub Desktop.
Booking ServiceNow knowledge article form
// ==UserScript==
// @name Destination LPS KB form
// @namespace https://gist.github.com/lucasvieites/49b318e354244cd3cef92ff5c40c713c
// @version 0.5
// @description Adds functionality for power users of the knowledge form
// @author Lucas Vieites
// @match https://*.service-now.com/*
// @grant GM_openInTab
// @icon https://www.google.com/s2/favicons?sz=64&domain=booking.com
// @updateURL https://gist.github.com/lucasvieites/49b318e354244cd3cef92ff5c40c713c/raw/bkng_dlpskb_form.user.js
// @downloadURL https://gist.github.com/lucasvieites/49b318e354244cd3cef92ff5c40c713c/raw/bkng_dlpskb_form.user.js
// @run-at document-idle
// ==/UserScript==
// the "?sys_id=" in the @match is important, otherwise the script runs twice due to the iframe
/** ChangeLog:
v0.1: Initial release for booking.service-now.com
- Adds icons and links next to the "Number" label to open the KB article (PermaLink, Draft, or Incognito)
- Updates tab title to: KBnumber - Title
v0.2: - Add support for Knowledge Blocks
- Add ChangeLog
v0.3: - Add button to open article in Workspace
v0.4: - Add custom stylesheet to TinyMCE iframe
v0.5: - Added condition for the script to run only when it's a KM-related page (isKM)
*/
var delay = 1000; // milliseconds. Delay before starting to run this script.
// This is needed because the KM portal loads the UI first, and then the content asynchronously
setTimeout(portalMagic, delay); // Wait for everything to load before starting the _real_ script
function portalMagic() {
'use strict';
var thisLabel, thisNumber, thisSysID, thisTitle, sourceURL, sourceNumber, sourceLabel;
var isKM = false;
var logPrefix = "## LVF - KB Form - ";
var baseURL = window.location.origin;
console.log(logPrefix + "sys_readonly.kb_knowledge.number: " + document.getElementById("sys_readonly.kb_knowledge.number"));
// Get the KB number from the form
if (document.getElementById("sys_readonly.kb_knowledge.number")) {
// for regular kb articles
isKM = true;
console.log(logPrefix + "It's a regular KB article");
thisLabel = document.querySelector("label[for='sys_readonly.kb_knowledge.number']");
thisNumber = document.getElementById("sys_readonly.kb_knowledge.number").value;
thisTitle = document.getElementById("kb_knowledge.short_description").value;
// Get the sys_id from the URL query string
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const thisSysID = urlParams.get('sys_id')
console.log(logPrefix + "This sys_id: " + thisSysID);
} else if (document.getElementById("sys_readonly.kb_knowledge_block.number")) {
// for knowledge blocks
isKM = true;
console.log(logPrefix + "It's a Knowledge Block");
thisLabel = document.querySelector("label[for='sys_readonly.kb_knowledge_block.number']");
thisNumber = document.getElementById("sys_readonly.kb_knowledge_block.number").value;
thisTitle = document.getElementById("kb_knowledge_block.short_description").value;
} else {
console.log(logPrefix + "I don't know what this is");
isKM = false;
}
console.log(logPrefix + "thisLabel is: " + thisLabel); // Should return an HTMLLabelElement
console.log(logPrefix + "sourceNumber is: " + thisNumber);
// Get the sys_id from the form
if (document.getElementById("sys_uniqueValue")) {
thisSysID = document.getElementById("sys_uniqueValue").value;
}
if (isKM) {
// Create the anchor element
var linkElement = document.createElement('a');
linkElement.text = " ";//thisNumber;
//linkElement.href = "/kb?id=kb_article_view&sysparm_article=" + thisNumber;
//linkElement.target = "_blank";
linkElement.classList.add("icon-preview"); // Adds the eye icon as the clickable text
linkElement.style.paddingLeft = "2px";
linkElement.setAttribute("title", "View last published version in DLPS");
linkElement.setAttribute("id", thisNumber + "_pub");
thisLabel.appendChild(linkElement);
linkElement.addEventListener("click", function(event) {
console.log("opening " + thisNumber);
// window.open("/kb?id=kb_article_view&sysparm_article=" + thisNumber, "_blank");
window.open("/lps?id=kb_article_view&sysparm_article=" + thisNumber, "_blank");
});
var linkSysID = document.createElement('a');
linkSysID.text = " ";
//linkSysID.href = "/kb?id=kb_article_view&sys_kb_id=" + thisSysID;
//linkSysID.target = "_blank";
linkSysID.classList.add("icon-article-document"); // Adds the document icon as the clickable text
linkSysID.style.paddingLeft = "2px";
linkSysID.setAttribute("title", "View this version in DLPS");
thisLabel.appendChild(linkSysID);
linkSysID.addEventListener("click", function(event) {
console.log(logPrefix + "Opening " + thisSysID);
// window.open("/kb?id=kb_article_view&sys_kb_id=" + thisSysID, "_blank");
window.open("/lps?id=kb_article_view&sys_kb_id=" + thisSysID, "_blank");
});
// Show link to open in incognito browser
// chrome.windows.create({"url": url, "incognito": true
// GM_openInTab('https://github.com', {incognito: true })
var privateLink = document.createElement('a');
privateLink.text = " ";
privateLink.classList.add("icon-preview"); // Adds the eye icon as the clickable text
privateLink.style.color = "#000";
privateLink.style.paddingLeft = "2px";
privateLink.setAttribute("title", "View in private browser");
thisLabel.appendChild(privateLink);
privateLink.addEventListener("click", function(event) {
console.log(logPrefix + "Opening " + thisSysID);
GM_openInTab(baseURL + "/kb?id=kb_article_view&sysparm_article=" + thisNumber, {incognito: true });
});
// Show the article in Workspace view (for easier comparing versions)
var workspaceURL = "/now/sow/kb_view/kb_knowledge/" + thisSysID
+ "/sub/record/kb_knowledge/" + thisSysID // Open the
+ "/params/selected-tab-index/4"; // Open in te 4th tab, which is "Versions"
var workspaceLink = document.createElement('a');
workspaceLink.text = " ";
workspaceLink.classList.add("icon-cog"); // Adds the eye icon as the clickable text
workspaceLink.style.paddingLeft = "2px";
workspaceLink.setAttribute("title", "View in Workspace");
thisLabel.appendChild(workspaceLink);
workspaceLink.addEventListener("click", function(event) {
console.log(logPrefix + "Opening " + thisSysID);
GM_openInTab(baseURL + workspaceURL);
});
// Change the form's tab title:
console.log(logPrefix + "Changing tab title " + thisNumber + " - " + thisTitle);
top.document.title = thisNumber + " - " + thisTitle;
// Fix the text in the "Checkout" button when it's available
if (document.getElementById("169ee7fcdb122200a042f278f0b8f58e")) {
var checkoutButton = document.getElementById("169ee7fcdb122200a042f278f0b8f58e");
//checkoutButton.style.color = "#f00";
checkoutButton.innerHTML = "Check out";
}
// Inject CSS into TinyMCE
// The file is <link href="eb29f348db64f010d9816d3405961927.cssdbx?" rel="stylesheet" type="text/css">
if (document.querySelector("#kb_knowledge\\.text_ifr")) {
//console.log(logPrefix + "We have an iframe: " + document.querySelector("#kb_knowledge\\.text_ifr").contentDocument);
console.log(logPrefix + "We have an iframe.");
var cssLink = document.createElement("link");
cssLink.href = "eb29f348db64f010d9816d3405961927.cssdbx";
cssLink.rel = "stylesheet";
cssLink.type = "text/css";
document.querySelector("#kb_knowledge\\.text_ifr").contentDocument.head.appendChild(cssLink);
} else {
console.log(logPrefix + "There is no iframe");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment