diff --git a/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml b/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml index 93016d7f4..d9ef1e40b 100644 --- a/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml +++ b/src/Orchard.Web/Core/Contents/Views/Content.Edit.cshtml @@ -1,4 +1,9 @@ -
+@using Orchard.Core.Shapes +@{ + var tabs = (IEnumerable)CoreShapes.HarvestAndSortTabs(Model.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..3e5172010 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] @@ -327,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.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.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 + diff --git a/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs b/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs index bb09e4bae..5a1c4a2ca 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; @@ -27,16 +28,16 @@ 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 + // Parse group placement. var group = placement.GetGroup(); if (!String.IsNullOrEmpty(group)) { _groupId = group; } - if (!string.Equals(context.GroupId ?? "", _groupId ?? "", StringComparison.OrdinalIgnoreCase)) + if (!String.Equals(context.GroupId ?? "", _groupId ?? "", StringComparison.OrdinalIgnoreCase)) return; dynamic parentShape = context.Shape; @@ -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,9 @@ namespace Orchard.ContentManagement.Drivers { newShapeMetadata.Prefix = _prefix; newShapeMetadata.DisplayType = displayType; newShapeMetadata.PlacementSource = placement.Source; + newShapeMetadata.Tab = placement.GetTab(); - // if a specific shape is provided, remove all previous alternates and wrappers + // 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 +81,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 +108,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; }