This post was prompted by a post in the MSDN Forums. Simply put … what order do things happen in the onet.xml?
This may seem like a trivial question .. but if you are writing custom features, it is important (to say vital) that you know whether or not your feature will activate before a list is created or after, or whether or not your files in modules will have been provisioned or not.
The order that I discovered is as follows:
- *<SiteFeatures> in onet.xml
- *Stapled Site Features (stapled using FeatureSiteTemplateAssociation)
- <WebFeature> in onet.xml
- Stapled Web Features (using FeatureSiteTemplateAssociation)
- <Lists> in onet.xml
- <Modules> in onet.xml
* note - obviously Site Features will only activate if you are creating a Site Collection, as opposed to a normal web site
How I worked this out
Well, you can copy the steps yourself, in 8 easy steps:
First off, create a new Custom Site Definition (a simple copy of STS). In my example, I called it MHSTS with a single configuration (#0) and created a WEBTEMP file to register it.
Second, create a new SPFeatureEventReceiver class (in an assembly from a class library). The code in the "FeatureActivated" should be something like this:
Third, create 5 features (the scope is in [ ])
- mhOnetSiteFeature [Site]
- mhStapledSiteFeature [Site]
- mhOnetWebFeature [WEB]
- mhStapledWebFeature [WEB
- mhManualWebFeature [WEB]
Each of these needs to be attached to the same SPFeatureEventReceiver class that we referenced earlier. This allows your debugger to work out which feature is activating … so each time your code steps into breakpoints, you can check the "Feature.Definition" properties and work out which feature is being activated.
Fourth step, create 2 more features which will act as Feature Staplers (using FeatureSiteTemplateAssociation elements).
- mhWebStapler [Site]
- Staples the mhStapledWebFeature to the MHSTS#0 template (when used to create a new Site or Site Collection).
- mhSiteStapler [WebApplication]
- Staples the mhStapledSiteFeature to the MHSTS#0 template (when used to create a Site Collection)
Fifth.. in your custom site definition (MHSTS) add the following features into the onet.xml:
- <SiteFeatures>
- mhOnetSiteFeature
- mhWebStapler
- <WebFeatures>
- mhOnetWebFeature
Sixth; Install all of the features using STSADM, and (in Central Administration) activate the "mhSiteStapler" feature within a test Web Application.
Seventh; Within that Web Application, create a new Site Collection using your new site definition (MHSTS). This should fire off all of your features in the following order;
- mhOnetSiteFeature
- mhStapledSiteFeature
- mhOnetWebFeature
- mhStapledWebFeature
(you can check this by debugging your event handler, and setting a watch on the properties.Definition.DisplayName value)
Now .. if you are keeping a close eye on your debugging window, then you can also start to look at the state of the SPWeb itself. The main thing is that None of the Lists or pages will have been created … at ANY point in this process. This means that <Lists> and <Modules> will not be executed until ALL of your features have activated.
Eighth; Now you can double-check the theory … navigate to your new Site Collection .. and activate your remaining feature manually (mhManualWebFeature).
You should still have your debugging window open .. which should let you know that now (after the site is provisioned) all of the Lists and web pages are now accessible from Code.
Ok .. that's great … errrr … so What ??
Well, this has quite a lot of importance for writing custom features. If you want to write features which automatically manipulate pages and lists then you are going to be a bit stuck, as the lists and pages won't have been created until after your feature activates! (bummer eh? But what you gonna do?)
To get around this problem you really have 2 options:
- Make your own custom definition. Strip the lists out of the onet.xml, and create ListInstance Features. These can then be created during the feature activation stage, allowing them to be modified by other features that are being activated / stapled.
- Put some While Loops with Thread.Sleep() statements in your code, basically waiting until the lists have been created. This is not a very elegant solution but you don't have much choice if you are trying to use staplers to modify out of the box definitions (or if you don't want to touch the onet.xml).
Thats it ... comments welcome as always :)
Nice Article, couple of things:
ReplyDelete1. Just for info, there is a similar article here: http://blogs.msdn.com/mcsnoiwb/archive/2008/05/28/site-provisioning-order.aspx
2. Re: "To get around this problem you really have 2 options". Actually there is a 3rd. You can create a custom site provisioning class derived from Microsoft.SharePoint.SPWebProvisioningProvider. You then specify this assembly in your template < Configuration > element. While a feature receiver is called before the default lists and pages get created, the site provisioning code gets called after the lists and other components have been created. (Andrew Connell's book spends some time covering this topic).
Thanks for this post, Martin. I had a request from a customer to remove the default lists from MySite on creation. When attempting to use feature stapling to accomplish this, I ran into the issue you describe. Lists aren't created until AFTER feature stapling has occurred. I tried using thread.sleep, but that didn't work either.
ReplyDeleteI was successful when I created a second feature and called it asynchronously from the first feature. The second feature deleted the lists. I had to have the second feature deactivate itself and do my deletes on the deactivating event after a 1 minute thread.sleep.
Thanks again for pointing me in the right direction.
Jerry,
ReplyDeleteCustomising My Site lists this way is very much a challenge. Part of the problem with "Sleep" is that it might not be reliable during periods of heavy load (when it takes more than 1 minute to process other activities).
The only other viable "solution" I have seen is using a Delegate Feature to drop in a control to the home page.
When someone visits the My Site for the first time it processes and sets up various things before de-activating the feature (and therefore removing itself from loading the next time).
Definately tricky, but I'm glad you got a solution working!