L
ast week I had a request come in from a client where they wanted to require users to accept certain terms and conditions before ordering a specific catalog item. I have seen this type of request before, but I don’t think I’ve really seen an elegant solution to it…or anything that could really be called a solution at all :). Usually you end up with some combination of a wizard, some custom form, and some crazy scripts that make no sense to anyone but the person who created it. Then I realized that I had just written about a solution a week or so ago when I wrote about how to create a UI Page popup using GlideDialogWindow! The specific application of the solution I wrote about was a little bit different, but the basic pieces were identical. By making some basic tweaks to a solution I already knew about, I was able to come up with what I think is a really nice way to require acceptance of some terms before a user orders a catalog item. This same method could also be used on other forms and tables in ServiceNow.
The first part of this solution is to set up a client-side trigger for your terms dialog. For this example, we assume that the Terms dialog only needs to be displayed for a particular catalog item so our trigger can be in an ‘onLoad’ catalog client script set against the item in question. The script simply needs to create and render the dialog when the item (or potentially variables) load. It also needs to point to a UI Page (by name) that should load within the dialog. It’s the UI Page that will contain all of the specific form elements and terms. If you want your dialog to redirect to a specific location if the ‘Cancel’ button is clicked you can include the ‘cancel_url’ preference as shown below…
Name: Load Terms
Type: onLoad
Script:
//Initialize and open the Dialog Window
var dialog = new GlideDialogWindow('terms_and_conditions_dialog'); //Render the dialog containing the UI Page 'terms_and_conditions_dialog'
dialog.setTitle('Terms and Conditions'); //Set the dialog title
dialog.setSize(600,600); //Set the dialog size
dialog.removeCloseDecoration(); //Remove the dialog close icon
dialog.setPreference('cancel_url', 'catalog_home.do?sysparm_view=catalog_default'); //Set optional cancel redirect URL
dialog.render(); //Open the dialog
}
Now that we’ve set up a trigger, we need to make sure that we have a UI Page with the correct name to be triggered. Notice in the script above that we’re looking for a UI Page named ‘terms_and_conditions_dialog’ in our dialog client script. The correct name is the only absolute in the UI Page. It needs to match the name given in the client script trigger.
The other elements of the UI Page are up to you. Typically when I’ve seen a terms and conditions page, the page contains the terms, an ‘Agree’ checkbox of some sort, and buttons to agree or cancel. I’ve commented those portions of code below. The buttons both trigger specific client-side functions that make sure the ‘Agree’ checkbox is checked (for the ‘OK’ button) and redirect back to the default homepage – or the homepage passed in via the ‘cancel_url’ parameter (for the ‘Cancel’ button). The only thing I haven’t provided is the actual terms and formatting. In order to produce that in a nice HTML format, I actually cheated and pasted my terms into the HTML editor on a blank KB article. Then I could get it formatted like I want and then just toggle to the code editor and copy the HTML into the correct place in my UI Page…
HTML
<!-- Get values from dialog preferences passed in -->
<g:evaluate var="jvar_cancel_url"
expression= "RP.getWindowProperties().get('cancel_url')" />
<input type="hidden" id="cancel_url" value="${jvar_cancel_url}" />
<table width="100%">
<tr>
<td>
<div style="width:584px; height:400px; overflow:auto; border: 1px solid gray;">
ENTER YOUR TERMS HERE...
</div>
</td>
</tr>
<tr>
<td>
<div style="margin-top: 10px;">
<!-- Pull in 'ui_checkbox' UI Macro for accept checkbox -->
<g:ui_checkbox id="accept_terms" name="accept_terms" value="false"/>
<label for="load_demo">I accept these terms and conditions.</label>
</div>
</td>
</tr>
<tr>
<td colspan="2">
</td>
</tr>
<tr id="dialog_buttons">
<td colspan="2" align="right">
<!-- Pull in 'dialog_buttons_ok_cancel' UI Macro for submit/cancel buttons -->
<g:dialog_buttons_ok_cancel ok="return termsOK()" cancel="termsCancel()" ok_type="button" cancel_type="button"/>
</td>
</tr>
</table>
</g:ui_form>
Client Script
//Gets called if the 'OK' dialog button is clicked
//Make sure terms have been accepted
var terms = gel('accept_terms').value;
if(terms != 'true'){
//If terms are false stop submission
alert('Please accept the terms and conditions to continue your order.');
return false;
}
//If accept checkbox is true do this...
GlideDialogWindow.get().destroy(); //Close the dialog window
//g_form.setValue('myvar', true); //Optionally set the value of a variable or field indicating acceptance
}
function termsCancel(){
//Redirect gets called if the 'Cancel' dialog button is clicked
if($('cancel_url').value != 'null'){
window.location = $('cancel_url').value;
}
else{
window.location = 'home.do'; //Redirect to default homepage
}
}
Hi,
The timing of this is awesome.
I tried this but the buttons doesn’t seem to check if the checkbox is checked or not.
also is this possible on the onsubmit function?
was playing around with it and I can’t get it to render, it always proceeds to the checkout
thanks for all your help
I posted the wrong version of code to validate the checkbox. It’s updated now. The ‘onSubmit’ would be a little bit trickier. You would have to check for the value of a variable to see if the acceptance had already been done. Then if it hadn’t, pop the dialog. If you created a ‘Yes/No’ variable defaulted to ‘No’ and added it to your item, that could be done like this…
//Check 'terms' variable to see if it has been marked
var termsOK = g_form.getValue('terms_ok');
//If not then pop the dialog and stop the submission
if(termsOK == 'No'){
//Initialize and open the Dialog Window
var dialog = new GlideDialogWindow("demo_terms_and_conditions_dialog"); //Render the dialog containing the UI Page 'demo_terms_and_conditions_dialog'
dialog.setTitle("Terms and Conditions"); //Set the dialog title
dialog.setSize(600,600);
dialog.removeCloseDecoration();
dialog.render(); //Open the dialog
return false;
}
}
Then you would need to modify your UI page client script to set the value of that variable on acceptance. You would also want to change the ‘Cancel’ function to simply close the dialog instead of redirecting…
//Gets called if the 'OK' dialog button is clicked
//Make sure terms have been accepted
var terms = gel('accept_terms').value;
if(terms != 'true'){
//If terms are false stop submission
alert("Please accept the terms and conditions to continue your order.");
return false;
}
//If accept checkbox is true do this...
GlideDialogWindow.get().destroy(); //Close the dialog window
g_form.setValue('terms_ok', 'Yes');
}
function termsCancel(){
//Gets called if the 'Cancel' dialog button is clicked
GlideDialogWindow.get().destroy(); //Close the dialog window
}
This works fine in my testing. The only thing I wish I could add would be to have the submission automatically happen once the user approved the dialog. That would involve capturing the original submit action and calling that again. I couldn’t get that to work when I tested. If somebody else can figure it out I’d be happy to post it.
Thanks Mark
It’s working now, but needs to click submit button again after the dialog closes ,
Hi Mark,
Quick Question
Can you control the position where the dialog pops out?
thanks
You can. There is a ‘moveTo’ function you can utilize to move the dialog to any x,y coordinate within the frame. The dialogs are all set up to naturally want to be centered when they are rendered so you have to set a callback function to set this positioning when the dialog finishes loading. You need to add the callback function before your ‘dialog.render’ call.
If I wanted to position the dialog in the top-left corner of my frame I could do this…
dialog.moveTo('0', '0');
});
I just tested this again. It looks like ‘setLoadCallback’ is not available for standard dialogs, just quickforms.
Hi Mark,
This works great. I was able to get the order to submit when clicking the OK button by adding a few lines to the Client Script. Hope this helps others…
//Gets called if the 'OK' dialog button is clicked
//Make sure terms have been accepted
var terms = gel('accept_terms').value;
if(terms != 'true'){
//If terms are false stop submission
alert('Please accept the terms and conditions to continue your order.');
return false;
}
//If accept checkbox is true do this...
GlideDialogWindow.get().destroy(); //Close the dialog window
//g_form.setValue('myvar', true); //Optionally set the value of a variable or field indicating acceptance
// This will submit the order
gel("order_now").onclick = "";
var item_guid = gel("sysparm_item_guid");
if (item_guid) {
item_guid = item_guid.value;
}
g_cart.order(gel("sysparm_id").value, getQuantity(), item_guid);
}
function termsCancel(){
//Gets called if the 'Cancel' dialog button is clicked
window.location = 'catalog_home.do?sysparm_view=catalog_default'; //Redirect to catalog homepage
}
Awesome! Thanks for sharing.
Hi Michael,
i tried this script but when i click on Ok button its not submitting order it is just collapsing window,please guide me to proceed.
Regards,
Bharath
This is working wonderfully for us in IE & Firefox.. but will not work (dialog pop up stays up and proceeds to fill the screen) in both Chrome & Safari. Any ideas on that?
I just found a fix for this problem. The UI page above uses dialog buttons that need to have a special setting so that the button acts as a regular button instead of trying to perform a form submission…and redirecting the user. Take a look at your ‘dialog_buttons’ line in your UI page and make sure that it has both an ‘ok_type’ and a ‘cancel_type’ of ‘button’ as shown here…
I am getting this XML error……
This page contains the following errors:
error on line 1 at column 47: Namespace prefix g on ui_form is not defined
error on line 12 at column 40: Namespace prefix g on ui_checkbox is not defined
error on line 24 at column 69: Namespace prefix g on dialog_buttons_ok_cancel is not defined
Below is a rendering of the page up to the first error.
ENTER YOUR TERMS HERE… I accept these terms and conditions.
I’m not sure why this would be necessary, but you can try wrapping the UI page script in tags like this…
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
EXISTING CODE HERE
</j:jelly>
Hi Mark,
Quick Question
Is there an option to control where the user can be returned to if they choose No.
We have a requirement that the user be returned to the homepage, as a hard cancel.
Would we need to build up a URL? Are we able to embed that into the dialog function, much the same as the setCallBack.
Thanks
Good idea. I’ve modified the solution above to include an optional ‘cancel_url’ preference that you can pass in when you render the dialog. The default cancel redirect just goes to the regular user homepage unless you pass in an override redirect URL of your choosing. Please let me know how this works for you.
See this page for details on passing parameters into a dialog…
https://servicenowguru.wpengine.com/system-ui/glidedialogwindow-advanced-popups-ui-pages/
HI Mark,
Is there any onsubmit client script for Confirmation message like: when user clicks on OK it going to URL(incident form),similarly when clicks on cancel stay in the same page?
Thanks in advance
Yes, you’ve got a client-side function that runs when you click the ‘OK’ button. In this case, it’s called ‘termsOK’. You could modify that button to redirect you to another location if you want.
Thank You!
Mark,
Thank you for the link. This is exactly what I need. Now I just need to put on my limited scripting hat and try to make it work for me. I guess my only concern here is, just like with any Service Now customization, what will the impact of applying the script be to any future updates?
Hey David, good question. This particular customization just sits on top of anything else you build so it’s very unlikely that it would ever cause any upgrade issues since it’s all custom code rather than modifications to system code.
I’ve been using GlideDialogWindow and have the call
gdw.removeCloseDecoration();
The problem I see is that even though the button no longer appears a user can hit the ‘Esc’ key and close the dialog without hitting Ok or Cancel which is desired since certain behavior needs to be done by those buttons. Do you know how to intercept this key or prevent it? I was going to try to overload the _eventKeyUp function on GlideDialogWindow but have no clue if this is possible.
Thanks,
Joe
A couple of thoughts here. If someone does hit the escape key to try and work around the process, I would consider that the same as acceptance really. If you’re not recording the fact that they accepted or not, then it really wouldn’t matter if they tried to cheat the system that way. If you really want to secure that, then I would probably advise you to modify the dialog (as shown above) to set a field on your form (which should be hidden) that indicates their acceptance. Then you could add a secondary, ‘onSubmit’ client script to check whether or not that field had been checked so that you would have a backup validation just in case. Your ‘onSubmit’ script could prevent the form submission if they didn’t accept.
Thanks Mark. That’s a clever way of doing it. I saw your post after I re-posted my solution. That seems more solid as it guarantees beyond the escape character issue. So if the dialog is destroyed some other way beyond the escape character the okay or cancel determination of if they have been ran can still be caught.
Thanks!
Joe
No problem. Whatever you choose to use, I’m glad you posted your solution since it might be helpful to someone else in the future. Thanks!
Thinking about this more, I like both solutions together. That way the dialog doesn’t go away with escape and if it goes away by other means it is still caught. Awesome.
I just figured this out on my own.
I added a keyup event handler in the one and only text area on the ui-page.
The function then looks like the following…
function keyup() {
//alert(“got a key = ” + event.keyCode);
// this will stop further processing of the event
// this is needed since the escape key will close the window
event.stopImmediatePropagation();
}
So without this if a user presses the escape key neither the ok or cancel buttons are invoked and the dialog goes away. However with it the escape key is now ignored.
Mark,
Do you know of any API for server side dialog window which can be triggered from a script action? My goal is to replicate something similar to UI 14 pop-up, but with a different intention in mind.
I don’t know of anything SN has created that allows you to invoke dialogs from the server side unfortunately.
This is great! The only issue I’m having is it doesn’t appear in the center of the screen but in the lower right with the options off the screen. It seemed to work earlier but not now. Any ideas?
I’m having trouble getting this to work with a Wizard Launcher. Is there something I need to modify to get this to work?
Nevermind. Answer is (obviously) to create a Wizard Client Script rather than the Catalog Client Script. Catalog Client Script will allow you to select the wizard but will not function. The same exact Catalog Client Script can be used for the Wizard Client Script and it functions as intended. Thanks Mark, this is great.
You’re welcome, I’m glad you’ve got it working!
Hi,
This script is very helpful. I am trying to get this to work on an Order Guide in Eureka. It works well except the following script that simulates that order submission.
gel("order_now").onclick = "";
var item_guid = gel("sysparm_item_guid");
if (item_guid) {
item_guid = item_guid.value;
}
g_cart.order(gel("sysparm_id").value, getQuantity(), item_guid);
I inspected the element of the “Check Out” button in Order Guide, it does not have an element id. Also, all the references in the above code snippet is not in the Order Guide. Is there a way to make this work on Order Guide – i.e. once the user accepted the Term and Condition, the order is submitted?
Thanks and regards,
Joel
Hi all – is there another call besides g:dialog_buttons_ok_cancel that will display just an OK button? I can’t seem to find a reference for it. The reason would be in situations where you want to use a better looking, formatable UI page instead of an ugly javascript alert to impart info to the user.
Cheers,
G
I don’t think there’s an existing call like this just for an ‘OK’ button, but it would be very easy to create one. You can find the ‘dialog_buttons_ok_cancel’ UI macro record in your system and then use that as a template to create your own custom UI macro with just the one button. Once you’ve done that you can reference it in the same way by whatever name you choose for the macro.
Mark,
This works great and I’ve put it to use on our cell phone request by putting our Wireless Policy on the screen that the user has to accept before submitting the request. Now our HR department would like us to capture the user acceptance in the activity section RITM. They would even like to go as far as capturing the verbiage of the Wireless Policy (UI page) in the activity section of the RITM. That way, in the event the policy is updated in the future then we know what version of the policy the user had accepted at that time. Can this be done, and if so, how?
Thanks,
Chris
In that case, it might be easier just to go with a checkbox and an HTML variable on the catalog item. This would allow you to capture point-in-time terms pretty easily. You could also set up the dialog to write the HTML terms to an HTML field on the RITM form, but you’re essentially getting that with the variable approach without any fancy scripting. You’ll want to restrict the ‘Create roles’ to ‘nobody’ on the HTML variable if you go that route.
Hello, Thanks for this is exactly what we are looking for. One odd thing is the popup renders halfway down the page and then you have to scroll back up to Add to Basket. This is only affecting IE and works seamlessly in Chrome. Any ideas?
Sounds like maybe a timing issue in IE. You might try using ‘setTimeout’ to slightly delay the rendering of the terms and conditions page. You can google ‘setTimeout’ to see how that is done.
Hi,
For me close button is working on the homepage loading page(which should not be) and the same close (X) is not shown if I open in new page. Can you please advise me, X should not be available even if I load on the same page
We have found recently that when Cancel is clicked on this UI popup it does not cancel or redirect to the catalogue. It seems to work fine in Chrome. Anyone else had this issue?