How to setup a dynamically generated sitemap by using the ServiceNow Sitemap Generator?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
15 hours ago - last edited 11 hours ago
One of our knowledgebases is available for the general public.
A small number of the knowledge articles in this knowledgebase are being indexed by Google (and other search engines) so we want to improve that so our audience can find all our public available knowledge articles.
Currently we do not have a sitemap so we are working in the ServiceNow Sitemap Generator to create a sitemap.
Creating a static sitemap seems to be straight forward but we want to create a dynamic sitemap by using the "script" option in the Sitemap Generator.
In the scripted version it should be possible to create a list of all the URL's of our public available knowledge articles so webcrawlers like Google can index these knowledge articles and make them findable to the general public.
However, the official ServiceNow documentation is not very clear on how this script should be setup and if we search the ServiceNow community all we can find is an older article that describes how to setup a similar script (not for Site Generator because that was not yet available at that time) and setup in a ServiceNow Processor which is now no longer available (see https://www.servicenow.com/community/developer-articles/create-a-dynamic-sitemap-in-servicenow/ta-p/... by @KevinCuster )
The example script provided in the ServiceNow Sitemap Generator is listed here.
Has anyone adapted this script to get a dynamic sitemap file?
/*
function processSitemap() {
// Your sitemap generation code goes here.
// This is a sample response that ensures the generated XML is valid. Additional URL elements can be included.
var urlEntities = ' <url>\n' +
' <loc>https://www.servicenow.com/</loc>\n' +
' <lastmod>2022-06-01</lastmod>\n' +
' <changefreq>monthly</changefreq>\n' +
' </url>\n';
return urlEntities;
}
processSitemap();
//Validates that URLs included in the sitemap are public URLs and do not return a 301 redirect.
function checkBrokenLink(url, callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status < 300) {
// When checking a kb_view.do page, it returns a HTTP 200 response
// even if the page does not exist. Different logic is needed to check these pages.
if (/\/kb_view\.do/ig.test(url)) {
if (/Undefined\(jelly\.jvar_obj\.name;\)/ig.test(xhr.responseText)) {
return callback(false);
}
}
return callback(true);
} else {
return callback(false);
}
}
};
if (/\/kb_view\.do/ig.test(url)) {
xhr.open("GET", url);
} else {
xhr.open("HEAD", url);
}
xhr.send();
}
// Sample code to get the human readable URL using the page record and the query string.
// Get the page record with page route map using the pageId, portal sys_id and preventReRoute which is a boolean
// value to prevent page re-routing. It should be false to get final routed page record.
var pageGR = global.SPSEOUtilsSNC.getPageRecordWithPageRoute(pageId, portalId, preventReRoute);
var hrURL = global.SPSEOUtilsSNC.getHumanReadableUrl(pageGR, queryString);
// Note : "If special characters <>&"' are used in the URL, then replace them with their equivalent escape characters."
// Please refer the SEO script of the page record to ensure that the page does not have NOINDEX meta tag and also
// to check if the URL has a canonical URL configured. If yes, that needs to be added to sitemap.
*/- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11 hours ago
By comparing the script from the ServiceNow Sitemap Generator and the script from the Community article by @KevinCuster listed above we were able to create a working alternative script.
function processSitemap() {
// ===== CONFIG =====
var kbSysId = "ae26315ec325aa50fd309bbc7a01317b"; // sys_id of the Knowledge Base
var escBase = gs.getProperty('glide.servlet.uri'); // EXACT base URL you want (no trailing slash)
var useDisplayTimezoneForLastmod = false; // see note below
function xmlEscape(s) {
if (s == null) return "";
return ("" + s)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function toDateYYYYMMDD(dateTimeStr) {
// "YYYY-MM-DD HH:mm:ss" -> "YYYY-MM-DD"
if (!dateTimeStr) return "";
return ("" + dateTimeStr).substring(0, 10);
}
// ===== QUERY PUBLISHED ARTICLES =====
var grKB = new GlideRecord("kb_knowledge");
grKB.addQuery("kb_knowledge_base", kbSysId);
grKB.addQuery("workflow_state", "published");
grKB.orderBy("number");
grKB.query();
// ===== BUILD ONLY <url> ENTRIES =====
var urlLines = [];
while (grKB.next()) {
var number = grKB.getValue("number"); // e.g. KB0011578
var loc = escBase + "?id=kb_article&sysparm_article=" + number;
var lastmodRaw = useDisplayTimezoneForLastmod ?
grKB.getDisplayValue("sys_updated_on") :
grKB.getValue("sys_updated_on");
var lastmod = toDateYYYYMMDD(lastmodRaw);
urlLines.push(" <url>");
urlLines.push(" <loc>" + xmlEscape(loc) + "</loc>");
if (lastmod) {
urlLines.push(" <lastmod>" + xmlEscape(lastmod) + "</lastmod>");
}
urlLines.push(" </url>");
}
// Return the concatenated <url> entries (NOT the urlset wrapper)
return urlLines.join("\n");
}
// If your platform expects the function to be invoked:
processSitemap();
We are still testing the new script but the output (through the ServiceNow Sitemap Generator) seems to be a proper Sitemap file.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10 hours ago
Our script is still missing a filter to only show the knowledge articles that have not passed their "Valid To" date.
We are working on that part.
