Content item specific shapes

Content manager creating shape names based on display mode
Template harvester converting known file patterns into appropriate shape names

--HG--
branch : theming
extra : rebase_source : a4d45b76ea0251bb3d6b7f24dba92563cc63addf
This commit is contained in:
Louis DeJardin
2010-09-09 17:14:38 -07:00
parent 646aab07b0
commit a03da9496a
10 changed files with 218 additions and 9 deletions

View File

@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy;
namespace Orchard.Tests.DisplayManagement.Descriptors {
[TestFixture]
public class BasicShapeTemplateHarvesterTests {
private static void VerifyShapeType(string givenSubPath, string givenFileName, string expectedShapeType) {
var harvester = new BasicShapeTemplateHarvester(Enumerable.Empty<IShapeTemplateViewEngine>());
var harvestShapeHits = harvester.HarvestShape(new HarvestShapeInfo { SubPath = givenSubPath, FileName = givenFileName });
Assert.That(harvestShapeHits.Count(), Is.EqualTo(1));
Assert.That(harvestShapeHits.Single().ShapeType, Is.EqualTo(expectedShapeType));
}
[Test]
public void BasicFileNamesComeBackAsShapes() {
VerifyShapeType("Views", "Hello", "Hello");
VerifyShapeType("Views", "World", "World");
}
[Test]
public void DashBecomesBreakingSeperator() {
VerifyShapeType("Views", "Hello-World", "Hello__World");
}
[Test]
public void DotBecomesNonBreakingSeperator() {
VerifyShapeType("Views", "Hello.World", "Hello_World");
}
[Test]
public void DefaultItemsContentTemplate() {
VerifyShapeType("Views/Items", "Content", "Items_Content");
}
[Test]
public void ImplicitSpecializationOfItemsContentTemplate() {
VerifyShapeType("Views/Items", "MyType", "Items_Content__MyType");
}
[Test]
public void ExplicitSpecializationOfItemsContentTemplate() {
VerifyShapeType("Views/Items", "Content-MyType", "Items_Content__MyType");
}
[Test]
public void ContentItemDisplayTypes() {
VerifyShapeType("Views/Items", "Content", "Items_Content");
VerifyShapeType("Views/Items", "Content.Summary", "Items_Content_Summary");
VerifyShapeType("Views/Items", "Content.Edit", "Items_Content_Edit");
}
[Test]
public void ExplicitSpecializationMixedWithDisplayTypes() {
VerifyShapeType("Views/Items", "Content-MyType", "Items_Content__MyType");
VerifyShapeType("Views/Items", "Content-MyType.Summary", "Items_Content_Summary__MyType");
VerifyShapeType("Views/Items", "Content-MyType.Edit", "Items_Content_Edit__MyType");
}
[Test]
public void ImplicitSpecializationMixedWithDisplayTypes() {
VerifyShapeType("Views/Items", "MyType", "Items_Content__MyType");
VerifyShapeType("Views/Items", "MyType.Summary", "Items_Content_Summary__MyType");
VerifyShapeType("Views/Items", "MyType.Edit", "Items_Content_Edit__MyType");
}
}
}

View File

@@ -196,6 +196,7 @@
<Compile Include="DisplayManagement\ArgsUtility.cs" />
<Compile Include="DisplayManagement\DefaultDisplayManagerTests.cs" />
<Compile Include="ContainerTestBase.cs" />
<Compile Include="DisplayManagement\Descriptors\BasicShapeTemplateHarvesterTests.cs" />
<Compile Include="DisplayManagement\Descriptors\DefaultShapeTableFactoryTests.cs" />
<Compile Include="DisplayManagement\Descriptors\DefaultShapeTableManagerTests.cs" />
<Compile Include="DisplayManagement\Descriptors\ShapeAttributeBindingStrategyTests.cs" />

View File

@@ -0,0 +1,3 @@
content item -> @Model.ContentItem.ContentType
@Display(Model.primary)

View File

@@ -0,0 +1,3 @@
content item -> @Model.ContentItem.ContentType
@Display(Model.primary)

View File

@@ -400,6 +400,8 @@
<Content Include="ContentsLocation\Views\Web.config" />
<Content Include="Messaging\Views\Web.config" />
<None Include="Contents\Views\Items\Content.cshtml" />
<None Include="Contents\Views\Items\Content.Edit.cshtml" />
<None Include="Contents\Views\Items\Content.Summary.cshtml" />
<None Include="Contents\Views\Item\Display.cshtml" />
<None Include="HomePage\Views\HomePage.cshtml" />
<None Include="Shapes\Views\Document.cshtml" />

View File

@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Xml;
using Orchard.DisplayManagement.Shapes;
using Orchard.Mvc.Filters;
namespace Orchard.DevTools {
public class DebugFilter : FilterProvider, IActionFilter {
public void OnActionExecuting(ActionExecutingContext filterContext) {
}
public void OnActionExecuted(ActionExecutedContext filterContext) {
var viewResultBase = filterContext.Result as ViewResultBase;
var debugValueResult = filterContext.Controller.ValueProvider.GetValue("$debug");
if (debugValueResult == null)
return;
var debugValue = (string)debugValueResult.ConvertTo(typeof(string));
if (debugValue == "model" && viewResultBase != null) {
filterContext.Result = new DebugModelResult(viewResultBase);
}
}
public class DebugModelResult : ActionResult {
private readonly ViewResultBase _viewResultBase;
public DebugModelResult(ViewResultBase viewResultBase) {
_viewResultBase = viewResultBase;
}
public override void ExecuteResult(ControllerContext context) {
context.HttpContext.Response.ContentType = "application/xml";
var output = context.HttpContext.Response.Output;
using (var writer = XmlWriter.Create(output, new XmlWriterSettings { Indent = true, IndentChars = " " })) {
try {
Writer = writer;
var model = _viewResultBase.ViewData.Model;
Accept(model);
}
finally {
Writer = null;
}
}
}
protected XmlWriter Writer { get; set; }
void Accept(dynamic model) {
Shape shape = model;
Visit(shape);
}
void Visit(Shape shape) {
Writer.WriteStartElement("Shape");
Writer.WriteAttributeString("Type", shape.Metadata.Type);
foreach (var item in shape.Items) {
Accept(item);
}
Writer.WriteEndElement();
}
}
}
}

View File

@@ -79,6 +79,7 @@
<Compile Include="Controllers\HomeController.cs" />
<Compile Include="Controllers\InventoryController.cs" />
<Compile Include="Controllers\MetadataController.cs" />
<Compile Include="DebugFilter.cs" />
<Compile Include="Handlers\DebugLinkHandler.cs" />
<Compile Include="Models\ShowDebugLink.cs" />
<Compile Include="Models\Simple.cs" />

View File

@@ -1,8 +1,11 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Autofac;
using ClaySharp.Implementation;
using Microsoft.CSharp.RuntimeBinder;
using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
@@ -368,17 +371,32 @@ namespace Orchard.ContentManagement {
return context.Metadata;
}
static readonly CallSiteCollection _shapeHelperCalls = new CallSiteCollection(shapeTypeName => Binder.InvokeMember(
CSharpBinderFlags.None,
shapeTypeName,
Enumerable.Empty<Type>(),
null,
new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
public dynamic BuildDisplayModel<TContent>(TContent content, string displayType) where TContent : IContent {
var shapeHelper = _shapeHelperFactory.CreateHelper();
var itemShape = shapeHelper.Items_Content(ContentItem:content.ContentItem);
var shapeTypeName = string.IsNullOrEmpty(displayType) ? "Items_Content" : ("Items_Content_" + displayType);
_shapeHelperCalls.Invoke(shapeHelper, shapeTypeName);
var itemShape = shapeHelper.Items_Content();
itemShape.ContentItem = content.ContentItem;
var context = new BuildDisplayModelContext(content, displayType, itemShape, _shapeHelperFactory);
Handlers.Invoke(handler => handler.BuildDisplayShape(context), Logger);
return context.Model;
}
public dynamic BuildEditorModel<TContent>(TContent content) where TContent : IContent {
var shapeHelper = _shapeHelperFactory.CreateHelper();
var itemShape = shapeHelper.Items_Content(ContentItem: content.ContentItem);
var itemShape = shapeHelper.Items_Content_Edit(ContentItem: content.ContentItem);
var context = new BuildEditorModelContext(content, itemShape, _shapeHelperFactory);
Handlers.Invoke(handler => handler.BuildEditorShape(context), Logger);
return context.Model;
@@ -386,7 +404,7 @@ namespace Orchard.ContentManagement {
public dynamic UpdateEditorModel<TContent>(TContent content, IUpdateModel updater) where TContent : IContent {
var shapeHelper = _shapeHelperFactory.CreateHelper();
var itemShape = shapeHelper.Items_Content(ContentItem: content.ContentItem);
var itemShape = shapeHelper.Items_Content_Edit(ContentItem: content.ContentItem);
var context = new UpdateEditorModelContext(content, updater, itemShape, _shapeHelperFactory);
Handlers.Invoke(handler => handler.UpdateEditorShape(context), Logger);
return context.Model;
@@ -426,4 +444,20 @@ namespace Orchard.ContentManagement {
// : new NullSearchBuilder();
//}
}
class CallSiteCollection : ConcurrentDictionary<string, CallSite<Func<CallSite, object, object>>> {
readonly Func<string, CallSite<Func<CallSite, object, object>>> _valueFactory;
public CallSiteCollection(Func<string, CallSite<Func<CallSite, object, object>>> callSiteFactory) {
_valueFactory = callSiteFactory;
}
public CallSiteCollection(Func<string, CallSiteBinder> callSiteBinderFactory) {
_valueFactory = key => CallSite<Func<CallSite, object, object>>.Create(callSiteBinderFactory(key));
}
public object Invoke(object callee, string key) {
var callSite = GetOrAdd(key, _valueFactory);
return callSite.Target(callSite, callee);
}
}
}

View File

@@ -29,29 +29,41 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy {
var lastDot = info.FileName.IndexOf('.');
if (lastDot <= 0) {
yield return new HarvestShapeHit {
ShapeType = Adjust(info.SubPath, info.FileName)
ShapeType = Adjust(info.SubPath, info.FileName, null)
};
}
else {
var displayType = info.FileName.Substring(lastDot + 1);
yield return new HarvestShapeHit {
ShapeType = Adjust(info.SubPath, info.FileName.Substring(0, lastDot)),
DisplayType = info.FileName.Substring(lastDot + 1)
ShapeType = Adjust(info.SubPath, info.FileName.Substring(0, lastDot), displayType),
DisplayType = displayType
};
}
}
static string Adjust(string subPath, string fileName) {
var leader="";
static string Adjust(string subPath, string fileName, string displayType) {
var leader = "";
if (subPath.StartsWith("Views/")) {
leader = subPath.Substring("Views/".Length) + "_";
}
if (leader == "Items_" && !fileName.StartsWith("Content")) {
leader = "Items_Content__";
}
// canonical shape type names must not have - or . to be compatible
// with display and shape api calls)))
return leader + fileName.Replace("--", "__").Replace("-", "__").Replace('.', '_');
var shapeType = leader + fileName.Replace("--", "__").Replace("-", "__").Replace('.', '_');
if (string.IsNullOrEmpty(displayType)) {
return shapeType;
}
var firstBreakingSeparator = shapeType.IndexOf("__");
if (firstBreakingSeparator <= 0) {
return shapeType + "_" + displayType;
}
return shapeType.Substring(0, firstBreakingSeparator) + "_" + displayType + shapeType.Substring(firstBreakingSeparator);
}
}

View File

@@ -63,13 +63,27 @@ Parts/Content
Fields/Content
"Items_Content"
"Items_Content_Summary"
"Items_Content_Edit"
base + "__" + id
base + "__" + contenttype
==template discovery strategy==
Items/Content.cshtml -> "Items_Content"
Items/Content.Summary.cshtml -> "Items_Content"
Items/Content.Edit.cshtml -> "Items_Content"
Items/Content-Page.cshtml -> "Items_Content__Page"
Items/Content-45.cshtml -> "Items_Content__45"
Items/Content-45.Summary.cshtml -> "Items_Content_Summary__45"
Items/Content.Summary-45.cshtml -> "Items_Content_Summary__Page"
Items/Page.cshtml -> "Items_Content__Page"
Items/BlogPost.cshtml -> "Items_Content__BlogPost"
Items/BlogPost.Summary.cshtml -> "Items_Content__BlogPost"
Items/BlogPost.Edit.cshtml -> "Items_Content__BlogPost"
Widgets-TwitterThing.cshtml -> "Widget__TwitterThing"