The WebProvisioned web event receiver is a new receiver type introduced in SharePoint 2010. It is one very useful addition to the already extensive suite of event receiver types in SharePoint, as it allows you to add some additional logic when a sub-site in your site collection gets created. In this sense it resembles to some extent the feature stapling functionality, but it differs from it because its scope is the site collection level whereas the feature stapling affects the whole farm (I personally dislike farm wide impact like this since you generally/potentially break all other solutions that may have been installed in the same farm). [updated 2010-12-29]
Creating and deploying a WebProvisioned event receiver is also a relatively simple task as you have a dedicated project item in Visual Studio 2010 – it basically supports all available receiver types, including the WebProvisioned one. With a couple of clicks you will be able to see an elements file for your WebProvisioned receiver that will look something like this:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Receivers >
<Receiver>
<Name>EventReceiver1WebProvisioned</Name>
<Type>WebProvisioned</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>Stefan.SharePoint.EventReceiver1.EventReceiver1</Class>
<SequenceNumber>10000</SequenceNumber>
</Receiver>
</Receivers>
</Elements>
the event receiver element definition will come with a new feature definition if you don’t have already one in your SharePoint project. Basically this is enough to have your new event receiver provisioned, but you may further consider two more important options: the first one is the Synchronization of the event receiver – you can specify either “synchronous” or “asynchronous” here (the “asynchronous” is the default one):
<Synchronization>Synchronous</Synchronization>
The Synchronization element should be placed below the “Receiver” element. Normally, I will opt for the synchronous type – this will be justified especially in cases where you will create your sites with the SharePoint UI and will expect to see immediately the results of the receiver when the home page of the site loads. Another thing is that with asynchronous receivers you have the risk of having save conflicts and concurrency issues especially when you create many sites simultaneously which is the case when you use a portal site definition.
The second option is the Scope option – you can specify it with the “Scope” attribute of the “Receivers” element – you can set it to either “Site” or “Web”:
<Receivers Scope="Site">
The scope determines whether the event receiver will be added to the SPSite.EventReceivers or SPWeb.EventReceivers collections. And there is a substantial difference in the behavior and application of the receiver depending on whether it is added to the SPSite or SPWeb level: in the first case it will be called for every sub-site created in the site collection, while in the second case, the receiver will be called only for the sub-sites that are immediate children of the site in whose EventReceivers collection the receiver is added. One other interesting “feature” (a rather peculiar one) is that when I tested my sample “Site” scoped WebProvisioned receiver it got called twice for one and the same site (no idea yet if this is an issue with my dev environment only or it is something by design). This is not the case for “Web” scoped WebProvisioned receivers.
Another important thing with the receiver’s scope is that the “Site” scope will be applied only if the activating feature also has “Site” scope (site collection scoped). If the feature has “Web” scope, the “Scope” attribute of the “Receivers” element will be ignored and the receiver will be added to the SPWeb.EventReceivers collection.
General purpose WebProvisioned event receiver
And now let’s have a look at one practical example - the general purpose WebProvisioned event receiver that I created (download source code from here). So, first let me say several words about the real life issues that I thought could be addressed with a WebProvisioned receiver – one of these is the ability of sub-sites to inherit certain settings from their parent site – for example the site default master page or the alternate CSS URL. This functionality is available for sub-sites based on the standard publishing site templates but that’s not the case for the non-publishing site definitions (like team site, blog site, wiki site, etc). This issue can be addressed with custom feature/features but it is not handy to create new site definitions that extend the standard ones only to add this extra functionality. Feature stapling is also an alternative in this case but it affects the whole farm which is definitely not a thing that I want to have in my SP installation [updated 2010-12-29]. And apart from the web settings we also have the settings managing the site navigation that are also suitable for inheriting – apart from the option to inherit the top navigation provided in the standard SharePoint “create site” page (and you can create your sites or site hierarchies with custom tools or scripting where this option is obviously not available).
One other important thing that I wanted to have in my WebProvisioned event receiver was that it should be fully configurable. And that this configurability is achieved via feature properties – because the event receiver will be naturally provisioned by a feature which will support a set of feature properties that will determine the behavior of the event receiver.
So, let me directly start with an example of the custom configuration feature properties so that you can get an idea of the type of functionality that this custom WebProvisioned receiver provides:
<Properties>
<Property Key="ReceiverScope" Value="Site" />
<Property Key="Web.MasterUrl" Value="~SiteCollection/_catalogs/masterpage/v4.master" />
<Property Key="Web.AlternateCssUrl" Value="/_layouts/styles/mydefault.css" />
<Property Key="CustomProperty.Test" Value="Some value" />
<Property Key="PublishingWeb.IncludeInCurrentNavigation" Value="true" />
<Property Key="Navigation.GlobalIncludePages" Value="true" />
<Property Key="Navigation.GlobalIncludeSubSites" Value="true" />
</Properties>
The first feature property – “ReceiverScope” – as its name suggests, determines whether the event receiver should be added to the SPSite or SPWeb EventReceivers collection respectively (you can use either “Site” or “Web” in its value, exactly the same as in the “Scope” attribute from the event receiver sample element above). And as you can probably figure this out already – the configurability of the receiver’s scope with a feature property means that you can’t use declarative CAML in a “Receiver” feature element but rather use code in a feature receiver that should create the WebProvisioned event receiver. You will see that in the sample code I simply commented out the CAML in the event receiver’s element file and added a feature receiver to the original receiver’s feature (as both were created by Visual Studio 2010) that creates everything with code. The code of the feature receiver works for both “Site” and “Web” scoped feature so you don’t have to couple the “Site” web event receiver’s scope with a site collection scoped feature as it is the case with the “Receiver” feature element.
The rest of the feature properties determine the concrete behavior of the WebProvisioned event receiver – as you see, their keys follow a specific naming convention – they consist of two parts separated by a dot. The first part can contain the following predefined values: Web, CustomProperty, PublishingWeb and Navigation. They correspond to the target instance that the receiver will modify – SPWeb, SPWeb.AllProperties, PublishingWeb and PublishingWeb.Navigation respectively (SPWeb instance in this case is the SPWeb of the newly created (provisioned) web that the WebProvisioned receiver is invoked for and the PublishingWeb is the object retrieved from this SPWeb instance using the PublishingWeb.GetPublishingWeb static method). The second part of the key specifies the name of a public instance property of the target class in the case of the SPWeb, PublishingWeb and PortalNavigation (PublishingWeb.Navigation) target objects and a key in the Hashtable instance in the case of the SPWeb.AllProperties target object. The properties of the SPWeb, PublishingWeb and PortalNavigation classes can be only of the following .NET types – System.String, primitive .NET types (boolean, integer, double, etc) or .NET enumerations, properties of other .NET types are not supported (meaning that you can modify only properties from the above mentioned types in the target instances with the general purpose WebProvisioned event receiver).
So, the feature properties from this first sample set fixed values to some of the properties of the new web that the web event receiver was invoked for, but what about inheriting these properties from the parent web. Have a look at this second sample:
<Property Key="Web.MasterUrl" Value="${~Parent}" />
<Property Key="Web.CustomMasterUrl" Value="${~ParentNoRoot}" />
<Property Key="Navigation.GlobalIncludePages" Value="${~SiteCollection}" />
As you see, you can specify special tokens in the “Value” attribute of the feature property elements too, these three options are available:
- ${~Parent} – with this token you specify that the property of the “Key” attribute will be copied from the parent web of the current web
- ${~ParentNoRoot} – this is almost the same as the first option, the only difference being that if the parent site is the root site of the current site collection the property won’t be copied to the current web (meaning that if the current web is not a child of the root web, the property will get copied).
- ${~SiteCollection} – this token specifies that the property will be copied from the root web of the current site collection (no matter whether it is the parent of the current web or not)
In the case of the ${~ParentNoRoot} token you saw that there will be cases when the specified web property won’t get copied to the current web (for first level children). In this case you will need to specify two feature property elements with the same “Key”:
<Property Key="Web0.CustomMasterUrl" Value="~SiteCollection/_catalogs/masterpage/v4.master" />
<Property Key="Web1.CustomMasterUrl" Value="${~ParentNoRoot}" />
… or almost the same “Key” – you see that there is an extra digit before the dot separator in the “Key” attribute – this is a special trick that the WebProvisioned event receiver supports because SharePoint doesn’t allow you to have feature property elements with the same “Key” attribute. Another important thing here is the order of evaluation of the feature property elements that specify one and the same web property – in this case the properties appearing later in the properties’ definition will have precedence – this means that the static “v4.master” value will be applied only for first level child webs and all other sub-webs will have their “CustomMasterUrl” property copied from their respective parent webs.
So much about web properties’ inheritance, but what about the much rarer case when you may want the opposite - to disallow the web properties’ inheritance (which is the default behavior for the SPWeb’s MasterUrl, CustomMasterUrl and AlternateCssUrl properties for publishing sites). This sample would do the trick:
<Property Key="Web.MasterUrl" Value="~SiteCollection/_catalogs/masterpage/v4.master" />
<Property Key="Web.CustomMasterUrl" Value="~SiteCollection/_catalogs/masterpage/v4.master" />
<Property Key="Web.AlternateCssUrl" Value="" />
<Property Key="CustomProperty.__InheritsMasterUrl" Value="false" />
<Property Key="CustomProperty.__InheritsCustomMasterUrl" Value="false" />
<Property Key="CustomProperty.__InheritsAlternateCssUrl" Value="false" />
As you see, when you create publishing sub-sites it is not enough to just set the SPWeb’s MasterUrl, CustomMasterUrl and AlternateCssUrl properties. To “break” the inheritance of the master page settings you will also need to set several custom properties in the SPWeb.AllProperties collection as well.
And one last option that the general purpose WebProvisioned event receiver supports:
<Property Key="[SPS;STS#1]Navigation.GlobalIncludeSubSites" Value="true" />
you can specify optionally the names of the target site definitions for which the property setting should be applied. You can specify one or more site definition names separated by semi-colon in the beginning of the “Key” attribute enclosed in square brackets. You can use names of site definitions with or without configuration number added (after the ‘#’ character) – when you use a site definition name without a configuration number the property will be applied for all available configurations in this site definition (actually for webs based on all configurations).
And lastly several words about the sample solution with the general purpose WebProvisioned receiver: it contains a feature named “WebProvisionedReceiver” – this is the feature which actually installs the custom WebProvisioned receiver. In order that you can use it you will need to provide it with the appropriate for your scenario feature properties (starting with the “ReceiverScope” one) – this you can do in the onet.xml file of a site definition of yours in which you will add a reference to the “WebProvisionedReceiver” feature. Optionally (handy for testing purposes) you can add the feature properties directly to the feature’s definition (the feature.xml template file).
Besides the “WebProvisionedReceiver” feature the sample solution contains one more feature which is called “WebSettings”. It can be configured with exactly the same properties as the “WebProvisionedReceiver” feature (except the “ReceiverScope” one). This feature uses the same internal logic as the general purpose WebProvisioned receiver but instead applies the specified web properties directly to the web in which it is activated.