Thursday, March 11, 2010

Check dependent (Parent) features for activation before activating child feature

One of the beautiful features that Microsoft provides is dependent (Parent) feature activation before activating its related sub features. For example if we try to activate "Office SharePoint Server Publishing Infrastructure" feature at the sub site level, before activating it at the parent site collection, it will redirect us to an application page under layouts "_layouts/ReqFeatures.aspx" saying that "One or more features must be turned on before this feature can be activated". This feature is very useful. Let me brief you how we can use this feature.

I was developing a highly customized SharePoint application for one of my clients where I got a situation like this. I was developing couple of "List definitions with Look up fields" which are to be deployed in one of the sub sites under a site collection. As we all know "List Definitions" only accept site columns and content types, I had to create Site Columns, Content Types that uses this site columns, for List definitions. Now the actual problem comes with the deployment. Problem is that "Site Columns and Content types" features should only be scoped at the "Site" i.e site collection level when we create them through feature, so I am forced to deploy them at the site collection level. I don't want List definitions to be deployed at the site collection level, since if we activate the feature on the site collection level, these list definitions are going to appear on all sub sites, which is not suggestible. So I thought I will deploy "Site Columns and Content types" scoped at site collection level and the "List definitions" scoped at the sub site level. What happens if the user activates sub site i.e "List Definitions" feature before activating required site collection i.e "Site columns and Content Types" feature

I was thinking of how to solve this problem then suddenly "Office SharePoint Server Publishing Infrastructure" stuff flashed my mind. How did Microsoft implement this? I thought I can control the sub site feature activation in the "Feature activated" feature receiver of the sub site, but by the time we get the control in the feature activated, feature receiver our sub site feature is already active. Then I was thinking why Microsoft didn't provide "Feature Activating" feature receiver. Finally here is the solution, to overcome this situation, in the feature activated feature receiver we will have to manually check if the required site collection features i.e "Site Columns and Content Types" is already activated, if it is activated then allow then allow this sub site feature to be activated successfully, otherwise deactivate this sub site feature and redirect the user to the error page with a message like "Please activate the parent feature(Site Columns and Content Type) on the site collection before activating this feature".

Now to check if the required feature is active on the site collection level I have written the following method.

//"Site" is the Root site collection

//"FeatureID" is the parent feature at the sitecollection level

private bool checkForParentFeatureActive(SPSite site, string featureID)

{
//This feature collection contains only features that are active.
SPFeatureCollection featureCollection = site.Features;

foreach (SPFeature feature in featureCollection)
{
if (feature.DefinitionId.ToString().ToLower() == featureID.ToLower())
{
return true;
}
}
return false;
}

We should keep in mind that below statement returns only features that are active on the site collection level.

SPFeatureCollection featureCollection = site.Features;

Below is the call to that method in the feature activated code

public override void FeatureActivated(SPFeatureReceiverProperties properties)

{

SPWeb web = SPContext.Current.Web;
web.AllowUnsafeUpdates = true;
SPSite site = web.Site;
try
{
//Check for parent feature(Content Types and custom Fields) activeness
if (checkForParentFeatureActive(site, parentFeatureID))
{
//do the necessary action if the parent feature is active.
}
else
{
/*deactivate the current feature and throw a error message to the user saying that he has to activate the parent feature before activating this feature*/

web.Features.Remove(childFeatureID, true);
SPUtility.TransferToErrorPage("Please activate the parent feature(Site Columns and Content Type) on the site collection before activating this feature.");
}
}
catch (System.Threading.ThreadAbortException ex)
{

PortalLog.LogString(ex.StackTrace);
}
catch (System.Exception ex)
{
PortalLog.LogString(ex.StackTrace);
throw new SPException(ex.Message);
}
finally
{
web.Update();
web.AllowUnsafeUpdates = false;
site.Dispose();
}

In the above code snippet, following code deactivates our sub site feature i.e the current feature

web.Features.Remove(childFeatureID, true);

and then redirect the user to the error page displaying a message.

No comments:

Post a Comment