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.