SharePoint Internals – Hristo Pavlov’s Blog

15 July, 2008

Specifying Properties when Activating Features through Code

The SharePoint object model allows features to be activated and deactivated using code but doesn’t allow you to specify the feature properties. Passing feature properties could be very useful to configure your feature receiver to run in a specific way. The properties I am referring to are the same properties that can be specified when activating the feature via a site definition XML.

As in many other cases if you are using Reflection you could pass the properties when activating the feature. The code below will allow you to do this. Just call any of the public methods that suits you:

public static SPFeature ActivateFeature(SPFeatureCollection features, string featureXml)

{

    return ActivateFeature(features, featureXml, false);

}

 

public static SPFeature ActivateFeature(SPFeatureCollection features, string featureXml, bool throwException)

{

    XmlDocument xmlDoc = new XmlDocument();

    xmlDoc.LoadXml(featureXml);

 

    return ActivateFeature(features, xmlDoc, throwException);

}

 

public static SPFeature ActivateFeature(SPFeatureCollection features, XmlDocument xmlDoc, bool throwException)

{

    return ActivateFeature(features, xmlDoc.DocumentElement, throwException);

}

 

public static SPFeature ActivateFeature(SPFeatureCollection features, XmlNode featureNode, bool throwException)

{

    if(featureNode != null &&

        “Feature”.Equals(featureNode.Name))

    {

        Guid featureId = new Guid(featureNode.Attributes[“ID”].Value);

        XmlNode properties = featureNode.SelectSingleNode(“./Properties”);

 

        if (properties != null)

            returnActivateFeature(features, featureId, properties, throwException);

    }

 

    return null;

}

 

public static SPFeature ActivateFeature(

    SPFeatureCollection features,

    string featureName,

    XmlNode propertiesNode,

    bool throwException)

{

    SPFeatureDefinition featureDef = SPFarm.Local.FeatureDefinitions[featureName];

    return ActivateFeature(features, featureDef.Id, propertiesNode, throwException);

}

 

public static SPFeature ActivateFeature(

    SPFeatureCollection features,

    Guid featureId,

    XmlNode propertiesNode,

    bool throwException)

{

    ConstructorInfo propCollConstr =

        typeof(SPFeaturePropertyCollection).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0];

    SPFeaturePropertyCollection properties = (SPFeaturePropertyCollection)propCollConstr.Invoke(new object[] { null });

 

    MethodInfo buildPropertyCollectionFromXmlNodeInternal =

        typeof(SPFeaturePropertyCollection).GetMethod(

            “BuildPropertyCollectionFromXmlNode”,

            BindingFlags.Instance | BindingFlags.NonPublic,

            null,

            new Type[] { typeof(XmlNode) },

            null);

 

    try

    {

        buildPropertyCollectionFromXmlNodeInternal.Invoke(properties, new object[] { propertiesNode });

    }

    catch (TargetInvocationException tex)

    {

        throw tex.InnerException;

    }

 

    return ActivateFeature(features, featureId, properties, throwException);

}

 

public static SPFeature ActivateFeature(

    SPFeatureCollection features,

    Guid featureId,

    IDictionary<string, string> activationProps,

    bool throwException)

{

    ConstructorInfo propCollConstr =

        typeof(SPFeaturePropertyCollection).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0];

    SPFeaturePropertyCollection properties = (SPFeaturePropertyCollection)propCollConstr.Invoke(new object[] { null });

 

    foreach (string key in activationProps.Keys)

        properties.Add(new SPFeatureProperty(key, activationProps[key]));

 

    return ActivateFeature(features, featureId, properties, throwException);

}

 

private static SPFeature ActivateFeature(

    SPFeatureCollection features,

    Guid featureId,

    SPFeaturePropertyCollection properties,

    bool throwException)

{

    MethodInfo getFeatureInternal =

        typeof(SPFeatureCollection).GetMethod(

            “GetFeature”,

            BindingFlags.Instance | BindingFlags.NonPublic,

            null,

            new Type[] { typeof(Guid) },

            null);

 

    try

    {

        SPFeature alreadyActivatedFeature = (SPFeature)getFeatureInternal.Invoke(features, new object[] { featureId });

        if (alreadyActivatedFeature != null)

            // The feature is already activated. No action required

            return null;

    }

    catch (TargetInvocationException tex)

    {

        throwtex.InnerException;

    }

 

    MethodInfo addInternal =

        typeof(SPFeatureCollection).GetMethod(

            “Add”,

            BindingFlags.Instance | BindingFlags.NonPublic,

            null,

            new Type[] { typeof(Guid), typeof(SPFeaturePropertyCollection), typeof(bool) },

            null);

 

    try

    {

        object result = addInternal.Invoke(features, new object[] { featureId, properties, throwException });

 

        return result as SPFeature;

    }

    catch (TargetInvocationException tex)

    {

        throw tex.InnerException;

    }

}

If you decide to pass the features XML then it should have the same format as in your site definitions under the SiteFeatures or WebFeatures elements, for example you can pass the XML below to call the ActivateFeature(SPFeatureCollection features, string featureXml) from the code above.

<Feature ID=78277796-98D9-4276-B7D2-E3374AAC43D8>

      <Properties>

            <Property Key=MyProperty1 Value=FALSE />

            <Property Key=MyProperty2 Value=6ECFC841-7FFF-4E06-9D50-0678CC43696D />

            <Property Key=MyProperty3 Value=TRUE />

            <Property Key=MyProperty4 Value=Custom Data/>

      </Properties>

</Feature>

And if it worries you to call SharePoint internal methods using reflection then – don’t worry, this code will not do anything different than what SharePoint would do when activating your features. I have checked that. Oh and if you want exceptions that have occured in your feature receiver to be brought to you then make sure you pass true for throwException.

1 Comment »

  1. […] Specifying Properties when Activating Features through Code […]

    Pingback by Links (7/15/2008) « Steve Pietrek - Everything SharePoint — 16 July, 2008 @ 12:41 am


RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.