Refactor of dashboards

* Remove dashboards from Configs().Dashboards
* Remove dashboard.config XML file & associated code reading from XML
* Load Dashboards from TypeLoader into a CollectionBuilder
* Load merge/concat C# Dashboard Types with package.manifest
* Add buildercolletion to an extension method on Composition
This commit is contained in:
Warren Buckley
2019-01-23 14:37:33 +00:00
parent 9b9c9ef455
commit 69a3c85bbc
53 changed files with 678 additions and 844 deletions

View File

@@ -2,7 +2,6 @@
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Dashboard;
using Umbraco.Core.Configuration.Grid;
using Umbraco.Core.Configuration.HealthChecks;
using Umbraco.Core.Configuration.UmbracoSettings;
@@ -22,9 +21,6 @@ namespace Umbraco.Core
public static IUmbracoSettingsSection Settings(this Configs configs)
=> configs.GetConfig<IUmbracoSettingsSection>();
public static IDashboardSection Dashboards(this Configs configs)
=> configs.GetConfig<IDashboardSection>();
public static IHealthChecks HealthChecks(this Configs configs)
=> configs.GetConfig<IHealthChecks>();
@@ -40,7 +36,6 @@ namespace Umbraco.Core
configs.Add<IGlobalSettings>(() => new GlobalSettings());
configs.Add<IUmbracoSettingsSection>("umbracoConfiguration/settings");
configs.Add<IDashboardSection>("umbracoConfiguration/dashBoard");
configs.Add<IHealthChecks>("umbracoConfiguration/HealthChecks");
configs.Add(() => new CoreDebug());

View File

@@ -1,30 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Umbraco.Core.Configuration.Dashboard
{
internal class AccessElement : RawXmlConfigurationElement, IAccess
{
public AccessElement()
{ }
public AccessElement(XElement rawXml)
: base(rawXml)
{ }
public IEnumerable<IAccessRule> Rules
{
get
{
var result = new List<AccessRule>();
if (RawXml == null) return result;
result.AddRange(RawXml.Elements("deny").Select(x => new AccessRule {Type = AccessRuleType.Deny, Value = x.Value }));
result.AddRange(RawXml.Elements("grant").Select(x => new AccessRule { Type = AccessRuleType.Grant, Value = x.Value }));
result.AddRange(RawXml.Elements("grantBySection").Select(x => new AccessRule { Type = AccessRuleType.GrantBySection, Value = x.Value }));
return result;
}
}
}
}

View File

@@ -1,32 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
namespace Umbraco.Core.Configuration.Dashboard
{
internal class AreaCollection : ConfigurationElementCollection, IEnumerable<IArea>
{
protected override ConfigurationElement CreateNewElement()
{
return new AreaElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((AreaElement) element).Value;
}
IEnumerator<IArea> IEnumerable<IArea>.GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
yield return BaseGet(i) as IArea;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@@ -1,12 +0,0 @@
using System.Configuration;
namespace Umbraco.Core.Configuration.Dashboard
{
internal class AreaElement : InnerTextConfigurationElement<string>, IArea
{
string IArea.AreaName
{
get { return Value; }
}
}
}

View File

@@ -1,15 +0,0 @@
using System.Configuration;
namespace Umbraco.Core.Configuration.Dashboard
{
internal class AreasElement : ConfigurationElement
{
[ConfigurationCollection(typeof(SectionCollection), AddItemName = "area")]
[ConfigurationProperty("", IsDefaultCollection = true)]
public AreaCollection AreaCollection
{
get { return (AreaCollection)base[""]; }
set { base[""] = value; }
}
}
}

View File

@@ -1,37 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
namespace Umbraco.Core.Configuration.Dashboard
{
internal class ControlCollection : ConfigurationElementCollection, IEnumerable<IDashboardControl>
{
internal void Add(ControlElement c)
{
BaseAdd(c);
}
protected override ConfigurationElement CreateNewElement()
{
return new ControlElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((ControlElement)element).ControlPath;
}
IEnumerator<IDashboardControl> IEnumerable<IDashboardControl>.GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
yield return BaseGet(i) as IDashboardControl;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@@ -1,44 +0,0 @@
using System.Configuration;
using System.Linq;
using System.Xml.Linq;
namespace Umbraco.Core.Configuration.Dashboard
{
internal class ControlElement : RawXmlConfigurationElement, IDashboardControl
{
public string PanelCaption
{
get
{
var panelCaption = RawXml.Attribute("panelCaption");
return panelCaption == null ? "" : panelCaption.Value;
}
}
public AccessElement Access
{
get
{
var access = RawXml.Element("access");
return access == null ? new AccessElement() : new AccessElement(access);
}
}
public string ControlPath
{
get
{
//we need to return the first (and only) text element of the children (wtf... who designed this configuration ! :P )
var txt = RawXml.Nodes().OfType<XText>().FirstOrDefault();
if (txt == null)
{
throw new ConfigurationErrorsException("The control element must contain a text node indicating the control path");
}
return txt.Value.Trim();
}
}
IAccess IDashboardControl.AccessRights => Access;
}
}

View File

@@ -1,24 +0,0 @@
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<ISection> IDashboardSection.Sections
{
get { return SectionCollection; }
}
}
}

View File

@@ -1,9 +0,0 @@
using System.Collections.Generic;
namespace Umbraco.Core.Configuration.Dashboard
{
public interface IAccess
{
IEnumerable<IAccessRule> Rules { get; }
}
}

View File

@@ -1,7 +0,0 @@
namespace Umbraco.Core.Configuration.Dashboard
{
public interface IArea
{
string AreaName { get; }
}
}

View File

@@ -1,11 +0,0 @@
namespace Umbraco.Core.Configuration.Dashboard
{
public interface IDashboardControl
{
string PanelCaption { get; }
string ControlPath { get; }
IAccess AccessRights { get; }
}
}

View File

@@ -1,9 +0,0 @@
using System.Collections.Generic;
namespace Umbraco.Core.Configuration.Dashboard
{
public interface IDashboardSection
{
IEnumerable<ISection> Sections { get; }
}
}

View File

@@ -1,13 +0,0 @@
using System.Collections.Generic;
namespace Umbraco.Core.Configuration.Dashboard
{
public interface IDashboardTab
{
string Caption { get; }
IEnumerable<IDashboardControl> Controls { get; }
IAccess AccessRights { get; }
}
}

View File

@@ -1,15 +0,0 @@
using System.Collections.Generic;
namespace Umbraco.Core.Configuration.Dashboard
{
public interface ISection
{
string Alias { get; }
IEnumerable<string> Areas { get; }
IEnumerable<IDashboardTab> Tabs { get; }
IAccess AccessRights { get; }
}
}

View File

@@ -1,37 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
namespace Umbraco.Core.Configuration.Dashboard
{
internal class SectionCollection : ConfigurationElementCollection, IEnumerable<ISection>
{
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<ISection> IEnumerable<ISection>.GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
yield return BaseGet(i) as ISection;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@@ -1,50 +0,0 @@
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
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 Areas
{
get { return (AreasElement)this["areas"]; }
}
[ConfigurationProperty("access")]
public AccessElement Access
{
get { return (AccessElement)this["access"]; }
}
[ConfigurationCollection(typeof(SectionCollection), AddItemName = "tab")]
[ConfigurationProperty("", IsDefaultCollection = true)]
public TabCollection TabCollection
{
get { return (TabCollection)base[""]; }
set { base[""] = value; }
}
IEnumerable<IDashboardTab> ISection.Tabs
{
get { return TabCollection; }
}
IEnumerable<string> ISection.Areas
{
get { return Areas.AreaCollection.Cast<AreaElement>().Select(x => x.Value); }
}
IAccess ISection.AccessRights
{
get { return Access; }
}
}
}

View File

@@ -1,37 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
namespace Umbraco.Core.Configuration.Dashboard
{
internal class TabCollection : ConfigurationElementCollection, IEnumerable<IDashboardTab>
{
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<IDashboardTab> IEnumerable<IDashboardTab>.GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
yield return BaseGet(i) as IDashboardTab;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@@ -1,38 +0,0 @@
using System.Collections.Generic;
using System.Configuration;
namespace Umbraco.Core.Configuration.Dashboard
{
internal class TabElement : ConfigurationElement, IDashboardTab
{
[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<IDashboardControl> IDashboardTab.Controls
{
get { return ControlCollection; }
}
IAccess IDashboardTab.AccessRights
{
get { return Access; }
}
}
}

View File

@@ -1,4 +1,4 @@
namespace Umbraco.Core.Configuration.Dashboard
namespace Umbraco.Core.Dashboards
{
/// <summary>
/// Implements <see cref="IAccessRule"/>.

View File

@@ -1,4 +1,4 @@
namespace Umbraco.Core.Configuration.Dashboard
namespace Umbraco.Core.Dashboards
{
/// <summary>
/// Defines dashboard access rules type.

View File

@@ -1,4 +1,4 @@
namespace Umbraco.Core.Configuration.Dashboard
namespace Umbraco.Core.Dashboards
{
/// <summary>
/// Represents an access rule.

View File

@@ -0,0 +1,30 @@
namespace Umbraco.Core.Dashboards
{
public interface IDashboardSection
{
/// <summary>
/// Display name of the dashboard tab
/// </summary>
string Name { get; }
/// <summary>
/// Alias to refer to this dashboard via code
/// </summary>
string Alias { get; }
/// <summary>
/// A collection of sections/application aliases that this dashboard will appear on
/// </summary>
string[] Sections { get; }
/// <summary>
/// The HTML view to load for the dashboard
/// </summary>
string View { get; }
/// <summary>
/// Dashboards can be shown/hidden based on access rights
/// </summary>
IAccessRule[] AccessRules { get; }
}
}

View File

@@ -1,7 +1,7 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Configuration.Dashboard;
using Umbraco.Core.Dashboards;
using Umbraco.Core.Serialization;
namespace Umbraco.Core.Manifest

View File

@@ -1,12 +1,12 @@
using System;
using System.ComponentModel;
using Newtonsoft.Json;
using Umbraco.Core.Configuration.Dashboard;
using Umbraco.Core.Dashboards;
using Umbraco.Core.IO;
namespace Umbraco.Core.Manifest
{
public class ManifestDashboardDefinition
public class ManifestDashboardDefinition : IDashboardSection
{
private string _view;
@@ -32,5 +32,6 @@ namespace Umbraco.Core.Manifest
[JsonProperty("access")]
public IAccessRule[] AccessRules { get; set; } = Array.Empty<IAccessRule>();
}
}

View File

@@ -211,26 +211,6 @@
<Compile Include="ConfigsExtensions.cs" />
<Compile Include="Configuration\Configs.cs" />
<Compile Include="Configuration\CoreDebug.cs" />
<Compile Include="Configuration\Dashboard\AccessElement.cs" />
<Compile Include="Configuration\Dashboard\AccessRule.cs" />
<Compile Include="Configuration\Dashboard\AccessRuleType.cs" />
<Compile Include="Configuration\Dashboard\AreaCollection.cs" />
<Compile Include="Configuration\Dashboard\AreaElement.cs" />
<Compile Include="Configuration\Dashboard\AreasElement.cs" />
<Compile Include="Configuration\Dashboard\ControlCollection.cs" />
<Compile Include="Configuration\Dashboard\ControlElement.cs" />
<Compile Include="Configuration\Dashboard\DashboardSection.cs" />
<Compile Include="Configuration\Dashboard\IAccess.cs" />
<Compile Include="Configuration\Dashboard\IAccessRule.cs" />
<Compile Include="Configuration\Dashboard\IArea.cs" />
<Compile Include="Configuration\Dashboard\IDashboardControl.cs" />
<Compile Include="Configuration\Dashboard\IDashboardSection.cs" />
<Compile Include="Configuration\Dashboard\IDashboardTab.cs" />
<Compile Include="Configuration\Dashboard\ISection.cs" />
<Compile Include="Configuration\Dashboard\SectionCollection.cs" />
<Compile Include="Configuration\Dashboard\SectionElement.cs" />
<Compile Include="Configuration\Dashboard\TabCollection.cs" />
<Compile Include="Configuration\Dashboard\TabElement.cs" />
<Compile Include="Configuration\FileSystemProviderElement.cs" />
<Compile Include="Configuration\FileSystemProviderElementCollection.cs" />
<Compile Include="Configuration\FileSystemProvidersSection.cs" />
@@ -331,6 +311,10 @@
<Compile Include="Constants.cs" />
<Compile Include="Composing\RegisterExtensions.cs" />
<Compile Include="ContentVariationExtensions.cs" />
<Compile Include="Dashboards\AccessRule.cs" />
<Compile Include="Dashboards\AccessRuleType.cs" />
<Compile Include="Dashboards\IAccessRule.cs" />
<Compile Include="Dashboards\IDashboardSection.cs" />
<Compile Include="DisposableObjectSlim.cs" />
<Compile Include="Events\ExportedMemberEventArgs.cs" />
<Compile Include="Events\RolesEventArgs.cs" />

View File

@@ -1,123 +1,124 @@
using System.Configuration;
using System;
using System.Configuration;
using System.IO;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core.Configuration.Dashboard;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Dashboards;
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"));
//[Obsolete("Dashboard tests not in config/XML", true)]
//[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);
// var fileMap = new ExeConfigurationFileMap() { ExeConfigFilename = config.FullName };
// var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
SettingsSection = configuration.GetSection("umbracoConfiguration/dashBoard") as DashboardSection;
// SettingsSection = configuration.GetSection("umbracoConfiguration/dashBoard") as DashboardSection;
Assert.IsNotNull(SettingsSection);
}
// Assert.IsNotNull(SettingsSection);
// }
protected IDashboardSection SettingsSection { get; private set; }
// protected IDashboardSection SettingsSection { get; private set; }
[Test]
public void Test_Sections()
{
Assert.AreEqual(5, SettingsSection.Sections.Count());
// [Test]
// public void Test_Sections()
// {
// Assert.AreEqual(5, SettingsSection.Sections.Count());
Assert.AreEqual("StartupSettingsDashboardSection", SettingsSection.Sections.ElementAt(0).Alias);
Assert.AreEqual("StartupDeveloperDashboardSection", SettingsSection.Sections.ElementAt(1).Alias);
Assert.AreEqual("StartupMediaDashboardSection", SettingsSection.Sections.ElementAt(2).Alias);
Assert.AreEqual("StartupDashboardSection", SettingsSection.Sections.ElementAt(3).Alias);
Assert.AreEqual("StartupMemberDashboardSection", SettingsSection.Sections.ElementAt(4).Alias);
}
// Assert.AreEqual("StartupSettingsDashboardSection", SettingsSection.Sections.ElementAt(0).Alias);
// Assert.AreEqual("StartupDeveloperDashboardSection", SettingsSection.Sections.ElementAt(1).Alias);
// Assert.AreEqual("StartupMediaDashboardSection", SettingsSection.Sections.ElementAt(2).Alias);
// Assert.AreEqual("StartupDashboardSection", SettingsSection.Sections.ElementAt(3).Alias);
// Assert.AreEqual("StartupMemberDashboardSection", SettingsSection.Sections.ElementAt(4).Alias);
// }
[Test]
public void Test_Section_Area()
{
Assert.AreEqual("settings", SettingsSection.Sections.ElementAt(0).Areas.First());
Assert.AreEqual("developer", SettingsSection.Sections.ElementAt(1).Areas.First());
Assert.AreEqual("media", SettingsSection.Sections.ElementAt(2).Areas.First());
Assert.AreEqual("content", SettingsSection.Sections.ElementAt(3).Areas.First());
Assert.AreEqual("default", SettingsSection.Sections.ElementAt(4).Areas.First());
Assert.AreEqual("member", SettingsSection.Sections.ElementAt(4).Areas.Last());
}
// [Test]
// public void Test_Section_Area()
// {
// Assert.AreEqual("settings", SettingsSection.Sections.ElementAt(0).Areas.First());
// Assert.AreEqual("developer", SettingsSection.Sections.ElementAt(1).Areas.First());
// Assert.AreEqual("media", SettingsSection.Sections.ElementAt(2).Areas.First());
// Assert.AreEqual("content", SettingsSection.Sections.ElementAt(3).Areas.First());
// Assert.AreEqual("default", SettingsSection.Sections.ElementAt(4).Areas.First());
// Assert.AreEqual("member", SettingsSection.Sections.ElementAt(4).Areas.Last());
// }
[Test]
public void Test_Section_Access()
{
// [Test]
// public void Test_Section_Access()
// {
Assert.AreEqual(3, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.Count());
// Assert.AreEqual(3, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.Count());
Assert.AreEqual("translator", SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(0).Value);
Assert.AreEqual(AccessRuleType.Deny, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(0).Type);
Assert.AreEqual("hello", SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(1).Value);
Assert.AreEqual(AccessRuleType.Grant, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(1).Type);
Assert.AreEqual("world", SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(2).Value);
Assert.AreEqual(AccessRuleType.GrantBySection, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(2).Type);
}
// Assert.AreEqual("translator", SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(0).Value);
// Assert.AreEqual(AccessRuleType.Deny, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(0).Type);
// Assert.AreEqual("hello", SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(1).Value);
// Assert.AreEqual(AccessRuleType.Grant, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(1).Type);
// Assert.AreEqual("world", SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(2).Value);
// Assert.AreEqual(AccessRuleType.GrantBySection, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(2).Type);
// }
[Test]
public void Test_Section_Tabs()
{
//Element 0 Alias "StartupSettingsDashboardSection"
Assert.AreEqual(2, SettingsSection.Sections.ElementAt(0).Tabs.Count());
// [Test]
// public void Test_Section_Tabs()
// {
// //Element 0 Alias "StartupSettingsDashboardSection"
// Assert.AreEqual(2, SettingsSection.Sections.ElementAt(0).Tabs.Count());
//Element 1 Alias "StartupDeveloperDashboardSection"
Assert.AreEqual(1, SettingsSection.Sections.ElementAt(1).Tabs.Count());
// //Element 1 Alias "StartupDeveloperDashboardSection"
// Assert.AreEqual(1, SettingsSection.Sections.ElementAt(1).Tabs.Count());
//Element 2 Alias "StartupMediaDashboardSection"
Assert.AreEqual(2, SettingsSection.Sections.ElementAt(2).Tabs.Count());
// //Element 2 Alias "StartupMediaDashboardSection"
// Assert.AreEqual(2, SettingsSection.Sections.ElementAt(2).Tabs.Count());
//Element 3 Alias "StartupDashboardSection"
Assert.AreEqual(3, SettingsSection.Sections.ElementAt(3).Tabs.Count());
// //Element 3 Alias "StartupDashboardSection"
// Assert.AreEqual(3, SettingsSection.Sections.ElementAt(3).Tabs.Count());
//Element 4 Alias "StartupMemberDashboardSection"
Assert.AreEqual(1, SettingsSection.Sections.ElementAt(4).Tabs.Count());
// //Element 4 Alias "StartupMemberDashboardSection"
// Assert.AreEqual(1, SettingsSection.Sections.ElementAt(4).Tabs.Count());
}
// }
[Test]
public void Test_Tab()
{
Assert.AreEqual("Get Started", SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Caption);
Assert.AreEqual(2, SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.Count());
}
// [Test]
// public void Test_Tab()
// {
// Assert.AreEqual("Get Started", SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Caption);
// Assert.AreEqual(2, SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.Count());
// }
[Test]
public void Test_Tab_Access()
{
Assert.AreEqual(1, SettingsSection.Sections.ElementAt(2).Tabs.ElementAt(1).AccessRights.Rules.Count());
Assert.AreEqual(AccessRuleType.Grant, SettingsSection.Sections.ElementAt(2).Tabs.ElementAt(1).AccessRights.Rules.ElementAt(0).Type);
Assert.AreEqual("admin", SettingsSection.Sections.ElementAt(2).Tabs.ElementAt(1).AccessRights.Rules.ElementAt(0).Value);
}
// [Test]
// public void Test_Tab_Access()
// {
// Assert.AreEqual(1, SettingsSection.Sections.ElementAt(2).Tabs.ElementAt(1).AccessRights.Rules.Count());
// Assert.AreEqual(AccessRuleType.Grant, SettingsSection.Sections.ElementAt(2).Tabs.ElementAt(1).AccessRights.Rules.ElementAt(0).Type);
// Assert.AreEqual("admin", SettingsSection.Sections.ElementAt(2).Tabs.ElementAt(1).AccessRights.Rules.ElementAt(0).Value);
// }
[Test]
public void Test_Control()
{
Assert.AreEqual("hello", SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(0).PanelCaption);
Assert.AreEqual("views/dashboard/settings/settingsdashboardintro.html",
SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(0).ControlPath);
// [Test]
// public void Test_Control()
// {
// Assert.AreEqual("hello", SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(0).PanelCaption);
// Assert.AreEqual("views/dashboard/settings/settingsdashboardintro.html",
// SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(0).ControlPath);
Assert.AreEqual("", SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(1).PanelCaption);
Assert.AreEqual("views/dashboard/settings/settingsdashboardvideos.html",
SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(1).ControlPath);
}
// Assert.AreEqual("", SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(1).PanelCaption);
// Assert.AreEqual("views/dashboard/settings/settingsdashboardvideos.html",
// SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(1).ControlPath);
// }
[Test]
public void Test_Control_Access()
{
Assert.AreEqual(2, SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.Count());
Assert.AreEqual(AccessRuleType.Deny, SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(0).Type);
Assert.AreEqual("editor", SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(0).Value);
Assert.AreEqual(AccessRuleType.Deny, SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(1).Type);
Assert.AreEqual("writer", SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(1).Value);
}
}
// [Test]
// public void Test_Control_Access()
// {
// Assert.AreEqual(2, SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.Count());
// Assert.AreEqual(AccessRuleType.Deny, SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(0).Type);
// Assert.AreEqual("editor", SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(0).Value);
// Assert.AreEqual(AccessRuleType.Deny, SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(1).Type);
// Assert.AreEqual("writer", SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(1).Value);
// }
//}
}

View File

@@ -7,12 +7,12 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration.Dashboard;
using Umbraco.Core.Logging;
using Umbraco.Core.Manifest;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.PropertyEditors.Validators;
using Umbraco.Core.Services;
using Umbraco.Core.Dashboards;
namespace Umbraco.Tests.Manifest
{

View File

@@ -937,7 +937,7 @@
},
"ansi-colors": {
"version": "1.1.0",
"resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
"integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
"dev": true,
"requires": {
@@ -955,7 +955,7 @@
},
"ansi-escapes": {
"version": "3.1.0",
"resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
"integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
"dev": true
},
@@ -1170,7 +1170,7 @@
"array-slice": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
"integrity": "sha1-42jqFfibxwaff/uJrsOmx9SsItQ=",
"integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==",
"dev": true
},
"array-sort": {
@@ -1216,7 +1216,7 @@
"arraybuffer.slice": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
"integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=",
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==",
"dev": true
},
"asap": {
@@ -1269,7 +1269,7 @@
"async-limiter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
"dev": true
},
"asynckit": {
@@ -2379,7 +2379,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
@@ -2459,7 +2459,7 @@
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"dev": true
},
"continuable-cache": {
@@ -2502,7 +2502,7 @@
"core-js": {
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
"integrity": "sha1-+XJgj/DOrWi4QaFqky0LGDeRgU4=",
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==",
"dev": true
},
"core-util-is": {
@@ -3425,7 +3425,7 @@
"doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
"integrity": "sha1-XNAfwQFiG0LEzX9dGmYkNxbT850=",
"integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"dev": true,
"requires": {
"esutils": "^2.0.2"
@@ -3953,7 +3953,7 @@
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
@@ -3989,7 +3989,7 @@
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
@@ -4153,7 +4153,7 @@
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"optional": true
}
@@ -4248,7 +4248,7 @@
"eslint-scope": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz",
"integrity": "sha1-UL8wcekzi83EMzF5Sgy1M/ATYXI=",
"integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==",
"dev": true,
"requires": {
"esrecurse": "^4.1.0",
@@ -4258,13 +4258,13 @@
"eslint-utils": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
"integrity": "sha1-moUbqJ7nxGA0b5fPiTnHKYgn5RI=",
"integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
"dev": true
},
"eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
"integrity": "sha1-PzGA+y4pEBdxastMnW1bXDSmqB0=",
"integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
"dev": true
},
"espree": {
@@ -4287,7 +4287,7 @@
"esquery": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
"integrity": "sha1-QGxRZYsfWZGl+bYrHcJbAOPlxwg=",
"integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
"dev": true,
"requires": {
"estraverse": "^4.0.0"
@@ -4296,7 +4296,7 @@
"esrecurse": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
"integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=",
"integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
"dev": true,
"requires": {
"estraverse": "^4.1.0"
@@ -4356,7 +4356,7 @@
"eventemitter3": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz",
"integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=",
"integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==",
"dev": true
},
"exec-buffer": {
@@ -4571,7 +4571,7 @@
"fill-range": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
"integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==",
"integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=",
"dev": true,
"requires": {
"is-number": "^2.1.0",
@@ -5891,7 +5891,7 @@
"global-modules": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
"integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=",
"integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
"dev": true,
"requires": {
"global-prefix": "^1.0.1",
@@ -6461,7 +6461,7 @@
"gulp-eslint": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-5.0.0.tgz",
"integrity": "sha1-KiaECV93Syz3kxAmIHjFbMehK1I=",
"integrity": "sha512-9GUqCqh85C7rP9120cpxXuZz2ayq3BZc85pCTuPJS03VQYxne0aWPIXWx6LSvsGPa3uRqtSO537vaugOh+5cXg==",
"dev": true,
"requires": {
"eslint": "^5.0.1",
@@ -7415,7 +7415,7 @@
"has-binary2": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
"integrity": "sha1-d3asYn8+p3JQz8My2rfd9eT10R0=",
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
"dev": true,
"requires": {
"isarray": "2.0.1"
@@ -7566,7 +7566,7 @@
"http-proxy": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz",
"integrity": "sha1-etOElGWPhGBeL220Q230EPTlvpo=",
"integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==",
"dev": true,
"requires": {
"eventemitter3": "^3.0.0",
@@ -7860,7 +7860,7 @@
"is-absolute": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
"integrity": "sha1-OV4a6EsR8mrReV5zwXN45IowFXY=",
"integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
"dev": true,
"requires": {
"is-relative": "^1.0.0",
@@ -8155,7 +8155,7 @@
"is-relative": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
"integrity": "sha1-obtpNc6MXboei5dUubLcwCDiJg0=",
"integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
"dev": true,
"requires": {
"is-unc-path": "^1.0.0"
@@ -8164,7 +8164,7 @@
"is-resolvable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
"integrity": "sha1-+xj4fOH+uSUWnJpAfBkxijIG7Yg=",
"integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
"dev": true
},
"is-retry-allowed": {
@@ -8212,7 +8212,7 @@
"is-unc-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
"integrity": "sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=",
"integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
"dev": true,
"requires": {
"unc-path-regex": "^0.1.2"
@@ -8369,7 +8369,7 @@
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"json-stable-stringify-without-jsonify": {
@@ -8496,7 +8496,7 @@
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
@@ -9146,7 +9146,7 @@
"make-iterator": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
"integrity": "sha1-KbM/MSqo9UfEpeSQ9Wr87JkTOtY=",
"integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
"dev": true,
"requires": {
"kind-of": "^6.0.2"
@@ -9327,7 +9327,7 @@
"mimic-fn": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
"integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=",
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
"dev": true
},
"minimatch": {
@@ -12855,7 +12855,7 @@
"pluralize": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
"integrity": "sha1-KYuJ34uTsCIdv0Ia0rGx6iP8Z3c=",
"integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
"dev": true
},
"posix-character-classes": {
@@ -13345,7 +13345,7 @@
"qjobs": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
"integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=",
"integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
"dev": true
},
"qs": {
@@ -13541,7 +13541,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
@@ -14039,7 +14039,7 @@
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"sax": {
@@ -14226,7 +14226,7 @@
"setprototypeof": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
"integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=",
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
"dev": true
},
"shebang-command": {
@@ -14516,7 +14516,7 @@
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
@@ -14796,7 +14796,7 @@
"stream-consume": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz",
"integrity": "sha1-0721mMK9CugrjKx6xQsRB6eZbEg=",
"integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==",
"dev": true
},
"stream-shift": {
@@ -14808,7 +14808,7 @@
"streamroller": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz",
"integrity": "sha1-odG3z4PTmvsNYwSaWsv5NJO99ks=",
"integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==",
"dev": true,
"requires": {
"date-format": "^1.2.0",
@@ -14835,7 +14835,7 @@
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
@@ -14850,7 +14850,7 @@
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -14867,7 +14867,7 @@
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
@@ -15409,7 +15409,7 @@
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
"dev": true,
"requires": {
"os-tmpdir": "~1.0.2"
@@ -15573,7 +15573,7 @@
"type-is": {
"version": "1.6.16",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
"integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=",
"integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
"dev": true,
"requires": {
"media-typer": "0.3.0",
@@ -15615,7 +15615,7 @@
"ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
"integrity": "sha1-n+FTahCmZKZSZqHjzPhf02MCvJw=",
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
"dev": true
},
"unc-path-regex": {
@@ -15777,13 +15777,13 @@
"upath": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz",
"integrity": "sha1-NSVll+RqWB20eT0M5H+prr/J+r0=",
"integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==",
"dev": true
},
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
"integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
@@ -16218,7 +16218,7 @@
"ws": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
"integrity": "sha1-8c+E/i1ekB686U767OeF8YeiKPI=",
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
"dev": true,
"requires": {
"async-limiter": "~1.0.0",

View File

@@ -21,8 +21,7 @@
<div ng-repeat="property in tab.properties">
<div class="clearfix">
<h3 ng-show="property.caption">{{property.caption}}</h3>
<div ng-include="property.path"></div>
<div ng-include="property.view"></div>
</div>
</div>

View File

@@ -178,6 +178,7 @@
<Content Include="App_Plugins\ModelsBuilder\modelsbuilder.controller.js" />
<Content Include="App_Plugins\ModelsBuilder\modelsbuilder.htm" />
<Content Include="App_Plugins\ModelsBuilder\modelsbuilder.resource.js" />
<Content Include="App_Plugins\Test\test.html" />
<Content Include="Config\grid.editors.config.js" />
<Content Include="Config\Lang\cs-CZ.user.xml" />
<Content Include="Config\Lang\da-DK.user.xml" />
@@ -205,6 +206,7 @@
<Content Include="Config\Splashes\noNodes.aspx" />
<Content Include="Umbraco\Install\Views\Web.config" />
<Content Include="App_Plugins\ModelsBuilder\package.manifest" />
<Content Include="App_Plugins\Test\package.manifest" />
<None Include="Config\404handlers.Release.config">
<DependentUpon>404handlers.config</DependentUpon>
</None>

View File

@@ -94,6 +94,7 @@
</control>
</tab>
</section>
<section alias="RedirectUrlManagement">
<areas>
<area>content</area>

View File

@@ -10,6 +10,7 @@ using Umbraco.Web.Routing;
using Umbraco.Web.ContentApps;
using Umbraco.Web.Tour;
using Umbraco.Web.Trees;
using Umbraco.Web.Dashboards;
// the namespace here is intentional - although defined in Umbraco.Web assembly,
// this class should be visible when using Umbraco.Core.Components, alongside
@@ -92,6 +93,13 @@ namespace Umbraco.Core.Components
public static BackOfficeSectionCollectionBuilder Sections(this Composition composition)
=> composition.WithCollectionBuilder<BackOfficeSectionCollectionBuilder>();
/// <summary>
/// Gets the backoffice dashboards collection builder.
/// </summary>
/// <param name="composition">The composition.</param>
public static DashboardCollectionBuilder Dashboards(this Composition composition)
=> composition.WithCollectionBuilder<DashboardCollectionBuilder>();
#endregion
#region Uniques

View File

@@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
namespace Umbraco.Web.Dashboards
{
[Weight(10)]
//[HideFromTypeFinder]
[DataContract]
public class ContentDashboard : IDashboardSection
{
[DataMember(Name = "name")]
public string Name => "Get Started";
[DataMember(Name = "alias")]
public string Alias => "contentIntro";
[IgnoreDataMember]
public string[] Sections => new string[] { "content" };
[DataMember(Name = "view")]
public string View => "views/dashboard/default/startupdashboardintro.html";
[IgnoreDataMember]
public IAccessRule[] AccessRules
{
get
{
//TODO: WB Not convinced these rules work correctly?!
var rules = new List<IAccessRule>();
rules.Add(new AccessRule { Type = AccessRuleType.Deny, Value = "translator" });
rules.Add(new AccessRule { Type = AccessRuleType.Grant, Value = "admin" });
return rules.ToArray();
}
}
}
}

View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
namespace Umbraco.Web.Dashboards
{
public class DashboardCollection : BuilderCollectionBase<IDashboardSection>
{
public DashboardCollection(IEnumerable<IDashboardSection> items)
: base(items)
{ }
}
}

View File

@@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
using Umbraco.Core.Manifest;
namespace Umbraco.Web.Dashboards
{
public class DashboardCollectionBuilder : WeightedCollectionBuilderBase<DashboardCollectionBuilder, DashboardCollection, IDashboardSection>
{
protected override DashboardCollectionBuilder This => this;
protected override IEnumerable<IDashboardSection> CreateItems(IFactory factory)
{
// get the manifest parser just-in-time - injecting it in the ctor would mean that
// simply getting the builder in order to configure the collection, would require
// its dependencies too, and that can create cycles or other oddities
var manifestParser = factory.GetInstance<ManifestParser>();
//TODO WB: We will need to re-sort items from package manifest with the C# Types
return base.CreateItems(factory).Concat(manifestParser.Manifest.Dashboards);
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
namespace Umbraco.Web.Dashboards
{
[Weight(20)]
[DataContract]
public class ExamineDashboard : IDashboardSection
{
[DataMember(Name = "name")]
public string Name => "Examine Management";
[DataMember(Name = "alias")]
public string Alias => "settingsExamine";
[IgnoreDataMember]
public string[] Sections => new string[] { "settings" };
[DataMember(Name = "view")]
public string View => "views/dashboard/settings/examinemanagement.html";
[IgnoreDataMember]
public IAccessRule[] AccessRules => Array.Empty<IAccessRule>();
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
namespace Umbraco.Web.Dashboards
{
[Weight(10)]
[DataContract]
public class FormsDashboard : IDashboardSection
{
[DataMember(Name = "name")]
public string Name => "Install Umbraco Forms";
[DataMember(Name = "alias")]
public string Alias => "formsInstall";
[IgnoreDataMember]
public string[] Sections => new string[] { "forms" };
[DataMember(Name = "view")]
public string View => "views/dashboard/forms/formsdashboardintro.html";
[IgnoreDataMember]
public IAccessRule[] AccessRules => Array.Empty<IAccessRule>();
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
namespace Umbraco.Web.Dashboards
{
[Weight(50)]
[DataContract]
public class HealthCheckDashboard : IDashboardSection
{
[DataMember(Name="name")]
public string Name => "Health Check";
[DataMember(Name = "alias")]
public string Alias => "settingsHealthCheck";
[IgnoreDataMember]
public string[] Sections => new string[] { "settings" };
[DataMember(Name = "view")]
public string View => "views/dashboard/settings/healthcheck.html";
[IgnoreDataMember]
public IAccessRule[] AccessRules => Array.Empty<IAccessRule>();
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
namespace Umbraco.Web.Dashboards
{
[Weight(10)]
[DataContract]
public class MediaDashboard : IDashboardSection
{
[DataMember(Name = "name")]
public string Name => "Content";
[DataMember(Name = "alias")]
public string Alias => "mediaFolderBrowser";
[IgnoreDataMember]
public string[] Sections => new string[] { "media" };
[DataMember(Name = "view")]
public string View => "views/dashboard/media/mediafolderbrowser.html";
[IgnoreDataMember]
public IAccessRule[] AccessRules => Array.Empty<IAccessRule>();
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
namespace Umbraco.Web.Dashboards
{
[Weight(10)]
[DataContract]
public class MembersDashboard : IDashboardSection
{
[DataMember(Name = "name")]
public string Name => "Get Started";
[DataMember(Name = "alias")]
public string Alias => "memberIntro";
[IgnoreDataMember]
public string[] Sections => new string[] { "member" };
[DataMember(Name = "view")]
public string View => "views/dashboard/members/membersdashboardvideos.html";
[IgnoreDataMember]
public IAccessRule[] AccessRules => Array.Empty<IAccessRule>();
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
namespace Umbraco.Web.Dashboards
{
[Weight(40)]
[DataContract]
public class ModelsBuilderDashboard : IDashboardSection
{
[DataMember(Name = "name")]
public string Name => "Models Builder";
[DataMember(Name = "alias")]
public string Alias => "settingsModelsBuilder";
[IgnoreDataMember]
public string[] Sections => new string[] { "settings" };
[DataMember(Name = "view")]
public string View => "/App_Plugins/ModelsBuilder/modelsbuilder.htm";
[IgnoreDataMember]
public IAccessRule[] AccessRules => Array.Empty<IAccessRule>();
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
namespace Umbraco.Web.Dashboards
{
[Weight(30)]
[DataContract]
public class PublishedStatusDashboard : IDashboardSection
{
[DataMember(Name = "name")]
public string Name => "Published Status";
[DataMember(Name = "alias")]
public string Alias => "settingsPublishedStatus";
[IgnoreDataMember]
public string[] Sections => new string[] { "settings" };
[DataMember(Name = "view")]
public string View => "views/dashboard/settings/publishedstatus.html";
[IgnoreDataMember]
public IAccessRule[] AccessRules => Array.Empty<IAccessRule>();
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
namespace Umbraco.Web.Dashboards
{
[Weight(20)]
[DataContract]
public class RedirectUrlDashboard : IDashboardSection
{
[DataMember(Name = "name")]
public string Name => "Redirect URL Management";
[DataMember(Name = "alias")]
public string Alias => "contentRedirectManager";
[IgnoreDataMember]
public string[] Sections => new string[] { "content" };
[DataMember(Name = "view")]
public string View => "views/dashboard/content/redirecturls.html";
[IgnoreDataMember]
public IAccessRule[] AccessRules => Array.Empty<IAccessRule>();
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
namespace Umbraco.Web.Dashboards
{
[Weight(10)]
[DataContract]
public class SettingsDashboard : IDashboardSection
{
[DataMember(Name = "name")]
public string Name => "Welcome";
[DataMember(Name = "alias")]
public string Alias => "settingsWelcome";
[IgnoreDataMember]
public string[] Sections => new string[] { "settings" };
[DataMember(Name = "view")]
public string View => "views/dashboard/settings/settingsdashboardintro.html";
[IgnoreDataMember]
public IAccessRule[] AccessRules => Array.Empty<IAccessRule>();
}
}

View File

@@ -16,6 +16,8 @@ using Umbraco.Web.WebApi.Filters;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Core.Dashboards;
using Umbraco.Web.Services;
namespace Umbraco.Web.Editors
{
@@ -27,7 +29,7 @@ namespace Umbraco.Web.Editors
[WebApi.UmbracoAuthorize]
public class DashboardController : UmbracoApiController
{
private readonly Dashboards _dashboards;
private readonly IDashboardService _dashboardService;
/// <summary>
/// Initializes a new instance of the <see cref="DashboardController"/> with auto dependencies.
@@ -38,10 +40,10 @@ namespace Umbraco.Web.Editors
/// <summary>
/// Initializes a new instance of the <see cref="DashboardController"/> with all its dependencies.
/// </summary>
public DashboardController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, Dashboards dashboards)
public DashboardController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, IDashboardService dashboardService)
: base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState)
{
_dashboards = dashboards;
_dashboardService = dashboardService;
}
//we have just one instance of HttpClient shared for the entire application
@@ -79,7 +81,7 @@ namespace Umbraco.Web.Editors
}
catch (HttpRequestException ex)
{
Logger.Error<DashboardController>(ex.InnerException ?? ex, "Error getting dashboard content from '{Url}'", url);
Logger.Error<DashboardController>(ex.InnerException ?? ex, "Error getting dashboard content from {Url}", url);
//it's still new JObject() - we return it like this to avoid error codes which triggers UI warnings
AppCaches.RuntimeCache.InsertCacheItem<JObject>(key, () => result, new TimeSpan(0, 5, 0));
@@ -117,7 +119,7 @@ namespace Umbraco.Web.Editors
}
catch (HttpRequestException ex)
{
Logger.Error<DashboardController>(ex.InnerException ?? ex, "Error getting dashboard CSS from '{Url}'", url);
Logger.Error<DashboardController>(ex.InnerException ?? ex, "Error getting dashboard CSS from {Url}", url);
//it's still string.Empty - we return it like this to avoid error codes which triggers UI warnings
AppCaches.RuntimeCache.InsertCacheItem<string>(key, () => result, new TimeSpan(0, 5, 0));
@@ -132,9 +134,9 @@ namespace Umbraco.Web.Editors
[ValidateAngularAntiForgeryToken]
[OutgoingEditorModelEvent]
public IEnumerable<Tab<DashboardControl>> GetDashboard(string section)
public IEnumerable<Tab<IDashboardSection>> GetDashboard(string section)
{
return _dashboards.GetDashboards(section, Security.CurrentUser);
return _dashboardService.GetDashboards(section, Security.CurrentUser);
}
}
}

View File

@@ -1,160 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Configuration.Dashboard;
using Umbraco.Core.IO;
using Umbraco.Core.Manifest;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Services;
namespace Umbraco.Web.Editors
{
public class Dashboards
{
private readonly ISectionService _sectionService;
private readonly IDashboardSection _dashboardSection;
private readonly ManifestParser _manifestParser;
public Dashboards(ISectionService sectionService, IDashboardSection dashboardSection, ManifestParser manifestParser)
{
_sectionService = sectionService ?? throw new ArgumentNullException(nameof(sectionService));
_dashboardSection = dashboardSection;
_manifestParser = manifestParser;
}
/// <summary>
/// Gets all dashboards, organized by section, for a user.
/// </summary>
public IDictionary<string, IEnumerable<Tab<DashboardControl>>> GetDashboards(IUser currentUser)
{
return _sectionService.GetSections().ToDictionary(x => x.Alias, x => GetDashboards(x.Alias, currentUser));
}
/// <summary>
/// Returns dashboards for a specific section, for a user.
/// </summary>
public IEnumerable<Tab<DashboardControl>> GetDashboards(string section, IUser currentUser)
{
var tabId = 1;
var configDashboards = GetDashboardsFromConfig(ref tabId, section, currentUser);
var pluginDashboards = GetDashboardsFromPlugins(ref tabId, section, currentUser);
// merge dashboards
// both collections contain tab.alias -> controls
var dashboards = configDashboards;
// until now, it was fine to have duplicate tab.aliases in configDashboard
// so... the rule should be - just merge whatever we get, don't be clever
dashboards.AddRange(pluginDashboards);
// re-sort by id
dashboards.Sort((tab1, tab2) => tab1.Id > tab2.Id ? 1 : 0);
// re-assign ids (why?)
var i = 1;
foreach (var tab in dashboards)
{
tab.Id = i++;
tab.IsActive = tab.Id == 1;
}
return configDashboards;
}
// note:
// in dashboard.config we have 'sections' which define 'tabs' for 'areas'
// and 'areas' are the true UI sections - and each tab can have more than
// one control
// in a manifest, we directly have 'dashboards' which map to a unique
// control in a tab
// gets all tabs & controls from the config file
private List<Tab<DashboardControl>> GetDashboardsFromConfig(ref int tabId, string section, IUser currentUser)
{
var tabs = new List<Tab<DashboardControl>>();
// disable packages section dashboard
if (section == "packages") return tabs;
foreach (var dashboardSection in _dashboardSection.Sections.Where(x => x.Areas.InvariantContains(section)))
{
// validate access to this section
if (!DashboardSecurity.AuthorizeAccess(dashboardSection, currentUser, _sectionService))
continue;
foreach (var tab in dashboardSection.Tabs)
{
// validate access to this tab
if (!DashboardSecurity.AuthorizeAccess(tab, currentUser, _sectionService))
continue;
var dashboardControls = new List<DashboardControl>();
foreach (var control in tab.Controls)
{
// validate access to this control
if (!DashboardSecurity.AuthorizeAccess(control, currentUser, _sectionService))
continue;
// create and add control
var dashboardControl = new DashboardControl
{
Caption = control.PanelCaption,
Path = IOHelper.FindFile(control.ControlPath.Trim())
};
if (dashboardControl.Path.InvariantEndsWith(".ascx"))
throw new NotSupportedException("Legacy UserControl (.ascx) dashboards are no longer supported.");
dashboardControls.Add(dashboardControl);
}
// create and add tab
tabs.Add(new Tab<DashboardControl>
{
Id = tabId++,
Alias = tab.Caption.ToSafeAlias(),
Label = tab.Caption,
Properties = dashboardControls
});
}
}
return tabs;
}
private List<Tab<DashboardControl>> GetDashboardsFromPlugins(ref int tabId, string section, IUser currentUser)
{
var tabs = new List<Tab<DashboardControl>>();
foreach (var dashboard in _manifestParser.Manifest.Dashboards.Where(x => x.Sections.InvariantContains(section)).OrderBy(x => x.Weight))
{
// validate access
if (!DashboardSecurity.CheckUserAccessByRules(currentUser, _sectionService, dashboard.AccessRules))
continue;
var dashboardControl = new DashboardControl
{
Caption = "",
Path = IOHelper.FindFile(dashboard.View.Trim())
};
if (dashboardControl.Path.InvariantEndsWith(".ascx"))
throw new NotSupportedException("Legacy UserControl (.ascx) dashboards are no longer supported.");
tabs.Add(new Tab<DashboardControl>
{
Id = tabId++,
Alias = dashboard.Alias.ToSafeAlias(),
Label = dashboard.Name,
Properties = new[] { dashboardControl }
});
}
return tabs;
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Web.Http.Filters;
using Umbraco.Core.Dashboards;
using Umbraco.Core.Events;
using Umbraco.Web.Models.ContentEditing;
@@ -14,9 +15,9 @@ namespace Umbraco.Web.Editors
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<MediaItemDisplay>> SendingMediaModel;
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<MemberDisplay>> SendingMemberModel;
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<UserDisplay>> SendingUserModel;
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<IEnumerable<Tab<DashboardControl>>>> SendingDashboardModel;
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<IEnumerable<Tab<IDashboardSection>>>> SendingDashboardModel;
private static void OnSendingDashboardModel(HttpActionExecutedContext sender, EditorModelEventArgs<IEnumerable<Tab<DashboardControl>>> e)
private static void OnSendingDashboardModel(HttpActionExecutedContext sender, EditorModelEventArgs<IEnumerable<Tab<IDashboardSection>>> e)
{
var handler = SendingDashboardModel;
handler?.Invoke(sender, e);
@@ -65,8 +66,8 @@ namespace Umbraco.Web.Editors
if (e.Model is UserDisplay)
OnSendingUserModel(sender, new EditorModelEventArgs<UserDisplay>(e));
if (e.Model is IEnumerable<Tab<DashboardControl>>)
OnSendingDashboardModel(sender, new EditorModelEventArgs<IEnumerable<Tab<DashboardControl>>>(e));
if (e.Model is IEnumerable<IDashboardSection>)
OnSendingDashboardModel(sender, new EditorModelEventArgs<IEnumerable<Tab<IDashboardSection>>>(e));
}
}
}

View File

@@ -22,15 +22,15 @@ namespace Umbraco.Web.Editors
[PluginController("UmbracoApi")]
public class SectionController : UmbracoAuthorizedJsonController
{
private readonly Dashboards _dashboards;
private readonly IDashboardService _dashboardService;
private readonly ISectionService _sectionService;
private readonly ITreeService _treeService;
public SectionController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState,
Dashboards dashboards, ISectionService sectionService, ITreeService treeService)
IDashboardService dashboardService, ISectionService sectionService, ITreeService treeService)
: base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState)
{
_dashboards = dashboards;
_dashboardService = dashboardService;
_sectionService = sectionService;
_treeService = treeService;
}
@@ -48,7 +48,7 @@ namespace Umbraco.Web.Editors
ControllerContext = ControllerContext
};
var dashboards = _dashboards.GetDashboards(Security.CurrentUser);
var dashboards = _dashboardService.GetDashboards(Security.CurrentUser);
//now we can add metadata for each section so that the UI knows if there's actually anything at all to render for
//a dashboard for a given section, then the UI can deal with it accordingly (i.e. redirect to the first tree)

View File

@@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace Umbraco.Web.Models.ContentEditing
{
[DataContract(Name = "control", Namespace = "")]
public class DashboardControl
{
[DataMember(Name = "path")]
public string Path { get; set; }
[DataMember(Name = "caption")]
public string Caption { get; set; }
}
}

View File

@@ -6,6 +6,7 @@ using Microsoft.AspNet.SignalR;
using Umbraco.Core;
using Umbraco.Core.Components;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
using Umbraco.Core.Dictionary;
using Umbraco.Core.Events;
using Umbraco.Core.Models.PublishedContent;
@@ -17,6 +18,7 @@ using Umbraco.Web.Actions;
using Umbraco.Web.Cache;
using Umbraco.Web.Composing.Composers;
using Umbraco.Web.ContentApps;
using Umbraco.Web.Dashboards;
using Umbraco.Web.Dictionary;
using Umbraco.Web.Editors;
using Umbraco.Web.Features;
@@ -95,14 +97,13 @@ namespace Umbraco.Web.Runtime
composition.RegisterUnique<ITreeService, TreeService>();
composition.RegisterUnique<ISectionService, SectionService>();
composition.RegisterUnique<IDashboardService, DashboardService>();
composition.RegisterUnique<IExamineManager>(factory => ExamineManager.Instance);
// configure the container for web
composition.ConfigureForWeb();
composition.RegisterUnique<Dashboards>();
composition
.ComposeUmbracoControllers(GetType().Assembly)
.SetDefaultRenderMvcController<RenderMvcController>(); // default controller for template views
@@ -202,6 +203,25 @@ namespace Umbraco.Web.Runtime
.Append<MembersBackOfficeSection>()
.Append<TranslationBackOfficeSection>();
// register core CMS dashboards as types - will be ordered by weight attribute & merged with package.manifest dashboards
// TODO WB Maybe use typeloader?!
composition.WithCollectionBuilder<DashboardCollectionBuilder>()
.Add(composition.TypeLoader.GetTypes<IDashboardSection>());
//.Add<ContentDashboard>()
//.Add<RedirectUrlDashboard>()
//.Add<MediaDashboard>()
//.Add<SettingsDashboard>()
//.Add<ExamineDashboard>()
//.Add<PublishedStatusDashboard>()
//.Add<ModelsBuilderDashboard>()
//.Add<HealthCheckDashboard>()
//.Add<MembersDashboard>()
//.Add<FormsDashboard>();
// register back office trees
foreach (var treeControllerType in umbracoApiControllerTypes
.Where(x => typeof(TreeControllerBase).IsAssignableFrom(x)))

View File

@@ -1,65 +1,63 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Configuration.Dashboard;
using Umbraco.Core.Dashboards;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
using Umbraco.Web.Services;
using Umbraco.Web.Dashboards;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Editors
namespace Umbraco.Web.Services
{
/// <summary>
/// A utility class for determine dashboard security
/// </summary>
internal class DashboardSecurity
internal class DashboardService : IDashboardService
{
//TODO: Unit test all this!!! :/
private readonly ISectionService _sectionService;
private readonly DashboardCollection _dashboardCollection;
public static bool AuthorizeAccess(ISection dashboardSection, IUser user, ISectionService sectionService)
public DashboardService(ISectionService sectionService, DashboardCollection dashboardCollection)
{
return CheckUserAccessByRules(user, sectionService, dashboardSection.AccessRights.Rules);
_sectionService = sectionService ?? throw new ArgumentNullException(nameof(sectionService));
_dashboardCollection = dashboardCollection ?? throw new ArgumentNullException(nameof(dashboardCollection));
}
public static bool AuthorizeAccess(IDashboardTab dashboardTab, IUser user, ISectionService sectionService)
{
return CheckUserAccessByRules(user, sectionService, dashboardTab.AccessRights.Rules);
}
public static bool AuthorizeAccess(IDashboardControl dashboardControl, IUser user, ISectionService sectionService)
/// <inheritdoc />
public IEnumerable<Tab<IDashboardSection>> GetDashboards(string section, IUser currentUser)
{
return CheckUserAccessByRules(user, sectionService, dashboardControl.AccessRights.Rules);
}
var tabs = new List<Tab<IDashboardSection>>();
var tabId = 0;
private static (IAccessRule[], IAccessRule[], IAccessRule[]) GroupRules(IEnumerable<IAccessRule> rules)
{
IAccessRule[] denyRules = null, grantRules = null, grantBySectionRules = null;
var groupedRules = rules.GroupBy(x => x.Type);
foreach (var group in groupedRules)
foreach (var dashboard in _dashboardCollection.Where(x => x.Sections.InvariantContains(section)))
{
var a = group.ToArray();
switch (group.Key)
// validate access
if (!CheckUserAccessByRules(currentUser, _sectionService, dashboard.AccessRules))
continue;
if (dashboard.View.InvariantEndsWith(".ascx"))
throw new NotSupportedException("Legacy UserControl (.ascx) dashboards are no longer supported.");
var dashboards = new List<IDashboardSection>();
dashboards.Add(dashboard);
tabs.Add(new Tab<IDashboardSection>()
{
case AccessRuleType.Deny:
denyRules = a;
break;
case AccessRuleType.Grant:
grantRules = a;
break;
case AccessRuleType.GrantBySection:
grantBySectionRules = a;
break;
default:
throw new Exception("panic");
}
Id = tabId++,
Label = dashboard.Name,
Alias = dashboard.Alias,
Properties = dashboards
});
}
return (denyRules ?? Array.Empty<IAccessRule>(), grantRules ?? Array.Empty<IAccessRule>(), grantBySectionRules ?? Array.Empty<IAccessRule>());
return tabs;
}
public static bool CheckUserAccessByRules(IUser user, ISectionService sectionService, IEnumerable<IAccessRule> rules)
/// <inheritdoc />
public IDictionary<string, IEnumerable<Tab<IDashboardSection>>> GetDashboards(IUser currentUser)
{
return _sectionService.GetSections().ToDictionary(x => x.Alias, x => GetDashboards(x.Alias, currentUser));
}
private bool CheckUserAccessByRules(IUser user, ISectionService sectionService, IEnumerable<IAccessRule> rules)
{
if (user.Id == Constants.Security.SuperUserId)
return true;
@@ -111,5 +109,32 @@ namespace Umbraco.Web.Editors
return hasAccess;
}
private (IAccessRule[], IAccessRule[], IAccessRule[]) GroupRules(IEnumerable<IAccessRule> rules)
{
IAccessRule[] denyRules = null, grantRules = null, grantBySectionRules = null;
var groupedRules = rules.GroupBy(x => x.Type);
foreach (var group in groupedRules)
{
var a = group.ToArray();
switch (group.Key)
{
case AccessRuleType.Deny:
denyRules = a;
break;
case AccessRuleType.Grant:
grantRules = a;
break;
case AccessRuleType.GrantBySection:
grantBySectionRules = a;
break;
default:
throw new Exception("panic");
}
}
return (denyRules ?? Array.Empty<IAccessRule>(), grantRules ?? Array.Empty<IAccessRule>(), grantBySectionRules ?? Array.Empty<IAccessRule>());
}
}
}

View File

@@ -0,0 +1,27 @@
using System.Collections.Generic;
using Umbraco.Core.Dashboards;
using Umbraco.Core.Models.Membership;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Services
{
public interface IDashboardService
{
/// <summary>
/// Gets dashboard for a specific section/application
/// For a specific backoffice user
/// </summary>
/// <param name="section"></param>
/// <param name="currentUser"></param>
/// <returns></returns>
IEnumerable<Tab<IDashboardSection>> GetDashboards(string section, IUser currentUser);
/// <summary>
/// Gets all dashboards, organized by section, for a user.
/// </summary>
/// <param name="currentUser"></param>
/// <returns></returns>
IDictionary<string, IEnumerable<Tab<IDashboardSection>>> GetDashboards(IUser currentUser);
}
}

View File

@@ -122,6 +122,18 @@
<Compile Include="Composing\LightInject\LightInjectContainer.cs" />
<Compile Include="Components\BackOfficeUserAuditEventsComponent.cs" />
<Compile Include="ContentApps\ListViewContentAppFactory.cs" />
<Compile Include="Dashboards\ContentDashboard.cs" />
<Compile Include="Dashboards\DashboardCollection.cs" />
<Compile Include="Dashboards\DashboardCollectionBuilder.cs" />
<Compile Include="Dashboards\ExamineDashboard.cs" />
<Compile Include="Dashboards\FormsDashboard.cs" />
<Compile Include="Dashboards\HealthCheckDashboard.cs" />
<Compile Include="Dashboards\MediaDashboard.cs" />
<Compile Include="Dashboards\MembersDashboard.cs" />
<Compile Include="Dashboards\ModelsBuilderDashboard.cs" />
<Compile Include="Dashboards\PublishedStatusDashboard.cs" />
<Compile Include="Dashboards\RedirectUrlDashboard.cs" />
<Compile Include="Dashboards\SettingsDashboards.cs" />
<Compile Include="Editors\BackOfficePreviewModel.cs" />
<Compile Include="Editors\PackageController.cs" />
<Compile Include="Editors\KeepAliveController.cs" />
@@ -169,6 +181,8 @@
<Compile Include="Media\UploadAutoFillProperties.cs" />
<Compile Include="Models\ContentEditing\MacroDisplay.cs" />
<Compile Include="Models\ContentEditing\MacroParameterDisplay.cs" />
<Compile Include="Services\DashboardService.cs" />
<Compile Include="Services\IDashboardService.cs" />
<Compile Include="Trees\BackOfficeSectionCollectionBuilder.cs" />
<Compile Include="Trees\MediaBackOfficeSection.cs" />
<Compile Include="Trees\MembersBackOfficeSection.cs" />
@@ -244,7 +258,6 @@
<Compile Include="WebApi\SerializeVersionAttribute.cs" />
<Compile Include="WebApi\TrimModelBinder.cs" />
<Compile Include="Editors\CodeFileController.cs" />
<Compile Include="Editors\Dashboards.cs" />
<Compile Include="Editors\DictionaryController.cs" />
<Compile Include="Editors\EditorModelEventArgs.cs" />
<Compile Include="Editors\EditorValidatorCollection.cs" />
@@ -705,7 +718,6 @@
<Compile Include="Editors\Filters\ContentSaveValidationAttribute.cs" />
<Compile Include="Editors\ContentTypeControllerBase.cs" />
<Compile Include="Editors\DashboardController.cs" />
<Compile Include="Editors\DashboardSecurity.cs" />
<Compile Include="Editors\DataTypeController.cs" />
<Compile Include="Editors\DataTypeValidateAttribute.cs" />
<Compile Include="Editors\ImagesController.cs" />
@@ -826,7 +838,6 @@
<Compile Include="Editors\EntityController.cs" />
<Compile Include="Editors\MemberController.cs" />
<Compile Include="Editors\CurrentUserController.cs" />
<Compile Include="Models\ContentEditing\DashboardControl.cs" />
<Compile Include="Models\ContentEditing\DataTypeDisplay.cs" />
<Compile Include="Models\ContentEditing\DataTypeSave.cs" />
<Compile Include="Models\ContentEditing\DataTypeConfigurationFieldDisplay.cs" />