|
Feature Receivers allow programmatic code to be run using methods overriding the SPFeatureReceiver interface:
This has great power in terms of setting up required elements for a feature and also cleaning up afterwards if it is deactivated or uninstalled. SPFeatureReceiverPropertiesEach method has a SPFeatureReceiverProperties object passed through to it with various properties on it. These can be used to get the context of the Feature and also to inspect the features state. How to add a Feature Receiver to a featureCreating the feature receiver classYou are going to need a feature receiver class, typically this is part of the Visual Studio project that contains the Feature Receiver. Most SharePoint Productivity tools (WSPBuilder, STSDev, VSeWSS) will create you a Feature Receiver with a feature receiver class already hooked up. This class will look something like this when first created: using System; using Microsoft.SharePoint; namespace WSPBuilderProject1 { class FeatureWithReceiver1 : SPFeatureReceiver { public override void FeatureActivated(SPFeatureReceiverProperties properties) { throw new Exception("The method or operation is not implemented."); } public override void FeatureDeactivating(SPFeatureReceiverProperties properties) { throw new Exception("The method or operation is not implemented."); } public override void FeatureInstalled(SPFeatureReceiverProperties properties) { throw new Exception("The method or operation is not implemented."); } public override void FeatureUninstalling(SPFeatureReceiverProperties properties) { throw new Exception("The method or operation is not implemented."); } } } If you are not going do anything on installing and uninstalling simply remove throwing the exceptions as this will effect performance. using System; using Microsoft.SharePoint; namespace WSPBuilderProject1 { class FeatureWithReceiver1 : SPFeatureReceiver { private const string _enhancedTitle = " Enhanced Version"; public override void FeatureActivated(SPFeatureReceiverProperties properties) { SPWeb web = (SPWeb) properties.Feature.Parent; web.Title = web.Title + _enhancedTitle; web.Update(); } public override void FeatureDeactivating(SPFeatureReceiverProperties properties) { SPWeb web = (SPWeb)properties.Feature.Parent; web.Title = web.Title.Replace(_enhancedTitle, string.Empty); web.Update(); } public override void FeatureInstalled(SPFeatureReceiverProperties properties) {} public override void FeatureUninstalling(SPFeatureReceiverProperties properties) {} } } Hooking the feature receiver class to the featureCreating the class and compiling the dll and putting it in the WSP package is not enough. You need to associate the feature with the class. This in done in the Feature XML, you will notice there is now a ReceiverAssembly and ReceiverClass attribute on the Feature element. The values for these attributes can be found by following How to build the assembly reference used in Feature files and SafeControls entries with class, assembly name and public key token. <?xml version="1.0" encoding="utf-8"?> <Feature Id="6eec15cf-5505-430f-a13e-c8ad96140c2f" Title="FeatureWithReceiver1" Description="Description for FeatureWithReceiver1" Version="12.0.0.0" Hidden="FALSE" Scope="Web" DefaultResourceFile="core" ReceiverAssembly="WSPBuilderProject1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=be02c50325b391ed" ReceiverClass="WSPBuilderProject1.FeatureWithReceiver1" xmlns="http://schemas.microsoft.com/sharepoint/"> <ElementManifests> <ElementManifest Location="elements.xml"/> </ElementManifests> </Feature> Debugging Feature ReceiversYou can debug feature receivers by attaching the debugger to the w3wp.exe process of your web application you are testing. For more on this see SharePoint Development Debugging Feature Receiver GotchasContext when activating feature using STSADMSource chakkaradeep There are multiple ways of getting the context of the SPWeb object: public override void FeatureActivated(SPFeatureReceiverProperties properties) { SPWeb CurWeb = SPContext.Current.Web; // Rest of the code follows.... } If you activate the feature via STSADM you will get an error: 'Object reference not set to an instance of the object'. This will work if you activate the feature via the web UI. The reason for this is that the cmd.exe process does not understand or know how to get your current context! If you are running via browser, you have the w3wp.exe process from which you can get the context! You will also get errors if you activate a feature that is scoped at 'Site' level and cast the properties.Feature.Parent to SPWeb instead of SPSite. The below code will also help manage this for you. Below is an extension method for you! (Matt Smith's blog public static class Extensions { /// <summary> /// Gets the web. /// </summary> /// <param name="properties">The properties.</param> /// <returns></returns> public static SPWeb GetWeb(this SPFeatureReceiverProperties properties) { SPWeb site; if (properties.Feature.Parent is SPWeb) { site = (SPWeb)properties.Feature.Parent; } else if (properties.Feature.Parent is SPSite) { site = ((SPSite)properties.Feature.Parent).RootWeb; } else { throw new Exception("Unable to retrieve SPWeb - this feature is not Site or Web-scoped."); } return site; } } Depending on the scope of your feature, you cast the Parent. And you use it like this: public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWeb CurWeb = properties.GetWeb();
Guid listId = CurWeb
.Lists
.Add(ListTitle, ListDescription, SPListTemplateType.GenericList);
CurWeb.Update();
}
Labels |
How to add a Feature Receiver to a Feature

This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License. Hosted generously by CustomWare









Comments (1)
Sep 11, 2009
Anonymous says:
Thanks Jeremy for this brilliant article. I have a problem with getting it to w...Thanks Jeremy for this brilliant article.
I have a problem with getting it to work with some MOSS out-of-the-box sites. It works perfectly for the Wiki, Blog, Basic Meeting Workspace, Record Centre, Team Site, Blank Site, and Document Workspace sites, but generates an error message for the Report Centre, Search Centre with Tabs, Site Directory, My Site Host, Collaboration portal, and Publishing portal sites. All the sites I am testting on are newly created.
Error messages:
**********
For the "Report Center" site the error message is:
Object reference not set to an instance of an object. at
TopMenu.TopMenuBar.FeatureActivated(SPFeatureReceiverProperties properties)
at Microsoft.SharePoint.SPFeature.DoActivationCallout(Boolean fActivate,
Boolean fForce)
at Microsoft.SharePoint.SPFeature.Activate(SPSite siteParent, SPWeb
webParent, SPFeaturePropertyCollection props, Boolean fForce)
at Microsoft.SharePoint.SPFeatureCollection.AddInternal(Guid featureId,
SPFeaturePropertyCollection properties, Boolean force, Boolean fMarkOnly)
at Microsoft.SharePoint.SPFeatureCollection.Add(Guid featureId)
at
Microsoft.SharePoint.WebControls.FeatureActivator.BtnActivateFeature_Click(Object
objSender, EventArgs evtargs)
at System.Web.UI.WebControls.Button.OnClick(EventArgs e)
at System.Web.UI.WebControls.Button.RaisePostBackEvent(String
eventArgument)
at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler
sourceControl, String eventArgument)
at System.Web.UI.Page.ProcessRequestMain(Boolean
includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
**********
For the Search Centre with Tabs, Site Directory, My Site Host, Collaboration portal, and Publishing portal sites the error message is:
Specified argument was out of the range of valid values. at
Microsoft.SharePoint.Navigation.SPNavigationNodeCollection.get_Item(Int32
index)
at TopMenu.TopMenuBar.FeatureActivated(SPFeatureReceiverProperties
properties)
at Microsoft.SharePoint.SPFeature.DoActivationCallout(Boolean fActivate,
Boolean fForce)
at Microsoft.SharePoint.SPFeature.Activate(SPSite siteParent, SPWeb
webParent, SPFeaturePropertyCollection props, Boolean fForce)
at Microsoft.SharePoint.SPFeatureCollection.AddInternal(Guid featureId,
SPFeaturePropertyCollection properties, Boolean force, Boolean fMarkOnly)
at Microsoft.SharePoint.SPFeatureCollection.Add(Guid featureId)
at
Microsoft.SharePoint.WebControls.FeatureActivator.BtnActivateFeature_Click(Object
objSender, EventArgs evtargs)
at System.Web.UI.WebControls.Button.OnClick(EventArgs e)
at System.Web.UI.WebControls.Button.RaisePostBackEvent(String
eventArgument)
at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler
sourceControl, String eventArgument)
at System.Web.UI.Page.ProcessRequestMain(Boolean
includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
*************
My Feature Receiver code:
************************************
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.Navigation;
using Microsoft.SharePoint.Administration;
namespace TopMenu
{
class TopMenuBar : SPFeatureReceiver
{
public override void FeatureActivated(SPFeatureReceiverProperties
properties)
{
SPWeb site = properties.GetWeb();
SPNavigationNodeCollection topNav =
site.Navigation.TopNavigationBar;
// create top navigation tab & add to top nav bar
SPNavigationNode topNavMenuItem = new SPNavigationNode("Top Menu
Site Scope", string.Empty, false);
topNav[0].Children.AddAsLast(topNavMenuItem);
// create first drop down pointing to Master Page Gallery
SPNavigationNode mpg = new SPNavigationNode("Master Page Gallery
TEST 777", string.Format("
/_catalogs/masterpage", site.Url), true);
topNavMenuItem.Children.AddAsFirst(mpg);
// create first drop down pointing to Web Part Gallery
SPNavigationNode wpg = new SPNavigationNode("Web Part Gallery",
string.Format("
/_catalogs/wp", site.Url), true);
topNavMenuItem.Children.AddAsLast(wpg);
// create first drop down pointing to List Template Gallery
SPNavigationNode ltg = new SPNavigationNode("List Template
Gallery", string.Format("
/_catalogs/lt", site.Url), true);
topNavMenuItem.Children.AddAsLast(ltg);
// create first drop down pointing to Site Template Gallery
SPNavigationNode stg = new SPNavigationNode("Site Template
Gallery", string.Format("
/_catalogs/wt", site.Url), true);
topNavMenuItem.Children.AddAsLast(stg);
// commit the changes
site.Update();
}
public override void FeatureDeactivating(SPFeatureReceiverProperties
properties)
{
SPWeb site = properties.GetWeb();
// delete folder of site pages provisioned during activation
SPFolder sitePagesFolder = site.GetFolder("TopMenuBar");
sitePagesFolder.Delete();
SPNavigationNodeCollection topNav =
site.Navigation.TopNavigationBar;
for (int i = topNav[0].Children.Count - 1; i >= 0; i--)
{
if (
topNav[0].Children[i].Title == "Top Menu Site Scope"
//|| topNav[0].Children[i].Title == "ASP Examples 2"
)
}
}
public override void FeatureInstalled(SPFeatureReceiverProperties
properties)
public override void FeatureUninstalling(SPFeatureReceiverProperties
properties)
}
// Add this class so this project is interchangable between
// SPWeb or SPSite
public static class Extensions
{
/// <summary>
/// Gets the web.
/// </summary>
/// <param name="properties">The properties.</param>
/// <returns></returns>
public static SPWeb GetWeb(this SPFeatureReceiverProperties
properties)
{
SPWeb site;
if (properties.Feature.Parent is SPWeb)
else if (properties.Feature.Parent is SPSite)
else
return site;
}
}
}
************************************
I would like to get this Feature Receiver working for the "Report Centre" site, but cannot understand why it works perfectly for one site but not another. Do I need to disable some default features to get it work within the Report Centre?
Sorry for this large message but I wanted to put as much info as possible so you maybe able to solve this problem.
Do you know what I am doing wrong? Many thanks
Alan