One of the great improvements in SharePoint 2010 are Web Templates. Mirjam van Olst wrote recently a great article about why using light-weight Web Templates is a better approach than using full blown Site Definitions. While using Web Templates for creating sites and Site Collections is pretty straight-forward things get complicated when you need to create the Site Collection programmatically.

The solution

Let’s start with looking at a working code sample that allows you to create a Site Collection using a Web Template:

SPWebApplication webApp = SPWebApplication.Lookup(new Uri("http://win2008"));
using (SPSite site = webApp.Sites.Add("/sites/site1", "SharePoint", null, 1033, null, "administrator", "Administrator", "admin@contoso.com"))
{
    SPWeb rootWeb = site.RootWeb;

    // Get Solutions Gallery
    SPDocumentLibrary solutions = (SPDocumentLibrary)site.GetCatalog(SPListTemplateType.SolutionCatalog);
    // Upload Solution File with the Web Template
    SPFile solutionFile = solutions.RootFolder.Files.Add("test.wsp", File.ReadAllBytes(@"..\..\test.wsp"));
    // Activate Solution
    SPUserSolution solution = site.Solutions.Add(solutionFile.Item.ID);
    // Activate Features
    Guid solutionId = solution.SolutionId;

    // Activate Site Collection Features
    SPFeatureDefinitionCollection siteFeatures = site.FeatureDefinitions;
    var features = from SPFeatureDefinition f
                   in siteFeatures
                   where f.SolutionId.Equals(solutionId) && f.Scope == SPFeatureScope.Site 
                   select f;
    foreach (SPFeatureDefinition feature in features)
    {
        site.Features.Add(feature.Id, false, SPFeatureDefinitionScope.Site);
    }

    // Get Web Template
    SPWebTemplateCollection webTemplates = site.RootWeb.GetAvailableWebTemplates(1033);
    SPWebTemplate webTemplate = (from SPWebTemplate t
                                 in webTemplates
                                 where t.Title == "test"
                                 select t).FirstOrDefault();
    if (webTemplate != null)
    {
        site.RootWeb.ApplyWebTemplate(webTemplate.Name);
    }
}

In order to create a new Site Collection you need a reference to the Web Application (1). You can create a new Site Collection by calling the SPSiteCollection.Add method (2). The important part of creating a Site Collection that uses a Web Template is, that you have to create a blank Site Collection at first and apply the template later. Todd discussed this in detail in his post.

After you create an empty Site Collection you can proceed with uploading the WSP file that contains the Web Template. You can do this by retrieving the reference of the Solutions Gallery (7) and adding the file to the Root Folder (9). While adding the file you need to store the reference to the SPFile object. Later on you will be needing the ID of the created List Item in order to activate the Sandboxed Solution.

The next step is to activate the Sandboxed Solution. You can do this by calling the SPUserSolutionCollection.Add method passing the ID of the solution file stored earlier (11). Important difference while activating a Sandboxed Solution programmatically and using the web UI is, that when using code no Features are getting activated. So in order to be able to use your Web Template you need to activate the Site Collection Features from the Sandboxed Solution.

In order to activate the Site Collection Features from the Sandboxed Solution you need the Solution ID (13). Using this ID you can find out which Features are a part of the Sandboxed Solution and should be activated. You can easily retrieve the Features using LINQ (16-20). The things that you have to check for are the Solution ID and Feature’s Scope (19): only Site Collection scoped Features must be activated. Site scoped Features are getting activated automatically by the Web Template itself when applied to the Site. If you activate Site scoped Features you will get error while applying the Web Template to your Site. While activating the Features it’s important to use the Add(Guid, bool, SPFeatureDefinitionScope) overload (23). By setting the third parameter to SPFeatureDefinitionScope.Site you let SharePoint know that the Feature is a part of a Sandboxed Solution. If you use a different overload or pass a different value you will get an exception during the Feature activation process.

The next step is to retrieve the Web Template that you want to apply to your empty Site Collection. You can do this by searching the list of available Web Templates (27) for a Web Template with the name provided while saving the Site as Template (test in this example; 30).

The last step is to apply the Web Template to the Site Collection. While using Web Templates you have to use the SPWeb.ApplyWebTemplate(string) overload (34). Web Templates are represented in the SharePoint API using the SPFeatureWebTemplate class which is internal and cannot be used in your code. By providing the name of the Web Template instead, you let SharePoint find the right Web Template for you. If you try to use the SPWeb.ApplyWebTemplate(SPWebTemplate) overload you will end up with a broken Site.

And that’s it. Using the code above allows you to programmatically create Sites and Site Collections based on Web Templates.

Technorati Tags: