Skip to content
snhackery

Adventures in mangling the ServiceNow platform

  snhackery
  • Home
  • About
  • Disclaimer
  • Update Sets

Content Selector Configuration Editor, Corrected (again)

Posted on March 21, 2023 | by snhackery

“You’ve got to think about big things while you’re doing small things, so that all the small things go in the right direction.”
— Alvin Toffler

Recently I was playing around with the Content Selector Configuration Editor to create a dashboard for my Service Account Management app, which is a Scoped Application, and realized that the last fix that I put in to make things work with a Scoped Application did not quite go far enough. Looking things over it is quite clear that the original design had only considered support for global scripts, and my first attempt to rectify that oversight did not resolve all of the issues for configuration scripts that were not in the global scope. Today it is time to finish that up and correct all of the other shortcomings in that tool when working outside of the global scope.

For starters, the pick list for available scripts in the current version includes all of the configuration scripts for all scopes. What we really need is to limit that selection list to just those scripts in the current scope. Otherwise, you could potentially be editing a script in one scope while you are working in another scope, which will not end well if it works at all. To limit the list to just the scripts in the current scope, we need to add something like this to the filter:

^sys_scope=javascript:gs.getCurrentApplicationId()

That will turn this attribute in the record picker:

default-query="'active=true^scriptLIKEObject.extendsObject(ContentSelectorConfig^ORscriptLIKEObject.extendsObject(global.ContentSelectorConfig'"

… to this:

default-query="'active=true^sys_scope=javascript:gs.getCurrentApplicationId()^scriptLIKEObject.extendsObject(ContentSelectorConfig^ORscriptLIKEObject.extendsObject(global.ContentSelectorConfig'"

It would also be good to add a little more information to the help text for that field, so the entire snh-form-field tag now looks like this:

<snh-form-field
  snh-label="Content Selector Configuration"
  snh-model="c.data.script"
  snh-name="script"
  snh-type="reference"
  snh-help="Select the Content Selector Configuration from the current Scope that you would like to edit."
  snh-change="scriptSelected();"
  placeholder="Choose a Content Selector Configuration"
  table="'sys_script_include'"
  default-query="'active=true^sys_scope=javascript:gs.getCurrentApplicationId()^scriptLIKEObject.extendsObject(ContentSelectorConfig^ORscriptLIKEObject.extendsObject(global.ContentSelectorConfig'"
  display-field="'name'"
  search-fields="'name'"
  value-field="'api_name'"/>

That solves one problem, but there are others. When building the new script from the user’s input in the save() function of the widget’s server script, this conditional only reduces the API Name to the root name for global scripts:

if (data.scriptInclude.startsWith('global.')) {
	data.scriptInclude = data.scriptInclude.split('.')[1];
}

This needs to be done for scripts in any scope, so the entire conditional should just go away and simply be reduced to this:

data.scriptInclude = data.scriptInclude.split('.')[1];

Further down in that same function, this line again assumes that you are working in the global scope:

scriptGR.api_name = 'global.' + name;

The API Name is actually set for you whenever you save a new script, so this line can actually just be removed entirely and things will work just fine.

With all of these changes, the new save() function now looks like this:

function save() {
		if (input.script && input.script.value) {
			data.scriptInclude = input.script.value;
			data.scriptInclude = data.scriptInclude.split('.')[1];
		}
		var name = data.scriptInclude;
		if (input.newRecord) {
			name = input.newScriptName;
		}
		var script = "var ";
		script += name;
		script += " = Class.create();\n";
		script += name;
		script += ".prototype = Object.extendsObject(";
		if (gs.getCurrentApplicationScope() != 'global') {
			script += "global.";			
		}
		script += "ContentSelectorConfig, {\n";
		script += "	initialize: function() {\n";
		script += "	},\n";
		script += "\n";
		script += "	perspective: [";
		var separator = '';
		for (var p=0; p<input.config.perspective.length; p++) {
			var thisPerspective = input.config.perspective[p];
			script += separator;
			script += "{\n		name: '";
			script += thisPerspective.name;
			script += "',\n		label: '";
			script += thisPerspective.label;
			script += "',\n		roles: '";
			script += thisPerspective.roles || '';
			script += "'\n	}";
			separator = ",";
		}
		script += "],\n\n";
		script += "	state: [";
		separator = '';
		for (var s=0; s<input.config.state.length; s++) {
			var thisState = input.config.state[s];
			script += separator;
			script += "{\n		name: '";
			script += thisState.name;
			script += "',\n		label: '";
			script += thisState.label;
			script += "'\n	}";
			separator = ",";
		}
		script += "],\n\n";
		script += "	table: {";
		separator = '';
		for (var tp=0; tp<input.config.perspective.length; tp++) {
			var tablePerspective = input.config.perspective[tp];
			script += separator;
			script += "\n		";
			script += tablePerspective.name;
			script += ": [";
			var tableSeparator = '';
			for (var tt=0; tt<input.config.table[tablePerspective.name].length; tt++) {
				var tableTable = input.config.table[tablePerspective.name][tt];
				script += tableSeparator;
				script += "{\n			name: '";
				script += tableTable.name;
				script += "',\n			displayName: '";
				script += tableTable.displayName;
				script += "'";
				for (var ts=0; ts<input.config.state.length; ts++) {
					var tableState = input.config.state[ts];
					script += ",\n			";
					script += tableState.name;
					script += ": {\n				filter: '";
					script += tableTable[tableState.name].filter;
					script += "',\n				fields: '";
					script += tableTable[tableState.name].fields;
					script += "',\n				svcarray: [";
					var lastSeparator = '';
					for (var v=0; v<tableTable[tableState.name].svcarray.length; v++) {
						var thisScriptedValue = tableTable[tableState.name].svcarray[v];
						script += lastSeparator;
						script += "{\n					name: '";
						script += thisScriptedValue.name;
						script += "',\n					label: '";
						script += thisScriptedValue.label;
						script += "',\n					heading: '";
						script += thisScriptedValue.heading;
						script += "',\n					script: '";
						script += thisScriptedValue.script;
						script += "'\n				}";
						lastSeparator = ",";
					}
					script += "]";
					script += ",\n				aggarray: [";
					lastSeparator = '';
					for (var g=0; g<tableTable[tableState.name].aggarray.length; g++) {
						var thisAggregate = tableTable[tableState.name].aggarray[g];
						script += lastSeparator;
						script += "{\n					name: '";
						script += thisAggregate.name;
						script += "',\n					label: '";
						script += thisAggregate.label;
						script += "',\n					heading: '";
						script += thisAggregate.heading;
						script += "',\n					table: '";
						script += thisAggregate.table;
						script += "',\n					field: '";
						script += thisAggregate.field;
						script += "',\n					filter: '";
						script += thisAggregate.filter;
						script += "',\n					source: '";
						script += thisAggregate.source;
						script += "',\n					hint: '";
						script += thisAggregate.hint;
						script += "',\n					page_id: '";
						script += thisAggregate.page_id;
						script += "'\n				}";
						lastSeparator = ",";
					}
					script += "]";
					script += ",\n				btnarray: [";
					lastSeparator = '';
					for (var b=0; b<tableTable[tableState.name].btnarray.length; b++) {
						var thisButton = tableTable[tableState.name].btnarray[b];
						script += lastSeparator;
						script += "{\n					name: '";
						script += thisButton.name;
						script += "',\n					label: '";
						script += thisButton.label;
						script += "',\n					heading: '";
						script += thisButton.heading;
						script += "',\n					icon: '";
						script += thisButton.icon;
						script += "',\n					color: '";
						script += thisButton.color;
						script += "',\n					hint: '";
						script += thisButton.hint;
						script += "',\n					page_id: '";
						script += thisButton.page_id;
						script += "',\n					condition: '";
						if (thisButton.condition) {
							var condition = thisButton.condition;
							if (condition.indexOf("'") != -1) {
								condition = condition.replace(/'/g, "\\'");
							}
							script += condition;
						}
						script += "'\n				}";
						lastSeparator = ",";
					}
					script += "]";
					script += ",\n				refmap: {";
					lastSeparator = '';
					var indent = '';
					for (var key  in tableTable[tableState.name].refmap) {
						script += lastSeparator;
						script += "\n					";
						script += key;
						script += ": '";
						script += tableTable[tableState.name].refmap[key];
						script += "'";
						lastSeparator = ",";
						indent = '\n				';
					}
					script += indent + "}";
					script += ",\n				actarray: [";
					lastSeparator = '';
					for (var a=0; a<tableTable[tableState.name].actarray.length; a++) {
						var thisAction = tableTable[tableState.name].actarray[a];
						script += lastSeparator;
						script += "{\n					name: '";
						script += thisAction.name;
						script += "',\n					label: '";
						script += thisAction.label;
						script += "'\n				}";
						lastSeparator = ",";
					}
					script += "]";
					script += "\n			}";
				}
				script += "\n		}";
				tableSeparator = ",";
			}
			script += "]";
			separator = ",";
		}
		script += "\n	},\n\n";
		script += "	type: '";
		script += name;
		script += "'\n});";
		var scriptGR = new GlideRecord('sys_script_include');
		if (input.newRecord) {
			scriptGR.initialize();
			scriptGR.name = name;
			scriptGR.description = name;
			scriptGR.access = 'public';
			scriptGR.insert();
		} else {
			scriptGR.get('name', name);
		}
		scriptGR.setValue('script', script);
		scriptGR.update();
		data.sys_id = scriptGR.getUniqueValue();
}

All in all, not a huge number of changes, but just enough to make things work. I bundled all of the relevant parts into another Update Set that includes these various changes, which you can find here. This component is also a part of the larger SNH Data Table Widget collection, so eventually I will need to publish a new version of that collection out on Share as well.

Share this:

  • Click to print (Opens in new window) Print
  • More
  • Click to share on WhatsApp (Opens in new window) WhatsApp
  • Pocket
  • Click to share on Telegram (Opens in new window) Telegram
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Tweet
  • Share on Tumblr
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...
Posted in Cool Stuff | Tagged Content Selector, Form Fields, Scoped Application, Service Account, Update Set, Widget

Post navigation

Service Account Management, Part XVIII
Service Account Management, Part XIX

Recent Posts

  • Periodic Review, Part XI
  • Periodic Review, Part X
  • Periodic Review, Part IX
  • Periodic Review, Part VIII
  • Periodic Review, Part VII

Recent Comments

  • snhackery on Service Account Management, Part XVI
  • Jennifer Schoenhoeft on Service Account Management, Part XVI
  • snhackery on Service Portal Form Fields, Broken
  • Joe Blogs on Service Portal Form Fields, Broken
  • Joe Blogs on Service Portal Form Fields, Broken

Archives

  • February 2024
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023
  • March 2023
  • February 2023
  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022
  • August 2022
  • July 2022
  • June 2022
  • May 2022
  • April 2022
  • March 2022
  • February 2022
  • January 2022
  • December 2021
  • November 2021
  • October 2021
  • September 2021
  • August 2021
  • July 2021
  • June 2021
  • May 2021
  • April 2021
  • March 2021
  • February 2021
  • January 2021
  • December 2020
  • November 2020
  • October 2020
  • September 2020
  • August 2020
  • July 2020
  • June 2020
  • May 2020
  • April 2020
  • March 2020
  • February 2020
  • January 2020
  • December 2019
  • November 2019
  • October 2019
  • September 2019
  • August 2019
  • July 2019
  • June 2019
  • May 2019
  • April 2019
  • March 2019
  • February 2019
  • January 2019
  • December 2018

Categories

  • Cool Stuff
  • Discoveries
  • General
  • Hackery
  • Projects

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org

Subscribe to snhackery via Email

Enter your email address to subscribe to snhackery and receive notifications of new posts by email.

Useful ServiceNow links:
Main web site: https://servicenow.com
Developer site: https://developer.servicenow.com
Annual Conference:   https://knowledge.servicenow.com
%d