Sharepoint 2007 End to End Feature Development

Download Demo Code

Introduction

This article will describe how to setup a basic sharepoint wsp solution project, build it and deploy it into Sharepoint. It included example code and instructions on building the Sharepoint 2007 feature project, the required directory structure, code and a basic description of each part of the project.

Background

Installing sharepoint features can have several challenges, but with a few well placed steps you can be off and running. This article will walk you through some basic steps to proper creation of a Sharepoint 2007 feature project in Visual Studio 2008 that can be built into a WSPBuilder deploy solution file, and includes some handy scripts to deploy into the Sharepoint site of your choice.

You will need to open this project in Visual Studio 2008 or modify the project and solution files for an earlier version. If this worked for you vote me a 5!

Getting Started

You will need to open project in Visual Studio 2008 and have the plug in for WSPBuilder to use the example code provided and follow the article. The next step to creating a project in Visual Studio for a feature or other code on your site is to set up your Visual Studio Project correctly. Below is a typical project hierarchy for a feature with some custom pages in Visual Studio:

 

Notice the main content files in a feature deploy are located under the TEMPLATE directory. This allows WSPBuilder to build a .wsp solution file that understands once deployed where to put everything. To build your feature correctly you need to have a few directories in place.

The main directories for this project are:

  • FEATURES/SampleFeature - this is the project area where the code and meta-data files (like feature.xml and elements.xml) reside for feature setup and activation. Modifications to the site definition (ONET.XML) or web templates can also be placed here (N/A for this example)
  • IMAGES - any feature-level images can be added here
  • LAYOUTS/1033/IMAGES - global layout images
  • LAYOUTS/1033/STYLES/SampleFeature - contains feature-level styles
  • LAYOUTS/SampleFeaturePages - this is an optional folder to extract functional feature controls and pages from the standard sharepoint ones, this is where and specialized pages are located.

For this example we will create "FEATURES/SampleFeature" to store the feature.xml and elements.xml for a feature. The feature for this example has functionality that will add a menu item to the edit control block menu (the right clicked popup menu on a document in a list in Sharepoint) and include this new menu item for all documents in site lists in the Sharepoint site collection for which this feature will be deployed.

The first part of the project we will examine is the feature.xml. This file contains the code which will tell Sharepoint what code to use for the feature, the "scope" of the code or where in the Sharepoint hierarchy the code will be availiable, and assign a Guid as an Id for use in installation. In the xml example below we see several attributes:

<Feature Id="8682CCBC-C49B-4957-ADAA-708E40C06121" Title="SampleFeature" Description="This is your feature description, notice the scope is 'Site' since this example deploys on the site collection" Scope="Site" Hidden="false" ImageUrl="SampleFeature\icon_contactlist.gif" ReceiverAssembly="SampleFeatureProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e30e9c1baed5c190" ReceiverClass="SampleFeatureProject.FeatureReceiver" xmlns="http://schemas.microsoft.com/sharepoint/"> <ElementManifests> <ElementManifest Location="elements.xml"/> </ElementManifests> </Feature>

Each attribute has a purpose and they are as follows:

  • Id - this is the feature id and will be used later in our WSP solution deployment script
  • Title - this is a general Sharepoint attribute and doesn't matter for this example
  • Description - ditto, for this example it is just for reference
  • Scope - this sets where in the site or site collection this feature will be availiable, since we will deploy this later to a site collection we set Scope to "Site" so all sub sites of the site collection can have this features functionailty availiable.
  • Hidden - N/A for this example
  • ImageUrl - N/A for this example
  • ReceiverAssembly - this is important, this is the assembly namespace for the FeatureReciever class
  • ReceiverClass - the name of the feature reciever class, also important
  • elments.xml - this is where we can define our custom actions for the feature.

The elements.xml holds specific functional actions for the feature.

The "Module" node has certain attributes like "List", "Url", and "Path" that determine what type of Sharepoint list this feature will be activated for. Since Sharepoint is a hierarichal collection of objects (or lists) these attributes determine which type and area to apply the feature to. The example below applies the feature to lists of type 113 which refers to a web part gallery. The "File" xml node gives us code that helps the solution to be deployable.

In the code below we see the xml node "Module" and "CustomAction".

    
<Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <Module Name="SampleFeature" List="113" Url="_catalogs/wp" Path="WebParts" RootWebOnly="False"> <File Url="wpSiteList.webpart" Type="GhostableInLibrary" > <Property Name="Group" Value="Sample Feature Project Web Parts" /> </File> </Module> <CustomAction Id="SampleFeatureProject.SampleItemMenu" RegistrationType="ContentType" RegistrationId="0x0101" ImageUrl="/_layouts/images/GORTL.GIF" Location="EditControlBlock" Sequence="120" Title="Send to Hello World" > <UrlAction Url="javascript:window.location='{SiteUrl}/_layouts/SampleFeaturePages/HelloWorld.aspx?ItemId={ItemId}&ListId={ListId}&Source=' + window.location" /> </CustomAction> </Elements>

The attributes on the "CustomAction" node have the following useage:

  • "RegistrationType" tells what type of Sharepoint list item to apply the features action to, in this case actual content.
  • "Location" tells us which Sharepoint object to tie the action to, in this case the edit control block menu of a list item.
  • "UrlAction" node has a url which will open a page and feed that page the basic data to find the item in the Sharepoint web it is located in.

The "CustomAction" xml node applies the feature to some sharepoint action. In this case it defines a menu item in the "EditControlBlock" which is an identifier for a Sharepoint object type indicating an editing menu for a document. It defines a "UrlAction" node which gives the element an action of going to a specified url. The url points back to the page we defined in the project (LAYOUTS/SampleFeaturePages/HellowWorld.aspx).

Here is how it will look once deployed:

A few other parts of the solution are interesting.

The "LAYOUTS/SampleFeaturePages/HellowWorld.aspx" is the page that is called from the features CustomAction/UrlAction and displays some basic information on the list item in question passed to this page (in this case the ItemId and ListId of the document as it is assigned in the Sharepoint site).

The FeatureReciever code contains events which programatically sets a top link bar item on the global navigation bar on top of the site, and applies a cascading style sheet when the feature is activated. Notice the FeatureDeactivated event completely uninstalls all the feature components that the FeatureActivating event installs. This kind of cleanup is advised, since old feature components left after activation can cause issues.

   
public override void FeatureActivated(SPFeatureReceiverProperties properties) { SPWeb site = SPContext.Current.Web; SPNavigationNodeCollection topNavigationNodes = site.Navigation.TopNavigationBar; //This gives us the collection of all top navigation node of the site. //Now we want to add one more navigation bar to it. SPNavigationNode objItem = null; objItem = new SPNavigationNode("MSN", "http://WWW.MSN.com", true); topNavigationNodes.AddAsLast(objItem); site.Update(); // add our custom theme site.ApplyTheme(""); site.AlternateCssUrl = "/_layouts/1033/STYLES/SampleFeature/Main.css"; site.Update(); } public override void FeatureDeactivating(SPFeatureReceiverProperties properties) { SPWeb site = SPContext.Current.Web; site.AlternateCssUrl = ""; site.Update(); SPNavigationNodeCollection topNavigationNodes = site.Navigation.TopNavigationBar; List<SPNavigationNode> nodesToDelete = new List<SPNavigationNode>(); //first get al the nodes to delete foreach (SPNavigationNode node in topNavigationNodes) { if (node.Title == "MSN") { nodesToDelete.Add(node); } } //then delete them since the colection does not have index finders by text foreach (SPNavigationNode node in nodesToDelete) { topNavigationNodes.Delete(node); } site.Update(); //SPWeb site2 = (SPWeb)properties.Feature.Parent; // delete folder of site pages provisioned during activation SPFolder sitePagesFolder = site.GetFolder("SitePages"); sitePagesFolder.Delete(); }

The final component of interest is the web part feature code. For this example the code in the wpSiteList.webpart files is a simple web part that gives a list of sites. The feature project solution build requires some feature code for deployment and I put this in the "FEATURES/SampleFeature/WebParts" directory using a basic web part with some sample code to accomplish this.

The main method in the web part is the CreateChildControls() method. In this method the visual components and datasource for the web part are initialized. Notice that this methods internal code is setup similiar to a .aspx page_load event combined with a designer class initialize event, so that controls are created and added to the web part, and then a datasource is queried if the page is not a post back from the original page post.

protected override void CreateChildControls() { base.CreateChildControls(); //Create text box listsView = new SPGridView(); listsView.AutoGenerateColumns = false; this.Controls.Add(listsView); BoundField colTitle = new BoundField(); colTitle.DataField = "Title"; colTitle.HeaderText = "Title"; listsView.Columns.Add(colTitle); if (!this.Page.IsPostBack) { GetSiteList(); listsView.DataKeyNames = new string[] { "Title" }; listsView.DataSource = dt; listsView.DataBind(); } } private void GetSiteList() { dt = new DataTable(); dt.Columns.Add("Title"); SPWeb mySite = SPContext.Current.Web; SPWebCollection sites = mySite.Webs; foreach (SPWeb subSite in sites) { DataRow dr = dt.NewRow(); dr["Title"] = subSite.Title; dt.Rows.Add(dr); } }

To build your project once you have completed development you must have installed the Visual Studio plugin for WSPBuilder (located here). To create your solution file you will need to right click on your project, select WSPBuilder and the menu item "Build WSP". This builds a Sharepoint solution file in the root directory of your project.

 

Once you have built your solution file you will need to run some sort of installation script to install it to Sharepoint. Included in the downloadable example is a handy one I created. To use it you only need to change the variables thus:

  • WSPNAME - this is the name of your solution file compiled using WSPBuilder.
  • SERVERSITEURL - this is a combination of your sharepoint server name, port, and site collection name (if applicable) where you would like the solution deployed.
  • FEATUREGUID - this comes from your feature.xml file, it is the feature id.
          
@echo off CLS SETLOCAL SET StartDateTime=%DATE% at %TIME% @SET STSADM="c:\program files\common files\microsoft shared\web server extensions\12\bin\stsadm" @SET WSPNAME="SampleFeatureProject.wsp" @SET SERVERSITEURL=[your server name: your port name]/sites/[Your Site Collection Name] @SET FEATUREGUID="8682CCBC-C49B-4957-ADAA-708E40C06121" ECHO -------------------------------------------- ECHO ^| %WSPNAME% deployment process ^| ECHO ^| Started on %StartDateTime% ^| ECHO -------------------------------------------- TITLE Deploying %WSPNAME% solution...

The FEATUREGUID varaiable is taken from the feature.xml id attribute:

<Feature Id="8682CCBC-C49B-4957-ADAA-708E40C06121" ... >

Once you have changed the variables in the script you can run this script and look in the Central Administration/Operations/Solution Management Page to see if errors occured or if the solution was deployed correctly.

 

If you ever want to uninstall the solution there is a script in the download for that as well, set the varaibles as you did in the install.cmd script:

@echo off CLS SETLOCAL SET StartDateTime=%DATE% at %TIME% @SET STSADM="c:\program files\common files\microsoft shared\web server extensions\12\bin\stsadm" @SET WSPNAME="SampleFeatureProject.wsp" @SET SERVERSITEURL=[your server name: your port name]/sites/[Your Site Collection Name] @SET FEATUREGUID="8682CCBC-C49B-4957-ADAA-708E40C06121" ....

Now lets see the feature in action. Once the solution is deployed, go to the "Site Collection Features" page on the site collection you deployed the site to and deactivate and the activate the feature.

A note here - to get this to work you have to go in after the install script has deployed and activated the feature and de-activate and re-activate the feature to get the "FeatureActivated" event to fire. Doesn't seem to work through activating the feature through the script.

 

Next go to any document library on the site and right-click one of the documents in the library.

Notice the "Send to Hello World" menu item. Click on this and the page HelloWorld.aspx is displayed with the path of the file: