MOSS MVP

I've moved my blog to http://blog.falchionconsulting.com!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Tuesday, August 21, 2007

Enumerate Features

One of the tasks I needed to do for my upgrade was to convert a sub site to a site collection. This is something I'm still trying to get right as of this writing but figured I'd share a new command I had to create to help with that. To convert a site to a site collection at a managed path you need to create the managed path, create a site with no site template, export your existing site, and then import to the new location all using STSADM. Before you can do the import though you have to make sure that certain required features are enabled.

I found that STSADM had the ability to activate and deactivate features but there was no way for me to tell which features were currently set (without using the web tool). I wanted/needed an easy way to identify existing features associated with a site collection as well as what the actual name is (if you've ever tried to match the descriptive name to the feature name it's not always obvious). So my solution was to create a simple command which would list out all features along with their actual name (not the user friendly name).

I snagged most of the code from the Microsoft.SharePoint.WebControls.FeatureActivator server control in the Microsoft.SharePoint.ApplicationPages assembly. This is the main control that SharePoint uses when you wish to activate or deactivate features on a site (or farm or web application). The long and short of it is that I'm building a DataTable of features by looping through a SPFeatureDefinitionCollection which I get by calling SPFarm.Local.FeatureDefinitions. Beyond that I'm merely filtering based on passed in parameters. In order to show which features are active at the given scope I check for existence in the active features collection which I get by calling either SPSite's Features property, SPWeb's Features property or SPSite's WebApplication.Features property. I then either return it as XML or a flat list based on a passed in parameter. The core code is shown below:

   1: private static void AddFeaturesToTable(ref DataTable dtblFeatureList, string url, SPFeatureScope scope, bool showHidden)
   2: {
   3:     CultureInfo info;
   4:     Dictionary activeFeatures = new Dictionary();
   5:  
   6:     // We need to get the SPSite and SPWeb so that we can get the culture info and any active features if a scope was passed in.
   7:     using (SPSite site = new SPSite(url))
   8:     {
   9:         using (SPWeb web = site.OpenWeb())
  10:         {
  11:             info = new CultureInfo((int)web.Language, false);
  12:  
  13:             activeFeatures[SPFeatureScope.Farm] = SPWebService.ContentService.Features;
  14:             activeFeatures[SPFeatureScope.WebApplication] = site.WebApplication.Features;
  15:             activeFeatures[SPFeatureScope.Site] = site.Features;
  16:             activeFeatures[SPFeatureScope.Web] = web.Features;
  17:         }
  18:     }
  19:  
  20:     foreach (SPFeatureDefinition definition in SPFarm.Local.FeatureDefinitions)
  21:     {
  22:         try
  23:         {
  24:             Guid featureID = definition.Id;
  25:             // If the scope is marked as invalid then that's our flag that it wasn't provided so we're going to 
  26:             // list everything regardless of scope and show those that are active for the Web scope.
  27:             if (definition.Scope != scope && scope != SPFeatureScope.ScopeInvalid)
  28:                 continue;
  29:  
  30:             if (definition.Hidden && !showHidden)
  31:             {
  32:                 continue;
  33:             }
  34:             if (!definition.SupportsLanguage(info))
  35:             {
  36:                 continue;
  37:             }
  38:  
  39:             bool isActive = false;
  40:             if (activeFeatures[definition.Scope] != null)
  41:                 isActive = (activeFeatures[definition.Scope][featureID] != null);
  42:  
  43:             DataRow row = BuildDataRowFromFeatureDefinition(dtblFeatureList, info, definition, isActive);
  44:             dtblFeatureList.Rows.Add(row);
  45:         }
  46:         catch (SPException)
  47:         {
  48:             continue;
  49:         }
  50:  
  51:     }
  52: }

The syntax of the command can be seen below:

C:\>stsadm -help gl-enumfeatures

stsadm -o gl-enumfeatures

Returns the list of features and their activation status.

Parameters:
        -url <site collection url>
        [-scope <Farm | Site | Web | WebApplication>]
        [-showhidden]
        [-xml]

Here’s an example of how to return the features for a publishing site site collection at the managed path "/hr":

stsadm –o gl-enumfeatures –url "http://intranet/hr/Pages/" -scope Site -xml

The results of running the above command can be seen below (note that the node names are somewhat confusing as the node DisplayName implies that this is what you would see in the web browser - in reality it's the Title node that shows in the browser - I thought about changing the name to something else but in the end I decided to be consistent with the property names on the objects):

   1: <Features>
   2:   <Feature>
   3:     <FeatureId>02464c6a-9d07-4f30-ba04-e9035cf54392</FeatureId>
   4:     <Title>Routing Workflows</Title>
   5:     <DisplayName>ReviewWorkflows</DisplayName>
   6:     <Description>Workflows that send a document for feedback or approval.</Description>
   7:     <Status>Active</Status>
   8:   </Feature>
   9:   <Feature>
  10:     <FeatureId>6c09612b-46af-4b2f-8dfc-59185c962a29</FeatureId>
  11:     <Title>Collect Signatures Workflow</Title>
  12:     <DisplayName>SignaturesWorkflow</DisplayName>
  13:     <Description>Gathers signatures needed to complete a Microsoft Office document.</Description>
  14:     <Status>Active</Status>
  15:   </Feature>
  16:   <Feature>
  17:     <FeatureId>7094bd89-2cfe-490a-8c7e-fbace37b4a34</FeatureId>
  18:     <Title>Reporting</Title>
  19:     <DisplayName>Reporting</DisplayName>
  20:     <Description>Creates reports about information in Windows SharePoint Services.</Description>
  21:     <Status>Active</Status>
  22:   </Feature>
  23:   <Feature>
  24:     <FeatureId>8581a8a7-cf16-4770-ac54-260265ddb0b2</FeatureId>
  25:     <Title>Office SharePoint Server Enterprise Site Collection features</Title>
  26:     <DisplayName>PremiumSite</DisplayName>
  27:     <Description>Features such as the business data catalog, forms services, and Excel Services, included in the Office SharePoint Server Enterprise License</Description>
  28:     <Status>Active</Status>
  29:   </Feature>
  30:   <Feature>
  31:     <FeatureId>b21b090c-c796-4b0f-ac0f-7ef1659c20ae</FeatureId>
  32:     <Title>Office SharePoint Server Standard Site Collection features</Title>
  33:     <DisplayName>BaseSite</DisplayName>
  34:     <Description>Features such as user profiles and search, included in the Office SharePoint Server Standard License</Description>
  35:     <Status>Active</Status>
  36:   </Feature>
  37:   <Feature>
  38:     <FeatureId>c6561405-ea03-40a9-a57f-f25472942a22</FeatureId>
  39:     <Title>Translation Management Workflow</Title>
  40:     <DisplayName>TranslationWorkflow</DisplayName>
  41:     <Description>Manages document translation by creating copies of the document to be translated and assigning translation tasks to translators.</Description>
  42:     <Status>Active</Status>
  43:   </Feature>
  44:   <Feature>
  45:     <FeatureId>c85e5759-f323-4efb-b548-443d2216efb5</FeatureId>
  46:     <Title>Disposition Approval Workflow</Title>
  47:     <DisplayName>ExpirationWorkflow</DisplayName>
  48:     <Description>Manages document expiration and retention by allowing participants to decide whether to retain or delete expired documents.</Description>
  49:     <Status>Active</Status>
  50:   </Feature>
  51:   <Feature>
  52:     <FeatureId>eaf6a128-0482-4f71-9a2f-b1c650680e77</FeatureId>
  53:     <Title>Office SharePoint Server Search Web Parts</Title>
  54:     <DisplayName>SearchWebParts</DisplayName>
  55:     <Description>This feature uploads all web parts required for Search Center</Description>
  56:     <Status>Inactive</Status>
  57:   </Feature>
  58:   <Feature>
  59:     <FeatureId>f6924d36-2fa8-4f0b-b16d-06b7250180fa</FeatureId>
  60:     <Title>Office SharePoint Server Publishing Infrastructure</Title>
  61:     <DisplayName>PublishingSite</DisplayName>
  62:     <Description>Provides centralized libraries, content types, master pages and page layouts and enables page scheduling and other publishing functionality for a site collection.</Description>
  63:     <Status>Active</Status>
  64:   </Feature>
  65:   <Feature>
  66:     <FeatureId>fde5d850-671e-4143-950a-87b473922dc7</FeatureId>
  67:     <Title>Three-state workflow</Title>
  68:     <DisplayName>IssueTrackingWorkflow</DisplayName>
  69:     <Description>Use this workflow to track items in a list.</Description>
  70:     <Status>Inactive</Status>
  71:   </Feature>
  72: </Features>

9 comments:

Gary Lapointe said...

I found this after I'd created my command: http://msdn2.microsoft.com/en-us/library/bb417382.aspx.

I like mine better though - has more options.

Nick said...

Gary

Do you have any commands for copying active features from one site collection and applying it to another site collection?

Gary Lapointe said...

Nick - I'm not sure I'm following what you are looking for - are just looking for something that will activate on one site collection all the same features that are activated on another? If that's the case then you could use the repairsitecollectionimportedfromsubsite command (it obviously does a bit more than just activate features though - you could always download the code and pull the AddMissingFeatures method into your own command).

Anonymous said...

hi I just facing difficulties, I have a site collection level feature when I activate it does goes and activates another site level feature in code with simply adding feature to it features collection. though features activated(added) the Activate button's caption remain ('Activate' ie. indicating its not active) how i can solve this, do you have any idea, I have been digging the same FeatureActivator class. And could not found a way of building this table within my featureReceiver class. thanks.

Gary Lapointe said...

Check your ULS logs and make sure that no exceptions are being thrown during the activation.

fromonesource said...

Gary, how could I get a list of all sites that a particular feature is activated on?

Gary Lapointe said...

You could do it using powershell - download my cmdlets and run the following:

function Get-SPFeatureActivations([string]$url, [string]$name)
{
$sites = Get-SPSite -Url $url
foreach ($site in $sites) {
$feature = $site.Features | where -FilterScript {$_.Definition.DisplayName -eq $name}
if ($feature -ne $null) {
Write-Host $site.Url
}
else
{
foreach ($web in $site.AllWebs) {
$feature = $web.Features | where -FilterScript {$_.Definition.DisplayName -eq $name}
if ($feature -ne $null) {
Write-Host $site.Url
}
}
}
}
}

Get-SPFeatureActivations "http://portal/*" "PublishingWeb"

Anonymous said...

Hi Gary, I have been dwellving in your great work the whole day. I am curretly in the testing phase of using your wonderfull extenstions for our deployment. I only came across this one issue outside of your tools so far.(currently trying to use gl-activatefeature) What is the folder name or guid for 'Publishing Infrastructure feature'. This needs to be activated for all my site collections(100+). Help is appreciated.

Gary Lapointe said...

I believe PremiumSite is what you want.