From b63e5f2bcd879f98011597c0fa31e12d57182df8 Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Sun, 6 Jun 2010 14:58:27 -0700 Subject: [PATCH] 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 --- .../Services/ContentDefinitionReaderTests.cs | 52 +++++++++++++++ .../Services/ContentDefinitionWriterTests.cs | 55 ++++++++++++++++ .../Orchard.Framework.Tests.csproj | 2 + .../Controllers/MetadataController.cs | 65 +++++++++++++++++++ .../Orchard.DevTools/Orchard.DevTools.csproj | 3 + .../ViewModels/MetadataIndexViewModel.cs | 11 ++++ .../Orchard.DevTools/Views/Home/Index.aspx | 1 + .../Views/Metadata/Index.aspx | 44 +++++++++++++ .../Orchard.MultiTenancy.csproj | 1 + .../Controllers/AdminController.cs | 1 + .../MetaData/IContentDefinitionReader.cs | 17 +++++ .../MetaData/IContentDefinitionWriter.cs | 9 +++ .../Services/ContentDefinitionReader.cs | 32 +++++++++ .../Services/ContentDefinitionWriter.cs | 42 ++++++++++++ .../MetaData/Services/SettingsFormatter.cs | 5 +- src/Orchard/Orchard.Framework.csproj | 4 ++ 16 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 src/Orchard.Tests/ContentManagement/MetaData/Services/ContentDefinitionReaderTests.cs create mode 100644 src/Orchard.Tests/ContentManagement/MetaData/Services/ContentDefinitionWriterTests.cs create mode 100644 src/Orchard.Web/Modules/Orchard.DevTools/Controllers/MetadataController.cs create mode 100644 src/Orchard.Web/Modules/Orchard.DevTools/ViewModels/MetadataIndexViewModel.cs create mode 100644 src/Orchard.Web/Modules/Orchard.DevTools/Views/Metadata/Index.aspx create mode 100644 src/Orchard/ContentManagement/MetaData/IContentDefinitionReader.cs create mode 100644 src/Orchard/ContentManagement/MetaData/IContentDefinitionWriter.cs create mode 100644 src/Orchard/ContentManagement/MetaData/Services/ContentDefinitionReader.cs create mode 100644 src/Orchard/ContentManagement/MetaData/Services/ContentDefinitionWriter.cs diff --git a/src/Orchard.Tests/ContentManagement/MetaData/Services/ContentDefinitionReaderTests.cs b/src/Orchard.Tests/ContentManagement/MetaData/Services/ContentDefinitionReaderTests.cs new file mode 100644 index 000000000..07a363a4d --- /dev/null +++ b/src/Orchard.Tests/ContentManagement/MetaData/Services/ContentDefinitionReaderTests.cs @@ -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(); + } + } +} diff --git a/src/Orchard.Tests/ContentManagement/MetaData/Services/ContentDefinitionWriterTests.cs b/src/Orchard.Tests/ContentManagement/MetaData/Services/ContentDefinitionWriterTests.cs new file mode 100644 index 000000000..6182f7b25 --- /dev/null +++ b/src/Orchard.Tests/ContentManagement/MetaData/Services/ContentDefinitionWriterTests.cs @@ -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")); + } + } +} diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj index 5d469d06c..4b9753276 100644 --- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj @@ -154,6 +154,8 @@ + + diff --git a/src/Orchard.Web/Modules/Orchard.DevTools/Controllers/MetadataController.cs b/src/Orchard.Web/Modules/Orchard.DevTools/Controllers/MetadataController.cs new file mode 100644 index 000000000..bc3a60e86 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.DevTools/Controllers/MetadataController.cs @@ -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"); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.DevTools/Orchard.DevTools.csproj b/src/Orchard.Web/Modules/Orchard.DevTools/Orchard.DevTools.csproj index b97bf93cd..fd88a2bd9 100644 --- a/src/Orchard.Web/Modules/Orchard.DevTools/Orchard.DevTools.csproj +++ b/src/Orchard.Web/Modules/Orchard.DevTools/Orchard.DevTools.csproj @@ -73,6 +73,7 @@ + @@ -80,6 +81,7 @@ + @@ -90,6 +92,7 @@ + diff --git a/src/Orchard.Web/Modules/Orchard.DevTools/ViewModels/MetadataIndexViewModel.cs b/src/Orchard.Web/Modules/Orchard.DevTools/ViewModels/MetadataIndexViewModel.cs new file mode 100644 index 000000000..574430485 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.DevTools/ViewModels/MetadataIndexViewModel.cs @@ -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 TypeDefinitions { get; set; } + public IEnumerable PartDefinitions { get; set; } + public string ExportText { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.DevTools/Views/Home/Index.aspx b/src/Orchard.Web/Modules/Orchard.DevTools/Views/Home/Index.aspx index 741c0cb7a..dab5ed155 100644 --- a/src/Orchard.Web/Modules/Orchard.DevTools/Views/Home/Index.aspx +++ b/src/Orchard.Web/Modules/Orchard.DevTools/Views/Home/Index.aspx @@ -2,4 +2,5 @@ <%@ Import Namespace="Orchard.Mvc.ViewModels"%>

<%=Html.TitleForPage(T("Dev Tools").ToString()) %>

<%=Html.ActionLink(T("Contents").ToString(), "Index", "Content") %>

+

<%=Html.ActionLink(T("Metadata").ToString(), "Index", "Metadata") %>

<%=Html.ActionLink(T("Test Unauthorized Request").ToString(), "NotAuthorized", "Home")%>

diff --git a/src/Orchard.Web/Modules/Orchard.DevTools/Views/Metadata/Index.aspx b/src/Orchard.Web/Modules/Orchard.DevTools/Views/Metadata/Index.aspx new file mode 100644 index 000000000..7c119bf89 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.DevTools/Views/Metadata/Index.aspx @@ -0,0 +1,44 @@ +<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage" %> + +<%@ Import Namespace="Orchard.DevTools.ViewModels" %> + +

+ Metadata

+

+ Content Type Definitions

+
    + <%foreach (var type in Model.TypeDefinitions) {%> +
  • + <%:type.Name %> +
      + <%foreach (var part in type.Parts) {%> +
    • + <%:part.PartDefinition.Name %>
    • + <% + }%> +
    +
  • + <% + }%> +
+

+ Content Part Definitions

+
    + <%foreach (var part in Model.PartDefinitions) {%> +
  • + <%:part.Name %>
  • + <% + }%> +
+

+Exported as xml

+<% using (Html.BeginFormAntiForgeryPost()) { %> +<%:Html.TextAreaFor(m=>m.ExportText, new{style="width:100%;height:640px;"}) %> +
+" /> +<%} %> diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Orchard.MultiTenancy.csproj b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Orchard.MultiTenancy.csproj index 6ab56cf17..9e3facaa7 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Orchard.MultiTenancy.csproj +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Orchard.MultiTenancy.csproj @@ -96,6 +96,7 @@ {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} Orchard.Framework + False diff --git a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs index e85de7d77..5578531ab 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs @@ -10,6 +10,7 @@ using Orchard.Users.Services; using Orchard.Users.ViewModels; namespace Orchard.Users.Controllers { + [ValidateInput(false)] public class AdminController : Controller, IUpdateModel { private readonly IMembershipService _membershipService; private readonly IUserService _userService; diff --git a/src/Orchard/ContentManagement/MetaData/IContentDefinitionReader.cs b/src/Orchard/ContentManagement/MetaData/IContentDefinitionReader.cs new file mode 100644 index 000000000..c24691de3 --- /dev/null +++ b/src/Orchard/ContentManagement/MetaData/IContentDefinitionReader.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/src/Orchard/ContentManagement/MetaData/IContentDefinitionWriter.cs b/src/Orchard/ContentManagement/MetaData/IContentDefinitionWriter.cs new file mode 100644 index 000000000..bc2b6f908 --- /dev/null +++ b/src/Orchard/ContentManagement/MetaData/IContentDefinitionWriter.cs @@ -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); + } +} diff --git a/src/Orchard/ContentManagement/MetaData/Services/ContentDefinitionReader.cs b/src/Orchard/ContentManagement/MetaData/Services/ContentDefinitionReader.cs new file mode 100644 index 000000000..ad1037632 --- /dev/null +++ b/src/Orchard/ContentManagement/MetaData/Services/ContentDefinitionReader.cs @@ -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> _settingsReader; + + public ContentDefinitionReader(IMapper> 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); + } + }); + } + } + } +} \ No newline at end of file diff --git a/src/Orchard/ContentManagement/MetaData/Services/ContentDefinitionWriter.cs b/src/Orchard/ContentManagement/MetaData/Services/ContentDefinitionWriter.cs new file mode 100644 index 000000000..fc61eb16e --- /dev/null +++ b/src/Orchard/ContentManagement/MetaData/Services/ContentDefinitionWriter.cs @@ -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, XElement> _settingsWriter; + + public ContentDefinitionWriter(IMapper, 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 settings) { + var element = new XElement(XmlConvert.EncodeLocalName(name)); + foreach(var settingAttribute in _settingsWriter.Map(settings).Attributes()) { + element.Add(settingAttribute); + } + return element; + } + } +} diff --git a/src/Orchard/ContentManagement/MetaData/Services/SettingsFormatter.cs b/src/Orchard/ContentManagement/MetaData/Services/SettingsFormatter.cs index f7a32f8dc..293a10018 100644 --- a/src/Orchard/ContentManagement/MetaData/Services/SettingsFormatter.cs +++ b/src/Orchard/ContentManagement/MetaData/Services/SettingsFormatter.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Xml; using System.Xml.Linq; namespace Orchard.ContentManagement.MetaData.Services { @@ -11,14 +12,14 @@ namespace Orchard.ContentManagement.MetaData.Services { if (source == null) return new Dictionary(); - 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 source) { if (source == null) 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))); } } } diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 80aea76e8..6195a1980 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -296,7 +296,10 @@ Code + + + Code @@ -304,6 +307,7 @@ Code + Code