Approve Workflow Tasks and Navigate to Workflow Approval Page with SharePoint REST API
Many times, we have faced a situation where we need to approve / reject the workflow task or open the approval page of workflow task of any list item with SharePoint Rest API. This blog explains how to make Rest calls to show the workflow approval form or to directly approve or reject the workflow task.
Background Scenario
Let’s take a scenario of HR Request approval workflow.
Here, we are having an HR_Request
list and ApprovalWorkflow
associated with it. The task list associated with the workflow is HRTasks
. Workflow will start automatically and assign task to HR Group when an item is added in the list.
First, let’s create a content editor webpart to show the status of the currently assigned task.
The following code shows how to create the webpart and the Rest API call for the same.
Using the Code
The webpart is made with Angular JS to show the status of the workflow task of the current item. The following code is used to show the design structure.
<style>
td{border: solid 1px black; padding: 5px;}
th{border: solid 1px black; padding: 5px;}
</style>
<script type="text/javascript" src="/sites/jccademo/style library/pnp.min.js"></script>
<script type="text/javascript" src="/sites/jccademo/style library/workflow.js"></script>
<table ng-app="mainapp" style="border:solid 1px black; background-color:white;">
<thead ng-controller="getHRRequest">
<tr>
<th>Task Title</th>
<th>Task Request</th>
<th>Approval Status</th>
<th>Link for Approve/Reject</th>
<th>Approve</th>
<th>Reject</th>
</tr>
<tr ng-repeat="item in items">
<td>{{item.Title}}</td>
<td>{{item.Request}}</td>
<td>
<div ng-if="item.Approval==16">Approved</div>
<div ng-if="item.Approval==15">Cancelled</div>
<div ng-if="item.Approval==17">Rejected</div>
<div ng-if="item.Approval==2">InProgress</div>
<div ng-if="item.Approval==5">Completed</div>
<div ng-if="item.Approval==0">NotStarted</div>
</td>
<td></td>
<td></td>
<td></td>
</tr>
</thead>
</table>
Following is the Rest API call to find the status of the task.
var mainapp = angular.module("mainapp", []);
mainapp.controller('getHRRequest', ['$http', 'dataService',
'$scope', function ($http, dataService, $scope) {
var getHRRequest = _spPageContextInfo.webAbsoluteUrl +
"/_api/web/Lists/GetByTitle('HR_Request')/items?$select=ID,Title,
Request,Approval&$orderby=Modified desc";
$scope.items = [];
dataService.get(getHRRequest).then(function (items) {
$scope.items = items.data.d.results;
angular.forEach(items.data.d.results, function (items) {
var getCurrentUSerId = _spPageContextInfo.webAbsoluteUrl +
"/_api/Web/CurrentUser?$select=Id";
dataService.get(getCurrentUSerId).then(function (users) {
var getTaskId = _spPageContextInfo.webAbsoluteUrl+
"/_api/lists/getbyTitle('HRTasks')/items?$select=Id,Title&$filter=WorkflowItemId eq " +
items.Id ;
dataService.get(getTaskId).then(function (tasks) {
var Id = tasks.data.d.results.Id;
});
});
})
});
}]);
This will bind the details of the task status of each item of HR_Request
list as shown below:
Rest Call to Get Associated Tasks from HRTasks List
Now, we need to make a rest call to get the tasks associated to current item from the HRTasks
list. The Rest API endpoint is as follows:
/_api/lists/getbyTitle('HRTasks')/items?$select=Id,Title,AssignedToId&$filter=WorkflowItemId eq " +
itemId
where itemId
is the Id
of the HR_Request
item and the WorkflowItemId
is the Id
of the item of associated list. This will show all the assigned tasks for the selected item.
Further, we can modify the rest call to get the assigned tasks to currently logged-in. REST call to get the logged-in user id is as below:
/_api/Web/CurrentUser?$select=Id
The modified Rest call for showing the link to approval page is shown below. Here, the link in HTML consists of the guid of the HR_Request
list, i.e., cb305ecc-17af-4f08-9d16-5f96195fbe55
.
<style>
td{border: solid 1px black; padding: 5px;}
th{border: solid 1px black; padding: 5px;}
</style>
<script type="text/javascript"
src="/sites/jccademo/style library/pnp.min.js"></script>
<script type="text/javascript"
src="/sites/jccademo/style library/workflow.js"></script>
<table ng-app="mainapp"
style="border:solid 1px black; background-color:white;">
<thead ng-controller="getHRRequest">
<tr>
<th>Task Title</th>
<th>Task Request</th>
<th>Approval Status</th>
<th>Link for Approve/Reject</th>
<th>Approve</th>
<th>Reject</th>
</tr>
<tr ng-repeat="item in items">
<td>{{item.Title}}</td>
<td>{{item.Request}}</td>
<td>
<div ng-if="item.Approval==16">Approved</div>
<div ng-if="item.Approval==15">Cancelled</div>
<div ng-if="item.Approval==17">Rejected</div>
<div ng-if="item.Approval==2">InProgress</div>
<div ng-if="item.Approval==5">Completed</div>
<div ng-if="item.Approval==0">NotStarted</div>
</td>
<td><a href="/sites/jccademo/_layouts/15/WrkTaskIP.aspx?
List=cb305ecc-17af-4f08-9d16-5f96195fbe55&
ID={{item.TaskId}}">Click</a></td>
<td></td>
<td></td>
</tr>
</thead>
</table>
var mainapp = angular.module("mainapp", []);
mainapp.controller('getHRRequest', ['$http',
'dataService', '$scope', function ($http, dataService, $scope) {
var getHRRequest = _spPageContextInfo.webAbsoluteUrl +
"/_api/web/Lists/GetByTitle('HR_Request')/items?$select=ID,
Title,Request,Approval&$orderby=Modified desc";
$scope.items = [];
dataService.get(getHRRequest).then(function (items) {
$scope.items = items.data.d.results;
angular.forEach(items.data.d.results, function (items) {
var getCurrentUSerId = _spPageContextInfo.webAbsoluteUrl +
"/_api/Web/CurrentUser?$select=Id";
dataService.get(getCurrentUSerId).then(function (users) {
var getTaskId = _spPageContextInfo.webAbsoluteUrl+
"/_api/lists/getbyTitle('HRTasks')/
items?$select=Id,Title&$filter=WorkflowItemId eq " +
items.Id +" and AssignedToId eq "+ users.data.d.Id;
dataService.get(getTaskId).then(function (tasks) {
items.TaskId=tasks.data.d.results[0].Id;
});
});
})
});
}]);
Rest Call to Approve / Reject Items
Now, we need to update the tasks as either approved or rejected. To approve / reject the task, the function is as below.
$scope.ApproveTask = function (TaskId) {
var data = {
"__metadata": { "type": "SP.Data.HRTasksListItem" },
"WorkflowOutcome": "Approved",
"Status": "Approved",
"PercentComplete": 1
};
var itemUrl = _spPageContextInfo.webAbsoluteUrl +
"/_api/lists/getbyTitle('HRTasks')/items(" + TaskId + ")";
dataService.post(itemUrl, JSON.stringify(data),TaskId).then(function (response) {
alert("Approved Successfully");
});
};
$scope.RejectTask = function (TaskId) {
var data = {
"__metadata": { "type": "SP.Data.HRTasksListItem" },
"WorkflowOutcome": "Rejected",
"Status": "Rejected",
"PercentComplete": 1
};
var itemUrl = _spPageContextInfo.webAbsoluteUrl +
"/_api/lists/getbyTitle('HRTasks')/items(" + TaskId + ")";
dataService.post(itemUrl, JSON.stringify(data),TaskId).then(function (response) {
alert("Rejected Successfully");
});
};
This will show the webpart as follows:
By clicking on Approve or Reject button, you can change the status of the tasks.
The whole code will look like the following:
HTML
<style>
td{border: solid 1px black; padding: 5px;}
th{border: solid 1px black; padding: 5px;}
</style>
<script type="text/javascript" src="/style library/pnp.min.js"></script>
<script type="text/javascript" src="/style library/workflow.js"></script>
<table ng-app="mainapp" style="border:solid 1px black; background-color:white;">
<thead ng-controller="getHRRequest">
<tr>
<th>Task Title</th>
<th>Task Request</th>
<th>Approval Status</th>
<th>Link for Approve/Reject</th>
<th>Approve</th>
<th>Reject</th>
</tr>
<tr ng-repeat="item in items">
<td>{{item.Title}}</td>
<td>{{item.Request}}</td>
<td>
<div ng-if="item.Approval==16">Approved</div>
<div ng-if="item.Approval==15">Cancelled</div>
<div ng-if="item.Approval==17">Rejected</div>
<div ng-if="item.Approval==2">InProgress</div>
<div ng-if="item.Approval==5">Completed</div>
<div ng-if="item.Approval==0">NotStarted</div>
</td>
<td><a href="/_layouts/15/WrkTaskIP.aspx?List=cb305ecc-17af-4f08-9d16-5f96195fbe55&
ID={{item.TaskId}}">Click</a></td>
<td><a href="#"
ng-click="ApproveTask(item.TaskId)">Approve</a></td>
<td><a href="#"
ng-click="RejectTask(item.TaskId)">Reject</a></td>
</tr>
</thead>
</table>
JavaScript
var mainapp = angular.module("mainapp", []);
mainapp.controller('getHRRequest', ['$http',
'dataService', '$scope', function ($http, dataService, $scope) {
var getHRRequest = _spPageContextInfo.webAbsoluteUrl +
"/_api/web/Lists/GetByTitle('HR_Request')/items?$select=ID,Title,
Request,Approval&$orderby=Modified desc";
$scope.items = [];
dataService.get(getHRRequest).then(function (items) {
$scope.items = items.data.d.results;
angular.forEach(items.data.d.results, function (items) {
var getCurrentUSerId = _spPageContextInfo.webAbsoluteUrl +
"/_api/Web/CurrentUser?$select=Id";
dataService.get(getCurrentUSerId).then(function (users) {
var getTaskId = _spPageContextInfo.webAbsoluteUrl +
"/_api/lists/getbyTitle('HRTasks')/
items?$select=Id,Title&$filter=WorkflowItemId eq " +
items.Id + " and AssignedToId eq " + users.data.d.Id;
dataService.get(getTaskId).then(function (tasks) {
items.TaskId = tasks.data.d.results[0].Id;
});
});
})
});
$scope.ApproveTask = function (TaskId) {
var data = {
"__metadata": { "type": "SP.Data.HRTasksListItem" },
"WorkflowOutcome": "Approved",
"Status": "Approved",
"PercentComplete": 1
};
var itemUrl = _spPageContextInfo.webAbsoluteUrl +
"/_api/lists/getbyTitle('HRTasks')/items(" + TaskId + ")";
dataService.post(itemUrl, JSON.stringify(data),TaskId).then(function (response) {
alert("Approved Successfully");
});
};
$scope.RejectTask = function (TaskId) {
var data = {
"__metadata": { "type": "SP.Data.HRTasksListItem" },
"WorkflowOutcome": "Rejected",
"Status": "Rejected",
"PercentComplete": 1
};
var itemUrl = _spPageContextInfo.webAbsoluteUrl +
"/_api/lists/getbyTitle('HRTasks')/items(" + TaskId + ")";
dataService.post(itemUrl, JSON.stringify(data),TaskId).then(function (response) {
alert("Rejected Successfully");
});
};
}]);
This way, we can update the associated tasks with the listitem
with SharePoint REST API.
I am working in Asp.net C#, MVC, SharePoint and emerging technologies like Bot Framework. I also have handful knowledge in office 365 migrations and SharePoint migrations.