Using SPWebProvisioningProvider to write better SharePoint site templates (and avoid writing site definitions!): Part 1

Most SharePoint developers have been here before:

The client needs a template for creating a site or site collection with Publishing enabled, custom branding and master pages, 3 custom site features and 8 custom web features activated in a specific order, a custom welcome page and a customized subset of allowed child site templates.

The Publishing featureset requirement immediately necessitates a site definition (as opposed to a simple site template webtemp_*.xml file).  And even if you can scrounge enough code from the interwebs to tape together all of the hidden features required to activate SharePoint’s OOB Publishing featureset, you still can’t guarantee the order in which your custom features are activated (for example, SharePoint creates the default.aspx page LAST– so if you have a feature that references it, good luck).

If you’re patient, it can be done.  Most of us have trudged through it at some point or another.  But writing custom site definitions still has major drawbacks:

  • You can’t debug site definition provisioning execution at runtime (This is the BIG stickler for me)
  • It’s impossible to truly ensure feature activation order.
  • Logging is limited to whatever is built into your feature activation code plus SharePoint’s native site creation logging, because your site definition is declarative.
  • onet.xml files are hard to read and harder to explain to your client in KT sessions.

Maybe there’s another way.  (There is!)

Since 2010 , we’ve actually had the ability to roll a custom flavor of SharePoint’s provisioning class and completely override the process.  Don’t feel bad if you didn’t know about this; I sure didn’t.

This method has the following advantages over building a site definition:

  • You can debug it at runtime because it’s written in C#.
  • You can specify the exact order of feature activation, as well as run any additional custom code that does anything you want (for example, writes custom logging data or creates a sub-site automatically).
  • You can choose to implement an out-of-the-box site template and then perform customizations, or you can implement a custom site definition and still control the order of feature activation.
  • The only XML required is a simple webtemp_*.xml file that defines your template name and some metadata.

The process (described in detail below) is fairly straightforward:

  1. Create a SharePoint solution that includes a subclass of SPWebProvisioningProvider.
  2. Override the “Provision” method of the class and call the static SPWeb.ApplyWebTemplate(string strWebTemplate)  method, passing the name of an out-of-the-box SharePoint site template (for example, “BLANKINTERNET#0”).
  3. Add a web template file (mapped to the TEMPLATE\1033\XML folder in the hive) with a custom web template to your solution and reference your custom provisioning class in the template definition.
  4. Deploy and instantiate!

Hey, listen! It’s important to note that what I described above is a very simple implementation of a custom provisioning provider.  This isn’t a “one size fits all” approach; you may find that your requirements necessitate spinning up a separate thread for the “ApplyWebTemplate” method and waiting for it to complete before proceeding with feature activation.  I describe this process in detail in Part 2 of this post.

1.  Create a SharePoint solution that includes a subclass of SPWebProvisioningProvider.

This demonstration creates a subclass of the SPWebProvisioningProvider class that instantiates the out-of-the-box Publishing Portal site, then customizes it.  Depending on the complexity of your customization, you may need to wait for the provisioning to fully complete before you apply any customization.  If that’s the case, you can do so with threading, which I explain later in this post.

  1. Create a new Empty SharePoint Project.
  2. Right-click the project –> Add Class
  3. Name the class file “CustomProvider.cs”, which will create a class called CustomProvider.
  4. In the new class file, declare the “CustomProvider” class as a subclass of the SPWebProvisioningProvider class (don’t forget to reference the Microsoft.SharePoint and Microsoft.SharePoint.Administration assemblies).
class CustomProvider : SPWebProvisioningProvider
{

2.  Override the “Provision” method of the class and call the static SPWeb.ApplyWebTemplate method, passing the name of an OOB SharePoint site template

In this example, we are using the Publishing Portal template, “BLANKINTERNET#0”, which means that our new web will be created just as if the user selected “Publishing” as the site type in Central Administration (you can find a list of all the OOB site template names in SharePoint 2013 here):

//Apply the web template-- in this case, we are using
//the Publishing Portal template, "BLANKINTERNET#0".
public override void Provision(SPWebProvisioningProperties props)
{
     props.Web.ApplyWebTemplate("BLANKINTERNET#0");
     //Activate custom features here
     //...

3.  Add a web template file (mapped to the TEMPLATE\1033\XML folder in the hive) with a custom web template to your solution and reference your custom provisioning class in the template definition.

1.  Right-click your project and select “Add –> SharePoint Mapped Folder…”

2.  Select the /TEMPLATE/1033/XML folder (NOT the /TEMPLATE/XML folder!):

XML Folder

3.  Add a new file called “webtemp_CustomTemplates.xml” to this mapped folder.  This is where we will define a single template, with multiple configurations, that uses our custom provisioning provider class.  Your project should look like this:

project layout - simple

4. In this file, specify a single “Templates” element containing a single “Template” element.  Within this element, we can place multiple configurations, each of which will pass a different “ProvisionData” attribute to our custom provisioning provider class (accessed via the “Data” property of the “props” parameter).  For now, we will create a single “Configuration” element as follows:


<?xml version="1.0" encoding="utf-8" ?>
<Templates>
<Template Name="CustomTemplates" ID="11003">
<Configuration
ID="0"
Title="OTB Custom Publishing Site"
Hidden="FALSE"
ImageUrl=""
Description="Create a custom Publishing site."
RootWebOnly="FALSE"
DisplayCategory="Outside the Box"
ProvisionAssembly="$SharePoint.Project.AssemblyFullName$"
ProvisionClass="OTB.SharePoint.ProvisioningProvider.CustomProvider"
ProvisionData="Publishing">
</Configuration>
</Template>
</Templates>

A few notes:

  • As aforementioned, the “ProvisionData” attribute gets passed to the provisioning provider class as the “Data” property of the “props” parameter; later on, I explain how we can use a “Switch” statement on the value of this attribute to determine what type of site to build.  In the example above, I just set this attribute to “Publishing” because we are creating a publishing site, but we can put anything we want here.
  • The “ProvisionClass” attribute specifies the class name of your custom provisioning provider class; this is what tells SharePoint to call your class
  • The “ProvisionAssembly” attribute can be used to specify the assembly containing your custom provisioning provider class; in this case, the class is in the same assembly as the template file, so we can use the special $SharePoint.Project.AssemblyFullName$ value and Visual Studio will replace this with the name of our assembly during compilation
  • The “ID” attribute of the “Template” element must be unique and greater than 11000.

That’s it!  If you deploy this project as-is, you will find your custom template in Central Administration “Create site collection” page, under the “Outside the Box” (or whatever you set as the value of the “Group” attribute) category.

Hey, listen!  In Visual Studio, you have to set a SharePoint site for deployment, and VS takes care of resetting the web application for you.  Because this assembly gets deployed to Central Administration, you will need to specify this as the deployment target, or reset the CA web application post-deployment.  A full IISRESET also does the trick.

Here is what your custom template will look like in CA:

Create site

As promised, you can debug it:

debug

You may notice that the call to the “ApplyWebTemplate” method takes quite a while to complete; this is because SharePoint is running through its process of implementing the out-of-the-box Publishing site template, which includes activating many, many features.

This is a great segway to the more advanced implementation of a custom provisioning provider, in which we control the thread operation so that we don’t configure a site before it’s ready.  I cover this strategy in Part 2 of this post.  But for now, you have a new working Publishing site, and a blank canvas on which to write as much post-creation code as you wish.

Done

You can download this simple implementation of a custom provisioning provider in a Visual Studio 2012 solution from my public OneDrive:

Stay tuned for Part 2…

Advertisements

3 thoughts on “Using SPWebProvisioningProvider to write better SharePoint site templates (and avoid writing site definitions!): Part 1”

  1. Any chance part 2 will be published? I want to remove some features from the provisioned web, e.g. the OOTB document library feature. I assume I need ApplyWebTemplate to finish first then?

    1. Sorry– I haven’t had a chance to blog lately. It’s next on my list.
      But yes, you should wait for that operation to complete. If you know how to spin off a separate thread, do that and wait for it to complete before removing features, and get a new instance of the SPWeb object before doing so.

      I’ll do my best to push this out sometime in the near-ish future. Thanks for reading!

  2. Stumbled on this only today! Thanks very much for writing this up, it looks very very promising. I know a fair few people who would be very keen to see part 2 if you were able to put it up, even if it was just a code dump for the time being it would be much appreciated. Cheers!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s