Content Selector Configuration Editor, Part VII

“Always do more than is required of you.”
George S. Patton

Well, we have come a long way since we first set to out to build our little Content Selector Configuration Editor. We have built the primary widget and quite a few modal pop-up widgets and have pretty much built out everything that you would need to maintain the configuration. The only thing left at this point is to actually save the data now that it has been edited. Not too long ago, we built a tool that saved its data in a Script Include by rewriting the script and updating a Script Include record. We can use that same technique here by building the script from the user’s input, and then storing it in the script column of a new or updated Script Include record. The first order of business, then, would be to build the script, starting with the initial definition of the class and its prototype:

var script = "var ";
script += name;
script += " = Class.create();\n";
script += name;
script += ".prototype = Object.extendsObject(ContentSelectorConfig, {\n";
script += "	initialize: function() {\n";
script += "	},\n";
script += "\n";

Assuming the name of your script was MyTestConfig, that would generate the following starting lines for your Script Include:

var MyTestConfig = Class.create();
MyTestConfig.prototype = Object.extendsObject(ContentSelectorConfig, {
	initialize: function() {
	},

Next, we need to build the Array of all of the defined Perspectives. This we can do via a simple loop through the Perspective data:

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";

Next, we will need to build the Array of all of the States, which will look very similar to what we just did for all of the Perspectives.

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";

Now it is time for the Tables, which is where things get a little more complicated. Here we have to loop through every defined Perspective, and then loop through every Table defined for that Perspective, and then loop through every defined State, and then if there are Buttons/Icons and/or Reference Pages, we will need to loop through all of those as well.

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				btnarray: [";
			var 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				}";
				lastSeparator = ",";
			}
			script += "],\n				refmap: {";
			lastSeparator = '';
			for (var key  in tableTable[tableState.name].refmap) {
				script += lastSeparator;
				script += "\n					";
				script += key;
				script += ": '";
				script += tableTable[tableState.name].refmap[key];
				script += "'";
				lastSeparator = ",";
			}
			script += "\n				}\n			}";
		}
		script += "\n		}";
		tableSeparator = ",";
	}
	script += "]";
	separator = ",";
}
script += "\n	},\n\n";

And finally, we have to close out the script with the type property and all of the closing characters.

		script += "	type: '";
		script += name;
		script += "'\n});";

That completes the creation of the script from the user’s input. Now we have to save it. If this is an existing record, then all we need to do is fetch it and update the script column, but if this is a new record, then we have a little bit more work to do.

var scriptGR = new GlideRecord('sys_script_include');
if (data.newRecord) {
	scriptGR.initialize();
	scriptGR.name = name;
	scriptGR.api_name = 'global.' + 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();

The reason that we grab the sys_id there at the end after the record has been saved is so that we can take the user to the saved Script Include once the record has been inserted/updated. We do that on the client-side in the code that is launched by the Save button.

$scope.save = function() {
	var missingData = false;
	if (c.data.config.perspective.length == 0) {
		missingData = true;
	} else if (c.data.config.state.length == 0) {
		missingData = true;
	} else {
		for (var p in c.data.config.perspective) {
			if (c.data.config.table[c.data.config.perspective[p].name].length == 0) {
				missingData = true;
			}
		}
	}
	if ($scope.form1.$valid && !missingData) {
		c.data.action = 'save';
		c.server.update().then(function(response) {
			window.location.href = '/sys_script_include.do?sys_id=' + response.sys_id;
		});
	} else {
		$scope.form1.$setSubmitted(true);
		spModal.alert('You must correct all form validation errors before saving');
	}		
};

This assumes that we are doing all of this in the main UI and not on some Portal Page, and to facilitate that, we add a menu item to the Tools menu so that we can launch this widget from within the primary UI.

Content Selector Configurator menu item definition

That’s about it for all of the parts and pieces necessary to make this initial version work. Certainly there are a number of things that we could do to make it a little better here and there, but overall I think it is a pretty good start. We should play around with it a bit and try building out a few different configurations, but for those of you who like to play along at home, here is an Update Set with what should be everything that you need to make this work.