AddConfigurationSection package action

Rating: 4.5 / 5 stars - 2 vote(s).



For a current project I’m working on (http://codeplex.com/pm4umbraco), I have the need to execute some actions when the complete package gets installed. One of those is adding a complete new section to the site’s web.config. If you don’t know what package actions are, have a look at the new amazing our.umbraco.org and it’s wiki. You’ll find some useful info.

The ‘AddConfigurationSection’ action will, as said before, change the web.config and include a complete new configuration section. In order to use this package action, you’ll need at least a class that inherits from the ConfigurationSection class.

Let’s have a look at the settings for this action. Below is the xml snippet that is required to add a new configuration section.

   1:  <Action runat="install" undo="true" alias="AddConfigurationSection">
   2:    <Section name="protectedMedia" assembly="netaddICTs.Umb.ProtectedMedia.Business.Configuration" type="netaddICTs.Umb.ProtectedMedia.Business.Configuration.ProtectedMedia" />
   3:  </Action>

In above snippet, class ProtectedMedia – which lives in the netaddICTs.Umb.ProtectedMedia.Business.Configuration namespace – is a class deriving from the standard .net class ConfigurationSection.

Other useful attributes of the Section child node:
‘name’ – Holds the value for the name of the section as it will be stored in the web.config
‘assembly’ – Name of the assembly containing the class that derives from ConfigurationSection  

   1:  namespace netaddICTs.Umb.ProtectedMedia.Business.Configuration {
   2:   
   3:      public class ProtectedMedia : ConfigurationSection {
   4:   
   5:          internal const string DownloadSHA1PropertyName = "downloadSHA1";
   6:   
   7:          [ConfigurationProperty(DownloadSHA1PropertyName, IsRequired = true, IsKey = false, IsDefaultCollection = false, DefaultValue = "Key1")]
   8:          public string SHA1{
   9:              get { return (string)base[DownloadSHA1PropertyName]; }
  10:              set { base[DownloadSHA1PropertyName] = value; }
  11:          }
  12:   
  13:          internal const string InternalDownloadSHA1PropertyName = "internalDownloadSHA1";
  14:   
  15:          [ConfigurationProperty(InternalDownloadSHA1PropertyName, IsRequired = true, IsKey = false, IsDefaultCollection = false, DefaultValue = "Key2")]
  16:          public string InternalDownloadSHA1{
  17:              get { return (string) base[InternalDownloadSHA1PropertyName]; }
  18:              set { base[InternalDownloadSHA1PropertyName] = value; }
  19:          }
  20:   
  21:          internal const string StoragePropertyName = "storage";
  22:   
  23:          [ConfigurationProperty(StoragePropertyName, IsRequired = true, IsKey = false, IsDefaultCollection = false)]
  24:          public Storage Storage{
  25:              get { return (Storage) base[StoragePropertyName]; }
  26:              set { base[StoragePropertyName] = value; }
  27:          }
  28:   
  29:          internal const string NodesPropertyName = "nodes";
  30:   
  31:          [ConfigurationProperty(NodesPropertyName, IsRequired = true, IsKey = false, IsDefaultCollection = false)]
  32:          public Nodes Nodes{
  33:              get { return (Nodes) base[NodesPropertyName]; }
  34:              set { base[NodesPropertyName] = value; }
  35:          }
  36:   
  37:          internal const string AliasesPropertyName = "aliases";
  38:   
  39:          [ConfigurationProperty(AliasesPropertyName, IsRequired = true, IsKey = false, IsDefaultCollection = false)]
  40:          public Aliases Aliases{
  41:              get { return (Aliases) base[AliasesPropertyName]; }
  42:              set { base[AliasesPropertyName] = value; }
  43:          }
  44:   
  45:          internal const string ProtectedMediaConfigurationSectionName = "protectedMedia";
  46:   
  47:          public static ProtectedMedia Instance{
  48:              get { return ConfigurationManager.GetSection(ProtectedMediaConfigurationSectionName) as ProtectedMedia; }
  49:          }
  50:      }
  51:  }

One more useful remark: If you’d like to have some default values for some of the configuration elements, consider using the DefaultValue settings on the different attributes, as in:

   1:   internal const string TypePropertyName = "type";
   2:   
   3:          [ConfigurationProperty(TypePropertyName, IsRequired = true, IsKey = false, IsDefaultCollection = false, DefaultValue = "VirtualDirectory|LocalFolder")]
   4:          public string Type{
   5:              get { return (string) base[TypePropertyName]; }
   6:              set { base[TypePropertyName] = value; }
   7:          }

In this case, the Type settings will get a default value ‘VirtualDirectory|LocalFolder’.
Here’s the complete listing of the package action that installs a configuration section in the web.config

   1:  using System;
   2:  using System.Configuration;
   3:  using System.Reflection;
   4:  using System.Web.Configuration;
   5:  using System.Xml;
   6:   
   7:  using umbraco.cms.businesslogic.packager.standardPackageActions;
   8:  using umbraco.interfaces;
   9:   
  10:  namespace netaddICTs.Umb.ProtectedMedia.Packager.Actions {
  11:   
  12:      public class AddConfigurationSection : IPackageAction {
  13:   
  14:          public bool Execute(string packageName, XmlNode xmlData) {
  15:              
  16:              try {
  17:                  var config = WebConfigurationManager.OpenWebConfiguration("~");
  18:                  var sectionName = xmlData.SelectSingleNode("//Section").Attributes["name"].Value;
  19:   
  20:                  if (config.Sections[sectionName] == null) {
  21:                      var assemblyName = xmlData.SelectSingleNode("//Section").Attributes["assembly"].Value;
  22:                      var typeName = xmlData.SelectSingleNode("//Section").Attributes["type"].Value;
  23:                      var assembly = Assembly.Load(assemblyName);
  24:   
  25:                      if (assembly == null) return false;
  26:   
  27:                      var configSection = assembly.CreateInstance(typeName) as ConfigurationSection;
  28:   
  29:                      if (configSection == null) return false;
  30:   
  31:                      config.Sections.Add(sectionName, configSection);
  32:                      configSection.SectionInformation.ForceSave = true;
  33:                      config.Save(ConfigurationSaveMode.Full);
  34:                  }
  35:   
  36:                  return true;
  37:              } catch (Exception exception) {
  38:   
  39:                  return false;
  40:              }
  41:          }
  42:   
  43:          public string Alias() {
  44:              return "AddConfigurationSection";
  45:          }
  46:   
  47:          public bool Undo(string packageName, XmlNode xmlData) {
  48:              try {
  49:                  var config = WebConfigurationManager.OpenWebConfiguration("~");
  50:                  var sectionName = xmlData.SelectSingleNode("//Section").Attributes["name"].Value;
  51:   
  52:                  if (config.Sections[sectionName] != null) {
  53:   
  54:                      config.Sections.Remove(sectionName);
  55:                      config.Save(ConfigurationSaveMode.Full);
  56:                  }
  57:                  return true;
  58:              } catch {
  59:   
  60:                  return false;
  61:              }
  62:          }
  63:   
  64:          public XmlNode SampleXml() {
  65:              var sample = "<Action runat=\"install\" undo=\"true\" alias=\"AddConfigurationSection\"><Section name=\"\" assembly=\"\" type=\"\" /></Action>";
  66:              return helper.parseStringToXmlNode(sample);
  67:          }
  68:      }
  69:  }

Running this package action using the ‘Package Actions Tester’ yields this result:

   1:  <configSections>
   2:      <section name="protectedMedia" type="netaddICTs.Umb.ProtectedMedia.Business.Configuration.ProtectedMedia, netaddICTs.Umb.ProtectedMedia.Business.Configuration, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" allowLocation="true" allowDefinition="Everywhere" allowExeDefinition="MachineToApplication" overrideModeDefault="Allow" restartOnExternalChanges="true" requirePermission="true" />
   3:  </configSections>

And

   1:  <protectedMedia downloadSHA1="Key1" internalDownloadSHA1="Key2">
   2:      <storage type="VirtualDirectory|LocalFolder" path="/path" />
   3:      <nodes>
   4:        <login umbracoNodeId="-1" />
   5:        <noAccess umbracoNodeId="-1" />
   6:        <error umbracoNodeId="-1" />
   7:      </nodes>
   8:      <aliases>
   9:        <umbracoFile umbracoAlias="" />
  10:        <internalFile umbracoAlias="" />
  11:        <internalDownloadLink umbracoAlias="" />
  12:        <umbracoFileExtension umbracoAlias="" />
  13:        <umbracoFileSize umbracoAlias="" />
  14:        <protectedFile umbracoAlias="" />
  15:        <protectedFolder umbracoAlias="" />
  16:        <permission umbracoAlias="" />
  17:      </aliases>
  18:    </protectedMedia>