Incremental work towards infoset field storage

Thinning out ContentField model slightly
Reducing ContentFieldDriver's burden of locating storage strategies
Implementing non-versioned infoset field storage on ContentItemRecord Data property

--HG--
branch : dev
This commit is contained in:
Louis DeJardin
2010-06-23 14:38:21 -07:00
parent f7a135b97a
commit f38620786e
15 changed files with 252 additions and 63 deletions

View File

@@ -2,6 +2,7 @@
using System.Linq;
using Autofac;
using NUnit.Framework;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers.FieldStorage;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.ContentManagement.MetaData.Models;
@@ -28,9 +29,10 @@ namespace Orchard.Tests.ContentManagement.Drivers.FieldStorage {
get { return "TestProvName"; }
}
public IFieldStorage BindStorage(ContentPartDefinition.Field partFieldDefinition) {
public IFieldStorage BindStorage(ContentPart contentPart, ContentPartDefinition.Field partFieldDefinition) {
throw new NotImplementedException();
}
}
[Test]

View File

@@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autofac;
using NUnit.Framework;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers.FieldStorage;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.Records;
namespace Orchard.Tests.ContentManagement.Drivers.FieldStorage {
public class InfosetFieldStorageProviderTests {
private IContainer _container;
private IFieldStorageProvider _provider;
[SetUp]
public void Init() {
var builder = new ContainerBuilder();
builder.RegisterType<InfosetFieldStorageProvider>().As<IFieldStorageProvider>();
_container = builder.Build();
_provider = _container.Resolve<IFieldStorageProvider>();
}
private ContentPartDefinition FooPartDefinition() {
return new ContentPartDefinitionBuilder()
.Named("Foo")
.WithField("Bar")
.Build();
}
private ContentPart CreateContentItemPart() {
var partDefinition = FooPartDefinition();
var typeDefinition = new ContentTypeDefinitionBuilder()
.WithPart(partDefinition, part => { })
.Build();
var contentItem = new ContentItem {
VersionRecord = new ContentItemVersionRecord {
ContentItemRecord = new ContentItemRecord()
}
};
var contentPart = new ContentPart {
TypePartDefinition = typeDefinition.Parts.Single()
};
contentItem.Weld(contentPart);
return contentPart;
}
[Test]
public void BoundStorageIsNotNull() {
var part = CreateContentItemPart();
var storage = _provider.BindStorage(part, part.PartDefinition.Fields.Single());
Assert.That(storage, Is.Not.Null);
}
[Test]
public void GettingUnsetNamedAndUnnamedValueIsSafeAndNull() {
var part = CreateContentItemPart();
var storage = _provider.BindStorage(part, part.PartDefinition.Fields.Single());
Assert.That(storage.Getter(null), Is.Null);
Assert.That(storage.Getter("value"), Is.Null);
Assert.That(storage.Getter("This is a test"), Is.Null);
}
[Test]
public void ValueThatIsSetIsAlsoReturned() {
var part = CreateContentItemPart();
var storage = _provider.BindStorage(part, part.PartDefinition.Fields.Single());
Assert.That(storage.Getter("alpha"), Is.Null);
storage.Setter("alpha", "one");
Assert.That(storage.Getter("alpha"), Is.Not.Null);
Assert.That(storage.Getter("alpha"), Is.EqualTo("one"));
}
[Test]
public void NullAndEmptyValueNamesAreTreatedTheSame() {
var part = CreateContentItemPart();
var storage = _provider.BindStorage(part, part.PartDefinition.Fields.Single());
Assert.That(storage.Getter(null), Is.Null);
Assert.That(storage.Getter(""), Is.Null);
storage.Setter(null, "one");
Assert.That(storage.Getter(null), Is.EqualTo("one"));
Assert.That(storage.Getter(""), Is.EqualTo("one"));
storage.Setter(null, "two");
Assert.That(storage.Getter(null), Is.EqualTo("two"));
Assert.That(storage.Getter(""), Is.EqualTo("two"));
}
[Test]
public void RecordDataPropertyReflectsChangesToFields() {
var part = CreateContentItemPart();
var storage = _provider.BindStorage(part, part.PartDefinition.Fields.Single());
storage.Setter(null, "one");
storage.Setter("alpha", "two");
Assert.That(part.ContentItem.Record.Data, Is.EqualTo("<Data><Foo><Bar alpha=\"two\">one</Bar></Foo></Data>"));
}
[Test]
public void ChangingRecordDataHasImmediateEffectOnStorageAccessors() {
var part = CreateContentItemPart();
var storage = _provider.BindStorage(part, part.PartDefinition.Fields.Single());
storage.Setter(null, "one");
storage.Setter("alpha", "two");
Assert.That(part.ContentItem.Record.Data, Is.EqualTo("<Data><Foo><Bar alpha=\"two\">one</Bar></Foo></Data>"));
part.ContentItem.Record.Data = "<Data><Foo><Bar alpha=\"four\">three</Bar></Foo></Data>";
storage.Setter(null, "three");
storage.Setter("alpha", "four");
}
[Test]
public void VersionedSettingOnInfosetField() {
Assert.Fail("todo");
}
}
}

View File

@@ -152,6 +152,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="ContentManagement\Drivers\FieldStorage\FieldStorageProviderSelectorTests.cs" />
<Compile Include="ContentManagement\Drivers\FieldStorage\InfosetFieldStorageProviderTests.cs" />
<Compile Include="ContentManagement\Handlers\ContentHandlerTests.cs" />
<Compile Include="ContentManagement\Handlers\ModelBuilderTests.cs" />
<Compile Include="ContentManagement\MetaData\Builders\ContentTypeDefinitionBuilderTests.cs" />
@@ -251,6 +252,10 @@
<Compile Include="Utility\ReflectTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Orchard.Web\Modules\Orchard.Users\Orchard.Users.csproj">
<Project>{79AED36E-ABD0-4747-93D3-8722B042454B}</Project>
<Name>Orchard.Users</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard\Orchard.Framework.csproj">
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
<Name>Orchard.Framework</Name>

View File

@@ -1,9 +0,0 @@
using System.Collections.Generic;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Handlers;
namespace Orchard.Core.Common.Handlers {
public class TextContentFieldHandler : ContentFieldHandler {
public TextContentFieldHandler(IEnumerable<IContentFieldDriver> contentFieldDrivers) : base(contentFieldDrivers) {}
}
}

View File

@@ -68,7 +68,6 @@
<Compile Include="Common\Drivers\TextContentFieldDriver.cs" />
<Compile Include="Common\Fields\TextContentField.cs" />
<Compile Include="Common\Handlers\RoutableAspectHandler.cs" />
<Compile Include="Common\Handlers\TextContentFieldHandler.cs" />
<Compile Include="Common\ViewModels\ContainerEditorViewModel.cs" />
<Compile Include="Common\ViewModels\TextContentFieldDisplayViewModel.cs" />
<Compile Include="Common\ViewModels\TextContentFieldEditorViewModel.cs" />

View File

@@ -1,14 +1,10 @@
using System;
using System.Collections.Generic;
using Orchard.ContentManagement.MetaData.Models;
namespace Orchard.ContentManagement {
public class ContentField {
public virtual ContentPart ContentPart { get; set; }
public string Name { get { return PartFieldDefinition.Name; } }
public IDictionary<string, string> Settings { get; private set; }
public new ContentPartDefinition PartDefinition { get { return ContentPart.PartDefinition; } }
public ContentPartDefinition.Field PartFieldDefinition { get; set; }
public ContentFieldDefinition FieldDefinition { get { return PartFieldDefinition.FieldDefinition; } }

View File

@@ -5,7 +5,7 @@ using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.Utilities;
namespace Orchard.ContentManagement {
public abstract class ContentPart : IContent {
public class ContentPart : IContent {
private readonly IList<ContentField> _fields;
public ContentPart() {
@@ -29,7 +29,6 @@ namespace Orchard.ContentManagement {
}
public void Weld(ContentField field) {
field.ContentPart = this;
_fields.Add(field);
}
}

View File

@@ -9,7 +9,6 @@ namespace Orchard.ContentManagement.Drivers {
public abstract class ContentFieldDriver<TField> : IContentFieldDriver where TField : ContentField, new() {
protected virtual string Prefix { get { return ""; } }
protected virtual string Zone { get { return "body"; } }
public IFieldStorageProviderSelector FieldStorageProviderSelector { get; set; }
DriverResult IContentFieldDriver.BuildDisplayModel(BuildDisplayModelContext context) {
var results = context.ContentItem.Parts.SelectMany(part => part.Fields).
@@ -36,22 +35,17 @@ namespace Orchard.ContentManagement.Drivers {
var contentFieldInfo = new[] {
new ContentFieldInfo {
FieldTypeName = typeof (TField).Name,
Factory = FieldInstanceFactory
Factory = (partFieldDefinition, storage) => new TField {
PartFieldDefinition = partFieldDefinition,
Getter = storage.Getter,
Setter = storage.Setter,
}
}
};
return contentFieldInfo;
}
private TField FieldInstanceFactory(ContentPartDefinition.Field partFieldDefinition) {
var fieldStorageProvider = FieldStorageProviderSelector.GetProvider(partFieldDefinition);
var fieldStorage = fieldStorageProvider.BindStorage(partFieldDefinition);
return new TField {
PartFieldDefinition = partFieldDefinition,
Getter = fieldStorage.Getter,
Setter = fieldStorage.Setter,
};
}
protected virtual DriverResult Display(TField field, string displayType) { return null; }
protected virtual DriverResult Editor(TField field) { return null; }

View File

@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Orchard.ContentManagement.Drivers.FieldStorage;
using Orchard.ContentManagement.Handlers;
using Orchard.Logging;
@@ -7,14 +9,36 @@ namespace Orchard.ContentManagement.Drivers.Coordinators {
[UsedImplicitly]
public class ContentFieldDriverCoordinator : ContentHandlerBase {
private readonly IEnumerable<IContentFieldDriver> _drivers;
private readonly IFieldStorageProviderSelector _fieldStorageProviderSelector;
public ContentFieldDriverCoordinator(IEnumerable<IContentFieldDriver> drivers) {
public ContentFieldDriverCoordinator(
IEnumerable<IContentFieldDriver> drivers,
IFieldStorageProviderSelector fieldStorageProviderSelector) {
_drivers = drivers;
_fieldStorageProviderSelector = fieldStorageProviderSelector;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public override void Initializing(InitializingContentContext context) {
var fieldInfos = _drivers.SelectMany(x => x.GetFieldInfo());
var parts = context.ContentItem.Parts;
foreach (var contentPart in parts) {
foreach (var partFieldDefinition in contentPart.PartDefinition.Fields) {
var fieldTypeName = partFieldDefinition.FieldDefinition.Name;
var fieldInfo = fieldInfos.FirstOrDefault(x => x.FieldTypeName == fieldTypeName);
if (fieldInfo != null) {
var storage = _fieldStorageProviderSelector
.GetProvider(partFieldDefinition)
.BindStorage(contentPart, partFieldDefinition);
var field = fieldInfo.Factory(partFieldDefinition, storage);
contentPart.Weld(field);
}
}
}
}
public override void BuildDisplayModel(BuildDisplayModelContext context) {
_drivers.Invoke(driver => {
var result = driver.BuildDisplayModel(context);

View File

@@ -3,6 +3,9 @@
namespace Orchard.ContentManagement.Drivers.FieldStorage {
public interface IFieldStorageProvider : IDependency {
string ProviderName { get; }
IFieldStorage BindStorage(ContentPartDefinition.Field partFieldDefinition);
IFieldStorage BindStorage(
ContentPart contentPart,
ContentPartDefinition.Field partFieldDefinition);
}
}

View File

@@ -1,4 +1,6 @@
using System;
using System.Xml;
using System.Xml.Linq;
using Orchard.ContentManagement.MetaData.Models;
namespace Orchard.ContentManagement.Drivers.FieldStorage {
@@ -7,8 +9,57 @@ namespace Orchard.ContentManagement.Drivers.FieldStorage {
get { return FieldStorageProviderSelector.DefaultProviderName; }
}
public IFieldStorage BindStorage(ContentPartDefinition.Field partFieldDefinition) {
throw new NotImplementedException();
public IFieldStorage BindStorage(ContentPart contentPart, ContentPartDefinition.Field partFieldDefinition) {
var partName = XmlConvert.EncodeLocalName(contentPart.PartDefinition.Name);
var fieldName = XmlConvert.EncodeLocalName(partFieldDefinition.Name);
return new Storage {
Getter = name => Get(contentPart.ContentItem.Record.Infoset.Element, partName, fieldName, name),
Setter = (name, value) => Set(contentPart.ContentItem.Record.Infoset.Element, partName, fieldName, name, value)
};
}
private static string Get(XElement element, string partName, string fieldName, string valueName) {
var partElement = element.Element(partName);
if (partElement == null) {
return null;
}
var fieldElement = partElement.Element(fieldName);
if (fieldElement == null) {
return null;
}
if (string.IsNullOrEmpty(valueName)) {
return fieldElement.Value;
}
var valueAttribute = fieldElement.Attribute(XmlConvert.EncodeLocalName(valueName));
if (valueAttribute == null) {
return null;
}
return valueAttribute.Value;
}
private static void Set(XElement element, string partName, string fieldName, string valueName, string value) {
var partElement = element.Element(partName);
if (partElement == null) {
partElement = new XElement(partName);
element.Add(partElement);
}
var fieldElement = partElement.Element(fieldName);
if (fieldElement == null) {
fieldElement = new XElement(fieldName);
partElement.Add(fieldElement);
}
if (string.IsNullOrEmpty(valueName)) {
fieldElement.Value = value;
}
else {
fieldElement.SetAttributeValue(XmlConvert.EncodeLocalName(valueName), value);
}
}
class Storage : IFieldStorage {
public Func<string, string> Getter { get; set; }
public Action<string, string> Setter { get; set; }
}
}
}

View File

@@ -1,26 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.ContentManagement.Drivers;
namespace Orchard.ContentManagement.Handlers {
public class ContentFieldHandler: ContentHandlerBase {
private readonly IEnumerable<IContentFieldDriver> _contentFieldDrivers;
public ContentFieldHandler(IEnumerable<IContentFieldDriver> contentFieldDrivers) {
_contentFieldDrivers = contentFieldDrivers;
}
public override void Activated(ActivatedContentContext context) {
var fieldInfos = _contentFieldDrivers.SelectMany(x => x.GetFieldInfo());
var parts = context.ContentItem.Parts;
foreach (var contentPart in parts) {
foreach (var field in contentPart.Fields) {
var fieldTypeName = field.FieldDefinition.Name;
var fieldInfo = fieldInfos.FirstOrDefault(x => x.FieldTypeName == fieldTypeName);
if (fieldInfo != null)
contentPart.Weld(fieldInfo.Factory(field.PartFieldDefinition));
}
}
}
}
}

View File

@@ -1,9 +1,10 @@
using System;
using Orchard.ContentManagement.Drivers.FieldStorage;
using Orchard.ContentManagement.MetaData.Models;
namespace Orchard.ContentManagement.MetaData {
public class ContentFieldInfo {
public string FieldTypeName { get; set; }
public Func<ContentPartDefinition.Field, ContentField> Factory { get; set; }
public Func<ContentPartDefinition.Field, IFieldStorage, ContentField> Factory { get; set; }
}
}

View File

@@ -1,17 +1,44 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Orchard.ContentManagement.Records {
public class ContentItemRecord {
public ContentItemRecord() {
// ReSharper disable DoNotCallOverridableMethodsInConstructor
// ReSharper disable DoNotCallOverridableMethodsInConstructor
Versions = new List<ContentItemVersionRecord>();
// ReSharper restore DoNotCallOverridableMethodsInConstructor
// ReSharper restore DoNotCallOverridableMethodsInConstructor
Infoset = new Infoset();
}
public virtual int Id { get; set; }
public virtual ContentTypeRecord ContentType { get; set; }
public virtual IList<ContentItemVersionRecord> Versions { get; set; }
public virtual IList<ContentItemVersionRecord> Infoset { get; set; }
public virtual string Data {
get { return Infoset.Data; }
set { Infoset.Data = value; }
}
public virtual Infoset Infoset { get; private set; }
}
public class Infoset {
private XElement _element;
private void SetElement(XElement value) {
_element = value;
}
public XElement Element {
get {
return _element ?? (_element = new XElement("Data"));
}
}
public string Data {
get { return Element.ToString(SaveOptions.DisableFormatting); }
set { SetElement(XElement.Parse(value, LoadOptions.PreserveWhitespace)); }
}
}
}

View File

@@ -223,7 +223,6 @@
<Compile Include="ContentManagement\Handlers\ContentContextBase.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ContentManagement\Handlers\ContentFieldHandler.cs" />
<Compile Include="ContentManagement\Handlers\ContentHandler.cs">
<SubType>Code</SubType>
</Compile>