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>