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.

Saturday, November 15, 2008

Activating Features at Different Scopes Using STSADM

How many times have you had a Feature, either out-of-the-box or custom, that you have needed to activate at lots of different scopes or re-activate at lots of different scopes?  To do this you may have found a way to get the list of site collections or webs and then somehow used that list in conjunction with the STSADM activatefeature command or worse you manually went to every site or web and manually activated or re-activated the Feature - this is extremely tedious and error prone as you may miss a site or web.  Another common scenario has to do with "My Sites" - perhaps you've written a custom Feature that configures a users my site when created and now you've made a change to that Feature and need to reactivate the Feature on all existing My Sites.  Doing this in the past was a pain - but not any more thanks to my new command: gl-activatefeature.

The specific scenario that prompted me to write this command was the need to re-activate a custom Feature everywhere it was currently activated without activating it anywhere it wasn't already activated.  I needed to do this because I had added a couple of event receivers to an already deployed Feature but I didn't know where that Feature was already activated and didn't wish to activate it if not already activated.  So I had two three core issues to solve - the first was to enable the activation (and eventually deactivation) of a Feature at the various scopes (Farm, Web Application, Site, and Web); the second was to be able to conditionally force a re-activation only if the Feature is already activated and not do anything if not activated; the third was to be able to iterate over various scopes - Farm, Web Application, Site, or Web (so if the Feature is scoped to Site and the user passes in a scope of Web Application then I need to look at every Site Collection within the specified Web Application).  I also wanted to have the parameters of the command work just like the OOTB command (along with any additional parameters I'd need).

So the first thing I need to do was make sure that I could get the Feature ID which would be used throughout the code - but I wanted the user to be able to pass in the ID (the easy part), the name, or the filename - just like the OOTB command.  I took a look at how the OOTB command worked by using Reflector and found a simple method that I was able to refactor slightly:

internal static Guid GetFeatureIdFromParams(SPParamCollection Params)
{
    Guid empty = Guid.Empty;
    if (!Params["id"].UserTypedIn)
    {
        SPFeatureScope scope;
        if (!Params["filename"].UserTypedIn)
        {
            if (Params["name"].UserTypedIn)
            {
                SPFeatureScope scope2;
                SPFeatureDefinition.GetFeatureIdAndScope(Params["name"].Value + @"\feature.xml", out empty, out scope2);
            }
            return empty;
        }
        SPFeatureDefinition.GetFeatureIdAndScope(Params["filename"].Value, out empty, out scope);
        return empty;
    }
    return new Guid(Params["id"].Value);
}

Once I had the Feature ID I could now use this to conditionally add or remove (activate or deactivate) the Feature from the appropriate scope.  The way you activate a Feature programmatically is to simply call the Add or Remove methods of an SPFeatureCollection object.  You can get this object from either the SPFarm, SPWebApplication, SPSite, or SPWeb objects - each containing a "Features" property which exposes the collection object.

private SPFeature ActivateDeactivateFeature(SPFeatureCollection features, bool activate, Guid featureId, string urlScope, bool force, bool ignoreNonActive)
{
    if (features[featureId] == null && ignoreNonActive)
        return null;
 
    if (!activate)
    {
        if (features[featureId] != null || force)
        {
            Log("Progress: Deactivating Feature {0} from {1}.", featureId.ToString(), urlScope);
            try
            {
                features.Remove(featureId, force);
            }
            catch (Exception ex)
            {
                Log("WARNING: {0}", ex.Message);
            }
        }
        else
        {
            Log("WARNING: " + SPResource.GetString("FeatureNotActivatedAtScope", new object[] { featureId }) + "  Use the -force parameter to force a deactivation.");
        }
 
        return null;
    }
    if (features[featureId] == null)
        Log("Progress: Activating Feature {0} on {1}.", featureId.ToString(), urlScope);
    else
    {
        if (!force)
        {
            SPFeatureDefinition fd = features[featureId].Definition;
            Log("WARNING: " + SPResource.GetString("FeatureAlreadyActivated", new object[] { fd.DisplayName, fd.Id, urlScope }) + "  Use the -force parameter to force a reactivation.");
            return features[featureId];
        }
 
        Log("Progress: Re-Activating Feature {0} on {1}.", featureId.ToString(), urlScope);
    }
 
    return features.Add(featureId, force);
}

One of the first things I do in this method is check if the user has chosen to ignore situations when the Feature is not already active - I do that by checking the item indexer and seeing if it returns null: features[featureId] == null.  If null is returned then the Feature is not activated.

Once I had the two methods above I could then create all the support code which basically just determines which scopes to consider based on the Feature scope and the user provided scope.  I also use my cool SPEnumerator class that I created a while back to help make iterating nice and easy.  I wrapped all this code up into a single helper class that I could then use with both my gl-activatefeature and gl-deactivatefeature commands (I'll cover the gl-deactivatefeature command in the next post).

using System;
using System.IO;
using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
 
namespace Lapointe.SharePoint.STSADM.Commands.Features
{
    public enum ActivationScope
    {
        Farm, WebApplication, Site, Web, Feature
    }
    public class FeatureHelper
    {
        private string m_Url;
        private bool m_Force;
        private Guid m_FeatureId = Guid.Empty;
        private bool m_IgnoreNonActive;
        private bool m_Activate;
 
        /// <summary>
        /// Logs the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="args">The args.</param>
        protected virtual void Log(string message, params string[] args)
        {
            SPOperation.Log(message, args);
        }
 
        /// <summary>
        /// Gets the feature id from params.
        /// </summary>
        /// <param name="Params">The params.</param>
        /// <returns></returns>
        internal static Guid GetFeatureIdFromParams(SPParamCollection Params)
        {
            Guid empty = Guid.Empty;
            if (!Params["id"].UserTypedIn)
            {
                SPFeatureScope scope;
                if (!Params["filename"].UserTypedIn)
                {
                    if (Params["name"].UserTypedIn)
                    {
                        SPFeatureScope scope2;
                        SPFeatureDefinition.GetFeatureIdAndScope(Params["name"].Value + @"\feature.xml", out empty, out scope2);
                    }
                    return empty;
                }
                SPFeatureDefinition.GetFeatureIdAndScope(Params["filename"].Value, out empty, out scope);
                return empty;
            }
            return new Guid(Params["id"].Value);
        }
 
        /// <summary>
        /// Activates or deactivates the feature at the specified scope.
        /// </summary>
        /// <param name="scope">The scope.</param>
        /// <param name="featureId">The feature id.</param>
        /// <param name="activate">if set to <c>true</c> [activate].</param>
        /// <param name="url">The URL.</param>
        /// <param name="force">if set to <c>true</c> [force].</param>
        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
        public void ActivateDeactivateFeatureAtScope(ActivationScope scope, Guid featureId, bool activate, string url, bool force, bool ignoreNonActive)
        {
            SPOperation.Verbose = true;
 
            m_FeatureId = featureId;
            m_Url = url;
            m_Force = force;
            m_IgnoreNonActive = ignoreNonActive;
            m_Activate = activate;
 
            if (m_FeatureId.Equals(Guid.Empty))
                throw new SPException("Unable to locate Feature.");
 
            SPFeatureDefinition feature = SPFarm.Local.FeatureDefinitions[m_FeatureId];
            if (feature == null)
                throw new SPException("Unable to locate Feature.");
 
            if (scope == ActivationScope.Feature)
                scope = (ActivationScope)Enum.Parse(typeof(ActivationScope), feature.Scope.ToString().ToLowerInvariant(), true);
 
            if (feature.Scope == SPFeatureScope.Farm)
            {
                if (scope != ActivationScope.Farm)
                    throw new SPSyntaxException("The Feature specified is scoped to the Farm.  The -scope parameter must be \"Farm\".");
                ActivateDeactivateFeatureAtFarm(activate, m_FeatureId, m_Force, m_IgnoreNonActive);
            }
            else if (feature.Scope == SPFeatureScope.WebApplication)
            {
                if (scope != ActivationScope.Farm && scope != ActivationScope.WebApplication)
                    throw new SPSyntaxException("The Feature specified is scoped to the Web Application.  The -scope parameter must be either \"Farm\" or \"WebApplication\".");
 
                if (scope == ActivationScope.Farm)
                {
                    SPEnumerator enumerator = new SPEnumerator(SPFarm.Local);
                    enumerator.SPWebApplicationEnumerated += enumerator_SPWebApplicationEnumerated;
                    enumerator.Enumerate();
                }
                else
                {
                    if (string.IsNullOrEmpty(m_Url))
                        throw new SPSyntaxException("The -url parameter is required if the scope is \"WebApplication\".");
                    SPWebApplication webApp = SPWebApplication.Lookup(new Uri(m_Url));
                    ActivateDeactivateFeatureAtWebApplication(webApp, m_FeatureId, activate, m_Force, m_IgnoreNonActive);
                }
            }
            else if (feature.Scope == SPFeatureScope.Site)
            {
                if (scope == ActivationScope.Web)
                    throw new SPSyntaxException("The Feature specified is scoped to Site.  The -scope parameter cannot be \"Web\".");
 
                SPSite site = null;
                SPEnumerator enumerator = null;
                try
                {
                    if (scope == ActivationScope.Farm)
                        enumerator = new SPEnumerator(SPFarm.Local);
                    else if (scope == ActivationScope.WebApplication)
                    {
                        SPWebApplication webApp = SPWebApplication.Lookup(new Uri(m_Url));
                        enumerator = new SPEnumerator(webApp);
                    }
                    else if (scope == ActivationScope.Site)
                    {
                        site = new SPSite(m_Url);
                        ActivateDeactivateFeatureAtSite(site, activate, m_FeatureId, m_Force, m_IgnoreNonActive);
                    }
                    if (enumerator != null)
                    {
                        enumerator.SPSiteEnumerated += enumerator_SPSiteEnumerated;
                        enumerator.Enumerate();
                    }
                }
                finally
                {
                    if (site != null)
                        site.Dispose();
                }
            }
            else if (feature.Scope == SPFeatureScope.Web)
            {
                SPSite site = null;
                SPWeb web = null;
                SPEnumerator enumerator = null;
                try
                {
                    if (scope == ActivationScope.Farm)
                        enumerator = new SPEnumerator(SPFarm.Local);
                    else if (scope == ActivationScope.WebApplication)
                    {
                        SPWebApplication webApp = SPWebApplication.Lookup(new Uri(m_Url));
                        enumerator = new SPEnumerator(webApp);
                    }
                    else if (scope == ActivationScope.Site)
                    {
                        site = new SPSite(m_Url);
                        enumerator = new SPEnumerator(site);
                    }
                    else if (scope == ActivationScope.Web)
                    {
                        site = new SPSite(m_Url);
                        web = site.AllWebs[Utilities.GetServerRelUrlFromFullUrl(m_Url)];
                        ActivateDeactivateFeatureAtWeb(site, web, activate, m_FeatureId, m_Force, m_IgnoreNonActive);
                    }
                    if (enumerator != null)
                    {
                        enumerator.SPWebEnumerated += enumerator_SPWebEnumerated;
                        enumerator.Enumerate();
                    }
                }
                finally
                {
                    if (web != null)
                        web.Dispose();
                    if (site != null)
                        site.Dispose();
                }
            }
        }
 
        #region Event Handlers
 
        /// <summary>
        /// Handles the SPWebEnumerated event of the enumerator control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Lapointe.SharePoint.STSADM.Commands.OperationHelpers.SPEnumerator.SPWebEventArgs"/> instance containing the event data.</param>
        private void enumerator_SPWebEnumerated(object sender, SPEnumerator.SPWebEventArgs e)
        {
            ActivateDeactivateFeatureAtWeb(e.Site, e.Web, m_Activate, m_FeatureId, m_Force, m_IgnoreNonActive);
        }
 
        /// <summary>
        /// Handles the SPSiteEnumerated event of the enumerator control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Lapointe.SharePoint.STSADM.Commands.OperationHelpers.SPEnumerator.SPSiteEventArgs"/> instance containing the event data.</param>
        private void enumerator_SPSiteEnumerated(object sender, SPEnumerator.SPSiteEventArgs e)
        {
            ActivateDeactivateFeatureAtSite(e.Site, m_Activate, m_FeatureId, m_Force, m_IgnoreNonActive);
        }
 
        /// <summary>
        /// Handles the SPWebApplicationEnumerated event of the enumerator control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Lapointe.SharePoint.STSADM.Commands.OperationHelpers.SPEnumerator.SPWebApplicationEventArgs"/> instance containing the event data.</param>
        private void enumerator_SPWebApplicationEnumerated(object sender, SPEnumerator.SPWebApplicationEventArgs e)
        {
            ActivateDeactivateFeatureAtWebApplication(e.WebApplication, m_FeatureId, m_Activate, m_Force, m_IgnoreNonActive);
        }
 
        #endregion
 
        /// <summary>
        /// Activates or deactivates the feature.
        /// </summary>
        /// <param name="features">The features.</param>
        /// <param name="activate">if set to <c>true</c> [activate].</param>
        /// <param name="featureId">The feature id.</param>
        /// <param name="urlScope">The URL scope.</param>
        /// <param name="force">if set to <c>true</c> [force].</param>
        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
        /// <returns></returns>
        private SPFeature ActivateDeactivateFeature(SPFeatureCollection features, bool activate, Guid featureId, string urlScope, bool force, bool ignoreNonActive)
        {
            if (features[featureId] == null && ignoreNonActive)
                return null;
 
            if (!activate)
            {
                if (features[featureId] != null || force)
                {
                    Log("Progress: Deactivating Feature {0} from {1}.", featureId.ToString(), urlScope);
                    try
                    {
                        features.Remove(featureId, force);
                    }
                    catch (Exception ex)
                    {
                        Log("WARNING: {0}", ex.Message);
                    }
                }
                else
                {
                    Log("WARNING: " + SPResource.GetString("FeatureNotActivatedAtScope", new object[] { featureId }) + "  Use the -force parameter to force a deactivation.");
                }
 
                return null;
            }
            if (features[featureId] == null)
                Log("Progress: Activating Feature {0} on {1}.", featureId.ToString(), urlScope);
            else
            {
                if (!force)
                {
                    SPFeatureDefinition fd = features[featureId].Definition;
                    Log("WARNING: " + SPResource.GetString("FeatureAlreadyActivated", new object[] { fd.DisplayName, fd.Id, urlScope }) + "  Use the -force parameter to force a reactivation.");
                    return features[featureId];
                }
 
                Log("Progress: Re-Activating Feature {0} on {1}.", featureId.ToString(), urlScope);
            }
 
            return features.Add(featureId, force);
        }
        /// <summary>
        /// Activates or deactivates the farm scoped feature.
        /// </summary>
        /// <param name="activate">if set to <c>true</c> [activate].</param>
        /// <param name="featureId">The feature id.</param>
        /// <param name="force">if set to <c>true</c> [force].</param>
        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
        /// <returns></returns>
        public SPFeature ActivateDeactivateFeatureAtFarm(bool activate, Guid featureId, bool force, bool ignoreNonActive)
        {
            SPWebService service = SPFarm.Local.Services.GetValue<SPWebService>(string.Empty);
            return ActivateDeactivateFeature(service.Features, activate, featureId, "Farm", force, ignoreNonActive);
        }
 
        /// <summary>
        /// Activates or deactivates the web application scoped feature.
        /// </summary>
        /// <param name="activate">if set to <c>true</c> [activate].</param>
        /// <param name="featureId">The feature id.</param>
        /// <param name="urlScope">The URL scope.</param>
        /// <param name="force">if set to <c>true</c> [force].</param>
        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
        /// <returns></returns>
        public SPFeature ActivateDeactivateFeatureAtWebApplication(bool activate, Guid featureId, string urlScope, bool force, bool ignoreNonActive)
        {
            SPWebApplication application = SPWebApplication.Lookup(new Uri(urlScope));
            if (application == null)
            {
                throw new FileNotFoundException(SPResource.GetString("WebApplicationLookupFailed", new object[] { urlScope }));
            }
            return ActivateDeactivateFeatureAtWebApplication(application, featureId, activate, force, ignoreNonActive);
        }
 
        /// <summary>
        /// Activates or deactivates the web application scoped feature.
        /// </summary>
        /// <param name="application">The application.</param>
        /// <param name="featureId">The feature id.</param>
        /// <param name="activate">if set to <c>true</c> [activate].</param>
        /// <param name="force">if set to <c>true</c> [force].</param>
        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
        /// <returns></returns>
        public SPFeature ActivateDeactivateFeatureAtWebApplication(SPWebApplication application, Guid featureId, bool activate, bool force, bool ignoreNonActive)
        {
            return ActivateDeactivateFeature(application.Features, activate, featureId, application.GetResponseUri(SPUrlZone.Default).ToString(), force, ignoreNonActive);
        }
 
        /// <summary>
        /// Activates or deactivates the site scoped feature.
        /// </summary>
        /// <param name="activate">if set to <c>true</c> [activate].</param>
        /// <param name="featureId">The feature id.</param>
        /// <param name="urlScope">The URL scope.</param>
        /// <param name="force">if set to <c>true</c> [force].</param>
        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
        /// <returns></returns>
        public SPFeature ActivateDeactivateFeatureAtSite(bool activate, Guid featureId, string urlScope, bool force, bool ignoreNonActive)
        {
            using (SPSite site = new SPSite(urlScope))
            using (SPWeb web = site.OpenWeb(Utilities.GetServerRelUrlFromFullUrl(urlScope), true))
            {
                if (web.IsRootWeb)
                {
                    return ActivateDeactivateFeatureAtSite(site, activate, featureId, force, ignoreNonActive);
                }
                throw new SPException(SPResource.GetString("FeatureActivateDeactivateScopeAmbiguous", new object[] { site.Url }));
            }
        }
 
        /// <summary>
        /// Activates or deactivates the site scoped feature.
        /// </summary>
        /// <param name="site">The site.</param>
        /// <param name="activate">if set to <c>true</c> [activate].</param>
        /// <param name="featureId">The feature id.</param>
        /// <param name="force">if set to <c>true</c> [force].</param>
        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
        /// <returns></returns>
        public SPFeature ActivateDeactivateFeatureAtSite(SPSite site, bool activate, Guid featureId, bool force, bool ignoreNonActive)
        {
            return ActivateDeactivateFeature(site.Features, activate, featureId, site.Url, force, ignoreNonActive);
        }
 
        /// <summary>
        /// Activates or deactivates the web scoped feature.
        /// </summary>
        /// <param name="activate">if set to <c>true</c> [activate].</param>
        /// <param name="featureId">The feature id.</param>
        /// <param name="urlScope">The URL scope.</param>
        /// <param name="force">if set to <c>true</c> [force].</param>
        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
        /// <returns></returns>
        public SPFeature ActivateDeactivateFeatureAtWeb(bool activate, Guid featureId, string urlScope, bool force, bool ignoreNonActive)
        {
            using (SPSite site = new SPSite(urlScope))
            using (SPWeb web = site.OpenWeb())
            {
                return ActivateDeactivateFeatureAtWeb(site, web, activate, featureId, force, ignoreNonActive);
            }
        }
 
        /// <summary>
        /// Activates or deactivates the web scoped feature.
        /// </summary>
        /// <param name="site">The site.</param>
        /// <param name="web">The web.</param>
        /// <param name="activate">if set to <c>true</c> [activate].</param>
        /// <param name="featureId">The feature id.</param>
        /// <param name="force">if set to <c>true</c> [force].</param>
        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
        /// <returns></returns>
        public SPFeature ActivateDeactivateFeatureAtWeb(SPSite site, SPWeb web, bool activate, Guid featureId, bool force, bool ignoreNonActive)
        {
            return ActivateDeactivateFeature(web.Features, activate, featureId, web.Url, force, ignoreNonActive);
        }
 
    }
}

The help for the command is shown below:

C:\>stsadm -help gl-activatefeature

stsadm -o gl-activatefeature


Activates a feature at a given scope.

Parameters:
        {-filename <relative path to Feature.xml> |
         -name <feature folder> |
         -id <feature Id>}
        [-scope <farm | webapplication | site | web | feature> (defaults to Feature)]
        [-url <url>]
        [-force]
        [-ignorenonactive]

The following table summarizes the command and its various parameters:

Command Name Availability Build Date
gl-activatefeature WSS v3, MOSS 2007 Released: 11/15/2008

Parameter Name Short Form Required Description Example Usage
filename f Yes, if -name or -id is not provided

Path to feature must be a relative path to the 12\Template\Features directory. Can be any standard character that the Windows system supports for a file name.

Note:  If the feature file is not found on disk, the following error message is displayed: “Failed to find the XML file at location '12\Template\Features\<file path>'.”

-filename "MyFeature\feature.xml"

-f "MyFeature\feature.xml"
name n Yes, if -filename or -id is not provided Name of the feature folder located in the 12\Template\Features directory -name "MyFeature"

-n "MyFeature"
id   Yes, if -filename or -name is not provided

GUID that identifies the feature to activate

Note:  If the ID is specified but the feature does not exist, the following error message is returned: "Feature '<id>' is not installed in this farm, and cannot be added to this scope." 

-id "21d186e1-7036-4092-a825-0eb6709e9280"
scope s No The scope to look at when activating the Feature.  Valid values are "Farm", "WebApplication", "Site", "Web", or "Feature".  If "Feature" is specified then the scope of the Feature will be used.

Note: Be careful when using a scope of "Web" (or "Feature" when the Feature is scoped to Web) as this will work recursively upon not just the single web but all sub-webs as well.
-scope site

-s site
url   No

URL of the Web application, site collection, or Web site to which the feature is being activated

with respect to the provided scope.  So if the Feature is scoped to Web and you pass in a scope of Site then all webs within the Site Collection of the provided URL will have the Feature activated.  If the scope is Farm then an URL is not required.
-url http://portal
force   No

Forces the re-activation of the feature if already activated. This causes any custom code associated with the feature to rerun.

-force
ignorenonactive ignore No This will prevent the Feature from being activated if it is not already activated thus triggering a reactivation where already activated. -ignorenonactive

-ignore

The following is an example of how to activate a Site Collection scoped Feature on every site collection within a web application:

stsadm -o gl-activatefeature -name MyCustomFeature -scope webapplication -url http://mysites -force

The following is an example of how to re-activate a Web scoped Feature on every web within a web application where the Feature is already activated:

stsadm -o gl-activatefeature -name MyCustomFeature -scope webapplication -url http://portal -ignorenonactive

10 comments:

Mike Caines said...

Great command Gary.

Question: Will forcing a reactivate of an already activated feature cause Feature Receiver code to re-run in this case?

I too needed to add a few event handlers to my elements manifest for a feature that already was deployed and activated at the site collection level. But I'd prefer if the Feature Receiver code not run when forcing a reactivation.

Input appreciated. Thanks!
Mike

Gary Lapointe said...

It will run again. I recommend adding some code to your feature receiver so that you can effectively version it - usually I'll store a version number in the properties collection of the SPWeb object and then check that version number to see what version of the feature is active (for already deployed features you'll probably want to check for a known artifact or something).

Amores said...

(This is about my tenth try -- it keeps silently swallowing my posts -- but I'm persistent, because I really enjoy reading your posts, Gary.)

===============

Have you any solution for the problem that SPFeatureCollection.Add doesn't indicate error if it fails -- and it still returns a filled out SPFeature object, which it did add to the in-memory collection, even though it actually failed?

The easiest way to reproduce this bug(?), is to run code doing SPFeatureCollection.Add with an account which is not a site collection admin of the relevant site collection (activating a site collection level feature).

Also, if the feature receiver activate or deactivate throws an exception, SPFeatureCollection.Add or SPFeatureCollection.Remove will silently ignore it and succeed, which is in stark contrast to the GUI behavior in this case (the GUI will fail the activation or deactivation).

Gary Lapointe said...

Amores - I've not actually seen that before but most of what I do is with a privileged account. Off hand I'd recommend just requerying the collection and checking if it's there - beyond that I'd have to look deeper into it.

Anonymous said...

I had originally tried creating a new SPSite object locally from the URL of the original, but its Features collection showed the new feature (not truly added).

But your questiona bout this prompted me to sanity check and try again.

I just went back to double-check, and created a new SPSite object in a new method, from the ID of the original, and its feature collection is accurate -- I can successfully detect which activations failed.

So I'm now using that technique to determine when Features.Add is silently failing.

Thanks for the feedback.

(Trying over and over to post this; have loosened browser security considerably trying to get to where I can post again here. I think the captcha popup is very problematic.)

Anonymous said...

Hello,
I was wondering if there was a way that when using the scope "web" to avoid the activation of the feature at the root web level. Sometimes that could be useful.
Thanks in advance!
C

Simha said...

Gary, I tried you command to activate feature on all site collections in a webapp. Although it did not trigger the feature activated event in feature receiver. Were you able to run code in featureactivated?

Gary Lapointe said...

Make sure the dll for the feature is in the GAC otherwise it won't work from the command-line.

Simha said...

I have deployed the dll (webpart) in bin with necessary CAS . Can I get your command to work in this scenario.
Thanks.

Gary Lapointe said...

No. It's not a limitation of my command - it's a limitation of bin deployed solutions - they aren't accessible to the command line because there's no web context to find the dll. Temporarily add the dll to the GAC, run the command, then remove from the GAC.