Thursday, July 2, 2009

Feature Stapling bug

Scenario:
This was a strange situation, we had a stapling feature which replaces the default page of Team Site , with a new page with more web part on it. Now the issues was , when we create a new site , all the web part shows up but only listview webparts are missing.

Also this behavior was random and if you activate the same feature manually after site is created there was no such issue.

Reason:
With my knowledge with sharepoint only thing I could think of was, list view automatically go missing if you don't have the list in site. So I think it was because of while site is getting created, list is still not created and so web parts got missing. Also that is inline with our second observation, when you activate the feature manually, there is delay and lists are created in the meanwhile and so we don't have list views missing.

Bug:
Clearly its a bug as the code should fire at the end of the Web creation life cycle, which is not the case.

Solution:

-- Either you can stick to manually activating the feature
-- Write a timer job which does this for you. ( May not be best of the best solution but this guarantees that it will only fire certain number of times. Also this timer can be extended to do much more things in future.

Code:

using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace SPCore
{
[CLSCompliant(false)]
public class FeatureStaplingBugFixTimerJob : SPJobDefinition
{
public FeatureStaplingBugFixTimerJob() : base() { }

[Persisted]
private readonly string _featureIdList; // list of feature need to activated

public FeatureStaplingBugFixTimerJob(SPWebApplication webApp, string jobName, string featureIDs)
: base(jobName, webApp, null, SPJobLockType.ContentDatabase)
{
Title = jobName;
_featureIdList = featureIDs; //this is one time updated by feature receiver
}

public override void Execute(Guid targetInstanceId)
{
var featureIds = _featureIdList.Split(";".ToCharArray());

foreach (SPSite siteColl in WebApplication.Sites)
{
foreach (SPWeb web in siteColl.AllWebs)
{
// Check if the property name already exists
if (web.Properties.ContainsKey("FeatureStaplingFixDone"))
{
if (web.Properties["FeatureStaplingFixDone"] != "True")
{
foreach (var featureId in featureIds)
{
if (featureId.Length > 0)
{
var featureGuid = new Guid(featureId);
var spFeature = web.Features[featureGuid];
if (null == spFeature)
{
web.Features.Add(featureGuid);
}
}
}
// Update property with new value
web.Properties["FeatureStaplingFixDone"] = "True";
}
}
else
{
// Add new property with name and value
web.Properties.Add("FeatureStaplingFixDone", "False");
}
web.Properties.Update();
web.Dispose();
}
siteColl.Dispose();
}
}
}
}
Enhancements:
-- This code only works for Feature need to be stapled at Web Level
-- More error handling in case specified features are missing

Download:
You can download the complete solution from here. To be able to build the solution on your machine, make sure to point the location of WSPBuilder to right location in INSTALL.bat file

4 comments:

norvin July 3, 2009 at 12:21 AM  

Hi,

Why don't u try to activate u'r feature using a user control over the Master Page.

Thank you,
-nn

Sandeep K Nahta July 3, 2009 at 7:32 AM  

hi norvin ,

I don' think thats a good idea because if you have 4k visitors every day then it will fire the code that many times ever day. Also solution should work for all the possible site definitions.

norvin July 3, 2009 at 7:50 AM  

Hi,

Use a web.Properties and once the Feature is activated ..Update a Web.Property.So that the feature is activated once.

Thank you.
-nn

Sandeep K Nahta July 3, 2009 at 7:53 AM  

Norvin

Still firing the code from master page is never a good idea because then you take away the choice of changing master page from client.