Prompt Alert Message and Follow Up Field in Record Producer

chrisds
Kilo Contributor

Hello, 

I have a record producer where you choose a user (reference field on sys_user table) you want to offboard. I would like to make the record producer more robust by alerting the submitter if the chosen user currently has a role and consequently prompting a follow up field for the submitter to select the user's replacement. 

For the prompt(Catalog Client Script), I have the following script...

 	var usrRole = new GlideRecord('sys_user_has_role');
 		usrRole.addQuery('user', opened_for);  
 		usrRole.addQuery('active', true);
 		usrRole.query();
 	if(usrRole == '') {
     return;
 } else {
     g_form.showFieldMsg('opened_for', 'User has roles', 'error');
 }

I am getting the prompt, however the problem is that it is appearing for users that do NOT have a role either. Can you suggest how I can edit this script so that it only applies to users that have a record in the sys_user_has_role table?

 

Secondly, I would like my follow up field for a replacement user to appear based on the same logic. For the Catalog UI Policy I have the following script...

function onCondition() {
	var usrRole = current.variables.opened_for.hasRoles();
	if(usrRole == 'true') {
    return;
}
}

however this script is causing an Javascript error on the record producer. Can you provide some suggestions on these problems?

 

Thank you and be safe everyone.

1 ACCEPTED SOLUTION

Jim Coyne
Kilo Patron

There's a bit of a logic error in your script.  You perform a query but you don't actually look at the results.  Kinda like getting a letter without looking inside.  Changing your logic to:

//check to see if there are any roles returned for that user
if (usrRole.next()) {
    g_form.showFieldMsg('opened_for', 'User has roles', 'error');
} else {
    return;
}

...should fix it.  HOWEVER, as Vikram mentioned, we shouldn't be using GlideRecord queries on the client-side.  That is where AJAX calls come in.

To get what you want, you would need to either add a function to an existing custom Script Include, or create a new one:

Name: u_CatalogAjaxUtils
Client callable: checked
Script:

var u_CatalogAjaxUtils = Class.create();
u_CatalogAjaxUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
	
	doesUserHaveARole: function() {
		var result = false;
		var userId = this.getParameter("sysparm_id");
		if (userId == "") {
			return false;
		}

		var count = 0;
		//using GlideAggregate is more efficient
		var  ga = new GlideAggregate("sys_user_has_role");
		ga.addAggregate("COUNT", "user");
		//you'll probably want to exclude snc_internal and snc_external from your checks
		ga.addEncodedQuery("role.name!=snc_internal^role.name!=snc_external^user=" + userId);
		ga.query();
		if (ga.next()){
			count = ga.getAggregate("COUNT", "user"); 
		}
		if (count > 0) {
			result = true;
		}

		return result;
	},

    type: 'u_CatalogAjaxUtils'
});

Then you call the Script Include from an onChange Catalog Client Script (on the opened_for variable):

function onChange(control, oldValue, newValue, isLoading) {
    if (isLoading) {
        return;
    }

    //clear any previous messages
    g_form.hideFieldMsg("opened_for", true);
    //clear the Replacement field
    g_form.setValue("replacement", "");

    if (newValue == "") {
        u_hideReplacementField();
        return;
    }

    var ga = new GlideAjax("u_CatalogAjaxUtils");
    ga.addParam("sysparm_name", "doesUserHaveARole");
    ga.addParam("sysparm_id", newValue);
    ga.getXMLAnswer(u_showOrHideReplacementField);

    //callback function to process the response returned from the server
    function u_showOrHideReplacementField(response) {
        if (response == "true") {
            //add a message
            g_form.showFieldMsg("opened_for", "Please select a replacement", "info");
            //show the Replacement field
            g_form.setMandatory("replacement", true);
            g_form.setDisplay("replacement", true);
        } else {
            //hide the Replacement field
            u_hideReplacementField();
        }
    }

    function u_hideReplacementField() {
        //clear then hide the Replacement field
        g_form.setValue("replacement", "");
        g_form.setMandatory("replacement", false);
        g_form.setDisplay("replacement", false);
    }
}

 

 

So you end up with something like this when the selected user has a role:

find_real_file.png

...and this when the user does not have a role:

find_real_file.png

Adding a UI Policy to initially hide the Replacement field should finish things off for you.

View solution in original post

4 REPLIES 4

Vikram Reddy
Giga Guru

Hi chrisds,

As a best practice it is not advised to use glide query in client script, instead create a GlideAjax that calls script include where you perform your validations and return value. You can reuse the same code in UI policy 

 

Thank you,

Vikram

Jim Coyne
Kilo Patron

There's a bit of a logic error in your script.  You perform a query but you don't actually look at the results.  Kinda like getting a letter without looking inside.  Changing your logic to:

//check to see if there are any roles returned for that user
if (usrRole.next()) {
    g_form.showFieldMsg('opened_for', 'User has roles', 'error');
} else {
    return;
}

...should fix it.  HOWEVER, as Vikram mentioned, we shouldn't be using GlideRecord queries on the client-side.  That is where AJAX calls come in.

To get what you want, you would need to either add a function to an existing custom Script Include, or create a new one:

Name: u_CatalogAjaxUtils
Client callable: checked
Script:

var u_CatalogAjaxUtils = Class.create();
u_CatalogAjaxUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
	
	doesUserHaveARole: function() {
		var result = false;
		var userId = this.getParameter("sysparm_id");
		if (userId == "") {
			return false;
		}

		var count = 0;
		//using GlideAggregate is more efficient
		var  ga = new GlideAggregate("sys_user_has_role");
		ga.addAggregate("COUNT", "user");
		//you'll probably want to exclude snc_internal and snc_external from your checks
		ga.addEncodedQuery("role.name!=snc_internal^role.name!=snc_external^user=" + userId);
		ga.query();
		if (ga.next()){
			count = ga.getAggregate("COUNT", "user"); 
		}
		if (count > 0) {
			result = true;
		}

		return result;
	},

    type: 'u_CatalogAjaxUtils'
});

Then you call the Script Include from an onChange Catalog Client Script (on the opened_for variable):

function onChange(control, oldValue, newValue, isLoading) {
    if (isLoading) {
        return;
    }

    //clear any previous messages
    g_form.hideFieldMsg("opened_for", true);
    //clear the Replacement field
    g_form.setValue("replacement", "");

    if (newValue == "") {
        u_hideReplacementField();
        return;
    }

    var ga = new GlideAjax("u_CatalogAjaxUtils");
    ga.addParam("sysparm_name", "doesUserHaveARole");
    ga.addParam("sysparm_id", newValue);
    ga.getXMLAnswer(u_showOrHideReplacementField);

    //callback function to process the response returned from the server
    function u_showOrHideReplacementField(response) {
        if (response == "true") {
            //add a message
            g_form.showFieldMsg("opened_for", "Please select a replacement", "info");
            //show the Replacement field
            g_form.setMandatory("replacement", true);
            g_form.setDisplay("replacement", true);
        } else {
            //hide the Replacement field
            u_hideReplacementField();
        }
    }

    function u_hideReplacementField() {
        //clear then hide the Replacement field
        g_form.setValue("replacement", "");
        g_form.setMandatory("replacement", false);
        g_form.setDisplay("replacement", false);
    }
}

 

 

So you end up with something like this when the selected user has a role:

find_real_file.png

...and this when the user does not have a role:

find_real_file.png

Adding a UI Policy to initially hide the Replacement field should finish things off for you.

Thanks a ton Jim. I have limited knowledge on GlideAjax and have been using GlideRecord. This use case has served as a good introduction to understanding Ajax. Much appreciated sir!

You are very welcome.

Technically it will work in the "regular" UI, but the use of GlideRecord in the Service Portal will not.  Your use case may not include the Service Portal right now but it's better using GlideAjax because if you want to use the Portal in the future, your work is already down, otherwise everything needs to be re-written.  And it does give a better user experience by not locking up the page while the data is retrieved