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:

 1internal static Guid GetFeatureIdFromParams(SPParamCollection Params)
 2{
 3    Guid empty = Guid.Empty;
 4    if (!Params["id"].UserTypedIn)
 5    {
 6        SPFeatureScope scope;
 7        if (!Params["filename"].UserTypedIn)
 8        {
 9            if (Params["name"].UserTypedIn)
10            {
11                SPFeatureScope scope2;
12                SPFeatureDefinition.GetFeatureIdAndScope(Params["name"].Value + @"\feature.xml", out empty, out scope2);
13            }
14            return empty;
15        }
16        SPFeatureDefinition.GetFeatureIdAndScope(Params["filename"].Value, out empty, out scope);
17        return empty;
18    }
19    return new Guid(Params["id"].Value);
20}

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.

 1private SPFeature ActivateDeactivateFeature(SPFeatureCollection features, bool activate, Guid featureId, string urlScope, bool force, bool ignoreNonActive)
 2{
 3    if (features[featureId] == null && ignoreNonActive)
 4        return null;
 5 
 6    if (!activate)
 7    {
 8        if (features[featureId] != null || force)
 9        {
10            Log("Progress: Deactivating Feature {0} from {1}.", featureId.ToString(), urlScope);
11            try
12            {
13                features.Remove(featureId, force);
14            }
15            catch (Exception ex)
16            {
17                Log("WARNING: {0}", ex.Message);
18            }
19        }
20        else
21        {
22            Log("WARNING: " + SPResource.GetString("FeatureNotActivatedAtScope", new object[] { featureId }) + "  Use the -force parameter to force a deactivation.");
23        }
24 
25        return null;
26    }
27    if (features[featureId] == null)
28        Log("Progress: Activating Feature {0} on {1}.", featureId.ToString(), urlScope);
29    else
30    {
31        if (!force)
32        {
33            SPFeatureDefinition fd = features[featureId].Definition;
34            Log("WARNING: " + SPResource.GetString("FeatureAlreadyActivated", new object[] { fd.DisplayName, fd.Id, urlScope }) + "  Use the -force parameter to force a reactivation.");
35            return features[featureId];
36        }
37 
38        Log("Progress: Re-Activating Feature {0} on {1}.", featureId.ToString(), urlScope);
39    }
40 
41    return features.Add(featureId, force);
42}

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).

  1using System;
  2using System.IO;
  3using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
  4using Microsoft.SharePoint;
  5using Microsoft.SharePoint.Administration;
  6 
  7namespace Lapointe.SharePoint.STSADM.Commands.Features
  8{
  9    public enum ActivationScope
 10    {
 11        Farm, WebApplication, Site, Web, Feature
 12    }
 13    public class FeatureHelper
 14    {
 15        private string m_Url;
 16        private bool m_Force;
 17        private Guid m_FeatureId = Guid.Empty;
 18        private bool m_IgnoreNonActive;
 19        private bool m_Activate;
 20 
 21        /// <summary>
 22        /// Logs the specified message.
 23        /// </summary>
 24        /// <param name="message">The message.</param>
 25        /// <param name="args">The args.</param>
 26        protected virtual void Log(string message, params string[] args)
 27        {
 28            SPOperation.Log(message, args);
 29        }
 30 
 31        /// <summary>
 32        /// Gets the feature id from params.
 33        /// </summary>
 34        /// <param name="Params">The params.</param>
 35        /// <returns></returns>
 36        internal static Guid GetFeatureIdFromParams(SPParamCollection Params)
 37        {
 38            Guid empty = Guid.Empty;
 39            if (!Params["id"].UserTypedIn)
 40            {
 41                SPFeatureScope scope;
 42                if (!Params["filename"].UserTypedIn)
 43                {
 44                    if (Params["name"].UserTypedIn)
 45                    {
 46                        SPFeatureScope scope2;
 47                        SPFeatureDefinition.GetFeatureIdAndScope(Params["name"].Value + @"\feature.xml", out empty, out scope2);
 48                    }
 49                    return empty;
 50                }
 51                SPFeatureDefinition.GetFeatureIdAndScope(Params["filename"].Value, out empty, out scope);
 52                return empty;
 53            }
 54            return new Guid(Params["id"].Value);
 55        }
 56 
 57        /// <summary>
 58        /// Activates or deactivates the feature at the specified scope.
 59        /// </summary>
 60        /// <param name="scope">The scope.</param>
 61        /// <param name="featureId">The feature id.</param>
 62        /// <param name="activate">if set to <c>true</c> [activate].</param>
 63        /// <param name="url">The URL.</param>
 64        /// <param name="force">if set to <c>true</c> [force].</param>
 65        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
 66        public void ActivateDeactivateFeatureAtScope(ActivationScope scope, Guid featureId, bool activate, string url, bool force, bool ignoreNonActive)
 67        {
 68            SPOperation.Verbose = true;
 69 
 70            m_FeatureId = featureId;
 71            m_Url = url;
 72            m_Force = force;
 73            m_IgnoreNonActive = ignoreNonActive;
 74            m_Activate = activate;
 75 
 76            if (m_FeatureId.Equals(Guid.Empty))
 77                throw new SPException("Unable to locate Feature.");
 78 
 79            SPFeatureDefinition feature = SPFarm.Local.FeatureDefinitions[m_FeatureId];
 80            if (feature == null)
 81                throw new SPException("Unable to locate Feature.");
 82 
 83            if (scope == ActivationScope.Feature)
 84                scope = (ActivationScope)Enum.Parse(typeof(ActivationScope), feature.Scope.ToString().ToLowerInvariant(), true);
 85 
 86            if (feature.Scope == SPFeatureScope.Farm)
 87            {
 88                if (scope != ActivationScope.Farm)
 89                    throw new SPSyntaxException("The Feature specified is scoped to the Farm.  The -scope parameter must be \"Farm\".");
 90                ActivateDeactivateFeatureAtFarm(activate, m_FeatureId, m_Force, m_IgnoreNonActive);
 91            }
 92            else if (feature.Scope == SPFeatureScope.WebApplication)
 93            {
 94                if (scope != ActivationScope.Farm && scope != ActivationScope.WebApplication)
 95                    throw new SPSyntaxException("The Feature specified is scoped to the Web Application.  The -scope parameter must be either \"Farm\" or \"WebApplication\".");
 96 
 97                if (scope == ActivationScope.Farm)
 98                {
 99                    SPEnumerator enumerator = new SPEnumerator(SPFarm.Local);
100                    enumerator.SPWebApplicationEnumerated += enumerator_SPWebApplicationEnumerated;
101                    enumerator.Enumerate();
102                }
103                else
104                {
105                    if (string.IsNullOrEmpty(m_Url))
106                        throw new SPSyntaxException("The -url parameter is required if the scope is \"WebApplication\".");
107                    SPWebApplication webApp = SPWebApplication.Lookup(new Uri(m_Url));
108                    ActivateDeactivateFeatureAtWebApplication(webApp, m_FeatureId, activate, m_Force, m_IgnoreNonActive);
109                }
110            }
111            else if (feature.Scope == SPFeatureScope.Site)
112            {
113                if (scope == ActivationScope.Web)
114                    throw new SPSyntaxException("The Feature specified is scoped to Site.  The -scope parameter cannot be \"Web\".");
115 
116                SPSite site = null;
117                SPEnumerator enumerator = null;
118                try
119                {
120                    if (scope == ActivationScope.Farm)
121                        enumerator = new SPEnumerator(SPFarm.Local);
122                    else if (scope == ActivationScope.WebApplication)
123                    {
124                        SPWebApplication webApp = SPWebApplication.Lookup(new Uri(m_Url));
125                        enumerator = new SPEnumerator(webApp);
126                    }
127                    else if (scope == ActivationScope.Site)
128                    {
129                        site = new SPSite(m_Url);
130                        ActivateDeactivateFeatureAtSite(site, activate, m_FeatureId, m_Force, m_IgnoreNonActive);
131                    }
132                    if (enumerator != null)
133                    {
134                        enumerator.SPSiteEnumerated += enumerator_SPSiteEnumerated;
135                        enumerator.Enumerate();
136                    }
137                }
138                finally
139                {
140                    if (site != null)
141                        site.Dispose();
142                }
143            }
144            else if (feature.Scope == SPFeatureScope.Web)
145            {
146                SPSite site = null;
147                SPWeb web = null;
148                SPEnumerator enumerator = null;
149                try
150                {
151                    if (scope == ActivationScope.Farm)
152                        enumerator = new SPEnumerator(SPFarm.Local);
153                    else if (scope == ActivationScope.WebApplication)
154                    {
155                        SPWebApplication webApp = SPWebApplication.Lookup(new Uri(m_Url));
156                        enumerator = new SPEnumerator(webApp);
157                    }
158                    else if (scope == ActivationScope.Site)
159                    {
160                        site = new SPSite(m_Url);
161                        enumerator = new SPEnumerator(site);
162                    }
163                    else if (scope == ActivationScope.Web)
164                    {
165                        site = new SPSite(m_Url);
166                        web = site.AllWebs[Utilities.GetServerRelUrlFromFullUrl(m_Url)];
167                        ActivateDeactivateFeatureAtWeb(site, web, activate, m_FeatureId, m_Force, m_IgnoreNonActive);
168                    }
169                    if (enumerator != null)
170                    {
171                        enumerator.SPWebEnumerated += enumerator_SPWebEnumerated;
172                        enumerator.Enumerate();
173                    }
174                }
175                finally
176                {
177                    if (web != null)
178                        web.Dispose();
179                    if (site != null)
180                        site.Dispose();
181                }
182            }
183        }
184 
185        #region Event Handlers
186 
187        /// <summary>
188        /// Handles the SPWebEnumerated event of the enumerator control.
189        /// </summary>
190        /// <param name="sender">The source of the event.</param>
191        /// <param name="e">The <see cref="Lapointe.SharePoint.STSADM.Commands.OperationHelpers.SPEnumerator.SPWebEventArgs"/> instance containing the event data.</param>
192        private void enumerator_SPWebEnumerated(object sender, SPEnumerator.SPWebEventArgs e)
193        {
194            ActivateDeactivateFeatureAtWeb(e.Site, e.Web, m_Activate, m_FeatureId, m_Force, m_IgnoreNonActive);
195        }
196 
197        /// <summary>
198        /// Handles the SPSiteEnumerated event of the enumerator control.
199        /// </summary>
200        /// <param name="sender">The source of the event.</param>
201        /// <param name="e">The <see cref="Lapointe.SharePoint.STSADM.Commands.OperationHelpers.SPEnumerator.SPSiteEventArgs"/> instance containing the event data.</param>
202        private void enumerator_SPSiteEnumerated(object sender, SPEnumerator.SPSiteEventArgs e)
203        {
204            ActivateDeactivateFeatureAtSite(e.Site, m_Activate, m_FeatureId, m_Force, m_IgnoreNonActive);
205        }
206 
207        /// <summary>
208        /// Handles the SPWebApplicationEnumerated event of the enumerator control.
209        /// </summary>
210        /// <param name="sender">The source of the event.</param>
211        /// <param name="e">The <see cref="Lapointe.SharePoint.STSADM.Commands.OperationHelpers.SPEnumerator.SPWebApplicationEventArgs"/> instance containing the event data.</param>
212        private void enumerator_SPWebApplicationEnumerated(object sender, SPEnumerator.SPWebApplicationEventArgs e)
213        {
214            ActivateDeactivateFeatureAtWebApplication(e.WebApplication, m_FeatureId, m_Activate, m_Force, m_IgnoreNonActive);
215        }
216 
217        #endregion
218 
219        /// <summary>
220        /// Activates or deactivates the feature.
221        /// </summary>
222        /// <param name="features">The features.</param>
223        /// <param name="activate">if set to <c>true</c> [activate].</param>
224        /// <param name="featureId">The feature id.</param>
225        /// <param name="urlScope">The URL scope.</param>
226        /// <param name="force">if set to <c>true</c> [force].</param>
227        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
228        /// <returns></returns>
229        private SPFeature ActivateDeactivateFeature(SPFeatureCollection features, bool activate, Guid featureId, string urlScope, bool force, bool ignoreNonActive)
230        {
231            if (features[featureId] == null && ignoreNonActive)
232                return null;
233 
234            if (!activate)
235            {
236                if (features[featureId] != null || force)
237                {
238                    Log("Progress: Deactivating Feature {0} from {1}.", featureId.ToString(), urlScope);
239                    try
240                    {
241                        features.Remove(featureId, force);
242                    }
243                    catch (Exception ex)
244                    {
245                        Log("WARNING: {0}", ex.Message);
246                    }
247                }
248                else
249                {
250                    Log("WARNING: " + SPResource.GetString("FeatureNotActivatedAtScope", new object[] { featureId }) + "  Use the -force parameter to force a deactivation.");
251                }
252 
253                return null;
254            }
255            if (features[featureId] == null)
256                Log("Progress: Activating Feature {0} on {1}.", featureId.ToString(), urlScope);
257            else
258            {
259                if (!force)
260                {
261                    SPFeatureDefinition fd = features[featureId].Definition;
262                    Log("WARNING: " + SPResource.GetString("FeatureAlreadyActivated", new object[] { fd.DisplayName, fd.Id, urlScope }) + "  Use the -force parameter to force a reactivation.");
263                    return features[featureId];
264                }
265 
266                Log("Progress: Re-Activating Feature {0} on {1}.", featureId.ToString(), urlScope);
267            }
268 
269            return features.Add(featureId, force);
270        }
271        /// <summary>
272        /// Activates or deactivates the farm scoped feature.
273        /// </summary>
274        /// <param name="activate">if set to <c>true</c> [activate].</param>
275        /// <param name="featureId">The feature id.</param>
276        /// <param name="force">if set to <c>true</c> [force].</param>
277        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
278        /// <returns></returns>
279        public SPFeature ActivateDeactivateFeatureAtFarm(bool activate, Guid featureId, bool force, bool ignoreNonActive)
280        {
281            SPWebService service = SPFarm.Local.Services.GetValue<SPWebService>(string.Empty);
282            return ActivateDeactivateFeature(service.Features, activate, featureId, "Farm", force, ignoreNonActive);
283        }
284 
285        /// <summary>
286        /// Activates or deactivates the web application scoped feature.
287        /// </summary>
288        /// <param name="activate">if set to <c>true</c> [activate].</param>
289        /// <param name="featureId">The feature id.</param>
290        /// <param name="urlScope">The URL scope.</param>
291        /// <param name="force">if set to <c>true</c> [force].</param>
292        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
293        /// <returns></returns>
294        public SPFeature ActivateDeactivateFeatureAtWebApplication(bool activate, Guid featureId, string urlScope, bool force, bool ignoreNonActive)
295        {
296            SPWebApplication application = SPWebApplication.Lookup(new Uri(urlScope));
297            if (application == null)
298            {
299                throw new FileNotFoundException(SPResource.GetString("WebApplicationLookupFailed", new object[] { urlScope }));
300            }
301            return ActivateDeactivateFeatureAtWebApplication(application, featureId, activate, force, ignoreNonActive);
302        }
303 
304        /// <summary>
305        /// Activates or deactivates the web application scoped feature.
306        /// </summary>
307        /// <param name="application">The application.</param>
308        /// <param name="featureId">The feature id.</param>
309        /// <param name="activate">if set to <c>true</c> [activate].</param>
310        /// <param name="force">if set to <c>true</c> [force].</param>
311        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
312        /// <returns></returns>
313        public SPFeature ActivateDeactivateFeatureAtWebApplication(SPWebApplication application, Guid featureId, bool activate, bool force, bool ignoreNonActive)
314        {
315            return ActivateDeactivateFeature(application.Features, activate, featureId, application.GetResponseUri(SPUrlZone.Default).ToString(), force, ignoreNonActive);
316        }
317 
318        /// <summary>
319        /// Activates or deactivates the site scoped feature.
320        /// </summary>
321        /// <param name="activate">if set to <c>true</c> [activate].</param>
322        /// <param name="featureId">The feature id.</param>
323        /// <param name="urlScope">The URL scope.</param>
324        /// <param name="force">if set to <c>true</c> [force].</param>
325        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
326        /// <returns></returns>
327        public SPFeature ActivateDeactivateFeatureAtSite(bool activate, Guid featureId, string urlScope, bool force, bool ignoreNonActive)
328        {
329            using (SPSite site = new SPSite(urlScope))
330            using (SPWeb web = site.OpenWeb(Utilities.GetServerRelUrlFromFullUrl(urlScope), true))
331            {
332                if (web.IsRootWeb)
333                {
334                    return ActivateDeactivateFeatureAtSite(site, activate, featureId, force, ignoreNonActive);
335                }
336                throw new SPException(SPResource.GetString("FeatureActivateDeactivateScopeAmbiguous", new object[] { site.Url }));
337            }
338        }
339 
340        /// <summary>
341        /// Activates or deactivates the site scoped feature.
342        /// </summary>
343        /// <param name="site">The site.</param>
344        /// <param name="activate">if set to <c>true</c> [activate].</param>
345        /// <param name="featureId">The feature id.</param>
346        /// <param name="force">if set to <c>true</c> [force].</param>
347        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
348        /// <returns></returns>
349        public SPFeature ActivateDeactivateFeatureAtSite(SPSite site, bool activate, Guid featureId, bool force, bool ignoreNonActive)
350        {
351            return ActivateDeactivateFeature(site.Features, activate, featureId, site.Url, force, ignoreNonActive);
352        }
353 
354        /// <summary>
355        /// Activates or deactivates the web scoped feature.
356        /// </summary>
357        /// <param name="activate">if set to <c>true</c> [activate].</param>
358        /// <param name="featureId">The feature id.</param>
359        /// <param name="urlScope">The URL scope.</param>
360        /// <param name="force">if set to <c>true</c> [force].</param>
361        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
362        /// <returns></returns>
363        public SPFeature ActivateDeactivateFeatureAtWeb(bool activate, Guid featureId, string urlScope, bool force, bool ignoreNonActive)
364        {
365            using (SPSite site = new SPSite(urlScope))
366            using (SPWeb web = site.OpenWeb())
367            {
368                return ActivateDeactivateFeatureAtWeb(site, web, activate, featureId, force, ignoreNonActive);
369            }
370        }
371 
372        /// <summary>
373        /// Activates or deactivates the web scoped feature.
374        /// </summary>
375        /// <param name="site">The site.</param>
376        /// <param name="web">The web.</param>
377        /// <param name="activate">if set to <c>true</c> [activate].</param>
378        /// <param name="featureId">The feature id.</param>
379        /// <param name="force">if set to <c>true</c> [force].</param>
380        /// <param name="ignoreNonActive">if set to <c>true</c> [ignore non active].</param>
381        /// <returns></returns>
382        public SPFeature ActivateDeactivateFeatureAtWeb(SPSite site, SPWeb web, bool activate, Guid featureId, bool force, bool ignoreNonActive)
383        {
384            return ActivateDeactivateFeature(web.Features, activate, featureId, web.Url, force, ignoreNonActive);
385        }
386 
387    }
388}

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 NameAvailabilityBuild Date
gl-activatefeatureWSS v3, MOSS 2007Released: 11/15/2008
Parameter NameShort FormRequiredDescriptionExample Usage
filenamefYes, if -name or -id is not providedPath 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&lt;file path>’.”-filename "MyFeature\feature.xml", -f "MyFeature\feature.xml"
namenYes, if -filename or -id is not providedName of the feature folder located in the 12\Template\Features directory-name "MyFeature", -n "MyFeature"
idYes, if -filename or -name is not providedGUID 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 ‘’ is not installed in this farm, and cannot be added to this scope.”-id "21d186e1-7036-4092-a825-0eb6709e9280"
scopesNoThe 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
urlNoURL 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
forceNoForces the re-activation of the feature if already activated. This causes any custom code associated with the feature to rerun.-force
ignorenonactiveignoreNoThis 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