diff --git a/src/Umbraco.Core/Configuration/BaseRest/BaseRestSection.cs b/src/Umbraco.Core/Configuration/BaseRest/BaseRestSection.cs index 0ca638402f..24e7c12e03 100644 --- a/src/Umbraco.Core/Configuration/BaseRest/BaseRestSection.cs +++ b/src/Umbraco.Core/Configuration/BaseRest/BaseRestSection.cs @@ -3,7 +3,7 @@ using System.Configuration; namespace Umbraco.Core.Configuration.BaseRest { - [ConfigurationKey("BaseRestExtensions")] + internal class BaseRestSection : UmbracoConfigurationSection, IBaseRestSection { private const string KeyEnabled = "enabled"; diff --git a/src/Umbraco.Core/Configuration/ConfigurationKeyAttribute.cs b/src/Umbraco.Core/Configuration/ConfigurationKeyAttribute.cs deleted file mode 100644 index 90eb0884e2..0000000000 --- a/src/Umbraco.Core/Configuration/ConfigurationKeyAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Umbraco.Core.Configuration -{ - /// - /// Indicates the configuration key for a section or a group. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public sealed class ConfigurationKeyAttribute : Attribute - { - /// - /// Initializes a new instance of the class with a configuration key. - /// - /// The configurationkey. - public ConfigurationKeyAttribute(string configurationKey) - { - ConfigurationKey = configurationKey; - } - - /// - /// Gets or sets the configuration key. - /// - public string ConfigurationKey { get; private set; } - } -} diff --git a/src/Umbraco.Core/Configuration/Dashboard/AccessElement.cs b/src/Umbraco.Core/Configuration/Dashboard/AccessElement.cs new file mode 100644 index 0000000000..e6cffac333 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/AccessElement.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class AccessElement : RawXmlConfigurationElement, IAccess + { + public IEnumerable Rules + { + get + { + var result = new List(); + if (RawXml != null) + { + result.AddRange(RawXml.Elements("deny").Select(x => new AccessItem {Action = AccessType.Deny, Value = x.Value })); + result.AddRange(RawXml.Elements("grant").Select(x => new AccessItem { Action = AccessType.Grant, Value = x.Value })); + result.AddRange(RawXml.Elements("grantBySection").Select(x => new AccessItem { Action = AccessType.GrantBySection, Value = x.Value })); + } + return result; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/AccessItem.cs b/src/Umbraco.Core/Configuration/Dashboard/AccessItem.cs new file mode 100644 index 0000000000..65ae6299d6 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/AccessItem.cs @@ -0,0 +1,15 @@ +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class AccessItem : IAccessItem + { + /// + /// This can be grant, deny or grantBySection + /// + public AccessType Action { get; set; } + + /// + /// The value of the action + /// + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/AccessType.cs b/src/Umbraco.Core/Configuration/Dashboard/AccessType.cs new file mode 100644 index 0000000000..115d416010 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/AccessType.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Core.Configuration.Dashboard +{ + public enum AccessType + { + Grant, + Deny, + GrantBySection + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/AreasElement.cs b/src/Umbraco.Core/Configuration/Dashboard/AreasElement.cs new file mode 100644 index 0000000000..8ab27f6387 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/AreasElement.cs @@ -0,0 +1,18 @@ +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class AreasElement : ConfigurationElement, IArea + { + [ConfigurationProperty("area", IsRequired = true)] + public InnerTextConfigurationElement AreaName + { + get { return (InnerTextConfigurationElement)this["area"]; } + } + + string IArea.AreaName + { + get { return AreaName; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/ControlCollection.cs b/src/Umbraco.Core/Configuration/Dashboard/ControlCollection.cs new file mode 100644 index 0000000000..2bd69d78b4 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/ControlCollection.cs @@ -0,0 +1,37 @@ +using System.Collections; +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class ControlCollection : ConfigurationElementCollection, IEnumerable + { + internal void Add(ControlElement c) + { + BaseAdd(c); + } + + protected override ConfigurationElement CreateNewElement() + { + return new ControlElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((ControlElement)element).Value; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (var i = 0; i < Count; i++) + { + yield return BaseGet(i) as IControl; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/ControlElement.cs b/src/Umbraco.Core/Configuration/Dashboard/ControlElement.cs new file mode 100644 index 0000000000..778882f1b2 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/ControlElement.cs @@ -0,0 +1,30 @@ +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class ControlElement : InnerTextConfigurationElement, IControl + { + [ConfigurationProperty("showOnce", DefaultValue = false)] + public bool ShowOnce + { + get { return (bool)this["showOnce"]; } + } + + [ConfigurationProperty("addPanel", DefaultValue = true)] + public bool AddPanel + { + get { return (bool)this["addPanel"]; } + } + + [ConfigurationProperty("panelCaption", DefaultValue = "")] + public string PanelCaption + { + get { return (string)this["panelCaption"]; } + } + + public string ControlPath + { + get { return Value; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/DashboardSection.cs b/src/Umbraco.Core/Configuration/Dashboard/DashboardSection.cs new file mode 100644 index 0000000000..12bf0522e0 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/DashboardSection.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class DashboardSection : ConfigurationSection, IDashboardSection + { + [ConfigurationCollection(typeof(SectionCollection), AddItemName = "section")] + [ConfigurationProperty("", IsDefaultCollection = true)] + public SectionCollection SectionCollection + { + get { return (SectionCollection)base[""]; } + set { base[""] = value; } + } + + IEnumerable IDashboardSection.Sections + { + get { return SectionCollection; } + } + } +} diff --git a/src/Umbraco.Core/Configuration/Dashboard/IAccess.cs b/src/Umbraco.Core/Configuration/Dashboard/IAccess.cs new file mode 100644 index 0000000000..9fee2f80e1 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/IAccess.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface IAccess + { + IEnumerable Rules { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/IAccessItem.cs b/src/Umbraco.Core/Configuration/Dashboard/IAccessItem.cs new file mode 100644 index 0000000000..7583d46306 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/IAccessItem.cs @@ -0,0 +1,15 @@ +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface IAccessItem + { + /// + /// This can be grant, deny or grantBySection + /// + AccessType Action { get; set; } + + /// + /// The value of the action + /// + string Value { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/IArea.cs b/src/Umbraco.Core/Configuration/Dashboard/IArea.cs new file mode 100644 index 0000000000..08775ee12f --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/IArea.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface IArea + { + string AreaName { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/IControl.cs b/src/Umbraco.Core/Configuration/Dashboard/IControl.cs new file mode 100644 index 0000000000..bfaaf9cbc9 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/IControl.cs @@ -0,0 +1,13 @@ +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface IControl + { + bool ShowOnce { get; } + + bool AddPanel { get; } + + string PanelCaption { get; } + + string ControlPath { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/IDashboardSection.cs b/src/Umbraco.Core/Configuration/Dashboard/IDashboardSection.cs new file mode 100644 index 0000000000..555c9b7439 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/IDashboardSection.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface IDashboardSection + { + IEnumerable Sections { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/ISection.cs b/src/Umbraco.Core/Configuration/Dashboard/ISection.cs new file mode 100644 index 0000000000..cf8a802d12 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/ISection.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface ISection + { + string Alias { get; } + + string Area { get; } + + IEnumerable Tabs { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/ITab.cs b/src/Umbraco.Core/Configuration/Dashboard/ITab.cs new file mode 100644 index 0000000000..ac64ba902c --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/ITab.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface ITab + { + string Caption { get; } + + IEnumerable Controls { get; } + + IAccess AccessRights { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/SectionCollection.cs b/src/Umbraco.Core/Configuration/Dashboard/SectionCollection.cs new file mode 100644 index 0000000000..395ce7f623 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/SectionCollection.cs @@ -0,0 +1,37 @@ +using System.Collections; +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class SectionCollection : ConfigurationElementCollection, IEnumerable + { + internal void Add(SectionElement c) + { + BaseAdd(c); + } + + protected override ConfigurationElement CreateNewElement() + { + return new SectionElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((SectionElement)element).Alias; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (var i = 0; i < Count; i++) + { + yield return BaseGet(i) as ISection; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/SectionElement.cs b/src/Umbraco.Core/Configuration/Dashboard/SectionElement.cs new file mode 100644 index 0000000000..db20fad4e5 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/SectionElement.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class SectionElement : ConfigurationElement, ISection + { + [ConfigurationProperty("alias", IsRequired = true)] + public string Alias + { + get { return (string) this["alias"]; } + } + + [ConfigurationProperty("areas", IsRequired = true)] + public AreasElement Area + { + get { return (AreasElement)this["areas"]; } + } + + [ConfigurationCollection(typeof(SectionCollection), AddItemName = "tab")] + [ConfigurationProperty("", IsDefaultCollection = true)] + public TabCollection TabCollection + { + get { return (TabCollection)base[""]; } + set { base[""] = value; } + } + + IEnumerable ISection.Tabs + { + get { return TabCollection; } + } + + string ISection.Area + { + get { return Area.AreaName; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/TabCollection.cs b/src/Umbraco.Core/Configuration/Dashboard/TabCollection.cs new file mode 100644 index 0000000000..e5f7d710b0 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/TabCollection.cs @@ -0,0 +1,37 @@ +using System.Collections; +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class TabCollection : ConfigurationElementCollection, IEnumerable + { + internal void Add(TabElement c) + { + BaseAdd(c); + } + + protected override ConfigurationElement CreateNewElement() + { + return new TabElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((TabElement)element).Caption; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (var i = 0; i < Count; i++) + { + yield return BaseGet(i) as ITab; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/TabElement.cs b/src/Umbraco.Core/Configuration/Dashboard/TabElement.cs new file mode 100644 index 0000000000..20d42a6711 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Dashboard/TabElement.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class TabElement : ConfigurationElement, ITab + { + [ConfigurationProperty("caption", IsRequired = true)] + public string Caption + { + get { return (string)this["caption"]; } + } + + [ConfigurationProperty("access")] + public AccessElement Access + { + get { return (AccessElement)this["access"]; } + } + + [ConfigurationCollection(typeof(ControlCollection), AddItemName = "control")] + [ConfigurationProperty("", IsDefaultCollection = true)] + public ControlCollection ControlCollection + { + get { return (ControlCollection)base[""]; } + set { base[""] = value; } + } + + IEnumerable ITab.Controls + { + get { return ControlCollection; } + } + + IAccess ITab.AccessRights + { + get { return Access; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoConfig.cs b/src/Umbraco.Core/Configuration/UmbracoConfig.cs index 4bdadc0a29..0c2e160388 100644 --- a/src/Umbraco.Core/Configuration/UmbracoConfig.cs +++ b/src/Umbraco.Core/Configuration/UmbracoConfig.cs @@ -5,6 +5,7 @@ using System.Configuration; using System.Linq; using System.Threading; using Umbraco.Core.Configuration.BaseRest; +using Umbraco.Core.Configuration.Dashboard; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; @@ -57,13 +58,31 @@ namespace Umbraco.Core.Configuration /// /// /// - public UmbracoConfig(IUmbracoSettingsSection umbracoSettings, IBaseRestSection baseRestSettings) + /// + public UmbracoConfig(IUmbracoSettingsSection umbracoSettings, IBaseRestSection baseRestSettings, IDashboardSection dashboardSettings) { SetUmbracoSettings(umbracoSettings); SetBaseRestExtensions(baseRestSettings); + SetDashboardSettings(dashboardSettings); } + private IDashboardSection _dashboardSection; private IUmbracoSettingsSection _umbracoSettings; + private IBaseRestSection _baseRestExtensions; + + /// + /// Gets the IDashboardSection + /// + public IDashboardSection DashboardSettings() + { + return _dashboardSection; + } + + //ONLY for unit testing + internal void SetDashboardSettings(IDashboardSection value) + { + _dashboardSection = value; + } //ONLY for unit testing internal void SetUmbracoSettings(IUmbracoSettingsSection value) @@ -78,9 +97,7 @@ namespace Umbraco.Core.Configuration { return _umbracoSettings; } - - private IBaseRestSection _baseRestExtensions; - + //ONLY for unit testing public void SetBaseRestExtensions(IBaseRestSection value) { diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs index 1403617fd7..ecafd9adb3 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs @@ -3,7 +3,7 @@ using System.Configuration; namespace Umbraco.Core.Configuration.UmbracoSettings { - [ConfigurationKey("umbracoConfiguration/settings/content")] + internal class ContentElement : ConfigurationElement, IContentSection { [ConfigurationProperty("imaging")] diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoSettingsSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoSettingsSection.cs index a84055f8a8..dd6fed5cd5 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoSettingsSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoSettingsSection.cs @@ -4,7 +4,7 @@ using System.Linq; namespace Umbraco.Core.Configuration.UmbracoSettings { - [ConfigurationKey("umbracoConfiguration/settings")] + public class UmbracoSettingsSection : ConfigurationSection, IUmbracoSettingsSection { [ConfigurationProperty("content")] diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 3375c4f078..c4e96d081e 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -154,11 +154,28 @@ - + + + + + + + + + + + + + + + + + + diff --git a/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config b/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config new file mode 100644 index 0000000000..43cac016c2 --- /dev/null +++ b/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config @@ -0,0 +1,111 @@ + + + +
+ + settings + + + + views/dashboard/settings/settingsdashboardintro.html + + + views/dashboard/settings/settingsdashboardvideos.html + + +
+ +
+ + developer + + + + views/dashboard/developer/developerdashboardintro.html + + + views/dashboard/developer/developerdashboardvideos.html + + + + /umbraco/dashboard/ExamineManagement.ascx + +
+ +
+ + media + + + + views/dashboard/media/mediafolderbrowser.html + + + + + admin + + + views/dashboard/media/mediadashboardintro.html + + + views/dashboard/media/desktopmediauploader.html + + + views/dashboard/media/mediadashboardvideos.html + + +
+ +
+ + translator + + + content + + + + admin + + + views/dashboard/default/startupdashboardintro.html + + + views/dashboard/default/startupdashboardkits.html + + editor + writer + + + + views/dashboard/default/startupdashboardvideos.html + + + + /umbraco/dashboard/latestEdits.ascx + + + + views/dashboard/changepassword.html + + +
+ +
+ + member + + + + views/dashboard/members/membersdashboardintro.html + + + /umbraco/members/membersearch.ascx + + + views/dashboard/members/membersdashboardvideos.html + + +
+
\ No newline at end of file diff --git a/src/Umbraco.Tests/Configurations/DashboardSettings/DashboardSettingsTests.cs b/src/Umbraco.Tests/Configurations/DashboardSettings/DashboardSettingsTests.cs new file mode 100644 index 0000000000..e4b31dc1b4 --- /dev/null +++ b/src/Umbraco.Tests/Configurations/DashboardSettings/DashboardSettingsTests.cs @@ -0,0 +1,35 @@ +using System.Configuration; +using System.IO; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Configuration.Dashboard; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Configurations.DashboardSettings +{ + [TestFixture] + public class DashboardSettingsTests + { + [SetUp] + public void Init() + { + var config = new FileInfo(TestHelper.MapPathForTest("~/Configurations/DashboardSettings/web.config")); + + var fileMap = new ExeConfigurationFileMap() { ExeConfigFilename = config.FullName }; + var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); + + SettingsSection = configuration.GetSection("umbracoConfiguration/dashBoard") as DashboardSection; + + Assert.IsNotNull(SettingsSection); + } + + protected IDashboardSection SettingsSection { get; private set; } + + [Test] + public void Test_Sections() + { + Assert.AreEqual(5, SettingsSection.Sections.Count()); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Configurations/DashboardSettings/web.config b/src/Umbraco.Tests/Configurations/DashboardSettings/web.config new file mode 100644 index 0000000000..8cf262cbff --- /dev/null +++ b/src/Umbraco.Tests/Configurations/DashboardSettings/web.config @@ -0,0 +1,14 @@ + + + + + +
+ + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index a33551f070..5fa259be3b 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -208,6 +208,7 @@ + @@ -474,6 +475,14 @@ Designer + + Designer + Always + + + Designer + Always + Designer Always diff --git a/src/Umbraco.Web.UI/App_Plugins/MyPackage/Trees/LegacyTestTree.cs b/src/Umbraco.Web.UI/App_Plugins/MyPackage/Trees/LegacyTestTree.cs index 4e46cae0e2..bfc0583813 100644 --- a/src/Umbraco.Web.UI/App_Plugins/MyPackage/Trees/LegacyTestTree.cs +++ b/src/Umbraco.Web.UI/App_Plugins/MyPackage/Trees/LegacyTestTree.cs @@ -33,7 +33,7 @@ namespace Umbraco.Web.UI.App_Plugins.MyPackage.Trees protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) { var menu = new MenuItemCollection(); - menu.Add(new MenuItem("create", "Create")); + menu.Items.Add(new MenuItem("create", "Create")); return menu; } }