Dumping the model in Shape Tracing

--HG--
branch : dev
This commit is contained in:
Sebastien Ros 2011-02-11 15:33:33 -08:00
parent 2d890f54a0
commit b7d8e56398
9 changed files with 342 additions and 32 deletions

View File

@ -109,8 +109,12 @@
<Content Include="Scripts\orchard-blogs-archives.js" />
<Content Include="Styles\orchard-blogs-admin.css" />
<Content Include="Styles\orchard-blogs-archives.css" />
<Content Include="Views\BlogAdmin\Create.cshtml" />
<Content Include="Views\BlogAdmin\Edit.cshtml" />
<Content Include="Views\BlogAdmin\Create.cshtml">
<SubType>Designer</SubType>
</Content>
<Content Include="Views\BlogAdmin\Edit.cshtml">
<SubType>Designer</SubType>
</Content>
<Content Include="Views\BlogAdmin\Item.cshtml" />
<Content Include="Views\BlogAdmin\List.cshtml" />
<Content Include="Views\BlogPostAdmin\Create.cshtml" />

View File

@ -39,6 +39,7 @@
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="ClaySharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.Data" />
@ -53,14 +54,16 @@
<Reference Include="System.Web" />
<Reference Include="System.Web.Abstractions" />
<Reference Include="System.Web.Routing" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Content Include="Scripts\orchard-designertools-shapetracing.js" />
<Content Include="Styles\orchard-designertools-shapetracing.css" />
<Content Include="Web.config" />
<Content Include="Web.config">
<SubType>Designer</SubType>
</Content>
<Content Include="Views\Web.config" />
<Content Include="Scripts\Web.config" />
<Content Include="Styles\Web.config" />
@ -78,6 +81,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Services\ObjectDumper.cs" />
<Compile Include="Services\ShapeTracingFactory.cs" />
</ItemGroup>
<ItemGroup>

View File

@ -1,18 +1,19 @@
(function($) {
$(function() {
$("<div id='debug-control'><ul><li id='debug-shape-templates'>shape templates</li></ul><div id='debug-control-toggle'>&raquo;</div></div>")
$("<div id='debug-control'><ul><li id='debug-shape-templates'>shape templates</li><li id='debug-shape-zones'>Zones</li></ul><div id='debug-control-toggle'>&raquo;</div></div>")
.appendTo("body");
$("#debug-shape-templates").click(function() {
$("#debug-shape-templates").click(function () {
var _this = $(this);
$("html").toggleClass(_this.attr("id"));
$(this).toggleClass("debug-active");
});
$("#debug-control-toggle").click(function() {
$("#debug-shape-zones").click(function () {
var _this = $(this);
$("html").toggleClass(_this.attr("id"));
$(this).toggleClass("debug-active");
});
$("#debug-control-toggle").click(function () {
var _this = $(this), open = "debug-open";
if (_this.is(":animated")) {
alert("aghhhh!");
return;
}
if (_this.is("."+open)) {
_this.prev().hide("fast", function() {_this.removeClass(open).html("&raquo;");});
} else {

View File

@ -0,0 +1,229 @@
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using ClaySharp;
using ClaySharp.Behaviors;
using Orchard.DisplayManagement;
namespace Orchard.DesignerTools.Services {
public class ObjectDumper {
private const int MaxStringLength = 60;
private readonly Stack<object> _parents = new Stack<object>();
private readonly int _levels;
private readonly XDocument _xdoc;
private XElement _node;
public ObjectDumper(int levels) {
_levels = levels;
_xdoc = new XDocument();
_xdoc.Add(_node = new XElement("ul"));
}
public XElement Dump(object o, string name) {
// prevent cyclic references
if (_parents.Contains(o)) {
return _node;
}
if(_parents.Count >= _levels) {
return _node;
}
_parents.Push(o);
// starts a new container
_node.Add(_node = new XElement("li"));
if(o == null) {
DumpValue(null, name);
}
else if (o.GetType().IsValueType || o is string) {
DumpValue(o, name);
}
else {
DumpObject(o, name);
}
_parents.Pop();
if(_node.DescendantNodes().Count() == 0) {
_node.Remove();
}
_node = _node.Parent;
return _node;
}
private void DumpValue(object o, string name) {
string formatted = FormatValue(o);
_node.Add(
new XElement("div", new XAttribute("class", "name"), name),
new XElement("div", new XAttribute("class", "value"), formatted)
);
}
private void DumpObject(object o, string name) {
if (_parents.Count >= _levels) {
_node.Add(
new XElement("div", new XAttribute("class", "name"), name),
new XElement("div", new XAttribute("class", "object last"), o)
);
return;
}
_node.Add(
new XElement("div", new XAttribute("class", "name"), name),
new XElement("div", new XAttribute("class", "type"), FormatType(o.GetType()))
);
if (o is IDictionary) {
DumpDictionary((IDictionary)o);
}
else if (o is IShape) {
DumpShape((IShape)o);
// a shape can also be IEnumerable
if (o is IEnumerable) {
DumpEnumerable((IEnumerable) o);
}
}
else if (o is IEnumerable) {
DumpEnumerable((IEnumerable)o);
}
else {
DumpMembers(o);
}
}
private void DumpMembers(object o) {
var members = o.GetType()
.GetFields(BindingFlags.Instance | BindingFlags.Public).Cast<MemberInfo>()
.Union(o.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
.Where(m => !m.Name.StartsWith("_")) // remove members with a name starting with '_' (usually proxied objects)
.ToList();
if(members.Count() == 0) {
return;
}
_node.Add(_node = new XElement("ul"));
foreach (var member in members) {
try {
DumpMember(o, member);
}
catch {
}
}
_node = _node.Parent;
}
private void DumpEnumerable(IEnumerable enumerable) {
if(!enumerable.GetEnumerator().MoveNext()) {
return;
}
_node.Add(_node = new XElement("ul"));
int i = 0;
foreach (var child in enumerable) {
Dump(child, string.Format("[{0}]", i++));
}
_node = _node.Parent;
}
private void DumpDictionary(IDictionary dictionary) {
if (dictionary.Keys.Count == 0) {
return;
}
_node.Add(_node = new XElement("ul"));
foreach (var key in dictionary.Keys) {
Dump(dictionary[key], string.Format("[\"{0}\"]", key));
}
_node = _node.Parent;
}
private void DumpShape(IShape shape) {
var b = ((IClayBehaviorProvider)(dynamic)shape).Behavior as ClayBehaviorCollection;
if (b == null)
return;
// seek the PropBehavior if exists
var propBehavior = b.OfType<PropBehavior>().FirstOrDefault();
if (propBehavior == null)
return;
// retrieve the internal dictionary for properties
var props = propBehavior.GetType().GetField("_props", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField).GetValue(propBehavior) as Dictionary<object, object>;
if (props == null)
return;
if (props.Keys.Count == 0) {
return;
}
_node.Add(_node = new XElement("ul"));
foreach (var key in props.Keys) {
Dump(props[key], key.ToString());
}
_node = _node.Parent;
}
private void DumpMember(object o, MemberInfo member) {
if (member is MethodInfo || member is ConstructorInfo || member is EventInfo)
return;
if (member is FieldInfo) {
var field = (FieldInfo)member;
Dump(field.GetValue(o), member.Name);
}
else if (member is PropertyInfo) {
var prop = (PropertyInfo)member;
if (prop.GetIndexParameters().Length == 0 && prop.CanRead) {
Dump(prop.GetValue(o, null), member.Name);
}
}
}
private static string FormatValue(object o) {
if (o == null)
return "null";
var formatted = o.ToString();
if (o is string) {
// remove central part if tool long
if(formatted.Length > MaxStringLength) {
formatted = formatted.Substring(0, MaxStringLength/2) + "..." + formatted.Substring(formatted.Length - MaxStringLength/2);
}
formatted = "\"" + formatted + "\"";
}
return formatted;
}
private static string FormatType(Type t) {
if(t.IsGenericType) {
var genericArguments = String.Join(", ", t.GetGenericArguments().Select(FormatType).ToArray());
return String.Format("{0}<{1}>", t.Name.Substring(0, t.Name.IndexOf('`')), genericArguments);
}
if(typeof(IShape).IsAssignableFrom(t)) {
return "Shape";
}
return t.Name;
}
}
}

View File

@ -8,7 +8,9 @@ namespace Orchard.DesignerTools.Services {
}
public void Created(ShapeCreatedContext context) {
if (context.ShapeType != "Layout" && context.ShapeType != "DocumentZone") {
if (context.ShapeType != "Layout"
&& context.ShapeType != "DocumentZone"
) {
context.Shape.Metadata.Wrappers.Add("ShapeTracing_Wrapper");
}
}

View File

@ -16,7 +16,7 @@
color:yellow;
}
#debug-control .debug-active {
color:greenyellow;
color:Lime;
}
#debug-control ul {
display:none;
@ -36,11 +36,45 @@
#debug-control li {
border-right:1px solid #999;
}
html.debug-shape-templates .shapeTracingWrapper {
.debug-shape-templates .shape-tracing .wrapper {
background:#FFF;
border:1px dotted #CA7230;
padding:5px;
padding:2px;
}
html.debug-shape-templates .shapeTracingWrapper:hover {
.debug-shape-templates .shape-tracing .wrapper :hover {
background:#E0E9EE;
}
.shape-tracing .meta {
display:none;
}
.debug-shape-templates .shape-tracing .meta {
clear:both;
display:block;
background:#cfc;
font-size:9pt;
font-family:Segoe;
color:black;
margin-top:5px;
}
.debug-shape-zones .shape-tracing .wrapper .zone {
border:1px dotted red;
margin:5px;
padding:2px;
}
.meta .shape {
color:black;
}
.meta .alternates {
color:red;
}
.meta .wrappers {
color:green;
}

View File

@ -1,24 +1,59 @@
@using Orchard;
@using Orchard.DisplayManagement.Descriptors;
@using Orchard.DesignerTools.Services;
@using System.Xml;
@{
Script.Require("jQuery");
Script.Include("orchard-designertools-shapetracing.js");
Style.Include("orchard-designertools-shapetracing.css");
Script.Require("jQuery");
Script.Include("tooltip.min.js");
Script.Include("orchard-designertools-shapetracing.js");
Style.Include("orchard-designertools-shapetracing.css");
var workContext = ViewContext.GetWorkContext();
var shapeTable = workContext.Resolve<IShapeTableManager>().GetShapeTable(workContext.CurrentTheme.Id);
var descriptor = shapeTable.Descriptors[Model.Metadata.Type];
var descriptor = shapeTable.Descriptors[Model.Metadata.Type];
}
@functions {
string FormatShape(string type, string themeId) {
return "~/Themes/" + themeId + "/Views/" + type.Replace("__", "-").Replace("_", ".") + ".cshtml";
}
string DumpObject(object o) {
var dumper = new ObjectDumper(6);
var el = dumper.Dump(o, "Model");
using(var sw = new StringWriter()) {
el.WriteTo(new XmlTextWriter(sw) { Formatting = Formatting.Indented });
return sw.ToString();
}
}
}
<div class="shapeTracingWrapper" title="@Model.Metadata.Type @descriptor.BindingSource">
@Display(Model.Metadata.ChildContent)
<div class="shape-tracing wrapper @Model.Metadata.Type">
@Display(Model.Metadata.ChildContent)
@foreach(var alternate in Model.Metadata.Alternates) {
<div style="color:red">@alternate</div>
}
@foreach(var wrapper in Model.Metadata.Wrappers) {
if(wrapper != "ShapeTracing_Wrapper") {
<div style="color:green">@wrapper</div>
}
}
</div>
<div class="shape-tracing meta">
<div class="shape">
Shape: @Model.Metadata.Type <br />
Definition: @descriptor.BindingSource <br />
Display Type: @(Model.Metadata.DisplayType ?? "n/a")<br />
Position: @(Model.Metadata.Position ?? "n/a") <br />
</div>
<div class="alternates">
@foreach(var alternate in Model.Metadata.Alternates) {
var formatted = @FormatShape(alternate, workContext.CurrentTheme.Id);
<div>@formatted</div>
}
</div>
<div class="wrappers">
@foreach(var wrapper in Model.Metadata.Wrappers) {
if(wrapper != "ShapeTracing_Wrapper") {
<div>@wrapper</div>
}
}
</div>
<div class="dump">
<pre>@DumpObject((object)Model)</pre>
</div>
</div>
</div>

View File

@ -32,6 +32,7 @@
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<add assembly="System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</assemblies>
</compilation>
</system.web>

View File

@ -109,7 +109,7 @@ namespace Orchard.ContentManagement {
ContentType = context.ContentItem.ContentType,
DisplayType = displayType,
Differentiator = differentiator,
Path = request.Path.Substring((request.ApplicationPath ?? "").Length)
Path = request.Path.Substring((request.ApplicationPath ?? "").Length) // get the current app-relative path, i.e. /my-blog
};
var location = descriptor.Placement(placementContext);
return location ?? defaultLocation;