mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-11-28 09:22:55 +08:00
Implementing a navigation menu system for admin pages.
--HG-- extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4039716
This commit is contained in:
@@ -39,6 +39,7 @@ namespace Orchard.Tests.Packages.Users.Services {
|
||||
var databaseFileName = System.IO.Path.GetTempFileName();
|
||||
_sessionFactory = DataUtility.CreateSessionFactory(
|
||||
databaseFileName,
|
||||
typeof(UserRecord),
|
||||
typeof(ModelRecord),
|
||||
typeof(ModelTypeRecord));
|
||||
}
|
||||
|
||||
@@ -138,6 +138,9 @@
|
||||
<Compile Include="Records\Foo.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Stubs\StubHttpContext.cs" />
|
||||
<Compile Include="UI\Navigation\MenuItemComparerTests.cs" />
|
||||
<Compile Include="UI\Navigation\NavigationManagerTests.cs" />
|
||||
<Compile Include="UI\Navigation\PositionComparerTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Orchard\Orchard.csproj">
|
||||
|
||||
87
src/Orchard.Tests/UI/Navigation/MenuItemComparerTests.cs
Normal file
87
src/Orchard.Tests/UI/Navigation/MenuItemComparerTests.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web.Routing;
|
||||
using NUnit.Framework;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Tests.UI.Navigation {
|
||||
[TestFixture]
|
||||
public class MenuItemComparerTests {
|
||||
[Test]
|
||||
public void TextShouldCauseDifferenceAndNullRouteValuesAreEqual() {
|
||||
var item1 = new MenuItem { Text = "hello" };
|
||||
var item2 = new MenuItem { Text = "hello" };
|
||||
var item3 = new MenuItem { Text = "hello3" };
|
||||
AssertSameSameDifferent(item1, item2, item3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NullRouteValuesShouldNotEqualEmptyRouteValues() {
|
||||
var item1 = new MenuItem { Text = "hello" };
|
||||
var item2 = new MenuItem { Text = "hello" };
|
||||
var item3 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary() };
|
||||
var item4 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary() };
|
||||
AssertSameSameDifferent(item1, item2, item3);
|
||||
AssertSameSameDifferent(item3, item4, item1);
|
||||
}
|
||||
[Test]
|
||||
public void AdditionalPropertiesShouldMismatch() {
|
||||
var item1 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary(new { one = 1 }) };
|
||||
var item2 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary(new { one = 1 }) };
|
||||
var item3 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary(new { one = 1, two = 2 }) };
|
||||
AssertSameSameDifferent(item1, item2, item3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValueTypeShouldMismatch() {
|
||||
var item1 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary(new { one = 1 }) };
|
||||
var item2 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary(new { one = 1 }) };
|
||||
var item3 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary(new { one = "1" }) };
|
||||
AssertSameSameDifferent(item1, item2, item3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValuesShouldMismatch() {
|
||||
var item1 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary(new { one = "1", two = "2" }) };
|
||||
var item2 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary(new { one = "1", two = "2" }) };
|
||||
var item3 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary(new { one = "1", two = "3" }) };
|
||||
AssertSameSameDifferent(item1, item2, item3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PositionAndChildrenDontMatter() {
|
||||
var item1 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary(new { one = "1", two = "2" }) };
|
||||
var item2 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary(new { one = "1", two = "2" }), Position = "4.0" };
|
||||
var item3 = new MenuItem { Text = "hello", RouteValues = new RouteValueDictionary(new { one = "1", two = "2" }), Contained = new[] { new MenuItem() } };
|
||||
AssertSameSameSame(item1, item2, item3);
|
||||
}
|
||||
|
||||
private static void AssertSameSameDifferent(MenuItem item1, MenuItem item2, MenuItem item3) {
|
||||
var comparer = new MenuItemComparer();
|
||||
|
||||
Assert.That(comparer.Equals(item1, item2), Is.True);
|
||||
Assert.That(comparer.Equals(item1, item3), Is.False);
|
||||
Assert.That(comparer.Equals(item2, item3), Is.False);
|
||||
|
||||
Assert.That(comparer.GetHashCode(item1), Is.EqualTo(comparer.GetHashCode(item2)));
|
||||
// - hash inequality isn't really guaranteed, now that you mention it
|
||||
//Assert.That(comparer.GetHashCode(item1), Is.Not.EqualTo(comparer.GetHashCode(item3)));
|
||||
//Assert.That(comparer.GetHashCode(item2), Is.Not.EqualTo(comparer.GetHashCode(item3)));
|
||||
}
|
||||
|
||||
private static void AssertSameSameSame(MenuItem item1, MenuItem item2, MenuItem item3) {
|
||||
var comparer = new MenuItemComparer();
|
||||
|
||||
Assert.That(comparer.Equals(item1, item2), Is.True);
|
||||
Assert.That(comparer.Equals(item1, item3), Is.True);
|
||||
Assert.That(comparer.Equals(item2, item3), Is.True);
|
||||
|
||||
Assert.That(comparer.GetHashCode(item1), Is.EqualTo(comparer.GetHashCode(item2)));
|
||||
Assert.That(comparer.GetHashCode(item1), Is.EqualTo(comparer.GetHashCode(item3)));
|
||||
Assert.That(comparer.GetHashCode(item2), Is.EqualTo(comparer.GetHashCode(item3)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
80
src/Orchard.Tests/UI/Navigation/NavigationManagerTests.cs
Normal file
80
src/Orchard.Tests/UI/Navigation/NavigationManagerTests.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Tests.UI.Navigation {
|
||||
[TestFixture]
|
||||
public class NavigationManagerTests {
|
||||
[Test]
|
||||
public void EmptyMenuIfNameDoesntMatch() {
|
||||
var manager = new NavigationManager(new[] { new StubProvider() });
|
||||
|
||||
var menuItems = manager.BuildMenu("primary");
|
||||
Assert.That(menuItems.Count(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NavigationManagerShouldUseProvidersToBuildNamedMenu() {
|
||||
var manager = new NavigationManager(new[] { new StubProvider() });
|
||||
|
||||
var menuItems = manager.BuildMenu("admin");
|
||||
Assert.That(menuItems.Count(), Is.EqualTo(2));
|
||||
Assert.That(menuItems.First(), Has.Property("Text").EqualTo("Foo"));
|
||||
Assert.That(menuItems.Last(), Has.Property("Text").EqualTo("Bar"));
|
||||
Assert.That(menuItems.Last().Contained.Count(), Is.EqualTo(1));
|
||||
Assert.That(menuItems.Last().Contained.Single().Text, Is.EqualTo("Frap"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NavigationManagerShouldMergeAndOrderNavigation() {
|
||||
var manager = new NavigationManager(new INavigationProvider[] { new StubProvider(), new Stub2Provider() });
|
||||
|
||||
var menuItems = manager.BuildMenu("admin");
|
||||
Assert.That(menuItems.Count(), Is.EqualTo(3));
|
||||
|
||||
var item1 = menuItems.First();
|
||||
var item2 = menuItems.Skip(1).First();
|
||||
var item3 = menuItems.Skip(2).First();
|
||||
|
||||
Assert.That(item1.Text, Is.EqualTo("Foo"));
|
||||
Assert.That(item1.Position, Is.EqualTo("1.0"));
|
||||
Assert.That(item2.Text, Is.EqualTo("Bar"));
|
||||
Assert.That(item2.Position, Is.EqualTo("2.0"));
|
||||
Assert.That(item3.Text, Is.EqualTo("Frap"));
|
||||
Assert.That(item3.Position, Is.EqualTo("3.0"));
|
||||
|
||||
Assert.That(item2.Contained.Count(), Is.EqualTo(2));
|
||||
var subitem1 = item2.Contained.First();
|
||||
var subitem2 = item2.Contained.Last();
|
||||
Assert.That(subitem1.Text, Is.EqualTo("Quad"));
|
||||
Assert.That(subitem1.Position, Is.EqualTo("1.a"));
|
||||
Assert.That(subitem2.Text, Is.EqualTo("Frap"));
|
||||
Assert.That(subitem2.Position, Is.EqualTo("1.b"));
|
||||
|
||||
}
|
||||
|
||||
public class StubProvider : INavigationProvider {
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder
|
||||
.Add("Foo", "1.0")
|
||||
.Add("Bar", "2.0", x => x.Add("Frap", "1.b"));
|
||||
}
|
||||
}
|
||||
|
||||
public class Stub2Provider : INavigationProvider {
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder
|
||||
.Add("Frap", "3.0")
|
||||
.Add("Bar", "4.0", x => x.Add("Quad", "1.a"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
141
src/Orchard.Tests/UI/Navigation/PositionComparerTests.cs
Normal file
141
src/Orchard.Tests/UI/Navigation/PositionComparerTests.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Tests.UI.Navigation {
|
||||
[TestFixture]
|
||||
public class PositionComparerTests {
|
||||
private IComparer<string> _comparer;
|
||||
|
||||
[SetUp]
|
||||
public void Init() {
|
||||
_comparer = new PositionComparer();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void LessThanAndGreaterThanShouldBeBelowAndAboveZero() {
|
||||
var lessThan = StringComparer.InvariantCultureIgnoreCase.Compare("alpha", "beta");
|
||||
var greaterThan = StringComparer.InvariantCultureIgnoreCase.Compare("gamma", "delta");
|
||||
|
||||
Assert.That(lessThan, Is.LessThan(0));
|
||||
Assert.That(greaterThan, Is.GreaterThan(0));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void NullIsLessThanEmptyAndEmptyIsLessThanNonEmpty() {
|
||||
Assert.That(_comparer.Compare(null, ""), Is.LessThan(0));
|
||||
Assert.That(_comparer.Compare("", "5"), Is.LessThan(0));
|
||||
Assert.That(_comparer.Compare(null, "5"), Is.LessThan(0));
|
||||
|
||||
Assert.That(_comparer.Compare("", null), Is.GreaterThan(0));
|
||||
Assert.That(_comparer.Compare("5", ""), Is.GreaterThan(0));
|
||||
Assert.That(_comparer.Compare("5", null), Is.GreaterThan(0));
|
||||
|
||||
Assert.That(_comparer.Compare(null, null), Is.EqualTo(0));
|
||||
Assert.That(_comparer.Compare("", ""), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NumericValuesShouldCompareNumerically() {
|
||||
AssertLess("3", "5");
|
||||
AssertMore("8", "5");
|
||||
AssertSame("5", "5");
|
||||
AssertMore("100", "5");
|
||||
AssertSame("007", "7");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DotsSplitParts() {
|
||||
AssertLess("0500.3", "0500.5");
|
||||
AssertMore("0500.8", "0500.5");
|
||||
AssertSame("0500.5", "0500.5");
|
||||
AssertMore("0500.100", "0500.5");
|
||||
AssertSame("0500.007", "0500.7");
|
||||
|
||||
AssertLess("0500.3.0300", "0500.5.0300");
|
||||
AssertMore("0500.8.0300", "0500.5.0300");
|
||||
AssertSame("0500.5.0300", "0500.5.0300");
|
||||
AssertMore("0500.100.0300", "0500.5.0300");
|
||||
AssertSame("0500.007.0300", "0500.7.0300");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NumericValuesShouldComeBeforeNonNumeric() {
|
||||
AssertLess("5", "x");
|
||||
AssertLess("50", "50a");
|
||||
AssertLess("1.50", "1.50a");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NonNumericValuesCompareOrdinallyAndIgnoreCase() {
|
||||
AssertSame("x", "X");
|
||||
AssertLess("rt675x", "rt685x");
|
||||
AssertMore("ru675x", "rt675x");
|
||||
AssertLess("rt675x", "rt675y");
|
||||
|
||||
AssertSame("1.x.5", "1.X.5");
|
||||
AssertLess("1.rt675x.5", "1.rt685x.5");
|
||||
AssertMore("1.ru675x.5", "1.rt675x.5");
|
||||
AssertLess("1.rt675x.5", "1.rt675y.5");
|
||||
|
||||
AssertSame("x.5", "X.5");
|
||||
AssertLess("rt675x.5", "rt685x.5");
|
||||
AssertMore("ru675x.5", "rt675x.5");
|
||||
AssertLess("rt675x.5", "rt675y.5");
|
||||
|
||||
AssertSame("1.x", "1.X");
|
||||
AssertLess("1.rt675x", "1.rt685x");
|
||||
AssertMore("1.ru675x", "1.rt675x");
|
||||
AssertLess("1.rt675x", "1.rt675y");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LongerNonNumericShouldComeLater() {
|
||||
AssertLess("rt675x", "rt675xx");
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void EmptyBitsAreSafeAndShouldComeFirst() {
|
||||
AssertSame("1.2.3", "1.2.3");
|
||||
AssertSame(".1.2.3.", ".1.2.3.");
|
||||
AssertSame(".1..3.", ".1..3.");
|
||||
AssertLess("1..3", "1.2.3");
|
||||
AssertLess(".1..3.", ".1.2.3.");
|
||||
|
||||
AssertSame("a.b.c", "a.b.c");
|
||||
AssertSame(".a.b.c.", ".a.b.c.");
|
||||
AssertSame(".a..c.", ".a..c.");
|
||||
AssertLess("a..c", "a.b.c");
|
||||
AssertLess(".a..c.", ".a.b.c.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AdditionalNonEmptySegmentsShouldComeLater() {
|
||||
AssertLess("1.2", "1.2.3");
|
||||
AssertSame("1.2", "1.2.");
|
||||
|
||||
AssertLess("a.b", "a.b.c");
|
||||
AssertSame("a.b", "a.b.");
|
||||
|
||||
}
|
||||
|
||||
void AssertLess(string x, string y) {
|
||||
Assert.That(_comparer.Compare(x, y), Is.LessThan(0));
|
||||
Assert.That(_comparer.Compare(y, x), Is.GreaterThan(0));
|
||||
}
|
||||
void AssertMore(string x, string y) {
|
||||
Assert.That(_comparer.Compare(x, y), Is.GreaterThan(0));
|
||||
Assert.That(_comparer.Compare(y, x), Is.LessThan(0));
|
||||
}
|
||||
void AssertSame(string x, string y) {
|
||||
Assert.That(_comparer.Compare(x, y), Is.EqualTo(0));
|
||||
Assert.That(_comparer.Compare(y, x), Is.EqualTo(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/Orchard.Web/Packages/Orchard.CmsPages/AdminMenu.cs
Normal file
15
src/Orchard.Web/Packages/Orchard.CmsPages/AdminMenu.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.CmsPages {
|
||||
public class AdminMenu : INavigationProvider {
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add("Pages", "1",
|
||||
menu => menu
|
||||
.Add("Manage Pages", "1.0", item => item.Action("Index", "Admin", new { area = "Orchard.CmsPages" }))
|
||||
.Add("Add a Page", "1.1", item => item.Action("Create", "Admin", new { area = "Orchard.CmsPages" }))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,7 @@
|
||||
<Reference Include="System.Web.Mobile" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Controllers\TemplatesController.cs" />
|
||||
<Compile Include="Models\ContentItem.cs" />
|
||||
@@ -175,7 +176,7 @@
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>31696</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>65251</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>18755</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>65263</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
|
||||
14
src/Orchard.Web/Packages/Orchard.Media/AdminMenu.cs
Normal file
14
src/Orchard.Web/Packages/Orchard.Media/AdminMenu.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Media {
|
||||
public class AdminMenu : INavigationProvider {
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add("Media", "4",
|
||||
menu => menu
|
||||
.Add("Manage Folders", "1.0", item => item.Action("Index", "Admin", new { area = "Orchard.Media" }))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,7 @@
|
||||
<Reference Include="System.Web.Mobile" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Helpers\MediaHelpers.cs" />
|
||||
<Compile Include="Models\FolderNavigation.cs" />
|
||||
@@ -142,7 +143,7 @@
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>18754</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>65259</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
|
||||
14
src/Orchard.Web/Packages/Orchard.Roles/AdminMenu.cs
Normal file
14
src/Orchard.Web/Packages/Orchard.Roles/AdminMenu.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Roles {
|
||||
public class AdminMenu : INavigationProvider {
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add("Users", "5",
|
||||
menu => menu
|
||||
.Add("Manage Roles", "2.0", item => item.Action("Index", "Admin", new { area = "Orchard.Roles" }))
|
||||
.Add("Create a Role", "2.1", item => item.Action("Create", "Admin", new { area = "Orchard.Roles" })));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,7 @@
|
||||
<Reference Include="System.Web.Mobile" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Models\PermissionRecord.cs" />
|
||||
<Compile Include="Models\RoleRecord.cs" />
|
||||
|
||||
14
src/Orchard.Web/Packages/Orchard.Users/AdminMenu.cs
Normal file
14
src/Orchard.Web/Packages/Orchard.Users/AdminMenu.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Users {
|
||||
public class AdminMenu : INavigationProvider {
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add("Users", "5",
|
||||
menu => menu
|
||||
.Add("Manage Users", "1.0", item => item.Action("Index", "Admin", new { area = "Orchard.Users" }))
|
||||
.Add("Create New User", "1.1", item => item.Action("Create", "Admin", new { area = "Orchard.Users" })));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,7 @@
|
||||
<Compile Include="Models\UserRecord.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\MembershipService.cs" />
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="ViewModels\UserCreateViewModel.cs" />
|
||||
<Compile Include="ViewModels\UserEditViewModel.cs" />
|
||||
<Compile Include="ViewModels\UsersIndexViewModel.cs" />
|
||||
@@ -125,7 +126,7 @@
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>40373</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>65271</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<h2 class="separator">
|
||||
Edit User</h2>
|
||||
<p class="bottomSpacer">
|
||||
<%=Html.ActionLink("Users", "Index") %> > Edit #<%=Model.Id%> <strong><%=Html.Encode(Model.UserName)%></strong>
|
||||
<%=Html.ActionLink("Users", "Index") %> > Edit #<%= Model.Id%> <strong><%=Html.Encode(Model.UserName)%></strong>
|
||||
</p>
|
||||
</div>
|
||||
<div class="yui-u">
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>15061</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>65267</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>26570</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>65255</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
|
||||
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<AdminViewModel>" %>
|
||||
<%@ Import Namespace="Orchard.Mvc.ViewModels"%>
|
||||
|
||||
|
||||
</div><%-- yui-b --%>
|
||||
@@ -9,31 +10,15 @@
|
||||
<h4>
|
||||
Dashboard</h4>
|
||||
</div>
|
||||
<div class="leftNavMod">
|
||||
<h4>
|
||||
Pages</h4>
|
||||
<ul>
|
||||
<li><%=Html.ActionLink("Manage Pages", "Index", new {area="Orchard.CmsPages",controller="Admin"}) %></li>
|
||||
<li><%=Html.ActionLink("Add a Page", "Create", new {area="Orchard.CmsPages",controller="Admin"}) %></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="leftNavMod">
|
||||
<h4>
|
||||
Media</h4>
|
||||
<ul>
|
||||
<li><%=Html.ActionLink("Manage Folders", "Index", new {area="Orchard.Media",controller="Admin"}) %></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="leftNavMod">
|
||||
<h4>
|
||||
Users</h4>
|
||||
<ul>
|
||||
<li><%=Html.ActionLink("Manage Roles", "Index", new {area="Orchard.Roles",controller="Admin"}) %></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><%=Html.ActionLink("Add a Role", "Create", new {area="Orchard.Roles",controller="Admin"}) %></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<%if (Model.AdminMenu != null) {
|
||||
foreach (var menuSection in Model.AdminMenu) {%>
|
||||
<div class="leftNavMod"><h4><%=Html.Encode(menuSection.Text)%></h4><ul><%foreach (var menuItem in menuSection.Contained) { %>
|
||||
<li><%=Html.ActionLink(menuItem.Text, (string)menuItem.RouteValues["action"], menuItem.RouteValues)%></li>
|
||||
<%} %></ul></div>
|
||||
<%
|
||||
}
|
||||
}%>
|
||||
|
||||
</div>
|
||||
</div><%-- bd --%>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Mvc.ViewModels {
|
||||
public class AdminViewModel : BaseViewModel {
|
||||
public IEnumerable<MenuItem> AdminMenu { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,10 +158,17 @@
|
||||
<Compile Include="Mvc\Routes\RouteExtensions.cs" />
|
||||
<Compile Include="Mvc\ViewModels\AdminViewModel.cs" />
|
||||
<Compile Include="Mvc\ViewModels\BaseViewModel.cs" />
|
||||
<Compile Include="Notify\Notifier.cs" />
|
||||
<Compile Include="Notify\NotifierExtensions.cs" />
|
||||
<Compile Include="Notify\NotifyEntry.cs" />
|
||||
<Compile Include="Notify\NotifyFilter.cs" />
|
||||
<Compile Include="UI\Menus\AdminMenuFilter.cs" />
|
||||
<Compile Include="UI\Navigation\INavigationBuilder.cs" />
|
||||
<Compile Include="UI\Navigation\INavigationProvider.cs" />
|
||||
<Compile Include="UI\Navigation\MenuItem.cs" />
|
||||
<Compile Include="UI\Navigation\MenuItemComparer.cs" />
|
||||
<Compile Include="UI\Navigation\NavigationManager.cs" />
|
||||
<Compile Include="UI\Navigation\PositionComparer.cs" />
|
||||
<Compile Include="UI\Notify\Notifier.cs" />
|
||||
<Compile Include="UI\Notify\NotifierExtensions.cs" />
|
||||
<Compile Include="UI\Notify\NotifyEntry.cs" />
|
||||
<Compile Include="UI\Notify\NotifyFilter.cs" />
|
||||
<Compile Include="Packages\PackageEntry.cs" />
|
||||
<Compile Include="Packages\PackageFolders.cs" />
|
||||
<Compile Include="Packages\PackageDescriptor.cs" />
|
||||
|
||||
34
src/Orchard/UI/Menus/AdminMenuFilter.cs
Normal file
34
src/Orchard/UI/Menus/AdminMenuFilter.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.Mvc.Filters;
|
||||
using Orchard.Mvc.ViewModels;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.UI.Menus {
|
||||
public class AdminMenuFilter : FilterProvider, IResultFilter {
|
||||
private readonly INavigationManager _navigationManager;
|
||||
|
||||
public AdminMenuFilter(INavigationManager navigationManager ) {
|
||||
_navigationManager = navigationManager;
|
||||
}
|
||||
|
||||
public void OnResultExecuting(ResultExecutingContext filterContext) {
|
||||
var viewResult = filterContext.Result as ViewResult;
|
||||
if (viewResult == null)
|
||||
return;
|
||||
|
||||
var adminViewModel = viewResult.ViewData.Model as AdminViewModel;
|
||||
if (adminViewModel == null)
|
||||
return;
|
||||
|
||||
adminViewModel.AdminMenu = _navigationManager.BuildMenu("admin");
|
||||
}
|
||||
|
||||
public void OnResultExecuted(ResultExecutedContext filterContext) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
85
src/Orchard/UI/Navigation/INavigationBuilder.cs
Normal file
85
src/Orchard/UI/Navigation/INavigationBuilder.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Routing;
|
||||
|
||||
namespace Orchard.UI.Navigation {
|
||||
public class NavigationBuilder {
|
||||
IEnumerable<MenuItem> Contained { get; set; }
|
||||
|
||||
public NavigationBuilder Add(string caption, string position, Action<NavigationItemBuilder> itemBuilder) {
|
||||
var childBuilder = new NavigationItemBuilder();
|
||||
|
||||
if (!string.IsNullOrEmpty(caption))
|
||||
childBuilder.Caption(caption);
|
||||
|
||||
if (!string.IsNullOrEmpty(position))
|
||||
childBuilder.Position(position);
|
||||
|
||||
itemBuilder(childBuilder);
|
||||
Contained = (Contained ?? Enumerable.Empty<MenuItem>()).Concat(childBuilder.Build());
|
||||
return this;
|
||||
}
|
||||
|
||||
public NavigationBuilder Add(string caption, Action<NavigationItemBuilder> itemBuilder) {
|
||||
return Add(caption, null, itemBuilder);
|
||||
}
|
||||
public NavigationBuilder Add(Action<NavigationItemBuilder> itemBuilder) {
|
||||
return Add(null, null, itemBuilder);
|
||||
}
|
||||
public NavigationBuilder Add(string caption, string position) {
|
||||
return Add(caption, position, x=> { });
|
||||
}
|
||||
public NavigationBuilder Add(string caption) {
|
||||
return Add(caption, null, x => { });
|
||||
}
|
||||
|
||||
public IEnumerable<MenuItem> Build() {
|
||||
return (Contained ?? Enumerable.Empty<MenuItem>()).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public class NavigationItemBuilder : NavigationBuilder {
|
||||
private readonly MenuItem _item;
|
||||
|
||||
public NavigationItemBuilder() {
|
||||
_item = new MenuItem();
|
||||
}
|
||||
|
||||
public NavigationItemBuilder Caption(string caption) {
|
||||
_item.Text = caption;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NavigationItemBuilder Position(string position) {
|
||||
_item.Position = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
public new IEnumerable<MenuItem> Build() {
|
||||
_item.Contained = base.Build();
|
||||
return new[] { _item };
|
||||
}
|
||||
|
||||
public NavigationItemBuilder Action(string actionName) {
|
||||
return Action(actionName, null, new RouteValueDictionary());
|
||||
}
|
||||
|
||||
public NavigationItemBuilder Action(string actionName, string controllerName) {
|
||||
return Action(actionName, controllerName, new RouteValueDictionary());
|
||||
}
|
||||
|
||||
public NavigationItemBuilder Action(string actionName, string controllerName, object values) {
|
||||
return Action(actionName, controllerName, new RouteValueDictionary(values));
|
||||
}
|
||||
|
||||
public NavigationItemBuilder Action(string actionName, string controllerName, RouteValueDictionary values) {
|
||||
_item.RouteValues = new RouteValueDictionary(values);
|
||||
if (!string.IsNullOrEmpty(actionName))
|
||||
_item.RouteValues["action"] = actionName;
|
||||
if (!string.IsNullOrEmpty(controllerName))
|
||||
_item.RouteValues["controller"] = controllerName;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
src/Orchard/UI/Navigation/INavigationProvider.cs
Normal file
6
src/Orchard/UI/Navigation/INavigationProvider.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Orchard.UI.Navigation {
|
||||
public interface INavigationProvider : IDependency {
|
||||
string MenuName { get; }
|
||||
void GetNavigation(NavigationBuilder builder);
|
||||
}
|
||||
}
|
||||
11
src/Orchard/UI/Navigation/MenuItem.cs
Normal file
11
src/Orchard/UI/Navigation/MenuItem.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Routing;
|
||||
|
||||
namespace Orchard.UI.Navigation {
|
||||
public class MenuItem {
|
||||
public string Text { get; set; }
|
||||
public string Position { get; set; }
|
||||
public RouteValueDictionary RouteValues { get; set; }
|
||||
public IEnumerable<MenuItem> Contained { get; set; }
|
||||
}
|
||||
}
|
||||
43
src/Orchard/UI/Navigation/MenuItemComparer.cs
Normal file
43
src/Orchard/UI/Navigation/MenuItemComparer.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Orchard.UI.Navigation {
|
||||
public class MenuItemComparer : IEqualityComparer<MenuItem> {
|
||||
public bool Equals(MenuItem x, MenuItem y) {
|
||||
if (!string.Equals(x.Text, y.Text)) {
|
||||
return false;
|
||||
}
|
||||
if (x.RouteValues != null || y.RouteValues != null) {
|
||||
if (x.RouteValues == null || y.RouteValues == null) {
|
||||
return false;
|
||||
}
|
||||
if (x.RouteValues.Keys.Any(key => y.RouteValues.ContainsKey(key) == false)) {
|
||||
return false;
|
||||
}
|
||||
if (y.RouteValues.Keys.Any(key => x.RouteValues.ContainsKey(key) == false)) {
|
||||
return false;
|
||||
}
|
||||
foreach (var key in x.RouteValues.Keys) {
|
||||
if (!object.Equals(x.RouteValues[key], y.RouteValues[key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetHashCode(MenuItem obj) {
|
||||
var hash = 0;
|
||||
if (obj.Text != null) {
|
||||
hash ^= obj.Text.GetHashCode();
|
||||
}
|
||||
if (obj.RouteValues != null) {
|
||||
foreach (var item in obj.RouteValues) {
|
||||
hash ^= item.Key.GetHashCode() ^ item.Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
src/Orchard/UI/Navigation/NavigationManager.cs
Normal file
65
src/Orchard/UI/Navigation/NavigationManager.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Orchard.UI.Navigation {
|
||||
public interface INavigationManager : IDependency {
|
||||
IEnumerable<MenuItem> BuildMenu(string menuName);
|
||||
}
|
||||
|
||||
public class NavigationManager : INavigationManager {
|
||||
private readonly IEnumerable<INavigationProvider> _providers;
|
||||
|
||||
public NavigationManager(IEnumerable<INavigationProvider> providers) {
|
||||
_providers = providers;
|
||||
}
|
||||
|
||||
public IEnumerable<MenuItem> BuildMenu(string menuName) {
|
||||
return Merge(AllSources(menuName)).ToArray();
|
||||
}
|
||||
|
||||
private IEnumerable<IEnumerable<MenuItem>> AllSources(string menuName) {
|
||||
foreach (var provider in _providers) {
|
||||
if (provider.MenuName == menuName) {
|
||||
var builder = new NavigationBuilder();
|
||||
provider.GetNavigation(builder);
|
||||
yield return builder.Build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<MenuItem> Merge(IEnumerable<IEnumerable<MenuItem>> sources) {
|
||||
var comparer = new MenuItemComparer();
|
||||
var orderer = new PositionComparer();
|
||||
|
||||
return sources.SelectMany(x => x).ToArray()
|
||||
.GroupBy(key => key, (key, items) => Join(items), comparer)
|
||||
.OrderBy(item => item.Position, orderer);
|
||||
}
|
||||
|
||||
static MenuItem Join(IEnumerable<MenuItem> items) {
|
||||
if (items.Count() < 2)
|
||||
return items.Single();
|
||||
|
||||
var joined = new MenuItem {
|
||||
Text = items.First().Text,
|
||||
RouteValues = items.First().RouteValues,
|
||||
Contained = Merge(items.Select(x => x.Contained)).ToArray(),
|
||||
Position = SelectBestPositionValue(items.Select(x => x.Position))
|
||||
};
|
||||
return joined;
|
||||
}
|
||||
|
||||
private static string SelectBestPositionValue(IEnumerable<string> positions) {
|
||||
var comparer = new PositionComparer();
|
||||
return positions.Aggregate(string.Empty,
|
||||
(agg, pos) =>
|
||||
string.IsNullOrEmpty(agg)
|
||||
? pos
|
||||
: string.IsNullOrEmpty(pos)
|
||||
? agg
|
||||
: comparer.Compare(agg, pos) < 0 ? agg : pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
105
src/Orchard/UI/Navigation/PositionComparer.cs
Normal file
105
src/Orchard/UI/Navigation/PositionComparer.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.UI.Navigation {
|
||||
public class PositionComparer : IComparer<string> {
|
||||
public int Compare(string x, string y) {
|
||||
if (x == null || y == null) {
|
||||
return x == null && y == null ? 0 : (x == null ? -1 : 1);
|
||||
}
|
||||
if (x == "" || y == "") {
|
||||
return x == "" && y == "" ? 0 : (x == "" ? -1 : 1);
|
||||
}
|
||||
|
||||
var xRange = new Range { Length = x.Length };
|
||||
var yRange = new Range { Length = y.Length };
|
||||
|
||||
while (xRange.Start != xRange.Length || yRange.Start != yRange.Length) {
|
||||
var xSize = xRange.NextDot(x);
|
||||
var ySize = yRange.NextDot(y);
|
||||
|
||||
if (xSize == 0 || ySize == 0) {
|
||||
// one or both sides are empty
|
||||
if (xSize != 0 || ySize != 0) {
|
||||
// favor the side that's not empty
|
||||
return xSize - ySize;
|
||||
}
|
||||
// otherwise continue to the next segment if both are
|
||||
}
|
||||
else if (xRange.NumericValue != -1 && yRange.NumericValue != -1) {
|
||||
// two strictly numeric values
|
||||
|
||||
// return the difference
|
||||
var diff = xRange.NumericValue - yRange.NumericValue;
|
||||
if (diff != 0)
|
||||
return diff;
|
||||
|
||||
// or continue to next segment
|
||||
}
|
||||
else {
|
||||
if (xRange.NumericValue != -1) {
|
||||
// left-side only has numeric value, right-side explicitly greater
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yRange.NumericValue != -1) {
|
||||
// right-side only has numeric value, left-side explicitly greater
|
||||
return 1;
|
||||
}
|
||||
|
||||
// two strictly non-numeric
|
||||
var diff = string.Compare(x, xRange.Start, y, yRange.Start, Math.Min(xSize, ySize),
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
if (diff != 0)
|
||||
return diff;
|
||||
if (xSize != ySize)
|
||||
return xSize - ySize;
|
||||
}
|
||||
|
||||
xRange.Advance();
|
||||
yRange.Advance();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct Range {
|
||||
public int Start { get; private set; }
|
||||
private int End { get; set; }
|
||||
public int Length { get; set; }
|
||||
|
||||
public int NumericValue { get; private set; }
|
||||
|
||||
public int NextDot(string value) {
|
||||
if (Start == -1) {
|
||||
End = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
End = value.IndexOf('.', Start);
|
||||
|
||||
int numeric;
|
||||
NumericValue = int.TryParse(Segment(value), out numeric) ? numeric : -1;
|
||||
|
||||
return End == -1 ? Length - Start : End - Start;
|
||||
}
|
||||
|
||||
private string Segment(string value) {
|
||||
if (Start == 0 && End == -1) {
|
||||
return value;
|
||||
}
|
||||
if (End == -1) {
|
||||
return value.Substring(Start);
|
||||
}
|
||||
return value.Substring(Start, End - Start);
|
||||
}
|
||||
|
||||
public void Advance() {
|
||||
if (End == -1)
|
||||
Start = Length;
|
||||
else
|
||||
Start = End + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user