mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-14 19:04:51 +08:00
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:
@@ -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]
|
||||
|
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@@ -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>
|
||||
|
@@ -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) {}
|
||||
}
|
||||
}
|
@@ -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" />
|
||||
|
@@ -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; } }
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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; }
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
@@ -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)); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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>
|
||||
|
Reference in New Issue
Block a user