Working towards definition import/export

Reader and writer mapping definition to infoset
Devtools placeholder providing way to see/alter metadata
Reader on builder over existing content definition provides merge capabilies on import

--HG--
branch : dev
This commit is contained in:
Louis DeJardin
2010-06-06 14:58:27 -07:00
parent 9ffb410c3d
commit b63e5f2bcd
16 changed files with 342 additions and 2 deletions

View File

@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using NUnit.Framework;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.ContentManagement.MetaData.Services;
namespace Orchard.Tests.ContentManagement.MetaData.Services {
[TestFixture]
public class ContentDefinitionReaderTests {
private IContentDefinitionReader _reader;
[SetUp]
public void Init() {
_reader = new ContentDefinitionReader(new SettingsFormatter());
}
[Test]
public void ReadingElementSetsName() {
var builder = new ContentTypeDefinitionBuilder();
_reader.Merge(new XElement("foo"), builder);
var type = builder.Build();
Assert.That(type.Name, Is.EqualTo("foo"));
}
[Test]
public void AttributesAreAppliedAsSettings() {
var builder = new ContentTypeDefinitionBuilder();
_reader.Merge(new XElement("foo", new XAttribute("x", "1")), builder);
var type = builder.Build();
Assert.That(type.Settings["x"], Is.EqualTo("1"));
}
[Test]
public void ChildElementsAreAddedAsPartsWithSettings() {
var builder = new ContentTypeDefinitionBuilder();
_reader.Merge(new XElement("foo", new XElement("bar", new XAttribute("y", "2"))), builder);
var type = builder.Build();
Assert.That(type.Parts.Single().PartDefinition.Name, Is.EqualTo("bar"));
Assert.That(type.Parts.Single().Settings["y"], Is.EqualTo("2"));
}
[Test, Ignore("Parts can be removed by name")]
public void PartsCanBeRemovedByNameWhenImporting() {
Assert.Fail();
}
}
}

View File

@@ -0,0 +1,55 @@
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using NUnit.Framework;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.ContentManagement.MetaData.Services;
namespace Orchard.Tests.ContentManagement.MetaData.Services {
[TestFixture]
public class ContentDefinitionWriterTests {
private ContentDefinitionWriter _writer;
[SetUp]
public void Init() {
_writer = new ContentDefinitionWriter(new SettingsFormatter());
}
[Test]
public void CreatesElementWithEncodedContentTypeName() {
var alphaInfoset = _writer.Export(new ContentTypeDefinitionBuilder().Named("alpha").Build());
var betaInfoset = _writer.Export(new ContentTypeDefinitionBuilder().Named(":beta").Build());
var gammaInfoset = _writer.Export(new ContentTypeDefinitionBuilder().Named(" g a m m a ").Build());
var deltaInfoset = _writer.Export(new ContentTypeDefinitionBuilder().Named("del\r\nta").Build());
Assert.That(XmlConvert.DecodeName(alphaInfoset.Name.LocalName), Is.EqualTo("alpha"));
Assert.That(XmlConvert.DecodeName(betaInfoset.Name.LocalName), Is.EqualTo(":beta"));
Assert.That(XmlConvert.DecodeName(gammaInfoset.Name.LocalName), Is.EqualTo(" g a m m a "));
Assert.That(XmlConvert.DecodeName(deltaInfoset.Name.LocalName), Is.EqualTo("del\r\nta"));
}
[Test]
public void ChildElementsArePartNames() {
var alphaInfoset = _writer.Export(new ContentTypeDefinitionBuilder().Named("alpha").WithPart(":beta").WithPart("del\r\nta").Build());
Assert.That(XmlConvert.DecodeName(alphaInfoset.Name.LocalName), Is.EqualTo("alpha"));
Assert.That(alphaInfoset.Elements().Count(), Is.EqualTo(2));
Assert.That(alphaInfoset.Elements().Select(elt => elt.Name.LocalName), Has.Some.EqualTo(XmlConvert.EncodeLocalName(":beta")));
Assert.That(alphaInfoset.Elements().Select(elt => elt.Name.LocalName), Has.Some.EqualTo(XmlConvert.EncodeLocalName("del\r\nta")));
}
[Test]
public void TypeAndTypePartSettingsAreAttributes() {
var alpha = new ContentTypeDefinitionBuilder()
.Named("alpha")
.WithSetting("x", "1")
.WithPart("beta", part => part.WithSetting(" y ", "2"))
.Build();
var alphaInfoset = _writer.Export(alpha);
Assert.That(alphaInfoset.Attributes("x").Single().Value, Is.EqualTo("1"));
Assert.That(alphaInfoset.Elements("beta").Attributes(XmlConvert.EncodeLocalName(" y ")).Single().Value, Is.EqualTo("2"));
}
}
}

View File

@@ -154,6 +154,8 @@
<Compile Include="ContentManagement\Handlers\ContentHandlerTests.cs" /> <Compile Include="ContentManagement\Handlers\ContentHandlerTests.cs" />
<Compile Include="ContentManagement\Handlers\ModelBuilderTests.cs" /> <Compile Include="ContentManagement\Handlers\ModelBuilderTests.cs" />
<Compile Include="ContentManagement\MetaData\Builders\ContentTypeDefinitionBuilderTests.cs" /> <Compile Include="ContentManagement\MetaData\Builders\ContentTypeDefinitionBuilderTests.cs" />
<Compile Include="ContentManagement\MetaData\Services\ContentDefinitionReaderTests.cs" />
<Compile Include="ContentManagement\MetaData\Services\ContentDefinitionWriterTests.cs" />
<Compile Include="ContentManagement\Models\Alpha.cs" /> <Compile Include="ContentManagement\Models\Alpha.cs" />
<Compile Include="ContentManagement\Models\AlphaHandler.cs" /> <Compile Include="ContentManagement\Models\AlphaHandler.cs" />
<Compile Include="ContentManagement\Models\Beta.cs" /> <Compile Include="ContentManagement\Models\Beta.cs" />

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Xml;
using System.Xml.Linq;
using Orchard.ContentManagement.MetaData;
using Orchard.DevTools.ViewModels;
namespace Orchard.DevTools.Controllers {
[ValidateInput(false)]
public class MetadataController : Controller {
private readonly IContentDefinitionManager _contentDefinitionManager;
private readonly IContentDefinitionWriter _contentDefinitionWriter;
private readonly IContentDefinitionReader _contentDefinitionReader;
public MetadataController(
IContentDefinitionManager contentDefinitionManager,
IContentDefinitionWriter contentDefinitionWriter,
IContentDefinitionReader contentDefinitionReader) {
_contentDefinitionManager = contentDefinitionManager;
_contentDefinitionWriter = contentDefinitionWriter;
_contentDefinitionReader = contentDefinitionReader;
}
public ActionResult Index() {
var model = new MetadataIndexViewModel {
TypeDefinitions = _contentDefinitionManager.ListTypeDefinitions(),
PartDefinitions = _contentDefinitionManager.ListPartDefinitions()
};
var types = new XElement("Types");
foreach (var type in model.TypeDefinitions) {
types.Add(_contentDefinitionWriter.Export(type));
}
var parts = new XElement("Parts");
foreach (var part in model.PartDefinitions) {
parts.Add(_contentDefinitionWriter.Export(part));
}
var stringWriter = new StringWriter();
using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true, IndentChars = " " })) {
if (xmlWriter != null) {
new XElement("Orchard", types, parts).WriteTo(xmlWriter);
}
}
model.ExportText = stringWriter.ToString();
return View(model);
}
[HttpPost]
public ActionResult Index(MetadataIndexViewModel model) {
var root = XElement.Parse(model.ExportText);
foreach (var element in root.Elements("Types").Elements()) {
var typeElement = element;
var typeName = XmlConvert.DecodeName(element.Name.LocalName);
_contentDefinitionManager.AlterTypeDefinition(typeName, alteration => _contentDefinitionReader.Merge(typeElement, alteration));
}
return RedirectToAction("Index");
}
}
}

View File

@@ -73,6 +73,7 @@
<Compile Include="Commands\ProfilingCommands.cs" /> <Compile Include="Commands\ProfilingCommands.cs" />
<Compile Include="Controllers\ContentController.cs" /> <Compile Include="Controllers\ContentController.cs" />
<Compile Include="Controllers\HomeController.cs" /> <Compile Include="Controllers\HomeController.cs" />
<Compile Include="Controllers\MetadataController.cs" />
<Compile Include="Handlers\DebugLinkHandler.cs" /> <Compile Include="Handlers\DebugLinkHandler.cs" />
<Compile Include="Models\ShowDebugLink.cs" /> <Compile Include="Models\ShowDebugLink.cs" />
<Compile Include="Models\Simple.cs" /> <Compile Include="Models\Simple.cs" />
@@ -80,6 +81,7 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ViewModels\ContentIndexViewModel.cs" /> <Compile Include="ViewModels\ContentIndexViewModel.cs" />
<Compile Include="ViewModels\ContentDetailsViewModel.cs" /> <Compile Include="ViewModels\ContentDetailsViewModel.cs" />
<Compile Include="ViewModels\MetadataIndexViewModel.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Module.txt" /> <Content Include="Module.txt" />
@@ -90,6 +92,7 @@
<Content Include="Views\Home\Index.aspx" /> <Content Include="Views\Home\Index.aspx" />
<Content Include="Views\DisplayTemplates\Parts\DevTools.ShowDebugLink.ascx" /> <Content Include="Views\DisplayTemplates\Parts\DevTools.ShowDebugLink.ascx" />
<Content Include="Views\EditorTemplates\Parts\DevTools.ShowDebugLink.ascx" /> <Content Include="Views\EditorTemplates\Parts\DevTools.ShowDebugLink.ascx" />
<Content Include="Views\Metadata\Index.aspx" />
<Content Include="Web.config" /> <Content Include="Web.config" />
<Content Include="Views\Web.config" /> <Content Include="Views\Web.config" />
</ItemGroup> </ItemGroup>

View File

@@ -0,0 +1,11 @@
using System.Collections.Generic;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.Mvc.ViewModels;
namespace Orchard.DevTools.ViewModels {
public class MetadataIndexViewModel : BaseViewModel {
public IEnumerable<ContentTypeDefinition> TypeDefinitions { get; set; }
public IEnumerable<ContentPartDefinition> PartDefinitions { get; set; }
public string ExportText { get; set; }
}
}

View File

@@ -2,4 +2,5 @@
<%@ Import Namespace="Orchard.Mvc.ViewModels"%> <%@ Import Namespace="Orchard.Mvc.ViewModels"%>
<h1><%=Html.TitleForPage(T("Dev Tools").ToString()) %></h1> <h1><%=Html.TitleForPage(T("Dev Tools").ToString()) %></h1>
<p><%=Html.ActionLink(T("Contents").ToString(), "Index", "Content") %></p> <p><%=Html.ActionLink(T("Contents").ToString(), "Index", "Content") %></p>
<p><%=Html.ActionLink(T("Metadata").ToString(), "Index", "Metadata") %></p>
<p><%=Html.ActionLink(T("Test Unauthorized Request").ToString(), "NotAuthorized", "Home")%></p> <p><%=Html.ActionLink(T("Test Unauthorized Request").ToString(), "NotAuthorized", "Home")%></p>

View File

@@ -0,0 +1,44 @@
<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage<MetadataIndexViewModel>" %>
<%@ Import Namespace="Orchard.DevTools.ViewModels" %>
<style title="text/css">
ul
{
margin-left: 12px;
}
</style>
<h1>
Metadata</h1>
<h2>
Content Type Definitions</h2>
<ul>
<%foreach (var type in Model.TypeDefinitions) {%>
<li>
<%:type.Name %>
<ul>
<%foreach (var part in type.Parts) {%>
<li>
<%:part.PartDefinition.Name %></li>
<%
}%>
</ul>
</li>
<%
}%>
</ul>
<h2>
Content Part Definitions</h2>
<ul>
<%foreach (var part in Model.PartDefinitions) {%>
<li>
<%:part.Name %></li>
<%
}%>
</ul>
<h2>
Exported as xml</h2>
<% using (Html.BeginFormAntiForgeryPost()) { %>
<%:Html.TextAreaFor(m=>m.ExportText, new{style="width:100%;height:640px;"}) %>
<br />
<input class="button primaryAction" type="submit" value="<%=_Encoded("Merge Changes") %>" />
<%} %>

View File

@@ -96,6 +96,7 @@
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj"> <ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project> <Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
<Name>Orchard.Framework</Name> <Name>Orchard.Framework</Name>
<Private>False</Private>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />

View File

@@ -10,6 +10,7 @@ using Orchard.Users.Services;
using Orchard.Users.ViewModels; using Orchard.Users.ViewModels;
namespace Orchard.Users.Controllers { namespace Orchard.Users.Controllers {
[ValidateInput(false)]
public class AdminController : Controller, IUpdateModel { public class AdminController : Controller, IUpdateModel {
private readonly IMembershipService _membershipService; private readonly IMembershipService _membershipService;
private readonly IUserService _userService; private readonly IUserService _userService;

View File

@@ -0,0 +1,17 @@
using System.Xml.Linq;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.ContentManagement.MetaData.Models;
namespace Orchard.ContentManagement.MetaData {
public interface IContentDefinitionReader : IDependency {
void Merge(XElement source, ContentTypeDefinitionBuilder builder);
}
public static class ContentDefinitionReaderExtensions {
public static ContentTypeDefinition Import(this IContentDefinitionReader reader, XElement source) {
var target = new ContentTypeDefinitionBuilder();
reader.Merge(source, target);
return target.Build();
}
}
}

View File

@@ -0,0 +1,9 @@
using System.Xml.Linq;
using Orchard.ContentManagement.MetaData.Models;
namespace Orchard.ContentManagement.MetaData {
public interface IContentDefinitionWriter : IDependency{
XElement Export(ContentTypeDefinition typeDefinition);
XElement Export(ContentPartDefinition partDefinition);
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Linq;
using Orchard.ContentManagement.MetaData.Builders;
namespace Orchard.ContentManagement.MetaData.Services {
public class ContentDefinitionReader : IContentDefinitionReader {
private readonly IMapper<XElement, IDictionary<string, string>> _settingsReader;
public ContentDefinitionReader(IMapper<XElement, IDictionary<string, string>> settingsReader) {
_settingsReader = settingsReader;
}
public void Merge(XElement source, ContentTypeDefinitionBuilder builder) {
builder.Named(XmlConvert.DecodeName(source.Name.LocalName));
foreach (var setting in _settingsReader.Map(source)) {
builder.WithSetting(setting.Key, setting.Value);
}
foreach (var iter in source.Elements()) {
var partElement = iter;
builder.WithPart(
XmlConvert.DecodeName(partElement.Name.LocalName),
partBuilder => {
foreach (var setting in _settingsReader.Map(partElement)) {
partBuilder.WithSetting(setting.Key, setting.Value);
}
});
}
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Linq;
using Orchard.ContentManagement.MetaData.Models;
namespace Orchard.ContentManagement.MetaData.Services {
public class ContentDefinitionWriter : IContentDefinitionWriter {
private readonly IMapper<IDictionary<string, string>, XElement> _settingsWriter;
public ContentDefinitionWriter(IMapper<IDictionary<string, string>, XElement> settingsWriter) {
_settingsWriter = settingsWriter;
}
public XElement Export(ContentTypeDefinition typeDefinition) {
var typeElement = NewElement(typeDefinition.Name, typeDefinition.Settings);
foreach(var typePart in typeDefinition.Parts) {
typeElement.Add(NewElement(typePart.PartDefinition.Name, typePart.Settings));
}
return typeElement;
}
public XElement Export(ContentPartDefinition partDefinition) {
var partElement = NewElement(partDefinition.Name, partDefinition.Settings);
foreach(var partField in partDefinition.Fields) {
var partFieldElement = NewElement(partField.Name, partField.Settings);
partFieldElement.SetAttributeValue("FieldType", partField.FieldDefinition.Name);
partElement.Add(partFieldElement);
}
return partElement;
}
private XElement NewElement(string name, IDictionary<string, string> settings) {
var element = new XElement(XmlConvert.EncodeLocalName(name));
foreach(var settingAttribute in _settingsWriter.Map(settings).Attributes()) {
element.Add(settingAttribute);
}
return element;
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
namespace Orchard.ContentManagement.MetaData.Services { namespace Orchard.ContentManagement.MetaData.Services {
@@ -11,14 +12,14 @@ namespace Orchard.ContentManagement.MetaData.Services {
if (source == null) if (source == null)
return new Dictionary<string, string>(); return new Dictionary<string, string>();
return source.Attributes().ToDictionary(attr => attr.Name.LocalName, attr => attr.Value); return source.Attributes().ToDictionary(attr => XmlConvert.DecodeName(attr.Name.LocalName), attr => attr.Value);
} }
public XElement Map(IDictionary<string, string> source) { public XElement Map(IDictionary<string, string> source) {
if (source == null) if (source == null)
return new XElement("settings"); return new XElement("settings");
return new XElement("settings", source.Select(kv => new XAttribute(kv.Key, kv.Value))); return new XElement("settings", source.Select(kv => new XAttribute(XmlConvert.EncodeLocalName(kv.Key), kv.Value)));
} }
} }
} }

View File

@@ -296,7 +296,10 @@
<Compile Include="ContentManagement\MetaData\ContentPartInfo.cs"> <Compile Include="ContentManagement\MetaData\ContentPartInfo.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="ContentManagement\MetaData\Services\ContentDefinitionWriter.cs" />
<Compile Include="ContentManagement\MetaData\IContentDefinitionManager.cs" /> <Compile Include="ContentManagement\MetaData\IContentDefinitionManager.cs" />
<Compile Include="ContentManagement\MetaData\IContentDefinitionReader.cs" />
<Compile Include="ContentManagement\MetaData\IContentDefinitionWriter.cs" />
<Compile Include="ContentManagement\MetaData\Models\ContentFieldDefinition.cs" /> <Compile Include="ContentManagement\MetaData\Models\ContentFieldDefinition.cs" />
<Compile Include="ContentManagement\MetaData\Models\ContentPartDefinition.cs"> <Compile Include="ContentManagement\MetaData\Models\ContentPartDefinition.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
@@ -304,6 +307,7 @@
<Compile Include="ContentManagement\MetaData\Models\ContentTypeDefinition.cs"> <Compile Include="ContentManagement\MetaData\Models\ContentTypeDefinition.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="ContentManagement\MetaData\Services\ContentDefinitionReader.cs" />
<Compile Include="ContentManagement\MetaData\Services\SettingsFormatter.cs" /> <Compile Include="ContentManagement\MetaData\Services\SettingsFormatter.cs" />
<Compile Include="ContentManagement\Records\ContentItemAlteration.cs"> <Compile Include="ContentManagement\Records\ContentItemAlteration.cs">
<SubType>Code</SubType> <SubType>Code</SubType>