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;
+
+
+
+}
\ 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; }