Sivarajan's Blog

SharePoint | Office 365 | Azure | JavaScript

SharePoint 2013 Designer Workflow – Creating a Custom Task Outcome – Part 3

Overview In this post, we will see that how to create the custom task outcome. First we will see what that is and why the task outcome is so important. Default Task Outcome By default, if you assign a task to someone in SharePoint 2013 workflow, the task output will look likes below. But as per our helpdesk workflow requirement, while employee raise a ticket, the new task will be created and assigned to IT Admin user group. In this stage (Waiting for IT Admin Approval stage), he can either assign to Support Team for implement or assign back to Employee if there is any clarification and he is not going to approve or reject functionality. So our new task form output will be look like that, We need to follow the below steps for implementing the new requirement. Create a new Task Outcome Column Create a new Content Type Changes in Helpdesk Workflow Use the Task Outcome Step 1: Create a new Task Outcome Column We will create a new site column RequestClarificationOrAssign with the following values Step 2: Create a new Content Type We will create a new site content type Request Clarification Or Assign - IT Admin View based on Workflow Task (SharePoint 2013) Content Type. And then add a site column RequestClarificaitonOrAssign with newly created content type. Now add this content type with Helpdesk Task list and the task list content type will look like, Step 3: Changes in Helpdesk Workflow Edit the Helpdesk WF workflow using SharePoint Designer 2013 Inside the Waiting for Approval Stage, select the Assign a Task to IT Admin action Select the Task Properties Expand the Outcome Options Select the Task content type as Request Clarification Or Assign – IT Admin View So the output will look like below Save and republish the workflow. When we create the new task, the task output screen will look like below Step 3: Use the Task Outcome IT Admin will click either the Request Clarification or Assign button. So we should capture the action using local variables and will use in If condition action Again select the Assign a Task to IT Admin action Select the Task Outcome Create the local variable ReqClarificationOrAssignOutcome 4. Now edit the If condition next to Waiting for Approval stage 5. Set the local variable ReqClarificationOrAssignOutcome like below 6. Based on the IT Admin action, the next stage will be either Request Clarification or Assign for Implementation Summary I hope that now we know how to create and use custom task outputcome.

SharePoint 2013 / Office 365 : Debugging a Designer Workflow – Part 2

In the designer workflow series, we will see that how to debug the current helpdesk workflow. Unfortunately we cannot debug a SharePoint Designer Workflow. But we can easily debug the workflow if we have developed using Visual Studio 2012/2013. Alternatively we can use the following tools to trace the designer workflow. Log to history list Activity Fiddler (This will work only if we are accessing a workflow using http and not https) Using <system.diagnostics> Log to history list Activity In the previous post, we saw that how to use local variables. For the demo purpose, I just edited the Helpdesk Config list item and changed the TASK_APPROVAL_TEAM key value from IT ADMIN to UNKNOWN ADMIN. Now we will edit the existing helpdesk workflow and add the Log to history activity to explore the assignedTo local variable. Save and republish the helpdesk workflow. When we create a new task, due to UNKNOWN ADMIN  SharePoint user group, the current workflow was suspended. Here Log to history list activity help us to explore the workflow local variables. Using Fiddler We aware that the SharePoint 2013 workflow is not running inside the SharePoint worker progress. Windows Azure Workflow platform will be used for hosting and monitoring the SharePoint 2013 workflow. The communication between our system and workflow platform using two web services (http://localhost:12291 and http://localhost:12290). For more information, We can easily explore the each response or request using Fiddler if our on-premise SharePoint platform is connected via http workflow service. But this option will not be helpful if we are developing a workflow using Office 365. By default Office 365 is running via https. Using <system.diagnostics> I found the blog post with detailed walkthrough on how to enable the default workflow foundation trace log. After enabling that we can use the ULS log viewer for filtering the SharePoint log data. <system.diagnostics>       <switches>           <add name="System.Workflow LogToFile" value="1" />           <add name="System.Workflow.Runtime" value="All" />           <add name="System.Workflow.Runtime.Hosting" value="All" />           <add name="System.Workflow.Runtime.Tracking" value="All" />           <add name="System.Workflow.Activities" value="All" />           <add name="System.Workflow.Activities.Rules" value="All" />            </switches>   </system.diagnostics>   References

SharePoint 2013 / Office 365 : How to use local variables in Designer Workflow – Part 1

In the previous post, we saw that how to create a helpdesk approval workflow. But we have hard coded task values like below. Dynamic Task Participant Value In the current workflow, Participant value is IT Admin and this value is hard coded now. In future, if we want to change the user group from IT Admin to Helpdesk IT Admin, we need to edit the workflow. For avoiding this, I have created list called Helpdesk Config to keep all the configurable variable related to our system. For the current case, I have added entry TASK_APPROVAL_TEAM to IT ADMIN. Site admin can change the approval group at any time. Edit the Helpdesk WF workflow with SharePoint designer. In the Waiting for IT Admin Approval Stage, drag and drop the Set Workflow variable activity from Actions – SharePoint 2013 Workflow Double click and rename to Local variable : Assigned To = IT Admin Property gear will be visible on mouse over the Local variable activity and select the Variable menu item in that. Set Workflow Variable Properties window will be opened. Select the dropdown next to Variable text in the Set Workflow Variable Properties window. In the dropdown list, select the Create a new variable menu item Enter the Name to AssignedTo and Type as String. Like that, select the button next to Value text in the Set Workflow Variable Properties window. On click the Add or Change Lookup button, Lookup for String window will be opened. Fill the following fields like below, Setting the Title and Description from Helpdesk Request In the current workflow, all the tasks title will be Please approve this task. Open an Assign a Task properties. On click the button next to Title text and the String Builder window will be opened. On click Add or Change Lookup button in the String Builder window, Lookup for String window will be opened. Set the Data source to Current Item and Field from source to Title (Note: The current item means that the associated Helpdesk request) Like Title, repeat the above steps for Description field.   Now save and republish the workflow. Test the workflow While creating a new request, the output of the task will be, Summary In this post, we saw that, how to use local variables inside the helpdesk workflow and we will see how to debug the designer workflow in the next post.

SharePoint 2013 / Office 365: How to develop an out of box Helpdesk Approval Workflow

Overview In this series, I am going to walkthrough how to leverage SharePoint 2013 or SharePoint Online for Office 365 to develop a Helpdesk system with out of box features. For this walkthrough, I am using SharePoint Online for Office 365. But the same approach will be applicable for SharePoint 2013 on-premise also. Helpdesk System Flow Diagram Completed Helpdesk Workflow Outline Helpdesk System User Groups I have created a new Helpdesk site using team site template and the system will have a three set of target audiences. So I have created the following user groups in Helpdesk site. I have created a Helpdesk list for capturing user requests and also have created a Tasks list for tracking the user requests. Fields Data Type Title Single line of text Description Multiple lines of text           Note: This is an incomplete list, I will add the required columns later for completing this whole system. Create a Helpdesk Approval Workflow Now we will create a list based workflow Helpdesk WF using SharePoint designer. We can create a workflow either Text-based or Visual designer in SharePoint Designer 2013. Earlier I wrote a simple Document Approval workflow based on Visual designer using SharePoint Designer 2013. But here we are going to develop a complex workflow with detailed walkthrough. Open the SharePoint Designer 2013 using the Helpdesk team site URL. In the left navigation, select the Workflows and then select the List Workflow dropdown in the top ribbon bar. In the dropdown list, click the Helpdesk item. Create List Workflow window will be opened. Enter the name of the Workflow Helpdesk WF and choose the platform type SharePoint 2013 Workflow. (Note: By default this option will not be available in SharePoint 2013 on-premises. We can find here on how to install and configure Windows Azure Workflow platform). Double click the Helpdesk WF in the right pane and please make sure the following details are mapped correctly. Edit the Helpdesk WF workflow As per our flow diagram, helpdesk ticket will be created by Employees and will be approved by IT Team and then it will be implemented by L1 / L2 Support Team. So our workflow will have to create the following stages a). Waiting for IT Admin Approval b). Request Clarification c). Respond Clarification d). Assign for Implementation e). Assign for Verification f). Reopen the Request g). Complete the Request Now we will see how to implement the first stage Waiting for IT Admin Approval Stage: Waiting for IT Admin Approval We assume that, employee1 is creating a helpdesk ticket to install an anti-virus to his machine. On submitting the ticket, the new task needs to be created and it should be assigned to IT Admin Team group. We will see here how to do that. After editing the Helpdesk WF workflow, in the top ribbon bar, select the Visual Designer in Views dropdown. In the left pane, select the Containers and Terminators – SharePoint 2013 Workflow and then drag and drop the Stage container. Double click the Stage 1 and rename to Waiting for IT Admin Approval In the left pane, select the Actions – SharePoint 2013 Workflow and then drag and drop the Assign a Task activity inside the stage called Waiting for IT Admin Approval. Double click the Assign a Task activity and rename it Assign a Task to IT Admin. Select the Task Settings on mouse over the Assign a Task to IT Admin activity. Select the IT Admin group for Participant box. Set the Task Title to Please approve this task The final output looks like below, Now save and publish the workflow. Verify the Approval Workflow Open the Helpdesk team site URL Open the Helpdesk list. Create the new ticket and output will be looks like, Click the link Waiting for IT Admin Approval to see the workflow history Summary In the next post, we will see that how to create the custom action for the each stage. Download Source

SharePoint 2013 / SharePoint Online : Search REST API with AngularJs (Alternative to Content Search Web Part)

In my previous two posts (post 1 and post 2), I have explained how to develop a search driven development using external data with Content Search WebPart.  In this post, we will see how to build the search driven development using SharePoint 2013 Search REST API with AngularJs (Alternative to Content Search WebPart).  In another reason for writing this post is, currently I am developing a public portal like Mavention website. Mainly the portal users are anonymous users and they will not do any CRUD operations. So I have customized the master page looks like Mavention website. If the page request from anonymous users then, I will remove all the SharePoint script files and server side control on each page response. But the authenticated users (Web Administrator), the site will be render with as usual SharePoint features like Ribbon, Navigation, etc. I will write more about this in my next post. But the Content Search WebPart will not work without core.js and sp.js file. So I plan to achieve the similar one using Search REST API with AngularJs framework.  Advantages and Disadvantages of Content Search WebPart The main advantage with Content Search WebPart is that we can develop the search driven pages with minimal efforts and can customize the display templates using SharePoint designer. On successful save, equivalent JavaScript template file will be generated. This script files will be used for UI rendering. The main disadvantages of this approach is that, this web part will not be available in SharePoint online and will available only for on-premises development. SharePoint scripts files (for example core.js, sp.js) are mandatory for Content Search WebPart. So we should carefully customize the master page. Customizing the display templates are also not easy like custom development. it will generate lot of html and script contents inline with each page requests. Working with SharePoint REST API with AngularJs Now a days, we have a lot of web application frameworks are available freely and can develop a single page kind of application (SPA) quickly.  AngularJs is not a mandatory here and you can use a plain JavaScript or different client frameworks like jQuery or KnockoutJs. In the post, I am not going to explain the AngularJs features and you can find the detailed documentations in the AngularJs website. SharePoint REST API service will allow a user to query the crawled data with different options. Easily we can learn the Search API options using SharePoint 2013 Search Query Tool.  This is the end results of this post, Search Data Source To bring the SQL database to SharePoint search results, you can follow my previous post. Solution Structure Here I have developed a simple Visual WebPart with the following solution structure. This is not generic one like Content Search WebPart and also this code is not normalized one. Currently I am developing generic WebParts for Search REST API for both SharePoint on-premise or SharePoint online development and will share the solution shortly via CodePlex. Search Results Web Part I am using the AngularJs external template for complete implementation. So the SearchResults.ascx webpart will have only the template reference. <SharePoint:ScriptLink ID="Scriptlink8" runat="server" Name="~Site/_layouts/15/SearchREST/js/angular.min.js"></SharePoint:ScriptLink> <SharePoint:ScriptLink ID="Scriptlink2" runat="server" Name="~Site/_layouts/15/SearchREST/js/jquery-1.9.1.min.js"></SharePoint:ScriptLink> <SharePoint:ScriptLink ID="Scriptlink3" runat="server" Name="~Site/_layouts/15/SearchREST/js/SearchController.js"></SharePoint:ScriptLink> <div ng-app="appSearch" >     <div ng-include src="'_layouts/15/SearchREST/template/search.html'"></div>      </div>   Display Templates Like mentioned earlier, we can easily customize the search.html file based on our requirement. This file will have the search refinements and results block. <div ng-controller="searchController" ng-init="init()"> <table style="width: 100%;">     <tr>         <td style="width: 10%; border: solid 1px red; vertical-align:top;">             <label>Production Companies</label>             <select ng-model="genres" ng-options="c.RefinementName as c.RefinementName + ' (' + c.RefinementCount + ')'   for c in refiners[1].values" ng-change="change()"></select>             <br />             <br />             <label>Genres</label>                          <ul>                 <li ng-repeat="value in refiners[1].values"><a href='#'  ng-click="refreshSearch(value.RefinementName)">{{value.RefinementName}} ({{value.RefinementCount}})</a></li>             </ul>         </td>         <td style="width: 90%; vertical-align: top; border: solid 1px red;">             <table>                 <tr ng-repeat="movie in movies">                     <td>                        <img style="width:50px;height:60px" ng-src="{{movie.MoviePosterPath}}" />                     </td>                     <td>                         {{movie.MovieTitle}}                     </td>                     <td>                         {{movie.MovieReleaseDate | date: 'dd-MMM-yyyy'}}                     </td>                     <td>                         <a ng-href="{{movie.Path}}">Path</a>                     </td>                                      </tr>             </table>         </td>     </tr> </table> <input type="button" ng-disabled="currentPage == 0" ng-click="decreasePage()" value="Previous"/> {{currentPage+1}}/{{numberOfPages()}} <input type="button" ng-disabled="currentPage >= dataRows/pageSize - 1" ng-click="increasePage()" value="Next"/> Current Page : {{currentPage}} </div>   Search Data The SearchController.js file will have the complete data retrieval related implementation. var appSearch = angular.module('appSearch', []); appSearch.controller("searchController", function ($scope, $http) {     $scope.movies = [];     $scope.refiners = [];     $scope.currentPage = 0;     $scope.pageSize = 10;     $scope.dataRows = 0;     $scope.numberOfPages = function () {         return Math.ceil($scope.dataRows / $scope.pageSize);     }     $scope.startrow = 0;     $scope.query = "/sites/publishing/_api/search/query?querytext='*'&trimduplicates=true&selectproperties='Path,MoviePosterPath,MovieTitle,MovieReleaseDate,MovieGenres'&refiners='MovieGenres,MovieProductionCompanies'" +                 "&sourceid='0bc7c264-6e00-48bf-84fc-a7bb9e9f75b2'";     $scope.view = {         getView: function () {             return "template/search.html";         }     };     $scope.decreasePage = function()     {         $scope.currentPage = $scope.currentPage - 1;         $scope.startrow = $scope.currentPage * $scope.pageSize;                 $scope.callService();     }     $scope.increasePage = function()     {         $scope.currentPage = $scope.currentPage + 1;         $scope.startrow = $scope.currentPage * $scope.pageSize;         $scope.callService();     }     $scope.init = function () {         $scope.callService();     };     $scope.change = function () {         $scope.startrow = 0;         $scope.currentPage = 0;             $scope.query = "/sites/publishing/_api/search/query?querytext='*'&trimduplicates=true&selectproperties='Path,MoviePosterPath,MovieTitle,MovieReleaseDate,MovieGenres'&refiners='MovieGenres,MovieProductionCompanies'&refinementfilters='MovieGenres:equals(" +             $scope.genres + ")'&sourceid='0bc7c264-6e00-48bf-84fc-a7bb9e9f75b2'";         $scope.callService();     };     $scope.refreshSearch = function (value)     {         $scope.startrow = 0;         $scope.currentPage = 0;         $scope.query = "/sites/publishing/_api/search/query?querytext='*'&trimduplicates=true&selectproperties='Path,MoviePosterPath,MovieTitle,MovieReleaseDate,MovieGenres'&refiners='MovieGenres,MovieProductionCompanies'&refinementfilters='MovieGenres:equals(" +             value + ")'&sourceid='0bc7c264-6e00-48bf-84fc-a7bb9e9f75b2'";         $scope.callService();     }     $scope.callService = function () {         $http({             method: 'GET', url: $scope.query + "&startrow="+ $scope.startrow +"&rowsperpage=0",             headers: { "Accept": "application/json;odata=verbose" }         }).         success(function (data, status, headers, config) {             $scope.movies = [];             //Results             angular.forEach(data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results, function (movie) {                 angular.forEach(movie.Cells, function (movieRow) {                     var obj = {};                     angular.forEach(movieRow, function (movieData) {                         obj[movieData.Key] = movieData.Value;                     });                     $scope.movies.push(obj);                 });             });             //Refiners                     angular.forEach(data.d.query.PrimaryQueryResult.RefinementResults.Refiners.results, function (refiner) {                 var objRefiner = {};                 objRefiner["name"] = refiner.Name;                 objRefiner["values"] = refiner.Entries.results;                 $scope.refiners.push(objRefiner);             });             $scope.dataRows = data.d.query.PrimaryQueryResult.RelevantResults.TotalRows;             console.log('Movie', $scope.movies);         }).         error(function (data, status, headers, config) {             // called asynchronously if an error occurs             // or server returns response with an error status.         });     }; });   Summary Here I just explained how to develop the search driven pages using Search REST API with AngularJs. Please get back me if you have any clarifications regarding this post. Update : I have referred the AngularJs and jQuery files from 15 hive. This will work in SharePoint on-premises development and you can use Script Box WebPart for SharePoint online. Download Source