Acknowledge button in email notifications

Ted18
Tera Contributor

I have been trying to make an acknowledge link similar to the approval/disapproval link for change requests that I believe is the default in ServiceNow. I saw the "On-Call: Assign by Acknowledgement "and "Update Approval Request" that an email is sent back to the instance which updates the appropriate field. I am not sure how those work though as there is a lot going on. Then I also see "Update Case via AcceptReject" and "Update Case via reply" which have some simple JS that changes the state of the approval. Not sure which is used for which though.

So I figure for my acknowledge button/hyperlink I could use in the HTML:

${mailto:mailto.approval}

${mailto:mailto.rejection}

I thought this because it seems for both the change approval and acknowledge approval, ServiceNow is looking for a reply with the word "approval" in the subject if it finds it changes the state of approval or in this case the acknowledged field. But that didn't work.

Here are the scripts for each of the actions below if anyone can help me create this acknowledge button.

The easy:

Update Case via AcceptReject

if (current.state == 6) {
	var subject = email.subject.trim().toLowerCase();
	if(subject.indexOf("reject") === 0){
		current.state = 10;
	}else if(subject.indexOf("accept") === 0){
		current.state = 3;
	}
	current.update();
}

Update Case via reply

//if case.state = resloved (6), then check first word in body for accept or reject
//else if case.state = Awaiting (18), then, move to open state
var case_state = current.state;
if (current.state == 6) {
	var lines = email.body_text.split('\n');
	var firstword = "";
	if (lines.length > 0)
		firstword = lines[0].replace(/^\s+|\s+$/g,'');
	firstline = firstword.toLowerCase();
	if (firstline) {
		if(firstline.indexOf("reject") == 0)
			current.state = 10;
		else if(firstline.indexOf("accept") == 0)
			current.state = 3;
	}
} else if (current.state == 18) {
	current.state = 10;
} else{
	current.sys_updated_on = "";
}

current.update();

The more difficult:

On-Call: Assign by Acknowledgement

var assignedTo = current.getElement("assigned_to");

// if current is already assigned then do nothing
if (assignedTo !== null && assignedTo.nil()) {
	// original message sent to user from on-call: assign by acknowledgement workflow
	var className = current.getValue("sys_class_name");
	className = className.charAt(0).toUpperCase() + className.slice(1);
	var emailMessage = gs.getMessage("You are being asked to assign yourself to {0}. To accept and assign yourself to the {1} reply to this email with 'ACC'. To reject reply to this email with 'REJ'. Rejecting will move this assignment process to the next person in your roster.", [current.getValue("number"), className]);

	if (email.body_text.indexOf(emailMessage) != -1) {
		// strip out the original email text that was sent out to the user "Reply with 'ACC' or 'REJ'"
		var str = email.body_text.replace(emailMessage, "");

		// match first occurrence of our expected response
		var regex = new SNC.Regex("/([0-9]*)(ACC|REJ)/i");
		var match = regex.match(str);

		// process valid response
		if (match !== null) {
			if (match.pop().equalsIgnoreCase("ACC")) {
				current.assigned_to = gs.getUserID();
				current.update();
			}

			// resume the workflow
			var wfScriptApi = new SNC.WorkflowScriptAPI();
			var wfContext = new GlideRecord("wf_context");
			wfContext.addQuery("workflow.name", "On-Call: Assign by Acknowledgement per Rota");
			wfContext.addQuery("id", current.getUniqueValue());
			wfContext.query();

			while (wfContext.next())
				wfScriptApi.broadcastEvent(wfContext.getUniqueValue(), "resume");
		}
	}
}

 

Update Approval Request

/*global current, email, gs, GlideController, GlideRecord*/
/*eslint-disable eqeqeq*/
processApprovalEmail();

function processApprovalEmail() {
	"use strict";
	var errorMsg = "";
	var msgArray = [];

	if (current.getTableName() != "sysapproval_approver")
		return;

	var displayValue = getApprovalDisplayValue(current);

	if (!validUser()) {
		gs.log(getFailurePreamble() + "Sender email does not match approval assignee.");
		msgArray.push(displayValue);
		msgArray.push(current.approver.getDisplayValue());
		msgArray.push(current.approver.email);
		errorMsg = gs.getMessage("approvalInvalidUser", msgArray);
		createEmailEvent(errorMsg);
		return;
	}

	if (current.state == 'cancelled') {
		gs.log(getFailurePreamble() + "The approval has been canceled.");
		msgArray.push(displayValue);
		errorMsg = gs.getMessage("approvalCancelled", msgArray);
		createEmailEvent(errorMsg);
		return;
	}

	if (email.body.state != undefined)
		current.state = email.body.state;

	if (email.subject.indexOf("approve") >= 0)
		current.state = "approved";

	if (email.subject.indexOf("reject") >= 0)
		current.state = "rejected";

	if (current.state != "approved" && current.state != "rejected") {
		gs.log(getFailurePreamble() + "The subject is malformed. The approver probably did not click the approve or reject button on the email.");
		msgArray.push(displayValue);
		errorMsg = gs.getMessage("approvalFailed", msgArray);
		createEmailEvent(errorMsg);
		return;
	}

	current.comments = "reply from: " + email.from + "\n\n" + email.body_text;
	var controller = new GlideController();
	controller.putGlobal("approvalSource", "email");
	current.update();
	controller.removeGlobal("approvalSource");

	function validUser() {
		if (current.approver == email.from_sys_id)
			return true;

		// check if the email is from a delegate of the approver
		var g = new GlideRecord("sys_user_delegate");
		g.addQuery("user", current.approver.toString());
		g.addQuery("delegate", email.from_sys_id);
		g.addQuery("approvals", "true");
		g.addQuery("starts", "<=", gs.daysAgo(0));
		g.addQuery("ends", ">=", gs.daysAgo(0));
		g.query();
		return g.hasNext();
	}

	function createEmailEvent(msg) {
		gs.eventQueue("approval.email.errorMsg", current, email.from, msg);
	}

	function getFailurePreamble() {
		return 'Approval email from ' + email.from + ' for task "' + displayValue + '" assigned to "' + current.approver.getDisplayValue()
				+ '" failed because: ';
	}

	function getApprovalDisplayValue(approval) {
		if (!gs.nil(approval.sysapproval))
			return approval.getDisplayValue();
		else {
			var target = new GlideRecord(approval.source_table);
			if (target.get(approval.document_id))
				return target.getDisplayValue();
		}
		gs.warn("Target for sysapproval_approver:" + approval.getUniqueValue() + " not found. Target=" + approval.source_table + ":" + approval.document_id);
		return "Unknown";
	}

}
2 REPLIES 2

Michael Fry1
Kilo Patron

If you include these email templates in your notification, all they are doing is pre-populating the email subject line with Approve or Reject etc:

find_real_file.png

 

Then in your reply inbound actions, you can read that subject line and check if it 'contains' your keyword. Something like:

if (email.subject.toLowerCase().indexOf("accept")

this line would evaluate to true if the email subject contains the word 'accept'.

Niuk
Mega Contributor

What I don't know here is how to reply to the email with just clicking link inside the body.