Detecting Order Guides in Service Portal

I was building a catalog solution recently, an order guide that wraps up all the access management catalog items into one place. This access management order guide is intended to be used when someone identifies that they need access to a new system (or system that is new to them) or additional access in something they already use. Onboarding is a separate beast, but still leverages the same items within its own order guide. This presented a challenge where I needed to identify which order guide is being used and take different actions in each catalog item based on the result. ServiceNow’s documentation of many its APIs is adequate (I use that word very intentionally), but for this specific need, I could find either inadequate solutions or nothing at all. Community ended up leading me in the right direction, as did exploring Chrome’s developer tools. Here’s how it unfolded:

A Community search for something like “service portal order guide script” led me to this post from 2015-ish by ewilks and the response by Jim Coyne: How can I tell if I am in an order guide?

With as much time as I spend in Community and my prior knowledge that ServiceNow more recently put controls in place to secure client scripts in the service portal, I had a hunch that Jim’s reply either didn’t work anymore or would require adjustments to make it work in 2022. Sure enough, copy-pasting Jim’s solution didn’t appear to have any significant effect, even with checking and unchecking the “Isolate script” checkbox on the catalog client script form (introduced in London).

Here’s what it looks like in a PDI running San Diego Patch 1 in a catalog client script on the baseline New Hire order guide directly:

Catalog Client Script on Order Guide with Proposed Solution

And the result:

JavaScript Error on Order Guide

I tried the same thing in a catalog client script on one of the catalog items:

Catalog Client Script on Catalog Item with Proposed Solution

And the result:

JavaScript Error on Catalog Item

Note that for the two client scripts I’ve included above, the “Isolate script” checkbox is unchecked. Checking those boxes made no difference, though unchecking should allow the script to do a few more “dangerous” things

So, what’s a developer to do? Dig in, of course!

I knew that I needed to do something outside of a ServiceNow API, as the Developer site didn’t produce any promising results.

The only mention of “catalog” in the client-side Developer API docs

ServiceNow’s Product Docs site does mention g_service_catalog.isOrderGuide(), but doesn’t provide any other detail. (⬅ Foreshadowing)

Nothing for “orderg” in the client-side Developer API docs
Nothing for “order_” in the client-side Developer API docs

I assumed that getting the URL parameter should be easy as long as the “Isolate script” checkbox is set to false. I’m not a pure JavaScripter… I know ServiceNow’s API sufficiently, but when it comes to the window or location or DOM and BOM, I’m lost. I frequently just use w3schools or hunt around in Chrome’s developer tools to find what I need. In this case, I use the developer tools’ Console feature to make sure I had the right data point:

Using Chrome’s Developer Tools (Console, specifically)
var windowPath = location.search; //ex: ?id=sc_cat_item_guide&sys_id=6690750f4f7b4200086eeed18110c761&sysparm_category=e15706fc0a0a0aa7007fc21e1ab70c2f

Now that I have the URL, I could parse it to get the parameters:

var windowPath = location.search;
var windowParams = windowPath.split('?')[1]; //get the URL part after the ?
var params = windowParams.split('&'); //split each parameter into its own item in an Array

I needed to pay close attention in the next step… things could get dicey since I could easily make assumptions about the order of the parameters, which, in theory, could change. But we know how to work around that:

var windowPath = location.search;
var windowParams = windowPath.split('?')[1];
var params = windowParams.split('&');
var sys_id = '';
for (var i = 0; i < params.length; i++) {
	//find the parameter with the sys_id – that's our order guide
	if (params[i].toString().indexOf('sys_id') == 0) {
		sys_id = params[i].split('=')[1];
		break;
	}
}

Now that I have the sys_id of the order guide, let’s see if it’s the right one.

var windowPath = location.search;
var windowParams = windowPath.split('?')[1];
var params = windowParams.split('&');
var sys_id = '';
for (var i = 0; i < params.length; i++) {
	if (params[i].toString().indexOf('sys_id') == 0) { //find the sys_id parameter
		sys_id = params[i].split('=')[1]; // get the sys_id parameter value
		break; // stop if we find the sys_id parameter
	}
}
var isAM = sys_id == '2608266d1b5749d0dae4535f034bcb19';

There’s a glaring issue here… that sys_id. ServiceNow’s best practice recommendation is to not hard-code sys_ids in, well, code. What’s the solution there? If this was an INC or CHG, I would create a system property, store the sys_id there, use a “display” business rule to put that sys_id in the scratchpad, and then use the scratchpad value in a client script. If this was a RITM or SCTASK, I would probably do the same thing, but I would need to check which catalog item was in use, too. Since this is in the catalog in a portal, I can’t do any of that.

The way I solved that issue was to add onto a catalog utility script include I previously built that allows me to access system properties using GlideAjax. (That sounds like a good idea for another post.)

Why doesn’t ServiceNow provide a mechanism for pulling properties into client scripts??

Frustrated Developer

But back to the actual issue at hand.

I can wrap this up by then doing all the messy work of the client script now that I know if this is access management or not. I also added in a check at the beginning to determine if this is even an order guide or not. (Remember my note above about foreshadowing?) It potentially saves a GlideAjax call and future-proofs this a bit in case the we ever decide to allow the catalog items to be ordered outside an order guide:

function onLoad() {
	var isOrderGuide = g_service_catalog.isOrderGuide(); //check if this is an order guide at all
	if (isOrderGuide) {
		var windowPath = location.search;
		var windowParams = windowPath.split('?')[1];
		var params = windowParams.split('&');
		var sys_id = '';
		for (var i = 0; i < params.length; i++) {
			if (params[i].toString().indexOf('sys_id') == 0) {
				sys_id = params[i].split('=')[1];
				break;
			}
		}

		var amID = new GlideAjax('ServiceCatalogUtilAjax');
		amID.addParameter('sysparm_name', 'get_property');
		amID.addParameter('sysparm_property', 'access_management_id');
		amID.getXMLAnswer(ajaxReturn);
	}

	function ajaxReturn(answer) {
		var isAM = sys_id == answer;

		if (isOrderGuide && isAM) {
			/*
			do this
			*/
		} else {
			/*
			do that
			*/
		}
	}
}

As I’ll say this often on this site… I’m not a professional JavaScripter. I’m not a classically trained full stack developer, coder, programmer, etc. I’m a ServiceNow professional. I learned the basics of JavaScript via Codecademy circa 2012 and ServiceNow’s use of JavaScript over the last decade. Soooo, I’m sure there’s lots in there that makes people squirm. There are probably things I could do to handle errors better. There are probably exceptions and possibilities I’m not thinking of. But what I have is functional! (Though if you have ideas or thoughts on how to improve this, I’m all ears! Let me know!)

Here is the first page of the order guide itself:

Order Guide with an Info Message

Interesting… the client script thinks we’re not in an order guide. We obviously are. Let’s check out the catalog item:

Catalog Item with an Info Message

Wait, what? The first page of the order guide (“Describe needs”) doesn’t know we’re in an order guide, but the second page (“Choose options”) does? Let’s come back to that later.

Now we can see that the onLoad client script runs (even though the catalog item (Standard Laptop) that I’m using a sample for this article hasn’t been clicked/expanded yet) and we get a good result.

So why does the order guide not know that it’s an order guide? Only ServiceNow could tell us for sure, but it appears that the catalog item form has something special that the order guide for doesn’t.

WHYYYYYY????

Really Frustrated Developer

In the end, this is a snippet of code that I could use again in the future to determine which order guide is being used. It might not be pretty, but it gets the job done.

Leave a Reply