Is it possible for a user to submit a survey by clicking on a hyperlink from an email?

patricklatella
Mega Sage

Hi all,

I'm looking to configure a survey that is sent to the Caller when an INC is closed.  I want to enable the recipient of the email to click an emoji (or image or whatever) that is a hyperlink to their survey instance in Service Portal, however with their selection captured...so that if the user does nothing else, their survey response is still captured.  

I'm thinking about a URL that passes the survey answer when the page is loaded automatically.

I'm seeing this as an example of the URL when the user just views their survey instance:

.service-now.com/sp?id=take_survey&instance_id=6d1842f11bc23010f3acfe6fdc4bcb28

 

My survey only has one question, so I'm looking to see if the answer can be passed in the URL, and if possible, for the user to not even have to click a "submit survey" button...just loading the URL could log the instance response.

Is this possible?  thanks!

 

 

1 ACCEPTED SOLUTION

patricklatella
Mega Sage

I was able to achieve this using a custom widget (Public = True) on a new portal page (also Public = True).

The widget parses the URL and runs the necessary logic to update the Survey record.

Here's the widget script in case anyone wants to create a similar solution.  The other part of the solution was creating a new survey notification with graphics included that link the user to the portal page.  The URL contains the survey instance sys_id as well as the value to be passed to the survey question.  The user does not need to be logged into ServiceNow for this to work, and no further action is required after the user clicks their selection in the email.  

This solution also allows the user to enter an additional comment which is mapped to a 2nd survey question.

The widget automatically closes the survey, so if the user clicks a link in the email again they are informed their survey has already been completed.

Works great for what I needed.

 

Custom Survey Widget Script (put this on a public page in the portal)

HTML

<div class="panel panel-default">
<div class="panel-heading">
<div class="form-group">

<!--Survey Completed-->
<div ng-if="data.surveyComplete == 'yes'" class="form-group">
<label>Your survey has already been completed. Thank you for your feedback.</label>
</div>

<!--Survey Canceled-->
<div ng-if="data.surveyCanceled == 'yes'" class="form-group">
<label>Your survey has been canceled.</label>
</div>

<!--Thank You -->
<div ng-if="data.showThankYou == 'yes'" class="form-group">
<label>Thanks so much for the feedback.<br><br>
If you have a moment, please add any additional comments to help us improve our service.</label><br>
<center><textarea rows = "5" cols = "60" name = "description" ng-model="c.data.short_description">
Optional comment
</textarea></center><br>
</div>

<!--Description String field for 5star-->
<div ng-if="data.showWidget == 'yes'" class="form-group">
<div>
<center><img src="yourImage_survey_5stars.png" width="128" height="38"/></center>
</div>
<div class="bigtext">
Awseome! We got it.
</div><br>
<label>
If you have a moment, tell us how it went so we can keep it up!</label><br>
<center><textarea rows = "5" cols = "60" name = "description" ng-model="c.data.short_description">
Optional comment
</textarea></center><br>
<!--<label>
If you have a moment, tell us how it went so we can keep it up!</label>
<input class="form-control" ng-model="c.data.short_description">-->
</div>

<!--The Submit button-->
<div ng-if="data.showSubmit == 'yes'" class="form-group">
<input class="btn btn-primary btn-block" ng-click="c.addItem()" type="submit" value="Enter your comments above and click here to Submit">

<!--popup modal-->
<script type="text/ng-template" id="modalTemplate3">

<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Your Text</h4>
</div>
<div class="panel-body wrapper-xl">
Thank you for the additional comments.<p>
You will now be redirected to the Service Portal Home page.<p>
If you are not currently logged in to ServiceNow, you will be directed to the whereever you link the user.
</div>
</div>
</div>
</script>
<!--end popup modal-->
</div>
</div>
</div>

 

CSS

.bigtext {
font-size: 20px;
}

 

Client

function($scope, $rootScope, spUtil, $window, $uibModal) {
/* widget controller */
var c = this;
//Triggered by the "Submit" button
c.addItem = function(){

//var user = c.data.user.value;

var desc = c.data.short_description;
//alert('desc is '+desc);

//Give alert if nothing entered
if(desc == undefined){
alert('Please enter your feedback before submitting.');
return;
}

//this calls the server script
c.server.update().then(function(response){
//alert('c.data.short_description '+c.data.short_description);

//popup modal
c.modalInstance = $uibModal.open({
templateUrl: 'modalTemplate3',
scope: $scope
});

//Redirect user after submit
setTimeout(myFunction, 4500);
function myFunction(){
$window.location.href = 'https://yourInstance.service-now.com/sp';//enter redirect URL
}
})
}
}

 

Server

(function() {
/* populate the 'data' object */
/* e.g., data.table = $sp.getValue('table'); */

//this is parses values from URL
// for my example, using testing page: sp?id=your_instance_page&surveyInstanceID='instanceID'&answer='answer'
// survey instance questions = [asmt_assessment_instance_question]
// survey instances = [asmt_assessment_instance]

//data.ritm = $sp.getParameter("ritmID");
//data.type = $sp.getParameter("type");

data.surveyInstance = $sp.getParameter("surveyInstanceID");
data.surveyInstanceAnswer = $sp.getParameter("answer");
data.showWidget = 'no';
data.showSubmit = 'no';
data.showThankYou = 'no';
data.surveyComplete = 'no';
data.surveyCanceled = 'no';

var surveyInstance = data.surveyInstance;
var surveyInstanceAnswer = data.surveyInstanceAnswer;

//look up the survey instance and if it is completed or canceled show completed message
//else run script to update survey
var instance = new GlideRecord('asmt_assessment_instance');
instance.addQuery('sys_id',surveyInstance);
instance.query();

if(instance.next()){
if(instance.state == 'complete'){
data.surveyComplete = 'yes';

//update the follow-up question in the survey when the first was a 5

if(input.short_description != '' && input.short_description != undefined){
var surveyQuestion2 = new GlideRecord('asmt_assessment_instance_question');
surveyQuestion2.addQuery('instance',surveyInstance);
//surveyQuestion2.addQuery('metric','ed80467d1b823010f3acfe6fdc4bcbd9');//If you have a moment, tell us how it went so we can keep it up!
var question2 = surveyQuestion2.addQuery('metric','ed80467d1b823010f3acfe6fdc4bcbd9');//INC
question2.addOrCondition('metric','4e128abd1b823010f3acfe6fdc4bcb9a');//RITM
surveyQuestion2.query();

if(surveyQuestion2.next()){
surveyQuestion2.string_value = input.short_description;
surveyQuestion2.value = input.short_description;
surveyQuestion2.update();
}
}
}
else if(instance.state == 'canceled'){
data.surveyCanceled = 'yes';
}else{
//look up the instance question and update it
var surveyQuestion = new GlideRecord('asmt_assessment_instance_question');
surveyQuestion.addQuery('instance',surveyInstance);
//surveyQuestion.addQuery('metric','ed80463d1b823010f3acfe6fdc4bcbe2');//How was our service for this request?
var question = surveyQuestion.addQuery('metric','ed80463d1b823010f3acfe6fdc4bcbe2');//INC
question.addOrCondition('metric','821206bd1b823010f3acfe6fdc4bcb38');//RITM
surveyQuestion.query();

if(surveyQuestion.next()){
surveyQuestion.string_value = surveyInstanceAnswer;
surveyQuestion.value = surveyInstanceAnswer;
surveyQuestion.update();
}

//show string field if surveyInstanceAnswer = 5; and complete the survey
if(surveyInstanceAnswer == '5'){
data.showWidget = 'yes';
data.showSubmit = 'yes';
data.showThankYou = 'no';

//close the survey instance
var survey3 = new GlideRecord('asmt_assessment_instance');
survey3.addQuery('sys_id', surveyInstance);
survey3.query();

if(survey3.next()){
survey3.state = 'complete';
survey3.update();
}
}

else{
data.showWidget = 'no';
data.showThankYou = 'yes';
data.showSubmit = 'yes';

//close the survey instance
var survey = new GlideRecord('asmt_assessment_instance');
survey.addQuery('sys_id', surveyInstance);
survey.query();

if(survey.next()){
survey.state = 'complete';
survey.update();
}
}
}
}
})();

 

Email

Here's what the email looks like.  Each star is its own graphic with a unique URL hyperlink to pass the survey instance sys_id and the value for the survey question.

 

find_real_file.png

View solution in original post

3 REPLIES 3

patricklatella
Mega Sage

I was able to achieve this using a custom widget (Public = True) on a new portal page (also Public = True).

The widget parses the URL and runs the necessary logic to update the Survey record.

Here's the widget script in case anyone wants to create a similar solution.  The other part of the solution was creating a new survey notification with graphics included that link the user to the portal page.  The URL contains the survey instance sys_id as well as the value to be passed to the survey question.  The user does not need to be logged into ServiceNow for this to work, and no further action is required after the user clicks their selection in the email.  

This solution also allows the user to enter an additional comment which is mapped to a 2nd survey question.

The widget automatically closes the survey, so if the user clicks a link in the email again they are informed their survey has already been completed.

Works great for what I needed.

 

Custom Survey Widget Script (put this on a public page in the portal)

HTML

<div class="panel panel-default">
<div class="panel-heading">
<div class="form-group">

<!--Survey Completed-->
<div ng-if="data.surveyComplete == 'yes'" class="form-group">
<label>Your survey has already been completed. Thank you for your feedback.</label>
</div>

<!--Survey Canceled-->
<div ng-if="data.surveyCanceled == 'yes'" class="form-group">
<label>Your survey has been canceled.</label>
</div>

<!--Thank You -->
<div ng-if="data.showThankYou == 'yes'" class="form-group">
<label>Thanks so much for the feedback.<br><br>
If you have a moment, please add any additional comments to help us improve our service.</label><br>
<center><textarea rows = "5" cols = "60" name = "description" ng-model="c.data.short_description">
Optional comment
</textarea></center><br>
</div>

<!--Description String field for 5star-->
<div ng-if="data.showWidget == 'yes'" class="form-group">
<div>
<center><img src="yourImage_survey_5stars.png" width="128" height="38"/></center>
</div>
<div class="bigtext">
Awseome! We got it.
</div><br>
<label>
If you have a moment, tell us how it went so we can keep it up!</label><br>
<center><textarea rows = "5" cols = "60" name = "description" ng-model="c.data.short_description">
Optional comment
</textarea></center><br>
<!--<label>
If you have a moment, tell us how it went so we can keep it up!</label>
<input class="form-control" ng-model="c.data.short_description">-->
</div>

<!--The Submit button-->
<div ng-if="data.showSubmit == 'yes'" class="form-group">
<input class="btn btn-primary btn-block" ng-click="c.addItem()" type="submit" value="Enter your comments above and click here to Submit">

<!--popup modal-->
<script type="text/ng-template" id="modalTemplate3">

<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Your Text</h4>
</div>
<div class="panel-body wrapper-xl">
Thank you for the additional comments.<p>
You will now be redirected to the Service Portal Home page.<p>
If you are not currently logged in to ServiceNow, you will be directed to the whereever you link the user.
</div>
</div>
</div>
</script>
<!--end popup modal-->
</div>
</div>
</div>

 

CSS

.bigtext {
font-size: 20px;
}

 

Client

function($scope, $rootScope, spUtil, $window, $uibModal) {
/* widget controller */
var c = this;
//Triggered by the "Submit" button
c.addItem = function(){

//var user = c.data.user.value;

var desc = c.data.short_description;
//alert('desc is '+desc);

//Give alert if nothing entered
if(desc == undefined){
alert('Please enter your feedback before submitting.');
return;
}

//this calls the server script
c.server.update().then(function(response){
//alert('c.data.short_description '+c.data.short_description);

//popup modal
c.modalInstance = $uibModal.open({
templateUrl: 'modalTemplate3',
scope: $scope
});

//Redirect user after submit
setTimeout(myFunction, 4500);
function myFunction(){
$window.location.href = 'https://yourInstance.service-now.com/sp';//enter redirect URL
}
})
}
}

 

Server

(function() {
/* populate the 'data' object */
/* e.g., data.table = $sp.getValue('table'); */

//this is parses values from URL
// for my example, using testing page: sp?id=your_instance_page&surveyInstanceID='instanceID'&answer='answer'
// survey instance questions = [asmt_assessment_instance_question]
// survey instances = [asmt_assessment_instance]

//data.ritm = $sp.getParameter("ritmID");
//data.type = $sp.getParameter("type");

data.surveyInstance = $sp.getParameter("surveyInstanceID");
data.surveyInstanceAnswer = $sp.getParameter("answer");
data.showWidget = 'no';
data.showSubmit = 'no';
data.showThankYou = 'no';
data.surveyComplete = 'no';
data.surveyCanceled = 'no';

var surveyInstance = data.surveyInstance;
var surveyInstanceAnswer = data.surveyInstanceAnswer;

//look up the survey instance and if it is completed or canceled show completed message
//else run script to update survey
var instance = new GlideRecord('asmt_assessment_instance');
instance.addQuery('sys_id',surveyInstance);
instance.query();

if(instance.next()){
if(instance.state == 'complete'){
data.surveyComplete = 'yes';

//update the follow-up question in the survey when the first was a 5

if(input.short_description != '' && input.short_description != undefined){
var surveyQuestion2 = new GlideRecord('asmt_assessment_instance_question');
surveyQuestion2.addQuery('instance',surveyInstance);
//surveyQuestion2.addQuery('metric','ed80467d1b823010f3acfe6fdc4bcbd9');//If you have a moment, tell us how it went so we can keep it up!
var question2 = surveyQuestion2.addQuery('metric','ed80467d1b823010f3acfe6fdc4bcbd9');//INC
question2.addOrCondition('metric','4e128abd1b823010f3acfe6fdc4bcb9a');//RITM
surveyQuestion2.query();

if(surveyQuestion2.next()){
surveyQuestion2.string_value = input.short_description;
surveyQuestion2.value = input.short_description;
surveyQuestion2.update();
}
}
}
else if(instance.state == 'canceled'){
data.surveyCanceled = 'yes';
}else{
//look up the instance question and update it
var surveyQuestion = new GlideRecord('asmt_assessment_instance_question');
surveyQuestion.addQuery('instance',surveyInstance);
//surveyQuestion.addQuery('metric','ed80463d1b823010f3acfe6fdc4bcbe2');//How was our service for this request?
var question = surveyQuestion.addQuery('metric','ed80463d1b823010f3acfe6fdc4bcbe2');//INC
question.addOrCondition('metric','821206bd1b823010f3acfe6fdc4bcb38');//RITM
surveyQuestion.query();

if(surveyQuestion.next()){
surveyQuestion.string_value = surveyInstanceAnswer;
surveyQuestion.value = surveyInstanceAnswer;
surveyQuestion.update();
}

//show string field if surveyInstanceAnswer = 5; and complete the survey
if(surveyInstanceAnswer == '5'){
data.showWidget = 'yes';
data.showSubmit = 'yes';
data.showThankYou = 'no';

//close the survey instance
var survey3 = new GlideRecord('asmt_assessment_instance');
survey3.addQuery('sys_id', surveyInstance);
survey3.query();

if(survey3.next()){
survey3.state = 'complete';
survey3.update();
}
}

else{
data.showWidget = 'no';
data.showThankYou = 'yes';
data.showSubmit = 'yes';

//close the survey instance
var survey = new GlideRecord('asmt_assessment_instance');
survey.addQuery('sys_id', surveyInstance);
survey.query();

if(survey.next()){
survey.state = 'complete';
survey.update();
}
}
}
}
})();

 

Email

Here's what the email looks like.  Each star is its own graphic with a unique URL hyperlink to pass the survey instance sys_id and the value for the survey question.

 

find_real_file.png

Nice ! 🙂

We had similar requirements and this suggestion is excellent! This got us 99% of the way we needed to go. One thing we had to consider is that the email with the hyperlink shows in the Activities section of the Record. This allows any internal user with access to submit a survey response just by clicking the link in the email preview.