Use PowerShell to create a demosite for all the common used Web Templates in SharePoint 2013

In my previous article I provided a list of all the common used web templates in SharePoint 2013. For demo purposes it can be handy to spin up a copy of all these templates and later easy remove them. Using PowerShell we can accomplish this with the snippets provided below. Change the parameters under “Configuration” to match your own environment.

Create new site collections

This snippet will create a new site collection for each of the web templates.


Add-PSSnapin Microsoft.SharePoint.PowerShell

# Configuration
$webApp = "http://intranet.contoso.com"
$managedPath = "sites"
$siteOwner = "contoso\sp_admin"
$languageId = 1033

# Create site collections for demo
Write "Create demo sites"
Start-SPAssignment -Global
New-SPSite "$webApp/$managedPath/demo-team-site" -Name "Team Site" -Template "STS#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-blog" -Name "Blog" -Template "BLOG#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-dev-site" -Name "Developer Site" -Template "DEV#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-project-iste" -Name "Project Site" -Template "PROJECTSITE#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-community-site" -Name "Community Site" -Template "COMMUNITY#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-document-center" -Name "Document Center" -Template "BDR#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-ediscovery-center" -Name "eDiscovery Center" -Template "EDISC#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-records-center" -Name "Records Center" -Template "OFFILE#1" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-bi-center" -Name "Business Intelligence Center" -Template "BICenterSite#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-ent-search-center" -Name "Enterprise Search Center" -Template "SRCHCEN#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-mysite-host" -Name "My Site Host" -Template "SPSMSITEHOST#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-community-portal" -Name "Community Portal" -Template "COMMUNITYPORTAL#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-basic-search-center" -Name "Basic Search Center" -Template "SRCHCENTERLITE#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-visio-process-rep" -Name "Visio Process Repository" -Template "visprus#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-pub-portal" -Name "Publishing Portal" -Template "BLANKINTERNETCONTAINER#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-ent-wiki" -Name "Enterprise Wiki" -Template "ENTERWIKI#0" -OwnerAlias $siteOwner -Language $languageId
New-SPSite "$webApp/$managedPath/demo-prod-catalog" -Name "Product Catalog" -Template "PRODUCTCATALOG#0" -OwnerAlias $siteOwner -Language $languageId
Stop-SPAssignment -Global

Clean up and remove all the sites

This snippet will delete all the site collections created with the snippet above.


# Configuration
$webApp = "http://intranet.contoso.com"
$managedPath = "sites"
$siteOwner = "contoso\sp_admin"
$languageId = 1033

# Remove site collections
Remove-SPSite "$webApp/$managedPath/demo-team-site" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-blog" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-dev-site" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-project-site" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-community-site" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-document-center" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-ediscovery-center" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-records-center" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-bi-center" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-ent-search-center" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-mysite-host" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-community-portal" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-basic-search-center" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-visio-process-rep" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-pub-portal" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-ent-wiki" -Confirm:$false
Remove-SPSite "$webApp/$managedPath/demo-prod-catalog" -Confirm:$false

Summary

This article demonstrated how easy we can create a copy of all the common used web templates in SharePoint for demo purposes, and later clean it up.

Performance issues when working with lists using SPFieldUser and large amount of items

On a SharePoint 2013 intranet project I have been working on, we got a report that a web part had started to perform bad after a while of use. The web part presents a personalized list of tools to the end-user, and uses a SPList to store the data. This list is quite simple and includes these fields:

Name Type Description
Title Text Title of the tool
URL Hyperlink URL to the tool
Users Person Users who have chosen to view the tool in their personalized list
Groups Managed Metadata Which groups of users the link is available to (matched with user profile)

 

The code is not very complicated either. It executes a SPQuery against the list with a filter on “Groups”, and iterates through the result. On each item in the result, it checks if the user have selected the tools. The user can customize their available links through a form listing all available links, also personalized to the user based upon a group membership.

This solution performed acceptable during development and test, and no apparent issues with performance at the time. Just as a note, load testing was not a part of testing in this project. After a short while in production, end-users experienced a degradation in performance. An analysis detected and an issue with the code retrieving the personalized tools in the web part.

Analysis and solution

To measure the performance of the solution, I used SharePoint’s built in Developer Dashboard. Each time I help with performance issues in solutions, this tool almost never been used during development. The tool first appeared in SharePoint 2010, and even better in 2013. It was easy to see that both execution time and SQL queries went sky high when the solution grew with larger amounts of items and values in the list. This is a clear indication that the code behind the web part was not able to scale very well over time, and a code refactor needed.

PowerShell snippet to enable Developer Dashboard


$service = [Microsoft.SharePoint.Administration.SPWebService]::ContentService
$addsetting =$service.DeveloperDashboardSettings
$addsetting.DisplayLevel = [Microsoft.SharePoint.Administration.SPDeveloperDashboardLevel]::OnDemand
$addsetting.Update()

Before optimalization

This code is a snippet from within a foreach-loop returning all the SPListItem’s in the result. For each of the items, a SPFieldUserValueCollection is initialized and looped through, using LINQ in this example. The performance issue with the extreme number of SQL queries is originating from this collection and the iteration through it.

Performance

Request Summary
Duration 44401.78 ms
CPU Time 26656 ms
Aggregate Stats
Number of SQL Queries 4208
Total SQL Duration 6163 ms
Number of SPRequests 6

 

Code

var users = listitem[“Users”];
var userValueColl = new SPFieldUserValueCollection(web, users);
if ((userValueColl.Find(u => u.User.LoginName.ToString(CultureInfo.InvariantCulture).Contains(SPContext.Current.Web.CurrentUser.LoginName)) != null))
{
    // TODO
}

After optimalization

The code was using regular methods in the SharePoint API to check if the user existed, and I did not find any other recommended methods to retrieve the users. The issue with the first approach was that each iteration through the SPFieldUserValueCollection fires a separate SQL query to lookup the user data. With my testdata of 100 items in the list and 250 users in each user field, this became several thousand unnecessary queries every time the user views the page. Instead of using the SPFieldUserValueCollection, I ended up with a check against the raw text data in the field value. The only thing that is important to remember when checking the field value, is to identify the user the Site Collection relative User ID must be used. The User ID is located in the AllUsers collection in the SPWeb.

Performance

Request Summary
Duration 1465.09 ms
CPU Time 898 ms
Aggregate Stats
Number of SQL Queries 65
Total SQL Duration 388 ms
Number of SPRequests 6

 

Code


var users = "#" + listitem[“Users”]; // Append a "#" to make it possible to do pattern matching for user id in string
var currentUserId = web.AllUsers[SPContext.Current.Web.CurrentUser.LoginName].ID;
var searchPattern = string.Format("#{0};", currentUserId);
if (!users.Contains(searchPattern)) continue;

Summary

Refactoring to simple string checking, instead of “correct” use of the possibilities of the API, removed the performance bottleneck. The web part now scales much better with both load and larger amounts of data. In my example, I managed to reduce the number of SQL queries by 65 times (!). Definitively a well couple of hours spent J

From a second perspective, with the knowledge of the performance issues by storing large amounts of values in a SPFieldUser field, I would consider a different approach to storing user values for this use later.

Commonly used Web Templates in SharePoint 2013 with screenshots

I created a list of all the common used web templates in SharePoint 2013 with a screenshot showing how the site looks out-of-the-box primary to have for my own reference, but thought it might be interesting for other to access to so I posted this as an article.

This is the web templates available when creating new site collections through Central Admin with SharePoint 2013 Enterprise SP1. As many know there are lots of more templates, but these are the ones common available, and I recommend to stick to these when building solutions.

Please feel free to copy the screenshots for your own use.

Collaboration

Name

WebTemplateId

Screenshot

Team Site

STS#0

Team Site

Blog

BLOG#0

 Blog

Developer Site

DEV#0

 Developer Site - DevHome

Project Site

PROJECTSITE#0

 Project Site

Community Site

COMMUNITY#0

 Community Site - Community Home

Enterprise

Name

WebTemplateId

Screenshot

Document Center

BDR#0

 Document Center

eDiscovery Center

EDISC#0

 eDiscovery Center

Records Center

OFFILE#1

 Records Center

Business Intelligence Center

BICenterSite#0

 Business Intelligence Center

Enterprise Search Center

SRCHCEN#0

 Search - Enterprise

My Site Host

SPSMSITEHOST#0

 MySite Host

Community Portal

COMMUNITYPORTAL#0

 Community Portal

Basic Search Center

SRCHCENTERLITE#0

 Search - Basic

Visio Process Repository

visprus#0

 Visio Process Repository

Publishing

Name

WebTemplateId

Screenshot

Publishing Portal

BLANKINTERNETCONTAINER#0

 Publishing Portal

Enterprise Wiki

ENTERWIKI#0

 Enterprise Wiki

Product Catalog

PRODUCTCATALOG#0

 Product Catalog

 

Download all screenshots

Note: If you are using Office 365 please note that at the time this article was posted the “Product Catalog” is missing and a special version of the “Team Site” template is available.

Remove orphaned tasks from the aggregated task list on MySite in SharePoint 2013

For some reason a deleted task was still visible in the aggregated task list for a end-user. The task itself had been deleted from the source site, so when you clicked the task you got an error message telling you it did not exist anymore. The problem was that SharePoint was unable to remove the task from the aggregated view, so now we had to deal with a ghost task!

Solution

The new task aggregation is performed with the help of the Service Application “Work Management Service”, and the users MySite. When the service has aggregated your tasks, it stores the data itself in a list called “WmaAggregatedList_User”. Since it is a traditional list,  you might think: “This is easy! Just go to the list and delete the task!”. Sorry, but no. This list is only intended as a system list, and nothing we ever should care about, so it’s actually has no available views.

Step-by-step to remove the task

  1. Start “SharePoint Manager 2013” on one server in the farm (download from CodePlex)
  2. Navigate to the correct web application, and locate the users MySite site collection under “/personal” or your preferred managed path.
  3. Expand the structure and locate the list “WmaAggregatorList_User”, and choose to browse the “GridView”. If the user doesn’t have to many tasks, you should now be able to use this to visualy inspect the data.
  4. Locate the column named “TxEditUrl” and verify that it matched the URL of the ghost task. In my case I ignored the “&source=” end of the URL. Make a note of the list item ID for the task (the first column)
  5. Fire up good old “SharePoint 2013 Management Shell” to do some PowerShell magic.
  6. Example how to locate the task and remove it:

# Open the users personal site colletion and retrieve the list
$web = Get-SPWeb "http://mysite/personal/adamb"
$list = $web.Lists["WmaAggregatorList_User"] 

# Get the task with the ID located with SharePoint Manager
$item = $list.GetItemById(1) # NOTE: Use the correct ID here

# Now delete the task
$list.Items.DeleteItemById(1)

Summary

In some very rare cases, you can end up with users having orphaned tasks in their aggregated task list on MySite. I never got a understanding why this happened, but it was possible to remove this and get thing back to normal.

Add web parts from the gallery in SharePoint by code

For some reason I have never until now needed to add web parts to a page from the gallery using code. Why would I ever do this? The code runs inside a Feature Receiver, and is executed when creating new sites. The web part had been extended from a standard Content by Query Web Part (CBWP) with no code, and for that reason it was not possible to reference a unique class. So for this rare case I ended up with creating a utility function I thought was nice to share.

Function to add web parts from gallery


private static WebPart AddWebPartFromGallery(SPWeb web, SPLimitedWebPartManager wpm, string webPartFilename)
{
	string errorMessage;

	var query = new SPQuery
	{
		Query = String.Format(CultureInfo.CurrentCulture,
			"<Where><Eq><FieldRef Name='FileLeafRef'/><Value Type='File'>{0}</Value></Eq></Where>",
			webPartFilename)
	};

	var webPartGallery = web.IsRootWeb ? web.GetCatalog(SPListTemplateType.WebPartCatalog) : web.Site.RootWeb.GetCatalog(SPListTemplateType.WebPartCatalog);
	var webParts = webPartGallery.GetItems(query);
	XmlReader xmlReader = new XmlTextReader(webParts[0].File.OpenBinaryStream());
	var webPart = (WebPart) wpm.ImportWebPart(xmlReader, out errorMessage);

	if (!string.IsNullOrEmpty(errorMessage))
		throw new ApplicationException("Error importing Web Part (" + webPartFilename + "): " + errorMessage);

	return webPart;
}

How to use the function

This example code shows how to retreive the web part manager, checkout the file, add the web part and finally publish it again.


// Get web and its web part manager
var web = new SPWeb("http://intranet.contoso.com");
var pagesLibraryName = SPUtility.GetLocalizedString("$Resources:List_Pages_UrlName", "cmscore", web.Language);
var file = web.GetFile(web.Url + "/" + pagesLibraryName + "/" + "default.aspx");
file.CheckOut();
var wpm = web.GetLimitedWebPartManager(web.Url + "/" + pagesLibraryName + "/" + "default.aspx", PersonalizationScope.Shared);

// Get the web part from the sites gallery
var customWebPart = AddWebPartFromGallery(web, wpm, "CustomWebPart.webpart");

// Add the web part to the page and publish the page
wpm.AddWebPart(customWebPart, MyZoneId, 1);
wpm.SaveChanges(customWebPart);
file.CheckIn("Checked in by feature");
file.Publish("Published by feature");

Summary

Retrieving and adding web parts from the web part gallery by code is more complex than utilizing web parts you can access by their class. This post shows you how to retrieve the web part from the gallery and add it to a web part zone within a standard publishing page.