How to add a "Resolve Incident", “Request Status” and “Cancel” buttons with modal popup for Service Portal (SP).
Please note that there are more than one way to do this, but might help you make new buttons of your own.
Results
Here is what the final result looks like:
Resolve Button
Modal Popup
INSTRUCTIONS
1. Create Widget
Name: Ticket Actions
Body HTML Template
<div class="panel b" ng-if="data.showWidget">
<div class="panel-heading bg-primary">Actions</div>
<div class="panel-body">
<button type="button" class="btn btn-primary btn-block" ng-click="c.openModalResolve()" ng-if="data.showResolve">Resolve</button>
<button type="button" class="btn btn-default btn-block" ng-click="c.openModalStatus()" ng-if="data.showStatus">Request Status</button>
<button type="button" class="btn btn-danger btn-block" ng-click="c.openModalCancel()" ng-if="data.showCancel">Cancel</button>
<button type="button" class="btn btn-default btn-block" ng-click="c.openModalReopen()" ng-if="data.showReopen">Reopen</button>
</div>
</div>
<script type="text/ng-template" id="modalTemplateResolve">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Provide a reason for the resolve</h4>
</div>
<div class="panel-body wrapper-xl">
<form name="modalTemplateResolve" ng-submit="c.uiAction('resolve')">
<div class="form-group">
<textarea required sp-autosize="true" ng-required="true" ng-model="data.resolveComments" id="resolveComments" placeholder="Comments required" class="form-control ng-pristine ng-valid ng-scope ng-empty ng-touched" aria-invalid="false" style="overflow: hidden; word-wrap: break-word; resize: horizontal;"></textarea>
</div>
<input class="btn btn-primary" type="submit" />
</form>
</div>
</div>
</script>
<script type="text/ng-template" id="modalTemplateCancel">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Provide a reason for the cancel</h4>
</div>
<div class="panel-body wrapper-xl">
<form name="modalTemplateResolve" ng-submit="c.uiAction('cancel')">
<div class="form-group">
<textarea required sp-autosize="true" ng-required="true" ng-model="data.cancelComments" id="cancelComments" placeholder="Comments required" class="form-control ng-pristine ng-valid ng-scope ng-empty ng-touched" aria-invalid="false" style="overflow: hidden; word-wrap: break-word; resize: horizontal;"></textarea>
</div>
<input class="btn btn-primary" type="submit" />
</form>
</div>
</div>
</script>
<script type="text/ng-template" id="modalTemplateStatus">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Provide any additional information</h4>
</div>
<div class="panel-body wrapper-xl">
<form name="modalTemplateStatus" ng-submit="c.uiAction('status')">
<div class="form-group">
<textarea required sp-autosize="true" ng-required="true" ng-model="data.statusComments" id="statusComments" placeholder="Comments required" class="form-control ng-pristine ng-valid ng-scope ng-empty ng-touched" aria-invalid="false" style="overflow: hidden; word-wrap: break-word; resize: horizontal;"></textarea>
</div>
<input class="btn btn-primary" type="submit" />
</form>
</div>
</div>
</script>
<script type="text/ng-template" id="modalTemplateReopen">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Provide a reason for reopening the Incident</h4>
</div>
<div class="panel-body wrapper-xl">
<form name="modalTemplateReopen" ng-submit="c.uiAction('reopen')">
<div class="form-group">
<textarea required sp-autosize="true" ng-required="true" ng-model="data.reopenComments" id="reopenComments" placeholder="Comments required" class="form-control ng-pristine ng-valid ng-scope ng-empty ng-touched" aria-invalid="false" style="overflow: hidden; word-wrap: break-word; resize: horizontal;"></textarea>
</div>
<input class="btn btn-primary" type="submit" />
</form>
</div>
</div>
</script>
Server Script
(function() {
// Get table & sys_id
data.table = input.table || $sp.getParameter("table");
data.sys_id = input.sys_id || $sp.getParameter("sys_id");
// Valid GlideRecord
gr = new GlideRecord(data.table);
if (!gr.isValid())
return;
// Valid sys_id
if (!gr.get(data.sys_id))
return;
//Button Visibility
if(data.table == 'incident' && gr.active == true && gr.incident_state != 6 && (gr.caller_id == gs.getUserID() || gs.hasRole("admin"))){
data.showWidget = true;
data.showResolve = true;
data.showCancel = true;
data.showStatus = true;
data.showReopen = false;
}
else if(data.table == 'incident' && gr.incident_state == 6 && (gr.caller_id == gs.getUserID() || gs.hasRole("admin"))){
data.showWidget = true;
data.showResolve = false;
data.showCancel = false;
data.showStatus = false;
data.showReopen = true;
}
else if(data.table == 'sc_req_item' && gr.active == true && (gr.request.requested_for == gs.getUserID() || gs.hasRole("admin"))){
data.showWidget = true;
data.showResolve = false;
data.showCancel = true;
data.showStatus = true;
data.showReopen = false;
}
else {
data.showWidget = false;
data.showResolve = false;
data.showCancel = false;
data.showStatus = false;
data.showReopen = false;
}
//input
if (input && input.action) {
var action = input.action;
//Incident table
if (data.table == 'incident') {
if (action == 'resolve' && input.resolveComments !='') {
gr.setValue('incident_state', 6);
gr.setValue('state', 6);
gr.setValue('resolved_by', gs.getUserID());
gr.setValue('close_code', 'Closed/Resolved by Caller');
gr.setValue('close_notes', "Closed by caller with comment: " + input.resolveComments);
//gr.work_notes = 'Closed by caller with comment: ' + input.resolveComments;
gr.update();
}
if (action == 'status' && input.statusComments !='') {
gr.comments.setJournalEntry("Status request by user: "+ input.statusComments);
gr.setValue('u_request_status',gr.u_request_status++);
gr.update();
}
if (action == 'cancel') {
gr.setValue('incident_state', 8);
gr.setValue('state', 8);
gr.setValue('resolved_by', gs.getUserID());
gr.setValue('close_code','Customer Cancelled');
gr.setValue('close_notes','Cancelled by caller with comment: '+ input.cancelComments);
//gr.work_notes = 'Cancelled by caller with comment: ' + input.resolveComments;
gr.update();
}
if (action == 'reopen') {
gr.setValue('incident_state', 9);
gr.setValue('state', 9);
gr.setDisplayValue('comments', 'Reopened by caller with comment: '+ input.reopenComments);
gr.update();
}
}
//Requested Item table
if (data.table == 'sc_req_item' && input.cancelComments !='') {
if (action == 'cancel') {
var workflow = new Workflow();
workflow.cancel(gr);
gr.setValue('approval','withdrawn');
gr.setValue('stage','Request Cancelled');
gr.setValue('state','4');
gr.setValue('active','false');
gr.comments.setJournalEntry("Closed by Requested For with comment: "+ input.cancelComments);
gr.update();
}
if (action == 'status' && input.statusComments !='') {
gr.comments.setJournalEntry("Status request by user: "+ input.statusComments);
gr.update();
}
}
}
})();
Client Controller
function($uibModal, $scope, spUtil) {
var c = this;
$scope.$on('record.updated', function(name, data) {
c.data.cancelComments = '';
c.data.statusComments = '';
c.data.resolveComments = '';
c.data.reopenComments = '';
spUtil.update($scope);
})
c.uiAction = function(action) {
c.data.action = action;
c.server.update().then(function() {
c.data.action = undefined;
})
c.modalInstance.close();
}
c.openModalResolve = function() {
c.modalInstance = $uibModal.open({
templateUrl: 'modalTemplateResolve',
scope: $scope
});
}
c.openModalCancel = function() {
c.modalInstance = $uibModal.open({
templateUrl: 'modalTemplateCancel',
scope: $scope
});
}
c.openModalStatus = function() {
c.modalInstance = $uibModal.open({
templateUrl: 'modalTemplateStatus',
scope: $scope
});
}
c.openModalReopen = function() {
c.modalInstance = $uibModal.open({
templateUrl: 'modalTemplateReopen',
scope: $scope
});
}
c.closeModal = function() {
c.modalInstance.close();
}
}
2. Drop Widget on Form
Go to the Ticket Page in SP, go look at an existing incident.
Ctrl-Right click and select "Page in Designer
On the Widgets Sidebar drag-n-drop the "Ticket Actions" widget on the form.
CTRL-Right click Options
Page Designer