I came across an interesting issue recently that only has limited documentation on the web.
I am creating my site templates using the methods descirbed here:
So, using this method, I create a blank Site Definition and 2 Web Templates, one visible and one hidden. I define a WPP (SPWebProvisioningProvider) in the hidden template and that piece of code applies the hidden template and runs some code against that new web to configure it. This keeps us supported so that we don't have to change the Site Definition post deployment to change the site template, we can just update the WPP assembly.
WebtempCustom.xml
<
Templates xmlns:ows="Microsoft SharePoint">
<Template Name="FakeCustomTeamSite" ID="10001">
<Configuration ID="0" Title="Team Site" Hidden="False"
ImageUrl="../images/stsprev.png"
Description="A Custom Team Site"
DisplayCategory="Custom"
ProvisionAssembly="Custom.SharePoint.SiteTemplates.TeamSite, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cb281607c9ad5cff"
ProvisionClass="Custom.SharePoint.SiteTemplates.TeamSite.WebProvisioningProvider">
</Configuration>
</Template>
<Template Name="CustomTeamSite" ID="10002">
<Configuration ID="0" Title="Hidden Custom Team Site" Hidden="True" ImageUrl="../images/stsprev.png" Description="A Custom Team Site" DisplayCategory="Custom">
</Configuration>
</Template>
</Templates>
WebProvisioningProvider.cs
using
System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
namespace
Custom.SharePoint.SiteTemplates.TeamSite
{
class WebProvisioningProvider : SPWebProvisioningProvider
{
public override void Provision(SPWebProvisioningProperties props)
{
SPWeb web = props.Web;
web.ApplyWebTemplate("CUSTOMTEAMSITE#0"); //apply a template to the web
web.Description = "Created from a privisioning class.";
web.Update();
}
}
}
Receiver code for Stapled Feature
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
using (SPWeb Web = (SPWeb)properties.Feature.Parent)
{
#region Create Calendar List
ExceptionBlock.LogSection("Create Calendar List:");
try
{
Guid listGuid = Web.Lists.Add("Calendar", "Calendar for team events", SPListTemplateType.Events);
SPList list = Web.Lists[listGuid];
list.OnQuickLaunch = true;
list.Update();
}
catch (Exception ex)
{}
#endregion
}
}
Then wanted to not define the features associated with that Site Definition in the onet.xml file, but use Feature Stapling. Again, making my solution more suportable in the future.
A problem I have come across though, is that when I have feature receivers in those stapled features that udate the SPWeb object, it clashes with the web updates in the SPWebProvisioningProvider.
The error message I receive is:
The web being updated was changed by an external process. at Microsoft.SharePoint.Library.SPRequestInternalClass.SetWebProps(String bstrUrl, String bstrTitle, String bstrDescription, UInt32 nLocale, UInt16 nTimeZone, Boolean bTime24, Int16 nCalendarType, Int16 nAdjustHijriDays, Int16 nAltCalendarType, Boolean bShowWeeks, Int16 nFirstWeekOfYear, Int16 nFirstDayOfWeek, Int16 nWorkDays, Int16 nWorkDayStartHour, Int16 nWorkDayEndHour, Int32 lFlags, Int16 nCollation, UInt32 nAuthor, String bstrCustomizedCssFileList, String bstrAlternateCssUrl, String bstrAlternateHeaderUrl, String bstrMasterUrl, String bstrCustomMasterUrl, String bstrSiteLogoUrl, String bstrSiteLogoDescription, Boolean bUpdateTimeCreated, DateTime dtTimeCreated, Boolean bUpdateTimeLastModified, DateTime dtTimeLastModified, UInt32& nCollationLCID)
at Microsoft.SharePoint.Library.SPRequest.SetWebProps(String bstrUrl, String bstrTitle, String bstrDescription, UInt32 nLocale, UInt16 nTimeZone, Boolean bTime24, Int16 nCalendarType, Int16 nAdjustHijriDays, Int16 nAltCalendarType, Boolean bShowWeeks, Int16 nFirstWeekOfYear, Int16 nFirstDayOfWeek, Int16 nWorkDays, Int16 nWorkDayStartHour, Int16 nWorkDayEndHour, Int32 lFlags, Int16 nCollation, UInt32 nAuthor, String bstrCustomizedCssFileList, String bstrAlternateCssUrl, String bstrAlternateHeaderUrl, String bstrMasterUrl, String bstrCustomMasterUrl, String bstrSiteLogoUrl, String bstrSiteLogoDescription, Boolean bUpdateTimeCreated, DateTime dtTimeCreated, Boolean bUpdateTimeLastModified, DateTime dtTimeLastModified, UInt32& nCollationLCID)
This appears to be because both pieces of code reference the same SPWeb Object (the contet one?) and cannot update it at the same time. This occurrs because the Stapled Feature Receivers don't know when the web has been provisioned and just run when they are ready.
So, I guess the only way around this is to thread the code, put in some waits to ensure the SPWebProvisioningProvider has completed or create our own SPWeb Objects in all WPPs and Feature Receivers.
I am investigating the solutions now and will update when I have more information.
Mark