Monday, September 29, 2014

SharePoint 2013: Branding with Theme

Theme is a nice and quick way of applying some new styles to SharePoint site. Using theme you can apply some customisation in branding: font, color, background image etc. In this post I’ll explain SharePoint 2013 Theme and you can download a sample SharePoint 2013 Visual Studio solution from here.

There’s some out-of-the-box theme preinstalled in SharePoint. However you can install your own custom theme in SharePoint site.

 

Theme - Concept

A theme is combination of the following items:

  • Color palette (a file with ‘.spcolor’ extension)
  • A font scheme (a file with ‘.spfont’ extension)
  • Background Image (if you would like to apply/change background image for this theme)
  • Master page, used for theme preview mainly

You can browse the URL http://{SiteCollection}/_catalogs/design/AllItems.aspx to view all theme list items.

Let me explain the theme components:

Color Palette

If you are not already familier with color palette – simply it’s a set of consistent colors to be used for designing/painting. You can find a lots of free site to choose color palette. Some free color platte sites are listed below. If you are designing your SharePoint site then these color palette sites can be really helpful:

Once you select your color palette, you can use a tool called ‘SharePoint Color Palette Tool’ to apply your color palette to design the theme. The following image shows the tool in action:

image

Few points to note about the tool:

  • You can only create the spcolor file
  • Though you can use background image to test the style in the tool, it’s for preview only. The background image will not be included in the spcolor file.
  • It’s quite unfortunate that there’s no user manual or documentation on how to use the tool. However there’s some videos available online, you can check.

 

 

Font Scheme

Font scheme defines the fonts to be used in different section of SharePoint site – navigation, heading, body etc when the theme will be applied. Most of the time you don’t need new font scheme. However if needed, the easiest way of creating a new font scheme is copy an existing one – for example ‘15/Template/Layouts/default.spfont’. If you don’t need custom font scheme as part of the theme, you don’t need to develop a custom font scheme.

 

Background Image

As part of the theme you can specify a background image that will used when the theme will be applied. Using background image is not quite common in SharePoint sites though.

 

Master Page

A theme can be associated with master page – but it doesn’t mean if theme is applied to a site, the master page will be changed too. Rather, the master page will be used for previewing the theme. You can view the theme preview by navigating to Site Settings => Change the look, or browsing “http://{siteurl}/_layouts/15/designgallery.aspx”. However, for custom theme if you specify a custom master page, then the master page should have a preview file associated, otherwise you will not see the theme in this design gallery.

 

Theme - Development/Deployment

I’ve provided a sample Visual Studio solution that shows how to develop a theme-based branding and deploy it. You can find code details from MSDN at http://msdn.microsoft.com/en-us/library/office/jj927175(v=office.15).aspx. However, simply developing, deploying and applying themes include the following steps:

  • Uploading files: You can upload your files as SharePoint modules(themes spcolor, spfonts, master pages etc.). In your visual studio solutions add these files as modules and as you deploy the solution the files will be uploaded in appropriate locations.
  • Install Theme: You need to create list items in ‘design catalog’ with populating different properties like theme url, order, title etc. Creating this list item will make the theme available in the design gallery at http://{siteurl}/_layouts/15/designgallery.aspx
  • Apply theme: If you would like to apply them when the feature is activated, you can apply the theme on feature activation. Alternatively you can apply theme manually from Site settings. In the sample solution, I’ve installed two themes (BrandingTestLight and BrandingTestDark) but only activated the dark theme on feature activation.

 

To install theme, we need to create a list item in design catalog gallery as shown below:

var themeItem = designCatalog.AddItem();
themeItem["Name"] = themeTitle;
themeItem["Title"] = themeTitle;
themeItem["MasterPageUrl"] = new SPFieldUrlValue(masterUrl);
themeItem["ThemeUrl"] = new SPFieldUrlValue(Web.Site.ServerRelativeUrl.TrimEnd('/') + "/_catalogs/theme/15/" + themeName);
themeItem["DisplayOrder"] = displayOrderCounter; 
themeItem.Update();

In the code snippet above, Master page URL is only for displaying purpose. In my sample application I’ve used seattle.master. This is because whichever master page you use, the master page will have to have a ‘preivew’ file. Since it’s only for display purpose and seattle master page has a preview file, I’ve reused the same preview instead of creating a custom preview of my own for my custom master page. However, if you would like to create a custom preview for your master page you can see more details at http://msdn.microsoft.com/en-us/library/office/jj927173(v=office.15).aspx

 

 

To apply theme, you need to apply theme by using ‘Theme.ApplyTo’ method. However, there’s more. In the theme catalog (http://{sitecollection}/_catalogs/design/AllItems.aspx), you will find a list item with title ‘current’ as shown below. You need update the current theme - list item also as you can see in the sample solution provided.

image

 

Download the code

The sample solution I’ve uploaded is available here . The sample solution adds two themes (BrandingTestLight and BrandingTestDark)  and a custom master page. Once you deploy the sample WSP, there’ll be a Web scoped feature “SharePoint Branding - Theme” will be available. Activating and deactivating the feature will deploy the themes and custom master pages, modify the default and custom master page, apply the dark theme.

Sunday, June 22, 2014

SharePoint: Access large list

SharePoint list throttling limits to access maximum 5000 items through view/query. However your list can hold as much as 30,000,000 items but you can’t access more than 5000 items at once by view or query. But I’ve seen scenarios where list items goes millions in a year. So accessing large list having more than 5000 items not very rare in real life.

But have you ever asked yourself, why the limit is 5000 items?
The real limit of 5000 items is related to SQL server. I’m not a SQL server expert but let me try to explain as a non-SQL server expert – “If a SQL query returns more than 5000 items, SQL server locks the entire table – which will affect other queries on the table”.

So as a SharePoint expert you might need to be in a situation where you need to work with large list (having more than 5000 items). However, there’s few options you have when you need to access large list. I’ll explain different options with pros and cons.

 

Option 1: Override the throttling limit (Less preferable)

You can override the throttling limit from web application settings or through code. I would not suggest to do it from web application as it’ll be applied throughout the web application, instead if you querying your list then you can override the setting as shown below:

SPQuery spqry = new SPQuery();
spqry.QueryThrottleMode = SPQueryThrottleOption.Override;

The easy thing about this approach is that you don’t need to go through any complexity of writing complex code but downside is your query is taking excessive resources affecting other users. Points to note:

  • Easy to use, as you don’t need to include any complexity in your code if you are using SPQuery. If your list views return more than 500 items, the views will not fail
  • Also this approach will send a single (possibly) SQL query that will return all items
  • However, other users browsing the site will find the site slow – as the full table lock will be applied in SQL server delaying other users request to be queued
  • If you are loading all of the items in memory, then your server should be able to handle the items
  • You can override the throttling limit, if your server is able to handle all the items in memory and you are executing your script during off-peak hour. I would prefer overriding the throttling limit, if, for example, I’m moving all data for archiving during off peak hour. More specifically, if you need to run the script periodically or once during off-peak hour then you may be override the throttling limit.

 

Option 2: Access less than 5000 items Iteratively

With this approach, you don’t override the SharePoint throttling limit (always preferable). Rather you loop through and access less than 5000 items on each iteration (say, 2000 items per iteration). The following block shows the idea:

var list = web.Lists[listName];
var maxItemId = list.Items[list.ItemCount - 1].ID; //get max item id Or use below code to get max id using CAML
//spquery.RowLimit = 1;
//spquery.Query = "<OrderBy><FieldRef Name='ID' /></OrderBy>";
var startIndex = 1;
const int processByCount = 4000; //max no of items to be returned by spquery
var endIndex = startIndex + processByCount;
while (startIndex < maxItemId)
{
    var query = new SPQuery
    {
        Query = string.Format(@"<Where>
                            <And>
                                <Geq><FieldRef Name='ID' /><Value Type='Counter'>{0}</Value></Geq>
                                <Leq><FieldRef Name='ID' /><Value Type='Counter'>{1}</Value></Leq>
                            </And>
                        </Where>", startIndex, endIndex)
    };
    var items = web.Lists["ListName"].GetItems(query);
    //process items to convert to CSV
    startIndex = endIndex + 1;
    endIndex = startIndex + processByCount;

The above approach is a good option for querying items through SPQuery. However, for list view, you need to modify your views to make sure the view doesn’t return more than 500 items. Points to note:

  • You need to modify your existing views so that they don’t return more than 5000 items.
  • Once you modify your list views (as well as change the code to access less than 5000 items), the queries/views will have less affect on other live users.
  • However, you need to modify your existing code (or new code) to use the pattern to access not more than 5000 items.
  • Also for each loop, you will send a SQL query that will return the items.

 

Option 3: Use ContentIterator

SharePoint Server provides an API to access individual items using ‘ContentIterator’. ContentIterator is kind of complete API for processing large list items. Points to note:

  • The API has full capabilities of handling different scenarios
  • And the API is available in Office Server version not in Foundation.

 

Conclusion

Overriding throttling limit is in web application is the last option I would suggest. However overriding throttling limit through SPQuery can be used if you are running your query during off-peak hour and possibly one time. Option 2 of ‘Iteratively accessing less than 5000 items’ is more suitable as you can control how many items you would like to access on each request. ContentIterator is the preferable way, if you are using Server version of SharePoint.

Tuesday, May 13, 2014

SharePoint: Safe Control Entry using elements file

Recently I’ve discovered that it’s possible to add safe control entries through elements.xml file. To do this, add an elements.xml file (or use an existing xml file). Then select the elements.xml file and open the properties window as shown below:

image

Then click the ... button next to (Safe Control Entries) as shown in the above image. Then in the safe control entries dialog click ‘Add’ and edit the namespace as shown below:

image

Finally, add the element xml file to a web/site scoped feature. As you will deploy the WSP, the safe control entry will be added in web.config file.

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)