Create a Copy button widget for Requested Item in Service Portal

TEdwards
Kilo Sage

Hello all,

We have a requirement to create a copy button for requests/requested items in the Service Portal to be used to copy closed requests so the user does not have to enter the same information into a new request. I have been researching what others have done, and have found:

1.It is suggested that there is already one OOB, however I do not find one in my personal dev instance when following the directions in How to make Copy button form expose on service portal?.

2. I have also found  Button to copy and insert a new ticket into table, but it is specific to incidents and I am not sure how to adapt it to requests, which have variables that can vary widely.

3. I also took at look at the UI action on Requests in the platform, which I thought might serve as a template for creating a widget, but I am not sure if it would work:

var helper = new GlideappScriptHelper();
var request = helper.copyRequest(current);
gs.addInfoMessage(gs.getMessage('Request copied successfully'));
action.openGlideRecord(request);

 

Has anyone done this before, or can someone assist with how I might do this? Is it possible to adapt 2. or 3. above to my purpose or can someone point me in the right direction for finding the OOB copy button from 1. above?

Any help will be appreciated. Thanks!

1 ACCEPTED SOLUTION

Chris Sanford
Giga Guru

Ok so yeah its like this widget to resubmit request on the ticket page, script include to build the URL, variable set called 'copy_variables_from_uri_ritm_id' with client script to parse the URL

Widget HTML:

<div ng-if="::c.data.show_button" class="panel panel-default b wrapper-md" ng-class="{'wrapper-md': options.native_mobile != 'true', 'wrapper-sm': options.native_mobile == 'true'}">
  <button name="resubmit" ng-disabled="c.resubmitting" ng-click="c.redirectToResubmit()" class="btn btn-primary btn-block ng-binding ng-scope">
    Resubmit Request
  </button>
</div>

Widget Client script:

function() {
  var c = this;
	c.resubmitting = false;
	c.redirectToResubmit = function() {
		c.resubmitting = true;
		c.server.get({
			resubmitting: true
		}).then(function(r) {
			top.window.location = r.data.resubmit_url;
		});
	}
}

Widget Server script:

(function() {
	var tableName = $sp.getParameter('table');
	var recordId = $sp.getParameter('sys_id');
	if(tableName && recordId) {
		var recordGr = new GlideRecord(tableName);
		recordGr.get(recordId);
		if(!input) {
			data.show_button = new CatalogVariableURLBuilder().hasVariableSet(recordGr, 'copy_variables_from_uri_ritm_id');
		}
	}
	if(input && input.resubmitting) {
		data.resubmit_url = new CatalogVariableURLBuilder().getResubmitURL('sc_cat_item', recordGr);
	}
})();

CatalogVariableURLBuilder script include:

var CatalogVariableURLBuilder = Class.create();
CatalogVariableURLBuilder.prototype = {
    initialize: function() {},

    getJsonFromGr: function(ritmGr) {
        var variablesObj = ritmGr.variables.getElements(false);
        var outputObj = {};
        for (var i = 0; i < variablesObj.length; ++i) {
            var variable = variablesObj[i];
            var question = variable.getQuestion();
            if (question.getValue() && question.getName()) {
                outputObj[question.getName()] = question.getValue();
            }
        }
		return JSON.stringify(outputObj);
    },
	
	hasVariableSet: function(ritmGr, variableSetName) {
		if(ritmGr.getTableName() != 'sc_req_item' || !ritmGr.getValue('cat_item')) {
			return false;
		}
		var ioSetItem = new GlideRecord('io_set_item');
		ioSetItem.addQuery('sc_cat_item', ritmGr.getValue('cat_item'));
		ioSetItem.addQuery('variable_set.internal_name', variableSetName);
		ioSetItem.query();
		return ioSetItem.hasNext();
	},
	
	getResubmitURL: function(catItemPageId, ritmGr) {
		var catItemSysId = ritmGr.cat_item.toString();
		return "?id=" + catItemPageId + "&sys_id=" + catItemSysId + "&sysparm_copy_from_ritm_id=" + ritmGr.getUniqueValue();
	},

    type: 'CatalogVariableURLBuilder'
};

Variable set:

find_real_file.png

Onload client script (in this variable set)

function onLoad() {
	
	var prop, propVal;
	//get parameter from URL
	var ritmId = getParameterValue('sysparm_copy_from_ritm_id');
	console.debug('sysparm_variables URI parameter value: ' + ritmId);
	if (!ritmId) {
		//If parameter is empty, stop here.
		console.debug('aborting due to a missing sysparm_copy_from_ritm_id URI parameter');
		return;
	}
	var getVariableJson = new GlideAjax('CopyVariablesAjax');
	getVariableJson.addParam('sysparm_name', 'getVariableJson');
	getVariableJson.addParam('sysparm_copy_from_ritm_id', ritmId);
	getVariableJson.getXMLAnswer(populateForm);
	
	function populateForm(variableJsonStr) {
		console.debug('sysparm_copy_from_ritm_id variables JSON object: ' + variableJsonStr);
		var variableJson = JSON.parse(variableJsonStr);
		for(var formVariable in variableJson) {
			if(g_form.hasField(formVariable)) {
				g_form.setValue(formVariable, variableJson[formVariable]);
			}
		}
	}
}

function getParameterValue(name) {
	name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
	var regexS = "[\\?&]" + name + "=([^&#]*)";
	var regex = new RegExp(regexS);
	//Check if portal (gel=undefined) or classic UI, and get the HREF data appropriately in either case.
	var hrefPath = (typeof gel == 'undefined') ? this.location.href : top.location.href;
	var results = regex.exec(hrefPath);
	
	console.debug('HREF: ' + hrefPath);
	
	if (results == null) {
		return "";
	} else {
		return unescape(results[1]);
	}
}

CopyVariablesAjax script include client callable:

var CopyVariablesAjax = Class.create();
CopyVariablesAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
	
	getVariableJson: function() {
		var ritmId = this.getParameter('sysparm_copy_from_ritm_id');
		var ritm = new GlideRecord('sc_req_item');
		if(!ritm.get(ritmId)) {
			return "{}";
		}
		return new CatalogVariableURLBuilder().getJsonFromGr(ritm);
	},

    type: 'CopyVariablesAjax'
});

Ok now you just need to, add the variable set to any catalog item that you want this functionality to work in, and also you should add the widget to the ticket page where the button needs to be.

View solution in original post

10 REPLIES 10

Ah yes sorry about that I didn't read your requirement well enough but good catch with state == '3'.

That's strange I'm not able to reproduce that issue in my instance with date variables, even with different date formats. Just to clarify are these date variables or date/time? It's possible it's a time zone issue with date/time as the Ajax is probably going to send date/times back in the UTC time zone. Let me know if that's the case and I'll see if I can reproduce or fix the issue.

Hmmm...they are just date variables. 

 

The time zone idea might be valid though. I also thought about caching, but I get the same result in an incognito window. 

So I figured out why the date was showing the day before, although it doesn't make a lot of sense to me. I had to change my date setting, which is personally configurable in my profile, to match the system date configuration. This might be a bit of a problem since the user can choose their date format themselves...It also fixed the problem with the date validation script. Thanks a lot for your help with this! 

Hi @Chris Sanford 

is there is any possibility to not having Variable set added to Catalogs and achieve this? because it will become a mandatory task when we build new catalog, so we need to add it and having added VSet to all catalogs also customer agreeing. Please suggest.

is there is any possibility to not having Variable set added to Catalogs and achieve this? because it will become a mandatory task when we build new catalog, so we need to add it and having added VSet to all catalogs also customer agreeing. Please suggest.