Sivarajan's Blog

SharePoint | Office 365 | Azure | JavaScript

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 Search Driven Development – Challenges and Workarounds

I had a few challenges in Search driven development approach. Here I am going to cover both the problems and workarounds. Overview Currently I am developing a  publishing portal based on Mavention site case study. The targeted site audiences are Anonymous Users (99%) and Web Administrators (1%). Initially I planned to develop a portal using SharePoint 2013 cross site publishing approach. But I have a million of products data in data warehouse system and can not load all these data into Authoring Site product catalog list. So I modified the development approach like below. I wrote the following posts based on the above approach. SharePoint 2013 – Develop a Publishing portal using BCS, Enterprise Search and Content Search Web Part SharePoint 2013 Search Refinements for External Content Type (BCS) data SharePoint 2013 / SharePoint Online : Search REST API with AngularJs (Alter native to content search Webpart) Problem #1 We assume that the authoring site URL is http://sp2013dc/sites/authoring and the publishing URL is  http://sp2013dc/sites/publishing and the product details should be accessible in both sites. For example, http://http://sp2013dc/sites/authoring/product/tt002343 and http://shttp://sp2013dc/sites/publishing/product/tt002343 For crawling the product URLs, we should create the Action URL like below and the URL will be unique. But I want to access the same product with two different action URLs. But SharePoint search will crawl the product using only one URL. I posted the issues in MSDN but I unable to find the solution for this issue. So we have to use only one site collection for both authoring and publishing. Workaround #1 So I modified the architecture like below. Now we have only one site collection for both Authoring and Publishing. Problem #2 The product URLs problem is solved now but I am aiming to develop a site looks like Mavention web site and they have completely customized their publishing portal master page. We can not found any SharePoint related script files (core.js, sp.js, ribbon.js) in the page view source. But they are using two site collections (Authoring and Publishing) and they are customized only the publishing master page and not authoring one. Workaround #2 But I am using only one site collection for both the functionalities. So I have planned to customize the master page based on roles. For anonymous users, I plan to remove all the SharePoint script using SPSecurityTrimmedControl control if the request from anonymous users. I wrote the separate post for master page customization.  How to customize the SharePoint 2013 Master Page for Anonymous Users       Summary In this post, I have covered few workarounds for search driven development. I hope this post will help you.

How to customize the SharePoint 2013 Master Page for Anonymous Users

We may have a situation to use the same master page for both authenticated and unauthenticated (anonymous) users. In that case, we will see here how to customize the SharePoint master pages for anonymous users. Why should we customize the Master page for Anonymous Users? Anonymous users normally will view the site contents and they will not do any CRUD operations in publishing portal.  So we can try to eliminate the SharePoint CRUD options related stuffs like scripts (SP.js, Core.js, SP.Ribbon.js) and server side controls whenever possible. In many of the SharePoint internet portal, we are simply hide the ribbons and SharePoint related stuffs if the request from anonymous users. We will not worry about server side / client side page rendering and browser response size. So we will load unwanted scripts and html contents for each page requests. Unauthenticated Vs. Authenticated Comparison For this walk through, I have used the starter master page which is available for downloading from CodePlex and also this is not a mandatory always. And you can try with default SharePoint master pages either Seattle or Oslo. You can see here the final output of the anonymous and authenticated users screen views. Anonymous User View Authenticated User View   Benchmark data from chrome developer console   Default master page Trimmed Master Page Total number of requests 16 4 Total page response time 425 ms 76 ms Total contents transferred 28.9 KB 6.6 KB     How to customize? I have seen few blog posts on eliminate the JavaScript files on each page response at server side in different approach. But I think that we can use the SPSecurityTrimmedControl for eliminating unwanted scripts / controls using the property called AuthenticationRestrictions. The AuthenticationRestrictions enum will have the three values AllUsers, AuthenticatedUsersOnly and AnonymousUserOnly. Based on our requirements, we can customize our master page using AuthenticatedUsersOnly or AnonymousUsersOnly enum value. Here I am not going to explain all the steps. You can download the customized master page at end of this post. Like that, if you want to customize the master page based on SharePoint group, we need to override the SPSecurityTrimmedControl. You can find the details on how to override the SPSecurityTrimmedControl here. How to remove the Scripts? We can apply the SPSecurityTrimmedControl for the following area if we do not need any SP.js or Core.js related scripts file for anonymous users.   <!-- ===== STARTER: SP Scripts and CSS  =========================================================================================== -->         <SharePoint:SPSecurityTrimmedControlID="SPSecurityTrimmedControl1"runat="server"AuthenticationRestrictions="AuthenticatedUsersOnly">             <SharePoint:StartScriptID="StartScript1"runat="server"/>             <SharePoint:CssLinkID="CssLink1"runat="server"Version="15"/>             <SharePoint:CacheManifestLinkID="CacheManifestLink1"runat="server"/>             <SharePoint:PageRenderModeID="PageRenderMode1"runat="server"RenderModeType="Standard"/>             <SharePoint:ScriptLinkID="ScriptLink1"Language="javascript"Name="core.js"OnDemand="true"runat="server"Localizable="false"/>             <SharePoint:ScriptLinkID="ScriptLink2"Language="javascript"Name="menu.js"OnDemand="true"runat="server"Localizable="false"/>             <SharePoint:ScriptLinkID="ScriptLink3"Language="javascript"Name="callout.js"OnDemand="true"runat="server"Localizable="false"/>             <SharePoint:ScriptLinkID="ScriptLink4"Language="javascript"Name="sharing.js"OnDemand="true"runat="server"Localizable="false"/>             <SharePoint:ScriptLinkID="ScriptLink5"Language="javascript"Name="suitelinks.js"OnDemand="true"runat="server"Localizable="false"/>             <SharePoint:CustomJSUrlID="CustomJSUrl1"runat="server"/>             <SharePoint:SoapDiscoveryLinkID="SoapDiscoveryLink1"runat="server"/>                          <!-- ===== STARTER: Additional header placeholder - overridden by asp:content on pages or page layouts  =========================== -->             <asp:ContentPlaceHolderID="PlaceHolderAdditionalPageHead"runat="server"/>             <SharePoint:DelegateControlID="DelegateControl1"runat="server"ControlId="AdditionalPageHead"AllowMultipleControls="true"/>             <!-- ===== STARTER: Core SharePoint CSS =========================================================================================== -->             <SharePoint:CssRegistrationID="CssRegistration1"Name="Themable/corev15.css"runat="server"/>         </SharePoint:SPSecurityTrimmedControl>   How to Remove the SharePoint Controls? We can apply the same SPSecurityTrimmedControl for the following area if we do not need any SharePoint title, Navigations and Ribbon bars for anonymous users. <SharePoint:SPSecurityTrimmedControlID="SPSecurityTrimmedControl4"runat="server"AuthenticationRestrictions="AuthenticatedUsersOnly">                                 <divid="s4-titlerow">                                     <!-- ===== STARTER: Site Logo with link to root ======================================================================= -->                                     <divclass="ms-dialogHidden">                                         <SharePoint:SPLinkButtonID="SPLinkButton1"runat="server"NavigateUrl="~sitecollection/">                                             <SharePoint:SiteLogoImageID="x63829de2201a4365a3904788f682d0a3"LogoImageUrl="&lt;% $SPUrl:~sitecollection/_layouts/15/vintage/logo.png %&gt;"AlternateText="Back to Home"runat="server"/>                                         </SharePoint:SPLinkButton>                                     </div>   Any script errors? Yes. I got the two errors in the final outputs. SharePoint farm will emit the two JavaScript method calls (ExecuteOrDelayUntilScriptLoaded and removeAllStatus). To avoid these two errors, either we should reference SP.js or can eliminate the SharePointForm tag from master page.  But the form tag is mandatory for SharePoint controls and WebParts rendering. So I added the two dummy methods in the master page and it will be rendered only if the request from anonymous users. <SharePoint:SPSecurityTrimmedControlID="SPSecurityTrimmedControl2"runat="server"AuthenticationRestrictions="AnonymousUsersOnly">             <SharePoint:ScriptBlockID="ScriptBlock1"runat="server">                 function ExecuteOrDelayUntilScriptLoaded(){}                 function removeAllStatus(){}             </SharePoint:ScriptBlock>         </SharePoint:SPSecurityTrimmedControl>   Summary After customize the unwanted scripts / server side controls, we can apply the required Web Application frameworks like AngularJs, KnockockJs or Bootstrap and also custom CSS. You can download the completely customized master page here. 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