Collaboration Store, Part XLIX

“Don’t tell me the sky’s the limit when there are footprints on the moon.”
Paul Brandt

Last time, we got started on the global UI Script that will run on the upload.do page to take over the page and repurpose it for our needs. Our interest is to convert an Update Set XML file back into an actual Update Set so that we can apply the Update Set, installing a shared Scoped Application. The upload.do page will help set us on that path, but we need our script to implement just a few little modifications. We got as far as launching the GlideAjax process which will fetch the Update Set XML file details from the server side, and now we need to build the function that will process the results coming back and do something with them. The “answer” returned will be a JSON string, so we just need to turn that back into an object so that we can extract the values. We can do just that much and verify the results by popping an alert using one of the values that should be found in the resulting object, the name of the XML file.

function submitForm(answer) {
	var app = {};
	try {
		app = JSON.parse(answer);
	} catch (e) {
		alert('Error parsing JSON response from server: ' + e);
	}
	alert(app.fileName);
}

There is not much here, but we can push the old Install button on the version page, just to verify that all is well so far.

Verification of the server side code, Ajax call, and JSON parsing

Although that wasn’t much in the way of code, it did verify that the server side Script Include that we built a while back does seem to work, as well as the Ajax call that we built last time and the JSON parsing that we just added today. At this point, we have built a UI Action that sends us over to the upload.do page, taken over the page for our own purposes, hiding the original content and adding content of our own, called back to the server side for the XML file information, and demonstrated that the XML file information has indeed been transferred over to the client side. Now that we have it in hand, we have to use it to emulate a file on the local system and send that faux file back over to the server side as an element of a form post. This is where things get a little tricky.

While digging around trying to find a way to do this, I came across the DataTransfer object. This object contains a list of File objects, and you can add to the list using the add() method of the items property. These two lines of code create a new DataTransfer object and add a new file to the empty list using the data that we retrieved from the Ajax call.

var fileList = new DataTransfer();
fileList.items.add(new File([app.xml], app.fileName, {type: 'application/xml'}));

Now that we have our “file” in a file list, we can populate the files attribute of the input element using the files attribute of our DataTransfer object.

document.getElementById('attachFile').files = fileList.files;

Now we just have to submit the form and see what happens. Actually, I did that, and nothing happened. It seems that there are a couple of other form fields that also need to be valued. What seems weird to me is that, if you look at the source code for the page, those fields do start out with a value, but somewhere along the line those values were removed before the form was posted, so I had to add a couple more lines to put those values back.

document.getElementsByName('sysparm_referring_url')[0].value = 'sys_remote_update_set_list.do?sysparm_fixed_query=sys_class_name=sys_remote_update_set';
document.getElementsByName('sysparm_target')[0].value = 'sys_remote_update_set';

Now we can submit the form, which is just one more line of code.

document.forms[0].submit();

All together, our new submitForm function looks like this:

function submitForm(answer) {
	var app = {};
	try {
		app = JSON.parse(answer);
	} catch (e) {
		alert('Error parsing JSON response from server: ' + e);
	}
	var fileList = new DataTransfer();
	fileList.items.add(new File([app.xml], app.fileName, {type: 'application/xml'}));
	document.getElementById('attachFile').files = fileList.files;
	document.getElementsByName('sysparm_referring_url')[0].value = 'sys_remote_update_set_list.do?sysparm_fixed_query=sys_class_name=sys_remote_update_set';
	document.getElementsByName('sysparm_target')[0].value = 'sys_remote_update_set';
	document.forms[0].submit();
}

And that completes (for now) our new global UI Script. Here is the entire script, including all of the work that we did last time out.

if (window.location.pathname == '/upload.do' && window.location.search.startsWith('?attachment_id=')) {
	waitForPageLoad();
}

function waitForPageLoad() {
	if (document.getElementById('attachFile')) {
		installApplication();
	} else {
		setTimeout(waitForPageLoad, 100);
	}
}

function installApplication() {
	var originalContent = document.getElementsByClassName('section-content')[0];
	originalContent.style.visibility = 'hidden';
	var newContent = document.createElement('div');
	newContent.innerHTML = '<h4 style="padding: 30px;">&nbsp;<img src="/images/loading_anim4.gif" height="18" width="18">&nbsp;Uploading Update Set XML file ...</h4>';
	originalContent.parentNode.insertBefore(newContent, originalContent);
	var attachmentId = window.location.search.substring(15);
	var ga = new GlideAjax('x_11556_col_store.ApplicationInstaller');
	ga.addParam('sysparm_name', 'getXML');
	ga.addParam('attachment_id', attachmentId);
	ga.getXMLAnswer(submitForm);
}

function submitForm(answer) {
	var app = {};
	try {
		app = JSON.parse(answer);
	} catch (e) {
		alert('Error parsing JSON response from server: ' + e);
	}
	var fileList = new DataTransfer();
	fileList.items.add(new File([app.xml], app.fileName, {type: 'application/xml'}));
	document.getElementById('attachFile').files = fileList.files;
	document.getElementsByName('sysparm_referring_url')[0].value = 'sys_remote_update_set_list.do?sysparm_fixed_query=sys_class_name=sys_remote_update_set';
	document.getElementsByName('sysparm_target')[0].value = 'sys_remote_update_set';
	document.forms[0].submit();
}

At this point, all that we have accomplished is to load the Update Set. We still have not installed anything. The Update Set still has to be Previewed and then Committed before the version is actually installed. The ultimate goal will be for the operator to be able to just click on that Install button and have everything else takes care of itself, including marking the version record as Installed (and any other version records of the app as not installed). Whether or not we can do all of that without human intervention has yet to be determined, but we have at least accomplished that first step of turning the XML file back into an Update Set. Next time, we will see where we can go from here.