Pages

Sunday, November 17, 2013

SharePoint 2013: Optimize your development Environment

SharePoint 2013 demands more resources – especially more memory. When we need to setup a SharePoint 2013 environment we can optimize resource usage in the development server by provisioning/configuring required services as well as following some other guidelines listed in this post.

 

Search Service Applications

Search Service is EXTREMELY resource hungry. noderunner process (Search Service process for crawling/indexing) is widely known for it’s resources usage footprint. In case you don’t need search service in your development server, you can delete the service application or at least stop the service. If you need to use the search service from time to time, you can keep the search service application running but stop the search service. However, if you try to stop the search service from windows service, you will find the service will be started again by SharePoint. The correct way to stopping service is from Central admin as shown below:

image

 

If you need the search service components (crawling/indexing) on development server, you can reduce the performance level by running the following command in SharePoint PowerShell. More information available at http://technet.microsoft.com/en-us/library/ff608126.aspx.

Set-SPEnterpriseSearchService -PerformanceLevel Reduced

You can also control the noderunner memory consumption by changing ‘memoryLimitMegabytes’ in the config file “C:\Program Files\Microsoft Office Servers\15.0\Search\Runtime\1.0\noderunner.exe.config”. Please remember, changing the value to too low might cause the search service to fail.

 

 

Provision Required Service Applications only

If you install SharePoint using Installation wizard, different service applications are installed but you many not need all of these service applications. From Central admin you can delete unnecessary Service applications as well as stop services. As you can see below

 

 

Stop unnecessary Web Application

Even web application running in your development environment will create w3wp process (if a separate application pool is used) or at least use resources. So if you don’t nee a web application for time being, you can stop the web application as well as related application pool from IIS Manager.

 

 

Visual Studio IntelliTrace

If you use Visual Studio debugging in the server and your Visual Studio supports IntelliTrace and the feature is enabled, you can disable the option. That might improve your debugging experience. More information on how to enable/disable the feature can be found at: http://msdn.microsoft.com/en-us/library/dd264948(v=vs.100).aspx

 

Multiple Disk Drive

If you have configured multi-server farm, you can keep some servers in another disk drive. For example, I’ve a multi-server (4 servers – AD, SQL, WFE and App) farm and I’ve kept two servers in my external USB3 supported external drive. So rather than four server vying for the same disk access, two are vying for internal disk access and other two are vying for external disk.

Even if are running a single server farm, you can use an external SSD drive (or USB3) for better I/O throughput. Especially if you can move your database files in an external drive, you will experience much better performance.

 

Tracing Service

SharePoint uses a windows Service ‘SharePoint Tracing Service’ for logging. sometimes, like during deployment, you will find the log file is growing few hundreds megabytes in short time. So tracing service might take a bit role in performance. If you don’t need the log file for a period of time, you can disable the windows service. During development I usually disable the service and when I need to see the log file, I enable the service.

Sunday, October 27, 2013

Upgrade SharePoint 2010 Visual Studio Solution to SharePoint 2013

With every new version of SharePoint, we need to upgrade our Visual Studio solution. There’s no easy way or tool out there that can automate these upgrade. So I’ll just describe few points you need to consider when you’ll upgrade the Visual Studio 2010 project for SharePoint 2010 to Visual Studio 2012 project for SharePoint 2013. First of all, you need to upgrade your Visual Studio pre-VS2012 solution to Visual Studio 2012.

Change Target .NET Framework

SharePoint 2010 uses .Net Framework 3.5 with SharePoint 2013 uses .Net Framework 4.5. So you need to change the target framework for the project. To change the target framework version, unload the project and then edit the project file in Visual Studio and change the property as shown below:

<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>

 

Change SharePoint Version

Unload the project and edit in Visual Studio, then add/edit the section to ensure the following entry near to the <TargetFrameworkVersion>

<TargetOfficeVersion>15.0</TargetOfficeVersion>

Then load the project and replace all SharePoint 2010 specific DLLs with SharePoint 2013 specific DLLs (version 15). Also find out any reference of SharePoint 2010 DLLs/Controls  version ‘14.0.0.0’ and replace with ‘15.0.0.0’

 

Change Generic Setup Path in code

In SharePoint 2013, you can deploy your solution in SharePoint 2010 more or SharePoint 2013 mode or both. For example to deploy your solution to both 2010 and 2013 mode, you can use the following command:

Install-SPSolution -Identity my_solution.wsp -GACDeployment -CompatibilityLevel {14,15}

If you skip the CompatibilityLevel (and we usually skip), then the solution is deployed default compatibility level included in the WSP manifest file – which is based on Visual Studio project’s target SharePoint Version. But since we are targeting to upgrade Visual Studio solution to SharePoint 2013, we need to make sure code looks for artifacts in 15 hive, instead of 14 hive. To ensure that code looks in 15 hive, we need to find all use of ‘SPUtility.GetGenericSetupPath()’ and need to replace it with ‘SPUtility.GetVersionedGenericSetupPath()’ passing parameter 15 as shown below:

SPUtility.GetVersionedGenericSetupPath(‘/_Layouts/myproject/script/test.js’,15)

Note – If you are still deploying your solution in compatibility 14 mode, then you should also replace the ‘GetGenericSetupPath()’ with ‘GetVersionedGenericSetupPath()’ passing parameter 14, but instead of hardcoding the version inline, you can use a constant so that you can replace the version number in one place to change it to 15. You can use constant ‘Microsoft.SharePoint.Utilities.SPUtility.ContextCompatibilityLevel’ which returns the current compatibility level of the SPSite. Your code then will look like:

SPUtility.GetVersionedGenericSetupPath(‘/_Layouts/myproject/script/test.js’,Microsoft.SharePoint.Utilities.SPUtility.ContextCompatibilityLevel)

However, if you want to use the same code both for SharePoint 2010 and SharePoint 2013, then you can code block like below to run code in different version of SharePoint:

//if SharePoint site collection is running in 2013 (15) mode
if (SPUtility.ContextCompatibilityLevel==SPUtility.CompatibilityLevel15)
{
    //Code needs to be executed in SharePoint 2013
}
else if (SPUtility.ContextCompatibilityLevel==14)
{
  // Code needs to executed in SharePoint 2010
}
//You can also use SPSite.CompatibilityLevel to find out site collection compatibiltiy mode

Change Reference to Layouts, ControlTemplates

Finally all references to ‘_Layouts/’ and ‘_ControlTemplates/’ needs to be changed to ‘_Layouts/15/’ and ‘_ControlTemplates/15/’ to ensure the artifacts are loaded from 15 hive. Either SharePoint will try to load files from 14 hive. However if you deploying the solution in SharePoint 2010 mode, (compatibility level is 14), then you don’t need to update the reference.

 

Conclusion

After making all the above changes, finally compile the solution. However, my suggestion would be to deploy the WSP in SharePoint 2010 mode first, test the deployment and adjust/modify code to comply with SharePoint 2010 object model. Once you are comfortable with changes, then upgrade code as described in the post and then deploy/test.

Sunday, October 13, 2013

SharePoint Suite/Chrome Control

SharePoint 2013 or Office 365 introduced a new ribbon on the top of the page known as Suite Control as shown below (‘SharePoint’ text):
image
However, we may want to control the text to appear in the suite control and how they will appear. I’ll explain how to put static text as well as dynamic text in the suite control. For example you may want to show your environment name like ‘DEV’, ‘Test’.

 

Static Content

If you would like to put just some static text replacing ‘SharePoint’ or ‘Office 365’ you can do so in few different ways that are described below:
Modify using PowerShell
You can modify the text using PowerShell and apply your own static text. You can apply text to web application level as shown below:
$webApplication = Get-SPWebApplication -Identity "http://web-application-url"
$webApplication.SuiteBarBrandingElementHtml = '<div class="ms-core-brandingText">My Text</div>'
$webApplication.Update()


This approach will work for whole web application but if you need different text in different sites, you may not be interested in this approach. Also for Office 365, you will not have these PowerShell command available.

 

Modify through MasterPage

However, if you don’t have access to PowerShell (as in Office 365, you have limited PowerShell access), you can modify master page to edit the text. For your information, if you are using HTML based master page design, then this approach will not work. For your information, in SharePoint 2013, you can develop HTML design first and then can convert the HTML to master page – hence HTML based master page design. Open the default master page in your site using SharePoint Designer, or if you are using custom branding, open your custom master page in edit mode (either in SharePoint Designer or Visual Studio). Find a div with Id ‘suiteBar’ and there’s a delegate control inside the div with id ‘ID_SuiteBarBrandingDelegate’, set this delegate control visible property to ‘false’ (shown in the image below) as this will hide the default Chrome text. Then add your own chrome text in another div after the delegate control

<div class="ms-core-brandingText">My Static Chrome Text</div>

Finally the change will look like below:

image
However, if you are using HTML based master page design (where you have editing html page which is generating master page), this option will not work as the delegate control is not available in HTML file (rather available in master page file) and you are not supposed to edit the master page directly in HTML based master page design.

 
Modify Using JavaScript
If you want to use JavaScript to modify the text, edit your master page (or corresponding html file) and add the following script in the file.
<script type="text/javascript">
    $(document).ready(function () {
        ApplySuiteBranding('My Text');

        //ApplySuiteBranding("<b>My Text</b>");
        //ApplySuiteBranding("<b>My Text</b>");
        //ApplySuiteBranding("<a href='http://abc.com'><img src='test.jpg' alt='icon'/></a>");
    });
    function ApplySuiteBranding(htmlToApply) {
        //find for office 365 suite control
        var brandingBox = $('#suiteBrandingBox');
        if ($(brandingBox).length == 0) {
            //office 365 suite control not found, so it's SharePoint
            brandingBox = $('#suiteBar').find(".ms-core-brandingText");
        }

        $(brandingBox).html(htmlToApply);
    }
</script>


For your information, the above script need to be applied in master page html file if you are using html based master page design. Otherwise apply the script in master page directly. Also you need to add reference to jQuery in the master page as the above script make use of jQuery Library. You can also modify the script to add dynamic text, like current site title by using ‘_spPageContextInfo.webTitle’. So this approach will enable developer to provide semi-dynamic text in the chrome – like web title or any other text that is accessible from jQuery/JavaScript.

 

Dynamic Content

If you would like to modify the suite control text dynamically, for example add some dynamic navigation or apply different text in different site collection, then you need to create a delegate control. I would recommend this solution, if possible. Though it requires custom development/deployment but it gives you more flexibility and more robust solution. Let’s describe how to do that. I’m not going to explain it steps by steps as you can find a nice example of this at: http://blogs.msdn.com/b/findnavish/archive/2013/02/07/sharepoint-2013-customizing-suite-bar.aspx.

 

Chrome Control in SharePoint 2013 Apps

If you want to use Chrome Control in SharePoint apps, you can find more details on MSDN: http://msdn.microsoft.com/en-us/library/fp179916.aspx.

 

Conclusion

So in summary, if you are happy with static text for whole web application and have PowerShell command available, then use the option of PowerShell. However, if you don’t have access to PowerShell command (as in Office 365), then you can use JavaScript based or direct Master Page edit option described. Finally, if you want a decent and manageable solution (considering, you are deploying a custom WSP solution), you can use Delegate control which gives you more control to change text dynamically. Using JavaScript option will give you the option of adding static text as well as some dynamic text.

Friday, July 26, 2013

SharePoint 2013: Breadcrumb for list/library

Maybe we’ve noticed the issue in SharePoint - if a list view webpart is added to page and user navigate to different folders in the list view, there’s no way for users to know current folder hierarchy. So basically breadcrumb for the list view webpart missing. If there would be a way of showing users the exact location in the folder hierarchy the user is current in (as shown in the image below), wouldn’t be that great?
image
Image 1: List view webpart with folder-navigation breadcrumb


I’ll explain in the post how you can achieve this kind of folder navigation with minimum changes/customisation.

Deployment

Download the FolderNavigation.js file from MSDN Code Gallery. You can deploy the script either in Layouts folder (in case of full trust solutions) or in Master Page gallery (in case of SharePoint Online or full trust). I would recommend to deploy in Master Page Gallery so that even if you move to cloud, it works without modification. If you deploy in Master page gallery, you don’t need to make any changes, but if you deploy in layouts folder, you need to make small changes in the script which is described in section ‘Deploy JS Link file in Layouts folder’.

Option 1: Deploy in Master Page Gallery (Suggested)
If you are dealing with SharePoint Online, you don’t have the option to deploy in Layouts folder. In that case you need to deploy it in Master page gallery. Note, deploying the script in other libraries (like site assets, site library) will not work, you need to deploy in master page gallery. To deploy in master page gallery manually, please follow the steps:
  1. Download the js file from MSDN Code Gallery.
  2. Navigate to Root web => site settings => Master Pages (under group ‘Web Designer Galleries’).
  3. From the ‘New Document’ ribbon try adding ’JavaScript Display Template’ and then upload the FolderNavigation.js file and set properties as shown below:
    image
    Image 2: Upload JS file to Master Page Gallery
In the above image, we’ve specified the content type to ‘JavaScript Display Template’, ‘target control type’ to view to use the js file in list view. Also I’ve set target scope to ‘/’ which means all sites and subsites will be applied. If you have a site collection ‘/sites/HR’, then you need to use ‘/Sites/HR’ instead. You can also use List Template ID, if you need.

Option 2: Deploy JS Link file in Layouts Folder
If you are deploying the FolderNavigation.js file in Layouts folder, you need to make small changes in the downloaded script’s RegisterModuleInti method as shown below:
RegisterModuleInit('FolderNavigation.js', folderNavigation);
In this case the ‘RegisterModuleInit’ first parameter will be the path relative to Layouts folder. If you deploy your file in path ‘/_Layouts/folder1’, the then you need to modify code as shown below:
RegisterModuleInit('Folder1/FolderNavigation.js', folderNavigation);

If you are deploying in other subfolders in Layouts folder, you need to update the path accordingly. What I’ve found till now, you can only deploy in Layouts and Master page gallery. But if you find deploying in other folders works, please share.

Use The JS File in List View WebPart

Once you deploy the FolderNavigation.js file, you can start using it in list view webpart. Edit the list view web part properties and then under ‘Miscellaneous’ section put the file url for JS Link as shown below:
image
Image 3: JS Link for list view webpart

Few points to note for this JS Link:
  • if you have deployed the js file in Master Page Gallery, You can use ~site or ~SiteCollection token, which means current site or current site collection respectively.
  • If you have deployed in Layouts folder, you need to use corresponding path in the JS Link properties. For example if you are deploying the file in Layouts folder, then use ‘/_layouts/15/FolderNavigation.js’, if you are deploying in ‘Layouts/Folder1’ then, use ‘/_layouts/15/Folder1/FolderNavigation.js’.

How it works?

If you are interested how it works, you can carry on reading, either move to deployment section. Basically I’ve utilized the SharePoint 2013 Client Side Rendering (CSR) concept. If you are not familiar with this CSR, you can get the basic idea by Googling. Simply, CSR allows us to customize the rendering of SharePoint fields using JavaScript. I’ve developed a client side rendering JavaScript file which is shown below:

///Author: Sohel Rana
//Version 1.2
//Last Modified on 27-Oct-2013
//Version History:
//  1. Added
//  2. Fixed the bug 'Filtering by clicking on field title would result duplicate navigation link'
//  3. Fixed the bug 'breadcrumb title always lowercase'. Now breadcrumb title is as like the folder name in the library even with case (lower/upper)

//replace query string key with value
function replaceQueryStringAndGet(url, key, value) {
    var re = new RegExp("([?|&])" + key + "=.*?(&|$)", "i");
    separator = url.indexOf('?') !== -1 ? "&" : "?";
    if (url.match(re)) {
        return url.replace(re, '$1' + key + "=" + value + '$2');
    }
    else {
        return url + separator + key + "=" + value;
    }
}


function folderNavigation() {
    function onPostRender(renderCtx) {
        if (renderCtx.rootFolder) {
            var listUrl = decodeURIComponent(renderCtx.listUrlDir);
            var rootFolder = decodeURIComponent(renderCtx.rootFolder);
            if (renderCtx.rootFolder == '' || rootFolder.toLowerCase() == listUrl.toLowerCase())
                return;

            //get the folder path excluding list url. removing list url will give us path relative to current list url
            var folderPath = rootFolder.toLowerCase().indexOf(listUrl.toLowerCase()) == 0 ? rootFolder.substr(listUrl.length) : rootFolder;
            var pathArray = folderPath.split('/');
            var navigationItems = new Array();
            var currentFolderUrl = listUrl;

            var rootNavItem =
                {
                    title: 'Root',
                    url: replaceQueryStringAndGet(document.location.href, 'RootFolder', listUrl)
                };
            navigationItems.push(rootNavItem);

            for (var index = 0; index < pathArray.length; index++) {
                if (pathArray[index] == '')
                    continue;
                var lastItem = index == pathArray.length - 1;
                currentFolderUrl += '/' + pathArray[index];
                var item =
                    {
                        title: pathArray[index],
                        url: lastItem ? '' : replaceQueryStringAndGet(document.location.href, 'RootFolder', encodeURIComponent(currentFolderUrl))
                    };
                navigationItems.push(item);
            }
            RenderItems(renderCtx, navigationItems);
        }
    }


    //Add a div and then render navigation items inside span
    function RenderItems(renderCtx, navigationItems) {
        if (navigationItems.length == 0) return;
        var folderNavDivId = 'foldernav_' + renderCtx.wpq;
        var webpartDivId = 'WebPart' + renderCtx.wpq;


        //a div is added beneth the header to show folder navigation
        var folderNavDiv = document.getElementById(folderNavDivId);
        var webpartDiv = document.getElementById(webpartDivId);
        if(folderNavDiv!=null){
            folderNavDiv.parentNode.removeChild(folderNavDiv);
            folderNavDiv =null;
        }
        if (folderNavDiv == null) {
            var folderNavDiv = document.createElement('div');
            folderNavDiv.setAttribute('id', folderNavDivId)
            webpartDiv.parentNode.insertBefore(folderNavDiv, webpartDiv);
            folderNavDiv = document.getElementById(folderNavDivId);
        }


        for (var index = 0; index < navigationItems.length; index++) {
            if (navigationItems[index].url == '') {
                var span = document.createElement('span');
                span.innerHTML = navigationItems[index].title;
                folderNavDiv.appendChild(span);
            }
            else {
                var span = document.createElement('span');
                var anchor = document.createElement('a');
                anchor.setAttribute('href', navigationItems[index].url);
                anchor.innerHTML = navigationItems[index].title;
                span.appendChild(anchor);
                folderNavDiv.appendChild(span);
            }

            //add arrow (>) to separate navigation items, except the last one
            if (index != navigationItems.length - 1) {
                var span = document.createElement('span');
                span.innerHTML = '&nbsp;>&nbsp;';
                folderNavDiv.appendChild(span);
            }
        }
    }


    function _registerTemplate() {
        var viewContext = {};

        viewContext.Templates = {};
        viewContext.OnPostRender = onPostRender;
        SPClientTemplates.TemplateManager.RegisterTemplateOverrides(viewContext);
    }
    //delay the execution of the script until clienttempltes.js gets loaded
    ExecuteOrDelayUntilScriptLoaded(_registerTemplate, 'clienttemplates.js');
};

//RegisterModuleInit ensure folderNavigation() function get executed when Minimum Download Strategy is enabled.
//if you deploy the FolderNavigation.js file in '_layouts' folder use 'FolderNavigation.js' as first paramter.
//if you deploy the FolderNavigation.js file in '_layouts/folder/subfolder' folder, use 'folder/subfolder/FolderNavigation.js as first parameter'
//if you are deploying in master page gallery, use '/_catalogs/masterpage/FolderNavigation.js' as first parameter
RegisterModuleInit('/_catalogs/masterpage/FolderNavigation.js', folderNavigation);

//this function get executed in case when Minimum Download Strategy not enabled.
folderNavigation();


Code Snippet 1: Folder Navigation JavaScript

Let me explain the code briefly.

  • The method ‘replaceQueryStringAndGet’ is used to replace query string parameter with new value. For example if you have url ‘http://abc.com?key=value&name=sohel’  and you would like to replace the query string ‘key’ with value ‘New Value’, you can use the method like
    replaceQueryStringAndGet("http://abc.com?key=value&name=sohel","key","New Value")
  • The function folderNavigation has three methods. Function ‘onPostRender’ is bound to rendering context’s OnPostRender event. The method first checks if the list view’s root folder is not null  and root folder url is not list url (which means user is browsing list’s/library’s root). Then the method split the render context’s folder path and creates navigation items as shown below:
    var item =
        {
            title: title,
            url: lastItem ? '' : replaceQueryStringAndGet(document.location.href, 'RootFolder', encodeURIComponent(rootFolderUrl))
        };
As shown above, in case of last item (which means current folder user browsing), the url is empty as we’ll show a text instead of link for current folder.
  • Function ‘RenderItems’ renders the items in the page. I think this is the place of customisation you might be interested. Having all navigation items passed to this function, you can render your navigation items in your own way. renderContext.wpq is unique webpart id in the page. As shown below with the wpq value of ‘WPQ2’ the webpart is rendered in a div with id ‘WebPartWPQ2’. 

    image
    Image 4: How list view webpart is rendered

    In ‘RenderItems’ function I’ve added a div just before the webpart div ‘WebPartWPQ2’ to put the folder navigation as shown in the image 1.
     
  • In the method ‘_registerTemplate’, I’ve registered the template and bound the OnPostRender event.
  • The final piece is RegisterModuleInit. In some example you will find the function folderNavigation is executed immediately along with the declaration. However, there’s a problem with Client Side Rendering and Minimal Download Strategy (MDS) working together as described in here. To avoid this problem, we need to Register foldernavigation function with RegisterModuleInit to ensure the script get executed in case of MDS-enabled site. The last line ‘folderNavigation()’ will execute normally in case of MDS-disabled site.
I’ve upload the js file in MSDN code gallery. Please download it from here.

Download

The JavaScript file is available in MSDN gallery for download.

Conclusion

The solution works for both SharePoint Server as well as SharePoint Online. Though I’ve shown manual process of deploying and updating the JS Link in webpart properties, you can develop powershell script to deploy the FolderNavigation. Few references might be useful:

Wednesday, July 24, 2013

SharePoint Reporting Service and Bing Map – Unable to connect to Remote Server

We’ve RDL reports running in SharePoint using SQL Server Reporting Service. The reports were using Bing Map to display some BI information. Reports were working fine in our development box but as soon as we moved the reports in UAT, Bing map was failed to rendered and the error was ‘Unable to Connect to Remote Server’.

After Googling it comes out that we need to configure proxy settings and we’ve a proxy in UAT. So we configured proxy settings for Reporting Service web.config file (which usually exists in Reporting Server location at Drive:\Program Files\Microsoft SQL Server\MSRS10_50.[instance name]\Reporting Services\ReportServer\web.config) as shown below. You need to add the following section in between <Configuration> section (possibly after </Runtime>):

<system.net>
    <defaultProxy enabled="true">
        <proxy bypassonlocal="True" proxyaddress="http://server:port" /> 
     </defaultProxy>
</system.net>

But it didn’t solve our problem. Still we were getting the error Unable to connect to remote server.

 

Solution

After investigating it turned out that we also need to configure SharePoint Reporting Services Service Application’s web.config file. You can find the Reporting Service Application by browsing the ‘SharePoint Web Service’ in IIS as shown below (in SharePoint 2013 box):

image

Figure : SharePoint Service Application – SQL Server Reporting Services in IIS

 

So you need to configure the proxy settings for both:

  • SQL Server Reporting Service (Drive:\Program Files\Microsoft SQL Server\MSRS10_50.[instance name]\Reporting Services\ReportServer\web.config)
  • as well as SharePoint Reporting Service Application’s web.config file (shown in above image).

Friday, July 19, 2013

SharePoint 2013: Browse and Upload Multiple Documents Missing

SharePoint 2013 introduced new features with more user-friendly User Interface. But Maybe it’s not true for ‘Multiple Documents upload’. SharePoint 2013 deprecated upload multiple files with ‘file upload control’ as described in the Microsoft Support page. Let’s take a look what it was in SharePoint 2010 and what’s we’re missing in SharePoint 2013.

SharePoint 2010 – Upload Multiple Documents Dialog

In SharePoint 2010, we are familiar with the following multiple upload control as shown below:
image
Image 1: SharePoint 2010 - Upload Multiple File

The ‘Upload Multiple Documents’ option in SharePoint 2010 allows to drag and drop files but there’s also an option to browse files as shown below:
image
Image 2: SharePoint 2010 ‘Upload Multiple Documents’ dialog with browse file option
The ‘Browse for files instead’ option (as shown in the Image 2 above) would be last resort for users – where drag and drop would not work. But this option is missing now in SharePoint 2013.

SharePoint 2013 – Multiple Documents Upload Options

As described in http://support.microsoft.com/kb/2761257/en-us, users have the two following options in SharePoint 2013 to upload multiple files:
  • Windows Explorer View
  • Drag-n-Drop
The following image shows how the new ‘upload document’ ribbon control in SharePoint 2013 looks now:
image
Image 3: Upload ribbon button in SharePoint 2013


With ‘browse multiple files and upload’ deprecated in SharePoint 2013, Drag And Drop is the only way of uploading multiple files inside browser. Unfortunately the drag and drop will only work with IE9 (and later versions of IE) but not in IE8. Also some users might find drag-n-drop a bit difficult to deal with (need to open windows explorer, select the files, drag the files in Internet explorer and in a particular section).

So what options left for users who are using IE8? They can use other browsers like Chrome, FireFox etc. (nice way of using non-IE for browsing SharePoint) for drag and drop. Otherwise they can use Windows Explorer View. However Windows Explorer view may need minor configuration in users’ computers and might not work for Office 365. There’s no official explanation of why the ‘browse and upload multiple documents’ option is removed from SharePoint 2013 in the Microsoft Support page.

It’s really disappointing to see the upload multiple files through file upload control gone in SharePoint 2013. Users who are using SharePoint 2013 and IE8, as well as they don’t have windows explorer view configured, they have only one option of using non-IE browsers. Also some users might find drag and drop a bit difficult to use. And Microsoft didn’t leave any choice for them.

Friday, June 14, 2013

SharePoint 2013: Start Workflow with JavaScript Client Object Model

In SharePoint 2013, you can start a workflow with JavaScript Client Object Model. Basically SharePoint use JavaScript in out-of-the-box ‘start workflow’ pages. If you develop a new SharePoint 2013 workflow in Visual Studio and add a Initiation form, you will find JavaScript functions added to the Initiation Form to start workflow. The source code I’ve provided below is taken from the Initiation Form and presented with modification to work independently.

//dialog element to show during processing
var dlg = null;      

//Subscription id - Workflow subscription id
//list item id for which to start workflow. If site workflow, then send null for itemId
function StartWorkflow(subscriptionId, itemId) {
   showInProgressDialog();
   var ctx = SP.ClientContext.get_current();
   var wfManager = SP.WorkflowServices.WorkflowServicesManager.newObject(ctx, ctx.get_web());
   var subscription = wfManager.getWorkflowSubscriptionService().getSubscription(subscriptionId);
   ctx.load(subscription, 'PropertyDefinitions');
   ctx.executeQueryAsync(
       function (sender, args) {
           var params= new Object();
           //Find initiation data to be passed to workflow.
           var formData = subscription.get_propertyDefinitions()["FormData"];
           if (formData != null && formData != 'undefined' && formData != "") {
               var assocParams = formData.split(";#");
               for (var i = 0; i < assocParams.length; i++) {
                   params[assocParams[i]] = subscription.get_propertyDefinitions()[assocParams[i]];
               }
           }
           if (itemId) {
               wfManager.getWorkflowInstanceService().startWorkflowOnListItem(subscription, itemId, params);
           }
           else {
               wfManager.getWorkflowInstanceService().startWorkflow(subscription, params);
           }
           ctx.executeQueryAsync(
               function (sender, args) {
                   closeInProgressDialog();
               },
               function (sender, args) {
                   closeInProgressDialog();
                   alert('Failed to run workflow');
               }
           );
       },
       function (sender, args) {
           closeInProgressDialog();
           alert('Failed to run workflow');
       }
   );
 }
function closeInProgressDialog() { if (dlg != null) { dlg.close(); } } function showInProgressDialog() { if (dlg == null) { dlg = SP.UI.ModalDialog.showWaitScreenWithNoClose("Please wait...", "Waiting for workflow...", null, null); } }

Code Snippet 1: Start workflow

 

As shown in the above code example, you need to pass subscription id and item id. Everytime you associate workflow with list or site, you will get an association id for this association.To get an association id, go to the workflow settings page and then right click on workflow name and from the URL, you can find the subscription id as shown below:

image

Figure 1: Get the Workflow Association Id from Workflow Settings Page

 

If your workflow is Site Workflow (for your information, SharePoint 2013 workflow can run on a list item or for site), then pass null value for itemId. ‘FormData’ retrieved from workflow Subscription, will be passed to workflow initiation. But if you would like to pass your own additional parameters, you can do so as shown below:

var params= new Object();
//Find initiation data to be passed to workflow.
var formData = subscription.get_propertyDefinitions()["FormData"];
if (formData != null && formData != 'undefined' && formData != "") {
    var assocParams = formData.split(";#");
    for (var i = 0; i < assocParams.length; i++) {
        params[assocParams[i]] = subscription.get_propertyDefinitions()[assocParams[i]];
    }
}

//your custom parameters
params['mykey']='this is custom value';

Code Snippet 2: Passing workflow initiation parameters

 

Similarly, if you would like to get JavaScript to associate workflow, you can add a workflow in Visual Studio and then add a association from. In the association form, you will find related JavaScript to associate workflow using JavaScript Object Model.

Finally you can all the javascript function provided in code snippet 1, as shown below (subscription id with curly braces):

StartWorkflow(‘{guid}’, itemId)

Monday, May 27, 2013

Programmatically Start SharePoint 2013 Workflow

SharePoint 2013 introduced a new set of API to work with new workflow model. In my previous post I described how to access workflows associated with a list/list item and access workflow properties. In this post I’ll describe how to run SharePoint 2013 workflow programmatically.

First you need to create instance of WorkflowServicesManager, then you need to find out the subscription (which actually represent the workflow association). Finally you can run the workflow with WorkflowInstance Service as shown below:

var workflowServiceManager = new WorkflowServicesManager(web);
var workflowSubscriptionService = workflowServiceManager.GetWorkflowSubscriptionService();

//get all workflows associated with the list
var subscriptions = workflowSubscriptionService.EnumerateSubscriptionsByList(listId);

//run all workflows associated with the list
foreach (var workflowSubscription in subscriptions)
{
    //initiation parameters
    var inputParameters = new Dictionary<string, object>();
    inputParameters.Add("MyProperty", "MyValue");

    workflowServiceManager.GetWorkflowInstanceService().StartWorkflowOnListItem(workflowSubscription, itemId, inputParameters);
}

In the StartWorkflowOnListItem, if you have any input parameters (which you usually pass from workflow initiation form), you can pass the input paramters as a dictionary. Otherwise, you can pass an empty dictionary.

 

Similarly you can terminate or suspend workflows using Workflow Instance Service.

For your information, workflow will not run if you use an elevated web in WorkflowServicesManager. You need to make sure you are running the code with an user who is not system account and who has a user profile.

Saturday, May 25, 2013

SharePoint 2013 Workflow: Programmatically Access Workflow Properties

SharePoint 2013 workflow introduced a new API to interact with SharePoint 2013 workflows. The old workflow API will not work for this new workflow model. The most basic API is available as part of the DLL (Microsoft.SharePoint.WorkflowServicesBase.dll) and the basic entry point for accessing workflow data is WorkflowServicesManager class. I’ll explain few uses of API but you can browse the API to find out more details usage.

 

Get all Workflows associated with a list

You need to create a new instance of WorkflowServiceManager and using the list id, you can ask WorkflowSubscriptionService to find all workflows associated with the list as shown below:

WorkflowServicesManager workflowServiceManager = new WorkflowServicesManager(web);
var workflowSubscriptionService = workflowServiceManager.GetWorkflowSubscriptionService();
var subscriptions = workflowSubscriptionService.EnumerateSubscriptionsByList(listId);

You can get subscription properties as shown below. If you association form has any custom properties, you can retrieve these property values by passing property name.

foreach (var subscription in subscriptions)
{
    var assocationName = subscription.Name;
    var propertyValue = subscription.GetProperty("Property_Name");
}

 

Get Workflow Instance

To get workflow instance details we need get the instance of WorkflowInstanceService from WorkflowServiceManager class as shown below:

WorkflowServicesManager workflowServiceManager = new WorkflowServicesManager(web);
var workflowInstanceService = workflowServiceManager.GetWorkflowInstanceService();
var workflowInstances = workflowInstanceService.EnumerateInstancesForListItem(listId, itemId);

Once we get the workflow Instance, we can get the instance properties as shown below. Any properties in Workflow Initiation form will also be available:

foreach (var instance in workflowInstances)
{
    var propertyValue= instance.Properties["Property_Name"];
}

The table below shows few common properties, but you can find out more properties by debugging code:

Property Name

Description

Microsoft.SharePoint.ActivationProperties.ContextListId

List Id

Microsoft.SharePoint.ActivationProperties.CurrentItemUrl

Current Item Url

Microsoft.SharePoint.ActivationProperties.InitiatorUserId

Initiator User login name, for example i:0#.w|domain\username

Microsoft.SharePoint.ActivationProperties.ItemId

List Item Id

 

Send Custom Notification to Workflow

In SharePoint 2013 workflow, there’s a activity ‘WaitForCustomEvent’. Using this activity you can keep your workflow wait for a custom event. In this activity you can pass the event name. To send the custom event to the workflow you can use workflow API as shown below. First you need to find out the workflow Instance.

WorkflowServicesManager workflowServiceManager = new WorkflowServicesManager(web);
var workflowInstanceService = workflowServiceManager.GetWorkflowInstanceService();
workflowInstanceService.PublishCustomEvent(workflowInstance, "CustomEventName", "");

Sunday, April 28, 2013

SharePoint 2013 Workflow: Use HttpSend Activity to call Custom SharePoint WCF Service

SharePoint 2013 workflow are declarative workflow – means no C# code inside the workflow. All you have in declarative workflow, is predefined activities (or maybe your own custom activities). If you would like to do some very customized things to do in workflow (like verifying data in some external system), you can do so with a WCF service. And then from workflow, you can invoke the WCF. In this post I’ll explain how to create a WCF service and invoke it from workflow. I’ve uploaded the source code in MSDN Code Gallery. Please make sure you have March update for Workflow Manager and Service Bus 1.0 installed, unless the source code might not work properly.

Create a WCF Service

I had described once how to create a WCF service and use it with jQuery for SharePoint 2010 and I’ve created the WCF service based on this post. The workflow Service interface is given below:
[ServiceContract]
public interface IServiceForWorkflow
{
    [OperationContract]
    [WebGet(BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, 
        ResponseFormat = WebMessageFormat.Json, UriTemplate = "/GetData?instanceId={wfInstanceId}")]
    WorkflowData GetWorkflowData(string wfInstanceId);

    [OperationContract]
    [WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, Method = "POST", RequestFormat = WebMessageFormat.Json, 
        ResponseFormat = WebMessageFormat.Json, UriTemplate = "/SendEmail?toEmail={to}&emailSubject={subject}")]
    void SendEmail(string to,string subject);
}

 Code Snippet: Service Interface
As shown in above code snippet, I’ve defined the service method endpoints in UriTemplate. In the UriTemplate, for example /GetData?instanceId={wfInstanceId}, the {wfInstanceId} value is mapped to GetWorflowData method parameter ‘wfInstanceId’. From workflow using HttpSend activity, the service can be invoked as ‘http://serverurl/_vti_bin/ServiceFielName.svc/GetData?instanceId={wfInstanceId}”. The current site url is retrievable from Workflow Context using LookupWorkflowContextProperty activity. Image 1 shows how the HttpSend Activity is used to call the service method from workflow.

Create Workflow

For simplicity, I’ve created a list workflow and once you deploy you need to manually associate the workflow with list/library. You can create either site or list workflow in SharePoint 2013. And if you want you can associate your workflow later instead of development time – maybe through feature receiver. The workflow I’ve created is need to be started manually. To manage workflow development in better ways, I’ve grouped my workflow activities in few groups (parse context values, call wcf service, parse service data etc.) – each group is a sequence of actions.





Use HttPSend Activity to call WCF Service:
To call WCF Service, you need to use HttPSend activity. In this activity you can configure HTTP Verb (GET, POST, MERGE etc.). Since our WCF service will return json data, we need to add two headers (accept and content-type) in the HTtPSend activity to support json data. Also you need to set authorization header to empty string to work properly. Please add the following headers in the HttPSend activity:
Accept "application/json; odata=verbose"
Content-Type "application/json; odata=verbose"
Authorization “”
In the HttpSend activity, the service response returned can be stored in a variable of type DynamicValue. You can read more about DynamicValue at http://msdn.microsoft.com/en-us/library/windowsazure/jj193505(v=azure.10).aspx. The following image shows how you can pass different properties in HttpSend activity:
image
Image 1: HttpSend Activity in use
In the above image, conetent-type and accept header is used to support json data whereas the ‘Authorization’ header is used for WCF service to be invoked properly from workflow.

Parse Service Response:
In the code example provided as part of this post, a workflow variable “WorkflowData” is used to store the returned service response. To retrieve the dynamic data property you can use GetDynamicValueProperty<T> activity, passing the property name. If you have a collection of items returned from service, you can use foreach control block from workflow Toolbox and can apply the GetDynamicValueProperty for each item in foreach. The response from service is stored in a workflow property ‘WorkflowData’ as shown in image 1. The response from service is the object of the following type:
    public class WorkflowData
    {
        public string Subject { get; set; }
        public bool SendEmail { get; set; }
        public string EmailTo { get; set; }
    }

Code Snippet: Workflow data returned from service

The service response is of type ‘WorkflowData’ as defined in above code snippet. To read any property of the ServiceResponse variable (as shown in Image 1) which is of type WorkflowData, GetDynamicValueProperty<T> is used as shown below – where WorkflowData.SendEmail is parsed:
image
Image 2: Parse Service Response property ‘SendEmail’

Finally in the workflow, based on the parsed value (SendEmail property), I’m updating the workflow status.

 

Download Source Code

I’ve uploaded the source code in MSDN code gallery. Please download the source code from http://code.msdn.microsoft.com/SharePoint-2013-Workflow-e14e0e7a

Conclusion

MSDN has good numbers of examples available to start with workflow. Let me give you some links to these examples that might be helpful:
You can get more examples of workflow manager at http://msdn.microsoft.com/en-us/library/windowsazure/jj193482(v=azure.10).aspx.

Wednesday, April 3, 2013

SharePoint 2013: Workflow Error ‘Cannot set unknown member’

I was trying to develop a workflow using Visual Studio 2012 for SharePoint 2013. When I was trying to publish the workflow i was getting the following exception:

Microsoft.Workflow.Client.ActivityValidationException: Workflow XAML failed validation due to the following errors:
Cannot set unknown member 'LookupSPListItem.ItemId'.
HTTP headers received from the server………….

Then after googling i had found the hint in the Microsoft Forum.

 

Solution

Basically you need to re-register the workflow using a Powershell command like below. I think when any Workflow Manager or SharePoint update is installed in the server, these two components get out of sync and re-running the registration will sync both again.

Register-SPWorkflowService -SPSite "SiteURL" -WorkflowHostUri 'WorkflowHostURL'  -Force

Remember to mention Force switch. After the command, restart the Visual Studio or SharePoint Designer and hopefully the issue will be fixed.

Sunday, March 17, 2013

SharePoint 2013: Use Twitter Bootstrap for SharePoint App development

If you have not heard of Bootstrap, it’s a CSS framework just like javaScript framework jQuery. Using Bootstrap, you can get various CSS classes that can be used in web design to make your web site looking consistent and impressive.

Introduction to Bootstrap

Bootstrap was initially developed by Twitter to enable consistent design across all Twitter tools. But now it’s the popular free CSS framework. Simply to understand the Bootstrap, let’s say you would like to add some styles in your web page. You are using HTML table in your web site and you want all tables to have same design/style all across the web site. In Bootstrap framework, you will find some CSS classes that you can use to have nice, impressive look for table all across the site. To apply default Bootstrap table style, just apply the CSS class “table”. If you would like to add hover style in the table add “table-hover” CSS in the the table class. Applying the following table styles results in the table shown in figure 1. The table has rounded corner, alternate color and nice/slick border width.

<table class="table table-bordered table-striped table-hover">
</table>

image

Figure 1: Table with round corner and slick look with Bootstrap CSS styles

 

You can check the default table styles provided by Bootstrap at this link: http://twitter.github.com/bootstrap/base-css.html#tables

While you will develop SharePoint Apps, you can easily integrate Bootstrap in your app and can get impressive look and feel.

 

How to integrate Bootstrap with SharePoint App

To use the Bootstrap in your app, first download the Bootstrap from Twitter Github. Once you extract the zip file, you will find three types of resources you need to use: CSS files, JavaScript Files and image files.

Step1: Copy the Bootstrap files in corresponding App folders (shown in Figure 2):
  • Copy contents of the the the Bootstrap “css” folder to “content” folder in your app in Visual Studio. I would recommend you to copy min file(s) only.
  • Copy contents of the Bootstrap “img” folder to the Visual Studio “Images” folder.
  • Copy contents of the Bootstrap “js” folder to the Visual Studio “Scripts” folder. I would recommend  you to copy min file(s) only

image

Figure 2: Copy Bootstrap files in your Visual Studio App project

 
Step 2: Make Image/img folder related Changes

Bootstrap uses two images files which defines icons. These image files supposed to copy in App “Img” folder, as Bootstrap looks for these images in “img” folder. But SharePoint image folder name “Images” whereas Bootstrap Images folder name is “img”. So we need to update Bootstrap CSS file. Open the file “bootstrap.min.css” in the Visual Studio from “Content” folder. Then find for “.png” using Ctrl + F, and finally modify the image path from “img” to “Images” as shown below. You need to make changes in few places in the file:

image

Figure 3: Update Image Reference in Bootstrap Css file.

 

Step 3: Add Reference to the Bootstrap files in your page

The final step is to add reference to these Bootstrap reference in you page. Add the following references in your page in App under content place holder “PlaceHolderAdditionalPageHead” or any other suitable places:

<link rel="Stylesheet" type="text/css" href="../Content/bootstrap-responsive.min.css" />
<link rel="Stylesheet" type="text/css" href="../Content/bootstrap.min.css" />

<script type="text/javascript" src="../Scripts/bootstrap.min.js"></script>
 

Download Source Code

I’ve created a sample SharePoint 2013 App project with the Bootstrap integrated. You can download the sample project BootstrapTest from my Skydrive.


Conclusion

With the growing popularity of Bootstrap, we should  know about the framework and need to use it for appropriate applications. I’ve not tried Bootstrap with SharePoint webparts or full-trust applications but I’ll try to investigate in future. However, if you have not already tried Twitter Bootstrap please give a try, I do believe you’ll like it!

Thursday, March 7, 2013

SharePoint 2013: Workflow Debug/Diagnosis

In SharePoint 2013, new workflow model introduced. Now the workflow runs in different servers (or farms) and SharePoint communicates to the workflow server via REST endpoints. This is really scalable and decoupled architecture but it brings new challenges for workflow developers. Now debugging is not so easy as it was before with SharePoint 2010 workflows. Still you can develop/debug SharePoint 2010 workflows in SharePoint 2013 but it’s not recommended anymore. I’ll explain the techniques you can use to debug your workflow. If the workflow fails to run you will find the workflow error like HTTP 500 or Internal server error. You need to find out by yourself what went wrong. Till date, Workflow doesn’t have inherent functionality to trace the requests though as per this forum post, Workflow team is working on some tracing tools for next version.

Now let me describe few external tools/techniques you can use to diagnosis your workflow.

Event Viewer

Windows Server running workflow service can trace workflow manager and service bus activities. By default the Analytic and debug log for workflow manager is disabled. You can enable the event viewer and see what’s going on under the hood. You can find out details on how to enable the event viewer in the link: http://technet.microsoft.com/en-us/library/jj193526

 

HTTP Traffic Tracing

You can use some HTTP packet traffic tracing tools in SharePoint Server and trace the request back and forth between SharePoint and Workflow server. One such nice tool (though not free) that I’ve found is HTTP Debugger. However you can use any other tools that watch HTTP traffic at the network level (not browser level). So tools like Fiddler will not help as it just trace requests made from Browser whereas we need tools that trace HTTP traffic at network level.

 

Database Log

In the workflow Management database, there’s a table ‘DebugTraces’. I’ve noticed that sometimes Workflow Manager write down errors/debugs in this database tables. You can go and take a look if something is useful for your workflow debugging.

 

Conclusion

You can use all above tools in combination in finding out problem in different parts of the full workflow lifecycle. You can use HTTP Tracing tools to find out if the Workflow Service call from SharePoint goes to Workflow Manager or vice versa. Using event viewer or database log, you can find out if there’s any errors in your workflow during execution. Until some built-in tools are available for workflow tracing, you can use these tools to find out your problem relatively quickly.

Thursday, February 28, 2013

Workflow Manager cumulative update (February) error

After installing workflow manager cumulative update in February 2013, the workflow manager stopped working. When I was trying to access workflow settings in the list/library I was getting unexpected exception. Then I found the following error in the log file:
The EXECUTE permission was denied on the object 'InsertTrackingAndStatus'

To fond out how to enable and view the workflow manager log file, please follow the technet guide at: http://technet.microsoft.com/en-us/library/jj193526.aspx. From the error message it seems the problem is related to database permission related. So connected to the database (using SQL management studio) and found out the workflow manager windows service user's (under whose credentila's the workflow service is running) security settings. The original permission I found was "WFServiceOperators" and then I added dbo permission to the user also.  Then I restarted the SQL service and it all started working. I know this is not the best solution but for time being the hack solved the problem in my Dev.

Update From Workflow Team

I've posted the bug in Workflow forum: http://social.msdn.microsoft.com/Forums/en-US/wflmgr/thread/054d2a58-8847-4a6a-b1ab-05a79f49fe65  . As described in the forum, you need to run the following script against the database:

IF  EXISTS (SELECT * FROM sys.database_principals WHERE name = N'WFServiceOperators' AND type = 'R')
BEGIN
-- Grant all permissions of stored procedures and tables
DECLARE @ObjectName sysname, @ObjectType char(20), @Cmd varchar(300)
DECLARE ObjectCursor CURSOR LOCAL FAST_FORWARD
FOR SELECT name, type FROM sys.objects UNION SELECT name, 'WFUDT' FROM sys.types WHERE is_user_defined = 1

OPEN ObjectCursor
FETCH ObjectCursor INTO @ObjectName, @ObjectType
WHILE (@@fetch_status <> -1)
BEGIN
SET @Cmd =
CASE @ObjectType
WHEN 'P' THEN N'GRANT EXECUTE ON [' + @ObjectName + N'] TO [WFServiceOperators]'
WHEN 'WFUDT' THEN N'GRANT CONTROL, REFERENCES ON TYPE::[' + @ObjectName + N'] TO [WFServiceOperators]'
ELSE ''
END

IF @Cmd <> ''
BEGIN
EXEC(@Cmd)
END
FETCH ObjectCursor INTO @ObjectName, @ObjectType
END
CLOSE ObjectCursor
DEALLOCATE ObjectCursor
END
GO



But still if the above script doesn't solve your problem, you can set the workflow user as dbo in the database

Sunday, February 17, 2013

SharePoint 2013: Workflow Manager Installation and Configuration

SharePoint 2013 introduced a new way of workflow management. Now workflow can be hosted in separate server – so scalable and reduces load on SharePoint Web Front End server. However this introduce a new level of complexity that we need to be aware of.

 

Introduction

To use a windows server as workflow manager you need to install/configure Workflow Manager in the windows server. Workflow manager is a new kind of application server that host/manage workflow execution. SharePoint 2013 workflow is based on .net framework 4.0. Workflow Manager is designed with Window Azure hosting in mind, but now it supports on-premise installation. If organizations now want to host the workflow on-premise they can still use Workflow Manager but in future if they want to move to Windows Azure hosting for their workflow, the migration will be smoother with this Workflow Manager.

 

You should Know

Before start installing/developing SharePoint Manager you need to know few points:

  • You should not use SharePoint ‘system account’ to test workflow. If you use ‘system account’ to develop/run workflow, the workflow will fail to run.
  • You need to make sure User Profile Service (UPS) is running and the user who is running workflow has profile in UPS. Workflow Manager use UPS under the hood.
  • Make sure App Management Service is created and running. You don’t need to configure SharePoint 2013 App settings in the server, just creating App Management service (with proxy) will do.
  • SharePoint 2013 workflow is declarative – means you can only define workflow in XML. You can’t write any C# code inside workflow as you used to do before. All your custom logic should be put outside of SharePoint, inside WCF Service. Then you will call the service from workflow to implement your custom logic in code.
  • To register workflow Server with SharePoint, a SharePoint site collection URL is provided (see the section Register Workflow Service with SharePoint later in the post). Apparently it seems, each and every site collection need to be registered with workflow server. But it’s not, registering a single SharePoint site  collection, will enable workflow manager for all SharePoint web applications/site collections.

 

Install/Configure Workflow Manager

The first step in workflow setup/configuration is to install workflow manager in the workflow server (either Web Front End or a separate server). To install the workflow Manager download it from Microsoft Site or alternatively you can download it from Web Platform Installer. The installation contains few components:

  • Workflow Manager: This is the host that runs workflows.
  • Workflow Manager Client: It contains the API to allow clients to communicate with Workflow host.
  • Workflow Tool: It needs to be installed in the development server to develop SharePoint 2013 workflow. It supports the workflow development in Visual Studio 2012.

Workflow Manager client needs to be installed in every SharePoint WFE server.

After installing Workflow Manger, you can configure workflow manager with Workflow Manager Configuration Wizard. The configuration involves two steps: Workflow Manager Configuration and Service Bus Configuration.

  • Workflow Manger Configuration (first step in the wizard) is the configuration related to work host server
  • Service Bus configuration (second step in the wizard): Service Bus is used to manage communication between Workflow Server and it’s client (so, SharePoint). Service Bus queues the income request to workflow manage, provide REST interface for Workflow Manager etc.

In workflow configuration wizard don’t use any admin or SharePoint setup user, create a new service user for workflow and use that user:

image

Figure 1: Workflow Manager Service Account

 

If you want SharePoint Server to communicate with Workflow Server over HTTP, you can select the option shown below. But please make sure this is secure in your case. For public site, this might not be secure but in case of Local Intranet with firewall, this might be secure.

image

Figure 2: Communication over HTTP

 

If you want to use the same service account (as well as auto generated key for certificate), you can use so as shown below:

image

Figure 3: Same service account and certificate key is used for both Workflow Manager and Service Bus Configuration. 

In the final step of the workflow configuration wizard you can generate PowerShell script that you can reuse across different SharePoint Farms.

 

Register Workflow Service with SharePoint

Once you have installed/configured Workflow Server, you need to register the workflow service to SharePoint Server. The registration depends on how the SharePoint and Workflow server is connected to each other. You can find more details at technet site. The workflow manager creates an HTTPS endpoint at port 12291 and HTTP port at 12290. If you use HTTP for communication you need to provide ‘AllowOAuthHttp’ switch in the PowerShell command. The PowerShell command looks like below:

Communication over HTTP

Register-SPWorkflowService –SPSite http://sharepointsite –WorkflowHostUri http://workflowhost:12291 –AllowOAuthHttp

 

Communication over HTTPS

Register-SPWorkflowService –SPSite http://sharepointsite –WorkflowHostUri https://workflowhost:12290

 

PowerShell Script to Install/Configure Workflow Manager

I have modified the wizard-generated PowerShell script a bit to make it more reusable. The Script reads configuration values from xml file and apply the configuration. The script uses auto-generate key for certificate. Also the database name are hard-coded in the script, but you can add prefixes (like dev, test, prod) to the database from xml file. The script also configure App Management Service, if the service is not already created. The sample PowerShell Script is provided below:

#Get current user full login name
$CurrentUserLoginName=[Environment]::UserName + '@' + [Environment]::UserDomainName;
#Get current server fully qualified domain name
$HostFQDN="$env:computername.$env:userdnsdomain";

#Load SharePoint Snapin
if ( (Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null ){
Add-PsSnapin Microsoft.SharePoint.PowerShell
}

#Get DB Connection String
function GetDBConnectionString([string]$connectionStringFormat, [string]$dbPrefix, [string]$dbName){
if($dbPrefix -ne ""){
$dbFullName=$(GetDBName $dbPrefix $dbName);
return [string]::Format($connectionStringFormat,$dbFullName);
}
else {
return $dbName;
}
}

#Add Dev, Test etc. environment prefix, if needed
function GetDBName([string]$dbPrefix,[string]$dbName){
if(($dbPrefix) -and ($dbPrefix -ne "")){
return $dbPrefix + "_" + $dbName;
}
return $dbName;
}

#Get current Script directory
function Get-ScriptDirectory
{
$Invocation = (Get-Variable MyInvocation -Scope 1).Value
Split-Path $Invocation.MyCommand.Path
}

function ConfigureWFManager([string]$settingsFile){
[xml]$wfsettings = Get-Content $settingsFile
$settings=$wfsettings.Settings;
$SharePointSiteUrl=$settings.SiteUrl;
$dbPrefix=$settings.DBPrefix;
$CertificateKey=$settings.CertificationKey;
$databaseServer=$settings.DBServer;
$ConnectionStringFormat="Data Source=$databaseServer;Initial Catalog={0};Integrated Security=True;Encrypt=False";
$RunAsAccount=$settings.WFManagerRunAsUser;
$RunAsPasswordPlain=$settings.WFManagerRunAsPassword
$WorkflowNamespace=$settings.WorkflowNamespace;
if(ShouldIProvision($settings.AppManagementService))
{
ProvisionAppManagementService($settings);
}

# To be run in Workflow Manager PowerShell console that has both Workflow Manager and Service Bus installed.
# Create new Service Bus Farm
$SBCertificateAutoGenerationKey = ConvertTo-SecureString -AsPlainText -Force -String $CertificateKey -Verbose;

New-SBFarm -SBFarmDBConnectionString $(GetDBConnectionString $connectionStringFormat $dbPrefix 'SBManagementDB') -InternalPortRangeStart 9000 -TcpPort 9354 -MessageBrokerPort 9356 -RunAsAccount $RunAsAccount -AdminGroup 'BUILTIN\Administrators' -GatewayDBConnectionString $(GetDBConnectionString $connectionStringFormat $dbPrefix 'SBGatewayDB') -CertificateAutoGenerationKey $SBCertificateAutoGenerationKey -MessageContainerDBConnectionString $(GetDBConnectionString $connectionStringFormat $dbPrefix 'SBMessageContainerDB') -Verbose;

# To be run in Workflow Manager PowerShell console that has both Workflow Manager and Service Bus installed.

# Create new Workflow Farm
$WFCertAutoGenerationKey = ConvertTo-SecureString -AsPlainText -Force -String $CertificateKey -Verbose;


New-WFFarm -WFFarmDBConnectionString $(GetDBConnectionString $connectionStringFormat $dbPrefix 'WFManagementDB') -RunAsAccount $RunAsAccount -AdminGroup 'BUILTIN\Administrators' -HttpsPort 12290 -HttpPort 12291 -InstanceDBConnectionString $(GetDBConnectionString $connectionStringFormat $dbPrefix 'WFInstanceManagementDB') -ResourceDBConnectionString $(GetDBConnectionString $connectionStringFormat $dbPrefix 'WFResourceManagementDB') -CertificateAutoGenerationKey $WFCertAutoGenerationKey -Verbose;

# Add Service Bus Host
$SBRunAsPassword = ConvertTo-SecureString -AsPlainText -Force -String $RunAsPasswordPlain -Verbose;


Add-SBHost -SBFarmDBConnectionString $(GetDBConnectionString $connectionStringFormat $dbPrefix 'SBManagementDB') -RunAsPassword $SBRunAsPassword -EnableFirewallRules $true -CertificateAutoGenerationKey $SBCertificateAutoGenerationKey -Verbose;

Try
{
# Create new Servie Bus Namespace
New-SBNamespace -Name $WorkflowNamespace -AddressingScheme 'Path' -ManageUsers $RunAsAccount,$CurrentUserLoginName -Verbose;

Start-Sleep -s 90
}
Catch [system.InvalidOperationException]
{
}

# Get Service Bus Client Configuration
$SBClientConfiguration = Get-SBClientConfiguration -Namespaces $WorkflowNamespace -Verbose;

# Add Workflow Host
$WFRunAsPassword = ConvertTo-SecureString -AsPlainText -Force -String $RunAsPasswordPlain -Verbose;


Add-WFHost -WFFarmDBConnectionString $(GetDBConnectionString $connectionStringFormat $dbPrefix 'WFManagementDB') -RunAsPassword $WFRunAsPassword -EnableFirewallRules $true -SBClientConfiguration $SBClientConfiguration -EnableHttpPort -CertificateAutoGenerationKey $WFCertAutoGenerationKey -Verbose;

Write-Host "Registering workflow host (HTTP) to site: $SharePointSiteUrl";
Register-SPWorkflowService –SPSite $SharePointSiteUrl –WorkflowHostUri $("http://$HostFQDN" + ":12291") –AllowOAuthHttp
}

function ProvisionAppManagementService([System.Xml.XmlNode] $settings){

$appManagementServices=Get-SPServiceApplication | Where-Object { $_.GetType().ToString() -eq "Microsoft.SharePoint.AppManagement.AppManagementServiceApplication"}
If($appManagementServices -ne $null)
{
Write-Host "An App Managemetn service is already running. Returning.." -ForegroundColor Yellow
return;
}

Write-Host "Provisioning App Management Service";
$appManagementService=$settings.AppManagementService;
$appPool=$(GetAppPool $appManagementService)
$dbName=$(GetDBName $settings.DBPrefix $appManagementService.DBName);
$appAppSvc = New-SPAppManagementServiceApplication -ApplicationPool $appPool -Name $appManagementService.Name -DatabaseName $dbName
New-SPAppManagementServiceApplicationProxy -ServiceApplication $appAppSvc
}

function GetAppPool([System.Xml.XmlNode] $appManagementService){
$pool = Get-SPServiceApplicationPool -Identity $AppManagementService.AppPoolName -ErrorVariable err -ErrorAction SilentlyContinue
If ($err) {
# The application pool does not exist so create.
Write-Host -ForegroundColor White " - Getting $($appManagementService.ManagedAccountUserName) account for application pool..."
$managedAccount = (Get-SPManagedAccount -Identity $appManagementService.ManagedAccountUserName -ErrorVariable err -ErrorAction SilentlyContinue)
If ($err) {
If (($appManagementService.ManagedAccountPassword -ne "") -and ($appManagementService.ManagedAccountPassword -ne $null))
{
$appPoolConfigPWD = (ConvertTo-SecureString $appManagementService.ManagedAccountPassword -AsPlainText -force)
$accountCred = New-Object System.Management.Automation.PsCredential $appManagementService.ManagedAccountUserName,$appPoolConfigPWD
}
Else
{
$accountCred = Get-Credential $appManagementService.ManagedAccountUserName
}
$managedAccount = New-SPManagedAccount -Credential $accountCred
}
Write-Host -ForegroundColor White " - Creating applicatoin pool $($appManagementService.AppPoolName)..."
$pool = New-SPServiceApplicationPool -Name $appManagementService.AppPoolName -Account $managedAccount
}
return $pool;
}

Function ShouldIProvision([System.Xml.XmlNode] $node)
{
If (!$node) {Return $false} # In case the node doesn't exist in the XML file
# Allow for comma- or space-delimited list of server names in Provision or Start attribute
If ($node.GetAttribute("Provision")) {$v = $node.GetAttribute("Provision").Replace(","," ")}
ElseIf ($node.GetAttribute("Start")) {$v = $node.GetAttribute("Start").Replace(","," ")}
ElseIf ($node.GetAttribute("Install")) {$v = $node.GetAttribute("Install").Replace(","," ")}
If ($v -eq $true) { Return $true; }
Return $false;
}

Write-Host "Configuring WF Manager"
$location=Get-ScriptDirectory
ConfigureWFManager "$location\WFFarmSettings.xml"


Code Snippet: PowerShell Script to configure Workflow Manager

The following XML file provides the input settings for the above PowerShell script (named as WFFarmSettings.xml in the above PowerShell script). Though you will use a site collection to register the workflow and SharePoint communication, I’ve found that workflow work for all others site collections/web application in the SharePoint Server.

<Settings>
  <SiteUrl>http://siteulr</SiteUrl>
  
  <!--Delete DBPrefix tag, if you don't want any prefix-->
  <DBPrefix>DEV</DBPrefix>
  
  <!--Key used to generate certificates-->
  <CertificationKey>CertificationKey</CertificationKey>
  
  <!--Database server name, database names are hardcoded in powershell-->
  <DBServer>DBServer</DBServer>
  
  <!--Format should be USERNAME@DOMAIN-->
  <WFManagerRunAsUser>user@domain</WFManagerRunAsUser>
  <WFManagerRunAsPassword>Password</WFManagerRunAsPassword>

  <!--dot (.) not allowed-->
  <WorkflowNamespace>WorkflowNamespace</WorkflowNamespace>
  
  <!--To work with workflow, app management service need to be provisioned-->
  <AppManagementService Provision="true">
    <Name>App Management Service Application</Name>
    <DBName>AppManagementServiceDB</DBName>
    <!--If managed account already exists with the same name, the existing one will be used-->
    <ManagedAccountUserName></ManagedAccountUserName>
    <ManagedAccountPassword></ManagedAccountPassword>
    <AppPoolName>App Management Service App Pool</AppPoolName>
  </AppManagementService>
</Settings>