From 86056e2e9caa897b63626cf049b1255b9d778c50 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 18 Jul 2015 19:31:20 +0100 Subject: [PATCH 1/4] Added System.Web.WebPages assembly reference. This removes a warning displayed by the code editor. --- .../Modules/Orchard.Autoroute/Orchard.Autoroute.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Orchard.Web/Modules/Orchard.Autoroute/Orchard.Autoroute.csproj b/src/Orchard.Web/Modules/Orchard.Autoroute/Orchard.Autoroute.csproj index ceff50e3f..22f1e5b76 100644 --- a/src/Orchard.Web/Modules/Orchard.Autoroute/Orchard.Autoroute.csproj +++ b/src/Orchard.Web/Modules/Orchard.Autoroute/Orchard.Autoroute.csproj @@ -67,6 +67,10 @@ + + False + ..\..\..\..\lib\aspnetmvc\System.Web.WebPages.dll + From 3c8e39a7c509791578491d22314d2160d2c483f8 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 18 Jul 2015 22:38:26 +0100 Subject: [PATCH 2/4] Implemented tabbed editor UI support. --- .../Core/Contents/Views/Content.Edit.cshtml | 14 +++++++- src/Orchard.Web/Core/Orchard.Core.csproj | 8 +++++ src/Orchard.Web/Core/Shapes/CoreShapes.cs | 22 +++++++++++-- .../Core/Shapes/ResourceManifest.cs | 4 ++- .../Shapes/Scripts/admin-localnavigation.js | 33 +++++++++++++++++++ src/Orchard.Web/Core/Shapes/Styles/site.css | 1 + .../Core/Shapes/Views/AdminTabs.cshtml | 24 ++++++++++++++ .../Core/Shapes/Views/LocalNavigation.cshtml | 3 ++ .../Drivers/ContentShapeResult.cs | 32 +++++++++++++----- .../DisplayManagement/Shapes/ShapeMetadata.cs | 1 + 10 files changed, 129 insertions(+), 13 deletions(-) create mode 100644 src/Orchard.Web/Core/Shapes/Scripts/admin-localnavigation.js create mode 100644 src/Orchard.Web/Core/Shapes/Styles/site.css create mode 100644 src/Orchard.Web/Core/Shapes/Views/AdminTabs.cshtml create mode 100644 src/Orchard.Web/Core/Shapes/Views/LocalNavigation.cshtml diff --git a/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml b/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml index 93016d7f4..31d40e223 100644 --- a/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml +++ b/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml @@ -1,4 +1,16 @@ -
+@{ + var tabs = ((IEnumerable)Model.Tabs).ToList(); + + // If we have any tabs, make sure we have at least the Content tab and that it is the first one, + // since that's where we will put antyhing else not part of a tab. + if (tabs.Any()) { + tabs.Remove("Content"); + tabs.Insert(0, "Content"); + } + + Display.LocalNavigation(Tabs: tabs); +} +
@if (Model.Content != null) {
diff --git a/src/Orchard.Web/Core/Orchard.Core.csproj b/src/Orchard.Web/Core/Orchard.Core.csproj index f7cec46b2..759a5db71 100644 --- a/src/Orchard.Web/Core/Orchard.Core.csproj +++ b/src/Orchard.Web/Core/Orchard.Core.csproj @@ -294,6 +294,7 @@ + @@ -340,6 +341,7 @@ + @@ -570,6 +572,12 @@ + + + + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/src/Orchard.Web/Core/Shapes/CoreShapes.cs b/src/Orchard.Web/Core/Shapes/CoreShapes.cs index f0c88e02d..1f01e168a 100644 --- a/src/Orchard.Web/Core/Shapes/CoreShapes.cs +++ b/src/Orchard.Web/Core/Shapes/CoreShapes.cs @@ -282,8 +282,26 @@ namespace Orchard.Core.Shapes { [Shape] public void ContentZone(dynamic Display, dynamic Shape, TextWriter Output) { - foreach (var item in Order(Shape)) - Output.Write(Display(item)); + var unordered = ((IEnumerable)Shape).ToArray(); + var tabbed = unordered.GroupBy(x => (string)x.Metadata.Tab); + + if (tabbed.Count() > 1) { + foreach (var tab in tabbed) { + var tabName = String.IsNullOrWhiteSpace(tab.Key) ? "Content" : tab.Key; + var tabBuilder = new TagBuilder("div"); + tabBuilder.Attributes["id"] = "tab-" + tabName.HtmlClassify(); + tabBuilder.Attributes["data-tab"] = tabName; + Output.Write(tabBuilder.ToString(TagRenderMode.StartTag)); + foreach (var item in Order(tab)) + Output.Write(Display(item)); + + Output.Write(tabBuilder.ToString(TagRenderMode.EndTag)); + } + } + else { + foreach (var item in Order(unordered)) + Output.Write(Display(item)); + } } [Shape] diff --git a/src/Orchard.Web/Core/Shapes/ResourceManifest.cs b/src/Orchard.Web/Core/Shapes/ResourceManifest.cs index 20bc2e965..4428b4dc5 100644 --- a/src/Orchard.Web/Core/Shapes/ResourceManifest.cs +++ b/src/Orchard.Web/Core/Shapes/ResourceManifest.cs @@ -5,7 +5,7 @@ namespace Orchard.Core.Shapes { public void BuildManifests(ResourceManifestBuilder builder) { var manifest = builder.Add(); manifest.DefineScript("ShapesBase").SetUrl("base.js").SetDependencies("jQuery"); - manifest.DefineStyle("Shapes").SetUrl("site.css"); // todo: missing + manifest.DefineStyle("Shapes").SetUrl("site.css"); manifest.DefineStyle("ShapesSpecial").SetUrl("special.css"); manifest.DefineScript("Html5Shiv").SetUrl("html5.js"); @@ -14,6 +14,8 @@ namespace Orchard.Core.Shapes { .SetDependencies("jQuery") .SetDependencies("ShapesBase"); manifest.DefineStyle("Switchable").SetUrl("jquery.switchable.css"); + + manifest.DefineScript("LocalNavigation").SetUrl("admin-localnavigation.js").SetDependencies("jQuery"); } } } diff --git a/src/Orchard.Web/Core/Shapes/Scripts/admin-localnavigation.js b/src/Orchard.Web/Core/Shapes/Scripts/admin-localnavigation.js new file mode 100644 index 000000000..13d9df9f4 --- /dev/null +++ b/src/Orchard.Web/Core/Shapes/Scripts/admin-localnavigation.js @@ -0,0 +1,33 @@ +(function($) { + $(function () { + var allViews = []; + + $("#local-navigation").on("click", "a", function(e) { + e.preventDefault(); + + for (i = 0; i < allViews.length; i++) { + allViews[i].hide(); + } + + $("#local-navigation li.selected").removeClass("selected"); + + var tab = $(this); + var selector = tab.attr("href"); + var views = $(selector); + + tab.closest("li").addClass("selected"); + views.show(); + }); + + $("#local-navigation a").each(function (e) { + var tab = $(this); + var selector = tab.attr("href"); + var views = $(selector); + + views.hide(); + allViews.push(views); + }); + + $("#local-navigation a:first").click(); + }); +})(jQuery); \ No newline at end of file diff --git a/src/Orchard.Web/Core/Shapes/Styles/site.css b/src/Orchard.Web/Core/Shapes/Styles/site.css new file mode 100644 index 000000000..9316cdfe7 --- /dev/null +++ b/src/Orchard.Web/Core/Shapes/Styles/site.css @@ -0,0 +1 @@ +/*Placeholder for Shapes style resource.*/ \ No newline at end of file diff --git a/src/Orchard.Web/Core/Shapes/Views/AdminTabs.cshtml b/src/Orchard.Web/Core/Shapes/Views/AdminTabs.cshtml new file mode 100644 index 000000000..01093ba43 --- /dev/null +++ b/src/Orchard.Web/Core/Shapes/Views/AdminTabs.cshtml @@ -0,0 +1,24 @@ +@using Orchard.Localization +@using Orchard.Utility.Extensions +@{ + var tabs = ((IEnumerable)Model.Tabs).ToArray(); + + if (tabs.Any()) { + Script.Require("LocalNavigation"); + } +} +@if (tabs.Any()) { + var tabIndex = 0; +
+
    + @foreach (var tab in tabs) { + var tabText = tab is string ? (string)tab : (string)(tab.Text is LocalizedString ? tab.Text.ToString() : tab.Text); + var defaultTabId = string.Format("tab-{0}", tabText.HtmlClassify()); + var tabId = tab is string ? defaultTabId : (string)(tab.TabName ?? defaultTabId); + var tabCssClass = tabIndex == 0 ? "first selected" : tabIndex <= tabs.Count() - 1 ? "last" : "middle"; +
  • @tabText
  • + tabIndex++; + } +
+
+} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Shapes/Views/LocalNavigation.cshtml b/src/Orchard.Web/Core/Shapes/Views/LocalNavigation.cshtml new file mode 100644 index 000000000..c899c7b63 --- /dev/null +++ b/src/Orchard.Web/Core/Shapes/Views/LocalNavigation.cshtml @@ -0,0 +1,3 @@ +@{ + Layout.LocalNavigation.Add(New.AdminTabs(Tabs: Model.Tabs)); +} \ No newline at end of file diff --git a/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs b/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs index bb09e4bae..468782a81 100644 --- a/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs +++ b/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Orchard.ContentManagement.Handlers; using Orchard.DisplayManagement.Shapes; @@ -30,7 +31,7 @@ namespace Orchard.ContentManagement.Drivers { if (string.IsNullOrEmpty(placement.Location) || placement.Location == "-") return; - // parse group placement + // Parse group placement. var group = placement.GetGroup(); if (!String.IsNullOrEmpty(group)) { _groupId = group; @@ -44,17 +45,17 @@ namespace Orchard.ContentManagement.Drivers { var newShape = _shapeBuilder(context); - // ignore it if the driver returned a null shape - if(newShape == null) { + // Ignore it if the driver returned a null shape. + if (newShape == null) { return; } - // add a ContentPart property to the final shape + // Add a ContentPart property to the final shape. if (ContentPart != null && newShape.ContentPart == null) { newShape.ContentPart = ContentPart; } - // add a ContentField property to the final shape + // Add a ContentField property to the final shape. if (ContentField != null && newShape.ContentField == null) { newShape.ContentField = ContentField; } @@ -63,8 +64,21 @@ namespace Orchard.ContentManagement.Drivers { newShapeMetadata.Prefix = _prefix; newShapeMetadata.DisplayType = displayType; newShapeMetadata.PlacementSource = placement.Source; - - // if a specific shape is provided, remove all previous alternates and wrappers + newShapeMetadata.Tab = placement.GetTab(); + + // If a tab name is specified, add it to the list of tabs on the parent shape so it can render them. + var tabs = (HashSet)parentShape.Tabs; + + if (tabs == null) { + tabs = new HashSet(); + parentShape.Tabs = tabs; + } + + if (!String.IsNullOrEmpty(newShapeMetadata.Tab)) { + tabs.Add(newShapeMetadata.Tab); + } + + // If a specific shape is provided, remove all previous alternates and wrappers. if (!String.IsNullOrEmpty(placement.ShapeType)) { newShapeMetadata.Type = placement.ShapeType; newShapeMetadata.Alternates.Clear(); @@ -79,7 +93,7 @@ namespace Orchard.ContentManagement.Drivers { newShapeMetadata.Wrappers.Add(wrapper); } - // the zone name is in reference of Layout, e.g. /AsideSecond + // Check if the zone name is in reference of Layout, e.g. /AsideSecond. if (placement.IsLayoutZone()) { parentShape = context.Layout; } @@ -106,7 +120,7 @@ namespace Orchard.ContentManagement.Drivers { } public ContentShapeResult OnGroup(string groupId) { - _groupId=groupId; + _groupId = groupId; return this; } diff --git a/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs b/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs index 60cbcecb8..7f454e4ad 100644 --- a/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs +++ b/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs @@ -17,6 +17,7 @@ namespace Orchard.DisplayManagement.Shapes { public string Type { get; set; } public string DisplayType { get; set; } public string Position { get; set; } + public string Tab { get; set; } public string PlacementSource { get; set; } public string Prefix { get; set; } public IList Wrappers { get; set; } From f4d630e2f601678f62bc3fc1248d89e0aa259a89 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 19 Jul 2015 19:50:07 +0100 Subject: [PATCH 3/4] Simplified tabs harvesting and implemented sorting by shape position. --- .../Core/Contents/Views/Content.Edit.cshtml | 23 +++++++----------- src/Orchard.Web/Core/Shapes/CoreShapes.cs | 24 +++++++++++++++++++ .../Drivers/ContentShapeResult.cs | 18 +++----------- 3 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml b/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml index 31d40e223..293cdf522 100644 --- a/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml +++ b/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml @@ -1,13 +1,6 @@ -@{ - var tabs = ((IEnumerable)Model.Tabs).ToList(); - - // If we have any tabs, make sure we have at least the Content tab and that it is the first one, - // since that's where we will put antyhing else not part of a tab. - if (tabs.Any()) { - tabs.Remove("Content"); - tabs.Insert(0, "Content"); - } - +@using Orchard.Core.Shapes +@{ + var tabs = (IEnumerable)CoreShapes.HarvestAndSortTabs(Model.Content); Display.LocalNavigation(Tabs: tabs); }
@@ -19,11 +12,6 @@ }
- @if (Model.Actions != null) { -
- @Display(Model.Actions) -
- } @if (Model.Sidebar != null) {
@Display(Model.Sidebar) @@ -31,6 +19,11 @@ }
+@if (Model.Actions != null) { +
+ @Display(Model.Actions) +
+} @if (!String.IsNullOrWhiteSpace(Request.QueryString["returnUrl"])) { @Html.Hidden("returnUrl", Request.QueryString["returnUrl"]) diff --git a/src/Orchard.Web/Core/Shapes/CoreShapes.cs b/src/Orchard.Web/Core/Shapes/CoreShapes.cs index 1f01e168a..3e5172010 100644 --- a/src/Orchard.Web/Core/Shapes/CoreShapes.cs +++ b/src/Orchard.Web/Core/Shapes/CoreShapes.cs @@ -345,6 +345,30 @@ namespace Orchard.Core.Shapes { return ordering.Select(ordered => ordered.item).ToList(); } + public static IEnumerable HarvestAndSortTabs(IEnumerable shapes) { + var orderedShapes = Order(shapes).ToArray(); + var tabs = new List(); + + foreach (var shape in orderedShapes) { + var tab = (string)shape.Metadata.Tab; + + if (String.IsNullOrEmpty(tab)) + continue; + + if(!tabs.Contains(tab)) + tabs.Add(tab); + } + + // If we have any tabs, make sure we have at least the Content tab and that it is the first one, + // since that's where we will put anything else not part of a tab. + if (tabs.Any()) { + tabs.Remove("Content"); + tabs.Insert(0, "Content"); + } + + return tabs; + } + [Shape] public void HeadScripts(dynamic Display, TextWriter Output) { WriteResources(Display, Output, "script", ResourceLocation.Head, null); diff --git a/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs b/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs index 468782a81..5a1c4a2ca 100644 --- a/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs +++ b/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs @@ -28,7 +28,7 @@ namespace Orchard.ContentManagement.Drivers { private void ApplyImplementation(BuildShapeContext context, string displayType) { var placement = context.FindPlacement(_shapeType, _differentiator, _defaultLocation); - if (string.IsNullOrEmpty(placement.Location) || placement.Location == "-") + if (String.IsNullOrEmpty(placement.Location) || placement.Location == "-") return; // Parse group placement. @@ -37,7 +37,7 @@ namespace Orchard.ContentManagement.Drivers { _groupId = group; } - if (!string.Equals(context.GroupId ?? "", _groupId ?? "", StringComparison.OrdinalIgnoreCase)) + if (!String.Equals(context.GroupId ?? "", _groupId ?? "", StringComparison.OrdinalIgnoreCase)) return; dynamic parentShape = context.Shape; @@ -65,19 +65,7 @@ namespace Orchard.ContentManagement.Drivers { newShapeMetadata.DisplayType = displayType; newShapeMetadata.PlacementSource = placement.Source; newShapeMetadata.Tab = placement.GetTab(); - - // If a tab name is specified, add it to the list of tabs on the parent shape so it can render them. - var tabs = (HashSet)parentShape.Tabs; - - if (tabs == null) { - tabs = new HashSet(); - parentShape.Tabs = tabs; - } - - if (!String.IsNullOrEmpty(newShapeMetadata.Tab)) { - tabs.Add(newShapeMetadata.Tab); - } - + // If a specific shape is provided, remove all previous alternates and wrappers. if (!String.IsNullOrEmpty(placement.ShapeType)) { newShapeMetadata.Type = placement.ShapeType; From d7595f62a74eaba8c84be02830451cfe9da954ea Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 19 Jul 2015 19:55:02 +0100 Subject: [PATCH 4/4] Restored Content.Edit.cshtml HTML structure. --- .../Core/Contents/Views/Content.Edit.cshtml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml b/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml index 293cdf522..d9ef1e40b 100644 --- a/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml +++ b/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml @@ -12,6 +12,11 @@ }
+ @if (Model.Actions != null) { +
+ @Display(Model.Actions) +
+ } @if (Model.Sidebar != null) {
@Display(Model.Sidebar) @@ -19,11 +24,6 @@ }
-@if (Model.Actions != null) { -
- @Display(Model.Actions) -
-} @if (!String.IsNullOrWhiteSpace(Request.QueryString["returnUrl"])) { @Html.Hidden("returnUrl", Request.QueryString["returnUrl"])