Adding data versioning capabilities which content parts may opt-into. Still needs query support though.

--HG--
extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4044752
This commit is contained in:
loudej
2009-12-30 00:36:35 +00:00
parent 8051ac212a
commit 556ab3c99f
52 changed files with 1683 additions and 2008 deletions

View File

@@ -37,7 +37,7 @@ namespace Orchard.Core.Tests.Common.Providers {
protected override IEnumerable<Type> DatabaseTypes {
get {
return new[] {typeof (ContentTypeRecord), typeof (ContentItemRecord), typeof (CommonRecord)};
return new[] { typeof(ContentTypeRecord), typeof(ContentItemRecord), typeof(ContentItemVersionRecord), typeof(CommonRecord) };
}
}

View File

@@ -39,7 +39,7 @@ namespace Orchard.Tests.Packages.Users.Controllers {
protected override IEnumerable<Type> DatabaseTypes {
get {
return new[] { typeof(UserRecord), typeof(ContentItemRecord), typeof(ContentTypeRecord) };
return new[] { typeof(UserRecord), typeof(ContentItemRecord), typeof(ContentItemVersionRecord), typeof(ContentTypeRecord) };
}
}

View File

@@ -43,6 +43,7 @@ namespace Orchard.Tests.Packages.Users.Services {
_sessionFactory = DataUtility.CreateSessionFactory(
databaseFileName,
typeof(UserRecord),
typeof(ContentItemVersionRecord),
typeof(ContentItemRecord),
typeof(ContentTypeRecord));
}

View File

@@ -29,6 +29,7 @@ namespace Orchard.Tests.Models {
databaseFileName,
typeof(GammaRecord),
typeof(DeltaRecord),
typeof(ContentItemVersionRecord),
typeof(ContentItemRecord),
typeof(ContentTypeRecord));
}
@@ -56,10 +57,11 @@ namespace Orchard.Tests.Models {
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
_session = _sessionFactory.OpenSession();
builder.Register(new DefaultModelManagerTests.TestSessionLocator(_session)).As<ISessionLocator>();
builder.Register(new DefaultContentManagerTests.TestSessionLocator(_session)).As<ISessionLocator>();
_session.Delete("from GammaRecord");
_session.Delete("from DeltaRecord");
_session.Delete("from ContentItemVersionRecord");
_session.Delete("from ContentItemRecord");
_session.Delete("from ContentTypeRecord");
_session.Flush();
@@ -187,19 +189,21 @@ namespace Orchard.Tests.Models {
_manager.Create<Gamma>("gamma", init => { init.Record.Frap = "three"; });
_manager.Create<Gamma>("gamma", init => { init.Record.Frap = "four"; });
_session.Flush();
_session.Clear();
var ascending = _manager.Query("gamma")
.OrderBy<GammaRecord, string>(x => x.Frap)
.List<Gamma>();
.List<Gamma>().ToList();
Assert.That(ascending.Count(), Is.EqualTo(5));
Assert.That(ascending.First().Record.Frap, Is.EqualTo("four"));
Assert.That(ascending.Last().Record.Frap, Is.EqualTo("two"));
_session.Clear();
var descending = _manager.Query<Gamma, GammaRecord>()
.OrderByDescending(x => x.Frap)
.List();
.List().ToList();
Assert.That(descending.Count(), Is.EqualTo(5));
Assert.That(descending.First().Record.Frap, Is.EqualTo("two"));

View File

@@ -0,0 +1,414 @@
using System;
using System.Diagnostics;
using System.Linq;
using Autofac;
using Autofac.Builder;
using Autofac.Modules;
using NHibernate;
using NUnit.Framework;
using Orchard.Data;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.Records;
using Orchard.Tests.Models.Records;
using Orchard.Tests.Models.Stubs;
namespace Orchard.Tests.Models {
[TestFixture]
public class DefaultContentManagerTests {
private IContainer _container;
private IContentManager _manager;
private ISessionFactory _sessionFactory;
private ISession _session;
[TestFixtureSetUp]
public void InitFixture() {
var databaseFileName = System.IO.Path.GetTempFileName();
_sessionFactory = DataUtility.CreateSessionFactory(
databaseFileName,
typeof(ContentTypeRecord),
typeof(ContentItemRecord),
typeof(ContentItemVersionRecord),
typeof(GammaRecord),
typeof(DeltaRecord),
typeof(EpsilonRecord));
}
[TestFixtureTearDown]
public void TermFixture() {
}
[SetUp]
public void Init() {
var builder = new ContainerBuilder();
builder.RegisterModule(new ImplicitCollectionSupportModule());
builder.Register<DefaultContentManager>().As<IContentManager>();
builder.Register<AlphaHandler>().As<IContentHandler>();
builder.Register<BetaHandler>().As<IContentHandler>();
builder.Register<GammaHandler>().As<IContentHandler>();
builder.Register<DeltaHandler>().As<IContentHandler>();
builder.Register<EpsilonHandler>().As<IContentHandler>();
builder.Register<FlavoredHandler>().As<IContentHandler>();
builder.Register<StyledHandler>().As<IContentHandler>();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
_session = _sessionFactory.OpenSession();
builder.Register(new TestSessionLocator(_session)).As<ISessionLocator>();
_container = builder.Build();
_manager = _container.Resolve<IContentManager>();
}
public class TestSessionLocator : ISessionLocator {
private readonly ISession _session;
public TestSessionLocator(ISession session) {
_session = session;
}
public ISession For(Type entityType) {
return _session;
}
}
[Test]
public void AlphaDriverShouldWeldItsPart() {
var foo = _manager.New("alpha");
Assert.That(foo.Is<Alpha>(), Is.True);
Assert.That(foo.As<Alpha>(), Is.Not.Null);
Assert.That(foo.Is<Beta>(), Is.False);
Assert.That(foo.As<Beta>(), Is.Null);
}
[Test]
public void StronglyTypedNewShouldTypeCast() {
var foo = _manager.New<Alpha>("alpha");
Assert.That(foo, Is.Not.Null);
Assert.That(foo.GetType(), Is.EqualTo(typeof(Alpha)));
}
[Test, ExpectedException(typeof(InvalidCastException))]
public void StronglyTypedNewShouldThrowCastExceptionIfNull() {
_manager.New<Beta>("alpha");
}
[Test]
public void AlphaIsFlavoredAndStyledAndBetaIsFlavoredOnly() {
var alpha = _manager.New<Alpha>("alpha");
var beta = _manager.New<Beta>("beta");
Assert.That(alpha.Is<Flavored>(), Is.True);
Assert.That(alpha.Is<Styled>(), Is.True);
Assert.That(beta.Is<Flavored>(), Is.True);
Assert.That(beta.Is<Styled>(), Is.False);
}
[Test]
public void GetByIdShouldDetermineTypeAndLoadParts() {
var modelRecord = CreateModelRecord("alpha");
var contentItem = _manager.Get(modelRecord.Id);
Assert.That(contentItem.ContentType, Is.EqualTo("alpha"));
Assert.That(contentItem.Id, Is.EqualTo(modelRecord.Id));
}
[Test]
public void ModelPartWithRecordShouldCallRepositoryToPopulate() {
CreateModelRecord("gamma");
CreateModelRecord("gamma");
var modelRecord = CreateModelRecord("gamma");
var model = _manager.Get(modelRecord.Id);
// create a gamma record
var gamma = new GammaRecord {
ContentItemRecord = _container.Resolve<IRepository<ContentItemRecord>>().Get(model.Id),
Frap = "foo"
};
_container.Resolve<IRepository<GammaRecord>>().Create(gamma);
_session.Flush();
_session.Clear();
// re-fetch from database
model = _manager.Get(modelRecord.Id);
Assert.That(model.ContentType, Is.EqualTo("gamma"));
Assert.That(model.Id, Is.EqualTo(modelRecord.Id));
Assert.That(model.Is<Gamma>(), Is.True);
Assert.That(model.As<Gamma>().Record, Is.Not.Null);
Assert.That(model.As<Gamma>().Record.ContentItemRecord.Id, Is.EqualTo(model.Id));
}
[Test]
public void CreateShouldMakeModelAndContentTypeRecords() {
var beta = _manager.New("beta");
_manager.Create(beta);
var modelRecord = _container.Resolve<IRepository<ContentItemRecord>>().Get(beta.Id);
Assert.That(modelRecord, Is.Not.Null);
Assert.That(modelRecord.ContentType.Name, Is.EqualTo("beta"));
}
[Test]
public void GetContentTypesShouldReturnAllTypes() {
var types = _manager.GetContentTypes();
Assert.That(types.Count(), Is.EqualTo(4));
Assert.That(types, Has.Some.With.Property("Name").EqualTo("alpha"));
Assert.That(types, Has.Some.With.Property("Name").EqualTo("beta"));
Assert.That(types, Has.Some.With.Property("Name").EqualTo("gamma"));
Assert.That(types, Has.Some.With.Property("Name").EqualTo("delta"));
}
private ContentItemRecord CreateModelRecord(string contentType) {
var contentTypeRepository = _container.Resolve<IRepository<ContentTypeRecord>>();
var contentItemRepository = _container.Resolve<IRepository<ContentItemRecord>>();
var contentItemVersionRepository = _container.Resolve<IRepository<ContentItemVersionRecord>>();
var modelRecord = new ContentItemRecord { ContentType = contentTypeRepository.Get(x => x.Name == contentType) };
if (modelRecord.ContentType == null) {
modelRecord.ContentType = new ContentTypeRecord { Name = contentType };
contentTypeRepository.Create(modelRecord.ContentType);
}
contentItemRepository.Create(modelRecord);
contentItemVersionRepository.Create(new ContentItemVersionRecord { ContentItemRecord = modelRecord, Latest = true, Published = true, Number = 1 });
_session.Flush();
_session.Clear();
return modelRecord;
}
[Test]
public void InitialVersionShouldBeOne() {
var gamma1 = _manager.Create<Gamma>("gamma");
Assert.That(gamma1.ContentItem.Record, Is.Not.Null);
Assert.That(gamma1.ContentItem.VersionRecord, Is.Not.Null);
Assert.That(gamma1.ContentItem.Version, Is.EqualTo(1));
Assert.That(gamma1.ContentItem.VersionRecord.Number, Is.EqualTo(1));
_session.Flush();
_session.Clear();
Trace.WriteLine("session flushed");
var gamma2 = _manager.Get<Gamma>(gamma1.ContentItem.Id);
Assert.That(gamma2.ContentItem.Record, Is.Not.Null);
Assert.That(gamma2.ContentItem.VersionRecord, Is.Not.Null);
Assert.That(gamma2.ContentItem.Version, Is.EqualTo(1));
Assert.That(gamma2.ContentItem.VersionRecord.Number, Is.EqualTo(1));
// asserts results are re-acquired from db
Assert.That(gamma1, Is.Not.SameAs(gamma2));
Assert.That(gamma1.Record, Is.Not.SameAs(gamma2.Record));
Assert.That(gamma1.ContentItem, Is.Not.SameAs(gamma2.ContentItem));
Assert.That(gamma1.ContentItem.Record, Is.Not.SameAs(gamma2.ContentItem.Record));
Assert.That(gamma1.ContentItem.VersionRecord, Is.Not.SameAs(gamma2.ContentItem.VersionRecord));
}
[Test]
public void InitialVersionCanBeSpecifiedAndIsPublished() {
var gamma1 = _manager.Create<Gamma>("gamma", VersionOptions.Number(4));
Assert.That(gamma1.ContentItem.Version, Is.EqualTo(4));
Assert.That(gamma1.ContentItem.VersionRecord.Published, Is.True);
_session.Flush();
_session.Clear();
}
[Test]
public void PublishedShouldBeLatestButNotDraft() {
var gamma1 = _manager.Create("gamma", VersionOptions.Published);
var gammaPublished = _manager.Get(gamma1.Id, VersionOptions.Published);
var gammaLatest = _manager.Get(gamma1.Id, VersionOptions.Latest);
var gammaDraft = _manager.Get(gamma1.Id, VersionOptions.Draft);
Assert.That(gammaPublished.VersionRecord.Id, Is.EqualTo(gamma1.VersionRecord.Id));
Assert.That(gammaLatest.VersionRecord.Id, Is.EqualTo(gamma1.VersionRecord.Id));
Assert.That(gammaDraft, Is.Null);
}
[Test]
public void DraftShouldBeLatestButNotPublished() {
var gamma1 = _manager.Create("gamma", VersionOptions.Draft);
var gammaPublished = _manager.Get(gamma1.Id, VersionOptions.Published);
var gammaLatest = _manager.Get(gamma1.Id, VersionOptions.Latest);
var gammaDraft = _manager.Get(gamma1.Id, VersionOptions.Draft);
Assert.That(gammaDraft.VersionRecord.Id, Is.EqualTo(gamma1.VersionRecord.Id));
Assert.That(gammaLatest.VersionRecord.Id, Is.EqualTo(gamma1.VersionRecord.Id));
Assert.That(gammaPublished, Is.Null);
}
[Test]
public void CreateDraftShouldNotCreateExtraDraftCopies() {
var gamma1 = _manager.Create("gamma", VersionOptions.Draft);
_session.Flush();
_session.Clear();
var gammaDraft1 = _manager.Get(gamma1.Id, VersionOptions.Draft);
Assert.That(gammaDraft1.VersionRecord.Id, Is.EqualTo(gamma1.VersionRecord.Id));
Assert.That(gammaDraft1.Record.Versions, Has.Count.EqualTo(1));
_session.Flush();
_session.Clear();
var gammaDraft2 = _manager.Get(gamma1.Id, VersionOptions.DraftRequired);
Assert.That(gammaDraft2.VersionRecord.Id, Is.EqualTo(gamma1.VersionRecord.Id));
Assert.That(gammaDraft2.Record.Versions, Has.Count.EqualTo(1));
_session.Flush();
_session.Clear();
var gammaDraft3 = _manager.Get(gamma1.Id, VersionOptions.Draft);
Assert.That(gammaDraft3.VersionRecord.Id, Is.EqualTo(gamma1.VersionRecord.Id));
Assert.That(gammaDraft3.Record.Versions, Has.Count.EqualTo(1));
_session.Flush();
_session.Clear();
var gammaDraft4 = _manager.Get(gamma1.Id, VersionOptions.DraftRequired);
Assert.That(gammaDraft4.VersionRecord.Id, Is.EqualTo(gamma1.VersionRecord.Id));
Assert.That(gammaDraft4.Record.Versions, Has.Count.EqualTo(1));
_session.Flush();
_session.Clear();
}
[Test]
public void DraftRequiredShouldBuildNewVersionIfLatestIsAlreadyPublished() {
Trace.WriteLine("gamma1");
var gamma1 = _manager.Create("gamma", VersionOptions.Published);
Trace.WriteLine("flush");
_session.Flush();
_session.Clear();
Trace.WriteLine("gammaDraft1");
var gammaDraft1 = _manager.Get(gamma1.Id, VersionOptions.Draft);
Assert.That(gammaDraft1, Is.Null);
Trace.WriteLine("flush");
_session.Flush();
_session.Clear();
Trace.WriteLine("gammaDraft2");
var gammaDraft2 = _manager.Get(gamma1.Id, VersionOptions.DraftRequired);
Assert.That(gammaDraft2.VersionRecord.Id, Is.Not.EqualTo(gamma1.VersionRecord.Id));
Assert.That(gamma1.Version, Is.EqualTo(1));
Assert.That(gammaDraft2.Version, Is.EqualTo(2));
Trace.WriteLine("flush");
_session.Flush();
_session.Clear();
foreach (var x in _container.Resolve<IRepository<ContentItemVersionRecord>>().Fetch(x => true)) {
Trace.WriteLine(string.Format("{0}/{1} #{2} published:{3} latest:{4}",
x.ContentItemRecord.Id,
x.Id,
x.Number,
x.Published,
x.Latest));
}
Trace.WriteLine("gammaDraft3");
var gammaDraft3 = _manager.Get(gamma1.Id, VersionOptions.Draft);
Assert.That(gammaDraft3.VersionRecord.Id, Is.EqualTo(gammaDraft2.VersionRecord.Id));
Assert.That(gammaDraft3.Record, Is.Not.SameAs(gammaDraft2.Record));
Assert.That(gammaDraft3.Record.Versions, Is.Not.SameAs(gammaDraft2.Record.Versions));
Assert.That(gammaDraft3.Record.Versions, Has.Count.EqualTo(2));
Trace.WriteLine("flush");
_session.Flush();
_session.Clear();
Trace.WriteLine("gammaDraft4");
var gammaDraft4 = _manager.Get(gamma1.Id, VersionOptions.DraftRequired);
Assert.That(gammaDraft4.VersionRecord.Id, Is.EqualTo(gammaDraft2.VersionRecord.Id));
Assert.That(gammaDraft4.Record.Versions, Has.Count.EqualTo(2));
Trace.WriteLine("flush");
_session.Flush();
_session.Clear();
Trace.WriteLine("gamma2");
var gamma2 = _manager.Get(gamma1.Id);
Assert.That(gamma2.Record.Versions, Has.Count.EqualTo(2));
}
[Test]
public void NonVersionedPartsAreBoundToSameRecord() {
Trace.WriteLine("gamma1");
var gamma1 = _manager.Create<Gamma>("gamma", VersionOptions.Published, init => init.Record.Frap = "version one");
Trace.WriteLine("gamma2");
var gamma2 = _manager.Get<Gamma>(gamma1.ContentItem.Id, VersionOptions.DraftRequired);
Assert.That(gamma1.Record.Frap, Is.EqualTo("version one"));
Assert.That(gamma2.Record.Frap, Is.EqualTo("version one"));
gamma2.Record.Frap = "version two";
Assert.That(gamma1.Record.Frap, Is.EqualTo("version two"));
Assert.That(gamma2.Record.Frap, Is.EqualTo("version two"));
Trace.WriteLine("flush");
_session.Flush();
_session.Clear();
Trace.WriteLine("gamma1B");
var gamma1B = _manager.Get<Gamma>(gamma1.ContentItem.Id, VersionOptions.Published);
Trace.WriteLine("gamma2B");
var gamma2B = _manager.Get<Gamma>(gamma1.ContentItem.Id, VersionOptions.Draft);
Assert.That(gamma1B.Record, Is.SameAs(gamma2B.Record));
Assert.That(gamma1B.Record.Frap, Is.EqualTo("version two"));
Assert.That(gamma2B.Record.Frap, Is.EqualTo("version two"));
Assert.That(gamma1B.ContentItem.VersionRecord.Id, Is.Not.EqualTo(gamma2B.ContentItem.VersionRecord.Id));
Assert.That(gamma1.ContentItem.Record, Is.Not.SameAs(gamma1B.ContentItem.Record));
Assert.That(gamma2.ContentItem.Record, Is.Not.SameAs(gamma2B.ContentItem.Record));
Assert.That(gamma1.ContentItem.Record, Is.SameAs(gamma2.ContentItem.Record));
Assert.That(gamma1B.ContentItem.Record, Is.SameAs(gamma2B.ContentItem.Record));
Assert.That(gamma1.ContentItem.VersionRecord, Is.Not.SameAs(gamma2.ContentItem.VersionRecord));
Assert.That(gamma1B.ContentItem.VersionRecord, Is.Not.SameAs(gamma2B.ContentItem.VersionRecord));
Trace.WriteLine("flush");
_session.Flush();
}
[Test]
public void VersionedPartsShouldBeDifferentRecordsWithClonedData() {
var gamma1 = _manager.Create<Gamma>("gamma", VersionOptions.Published, init => init.Record.Frap = "version one");
var epsilon1 = gamma1.As<Epsilon>();
epsilon1.Record.Quad = "epsilon one";
var gamma2 = _manager.Get<Gamma>(gamma1.ContentItem.Id, VersionOptions.DraftRequired);
var epsilon2 = gamma2.As<Epsilon>();
Assert.That(epsilon1.Record.Quad, Is.EqualTo("epsilon one"));
Assert.That(epsilon2.Record.Quad, Is.EqualTo("epsilon one"));
epsilon2.Record.Quad = "epsilon two";
Assert.That(epsilon1.Record.Quad, Is.EqualTo("epsilon one"));
Assert.That(epsilon2.Record.Quad, Is.EqualTo("epsilon two"));
_session.Flush();
_session.Clear();
var gamma1B = _manager.Get<Gamma>(gamma1.ContentItem.Id, VersionOptions.Published);
var epsilon1B = gamma1B.As<Epsilon>();
var gamma2B = _manager.Get<Gamma>(gamma1.ContentItem.Id, VersionOptions.Draft);
var epsilon2B = gamma2B.As<Epsilon>();
Assert.That(gamma1B.Record, Is.SameAs(gamma2B.Record));
Assert.That(epsilon1B.Record, Is.Not.SameAs(epsilon2B.Record));
Assert.That(epsilon1B.Record.Quad, Is.EqualTo("epsilon one"));
Assert.That(epsilon2B.Record.Quad, Is.EqualTo("epsilon two"));
Assert.That(epsilon1B.ContentItem.VersionRecord.Id, Is.Not.EqualTo(epsilon2B.ContentItem.VersionRecord.Id));
Assert.That(epsilon1.ContentItem.Record, Is.Not.SameAs(epsilon1B.ContentItem.Record));
Assert.That(epsilon2.ContentItem.Record, Is.Not.SameAs(epsilon2B.ContentItem.Record));
Assert.That(epsilon1.ContentItem.Record, Is.SameAs(epsilon2.ContentItem.Record));
Assert.That(epsilon1B.ContentItem.Record, Is.SameAs(epsilon2B.ContentItem.Record));
Assert.That(epsilon1.ContentItem.VersionRecord, Is.Not.SameAs(epsilon2.ContentItem.VersionRecord));
Assert.That(epsilon1B.ContentItem.VersionRecord, Is.Not.SameAs(epsilon2B.ContentItem.VersionRecord));
}
}
}

View File

@@ -1,176 +0,0 @@
using System;
using System.Linq;
using Autofac;
using Autofac.Builder;
using Autofac.Modules;
using NHibernate;
using NUnit.Framework;
using Orchard.Data;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.Records;
using Orchard.Tests.Models.Records;
using Orchard.Tests.Models.Stubs;
namespace Orchard.Tests.Models {
[TestFixture]
public class DefaultModelManagerTests {
private IContainer _container;
private IContentManager _manager;
private ISessionFactory _sessionFactory;
private ISession _session;
[TestFixtureSetUp]
public void InitFixture() {
var databaseFileName = System.IO.Path.GetTempFileName();
_sessionFactory = DataUtility.CreateSessionFactory(
databaseFileName,
typeof(GammaRecord),
typeof(ContentItemRecord),
typeof(ContentTypeRecord));
}
[TestFixtureTearDown]
public void TermFixture() {
}
[SetUp]
public void Init() {
var builder = new ContainerBuilder();
builder.RegisterModule(new ImplicitCollectionSupportModule());
builder.Register<DefaultContentManager>().As<IContentManager>();
builder.Register<AlphaHandler>().As<IContentHandler>();
builder.Register<BetaHandler>().As<IContentHandler>();
builder.Register<GammaHandler>().As<IContentHandler>();
builder.Register<FlavoredHandler>().As<IContentHandler>();
builder.Register<StyledHandler>().As<IContentHandler>();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
_session = _sessionFactory.OpenSession();
builder.Register(new TestSessionLocator(_session)).As<ISessionLocator>();
_container = builder.Build();
_manager = _container.Resolve<IContentManager>();
}
public class TestSessionLocator : ISessionLocator {
private readonly ISession _session;
public TestSessionLocator(ISession session) {
_session = session;
}
public ISession For(Type entityType) {
return _session;
}
}
[Test]
public void AlphaDriverShouldWeldItsPart() {
var foo = _manager.New("alpha");
Assert.That(foo.Is<Alpha>(), Is.True);
Assert.That(foo.As<Alpha>(), Is.Not.Null);
Assert.That(foo.Is<Beta>(), Is.False);
Assert.That(foo.As<Beta>(), Is.Null);
}
[Test]
public void StronglyTypedNewShouldTypeCast() {
var foo = _manager.New<Alpha>("alpha");
Assert.That(foo, Is.Not.Null);
Assert.That(foo.GetType(), Is.EqualTo(typeof(Alpha)));
}
[Test, ExpectedException(typeof(InvalidCastException))]
public void StronglyTypedNewShouldThrowCastExceptionIfNull() {
_manager.New<Beta>("alpha");
}
[Test]
public void AlphaIsFlavoredAndStyledAndBetaIsFlavoredOnly() {
var alpha = _manager.New<Alpha>("alpha");
var beta = _manager.New<Beta>("beta");
Assert.That(alpha.Is<Flavored>(), Is.True);
Assert.That(alpha.Is<Styled>(), Is.True);
Assert.That(beta.Is<Flavored>(), Is.True);
Assert.That(beta.Is<Styled>(), Is.False);
}
[Test]
public void GetByIdShouldDetermineTypeAndLoadParts() {
var modelRecord = CreateModelRecord("alpha");
var contentItem = _manager.Get(modelRecord.Id);
Assert.That(contentItem.ContentType, Is.EqualTo("alpha"));
Assert.That(contentItem.Id, Is.EqualTo(modelRecord.Id));
}
[Test]
public void ModelPartWithRecordShouldCallRepositoryToPopulate() {
CreateModelRecord("gamma");
CreateModelRecord("gamma");
var modelRecord = CreateModelRecord("gamma");
var model = _manager.Get(modelRecord.Id);
// create a gamma record
var gamma = new GammaRecord {
ContentItemRecord = _container.Resolve<IRepository<ContentItemRecord>>().Get(model.Id),
Frap = "foo"
};
_container.Resolve<IRepository<GammaRecord>>().Create(gamma);
_session.Flush();
_session.Clear();
// re-fetch from database
model = _manager.Get(modelRecord.Id);
Assert.That(model.ContentType, Is.EqualTo("gamma"));
Assert.That(model.Id, Is.EqualTo(modelRecord.Id));
Assert.That(model.Is<Gamma>(), Is.True);
Assert.That(model.As<Gamma>().Record, Is.Not.Null);
Assert.That(model.As<Gamma>().Record.ContentItemRecord.Id, Is.EqualTo(model.Id));
}
[Test]
public void CreateShouldMakeModelAndContentTypeRecords() {
var beta = _manager.New("beta");
_manager.Create(beta);
var modelRecord = _container.Resolve<IRepository<ContentItemRecord>>().Get(beta.Id);
Assert.That(modelRecord, Is.Not.Null);
Assert.That(modelRecord.ContentType.Name, Is.EqualTo("beta"));
}
[Test]
public void GetContentTypesShouldReturnAllTypes() {
var types = _manager.GetContentTypes();
Assert.That(types.Count(), Is.EqualTo(3));
Assert.That(types, Has.Some.With.Property("Name").EqualTo("alpha"));
Assert.That(types, Has.Some.With.Property("Name").EqualTo("beta"));
Assert.That(types, Has.Some.With.Property("Name").EqualTo("gamma"));
}
private ContentItemRecord CreateModelRecord(string contentType) {
var contentItemRepository = _container.Resolve<IRepository<ContentItemRecord>>();
var contentTypeRepository = _container.Resolve<IRepository<ContentTypeRecord>>();
var modelRecord = new ContentItemRecord { ContentType = new ContentTypeRecord { Name = contentType } };
contentTypeRepository.Create(modelRecord.ContentType);
contentItemRepository.Create(modelRecord);
_session.Flush();
_session.Clear();
return modelRecord;
}
}
}

View File

@@ -0,0 +1,7 @@
using Orchard.ContentManagement.Records;
namespace Orchard.Tests.Models.Stubs {
public class EpsilonRecord : ContentPartVersionRecord {
public virtual string Quad { get; set; }
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;
using Orchard.Tests.Models.Records;
namespace Orchard.Tests.Models.Stubs {
public class Epsilon : ContentPart<EpsilonRecord> {
}
public class EpsilonHandler : ContentHandler {
public EpsilonHandler(IRepository<EpsilonRecord> repository) {
Filters.Add(new ActivatingFilter<Epsilon>(x => x == "gamma"));
Filters.Add(new StorageVersionFilter<EpsilonRecord>(repository));
}
}
}

View File

@@ -1,7 +1,4 @@
using System.Linq;
using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;
using Orchard.Data;
using Orchard.Data;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.Tests.Models.Records;
@@ -12,7 +9,7 @@ namespace Orchard.Tests.Models.Stubs {
public class GammaHandler : ContentHandler {
public override System.Collections.Generic.IEnumerable<Orchard.ContentManagement.ContentType> GetContentTypes() {
public override System.Collections.Generic.IEnumerable<ContentType> GetContentTypes() {
return new[] { new ContentType { Name = "gamma" } };
}
@@ -23,30 +20,4 @@ namespace Orchard.Tests.Models.Stubs {
}
//public class ContentItemRecordAlteration : IAutoMappingAlteration {
// public void Alter(AutoPersistenceModel model) {
// model.OverrideAll(mapping => {
// var genericArguments = mapping.GetType().GetGenericArguments();
// if (!genericArguments.Single().IsSubclassOf(typeof (ContentPartRecord))) {
// return;
// }
// });
// model.Override<ContentItemRecord>(mapping => mapping.HasOne(record => (GammaRecord)record["GammaRecord"]).Access.NoOp().Fetch.Select());
// }
// interface IAlteration {
// void Override(object mapping);
// }
// class Alteration<T> : IAlteration where T : ContentPartRecord {
// public void Override(object mappingObj) {
// var mapping = (AutoMapping<T>)mappingObj;
// mapping.Id(x => x.Id).GeneratedBy.Foreign("ContentItem");
// mapping.HasOne(x => x.ContentItem).Constrained();
// }
// }
//}
}

View File

@@ -117,7 +117,7 @@
<Compile Include="Localization\NullLocalizerTests.cs" />
<Compile Include="Logging\LoggingModuleTests.cs" />
<Compile Include="Models\ContentQueryTests.cs" />
<Compile Include="Models\DefaultModelManagerTests.cs" />
<Compile Include="Models\DefaultContentManagerTests.cs" />
<Compile Include="Models\Drivers\ModelBuilderTests.cs" />
<Compile Include="Models\Drivers\ModelDriverTests.cs" />
<Compile Include="Models\PartDriverHandlerTests.cs" />
@@ -127,6 +127,8 @@
<Compile Include="Models\Stubs\BetaHandler.cs" />
<Compile Include="Models\Stubs\Delta.cs" />
<Compile Include="Models\Records\DeltaRecord.cs" />
<Compile Include="Models\Stubs\Epsilon.cs" />
<Compile Include="Models\Records\EpsilonRecord.cs" />
<Compile Include="Models\Stubs\Flavored.cs" />
<Compile Include="Models\Stubs\FlavoredHandler.cs" />
<Compile Include="Models\Stubs\Gamma.cs" />

View File

@@ -5,7 +5,6 @@ using Orchard.Localization;
using Orchard.ContentManagement;
using Orchard.Settings;
using Orchard.UI.Notify;
using Orchard.ContentManagement.Handlers;
namespace Orchard.Core.Settings.Controllers {
[ValidateInput(false)]
@@ -24,18 +23,19 @@ namespace Orchard.Core.Settings.Controllers {
public Localizer T { get; set; }
public ActionResult Index(string tabName) {
var model = new Orchard.Core.Settings.ViewModels.SettingsIndexViewModel {
Site = _siteService.GetSiteSettings().As<SiteSettings>() };
var model = new Orchard.Core.Settings.ViewModels.SettingsIndexViewModel {
Site = _siteService.GetSiteSettings().As<SiteSettings>()
};
model.EditorModel = _modelManager.BuildEditorModel(model.Site);
return View(model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(string tabName, FormCollection input) {
[HttpPost, ActionName("Index")]
public ActionResult IndexPOST(string tabName) {
var viewModel = new SettingsIndexViewModel { Site = _siteService.GetSiteSettings().As<SiteSettings>() };
viewModel.EditorModel = _modelManager.UpdateEditorModel(viewModel.Site.ContentItem, this);
if (!TryUpdateModel(viewModel, input.ToValueProvider())) {
if (!TryUpdateModel(viewModel)) {
return View(viewModel);
}

View File

@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
using System.Web.Routing;
using JetBrains.Annotations;
using Orchard.Blogs.Models;
using Orchard.ContentManagement;

View File

@@ -5,7 +5,7 @@
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{4A9C04A6-0986-4A92-A610-5F59FF273FB9}</ProjectGuid>
<ProjectTypeGuids>{F85E285D-A4E0-4152-9332-AB1D724D3325};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Orchard.Pages</RootNamespace>

View File

@@ -6,10 +6,9 @@ using Orchard.ContentManagement.Records;
using Orchard.ContentManagement.ViewModels;
namespace Orchard.ContentManagement {
public static class ContentCreateExtensions {
public static class ContentExtensions {
/* Item creation and accessing extension methods */
/* Item creation extension methods */
public static T New<T>(this IContentManager manager, string contentType) where T : class, IContent {
var contentItem = manager.New(contentType);
@@ -23,6 +22,11 @@ namespace Orchard.ContentManagement {
return part;
}
public static ContentItem Create(this IContentManager manager, string contentType) {
return manager.Create<ContentItem>(contentType, init => { });
}
public static T Create<T>(this IContentManager manager, string contentType) where T : class, IContent {
return manager.Create<T>(contentType, init => { });
}
@@ -37,11 +41,39 @@ namespace Orchard.ContentManagement {
return content;
}
public static ContentItem Create(this IContentManager manager, string contentType, VersionOptions options) {
return manager.Create<ContentItem>(contentType, options, init => { });
}
public static T Create<T>(this IContentManager manager, string contentType, VersionOptions options) where T : class, IContent {
return manager.Create<T>(contentType, options, init => { });
}
public static T Create<T>(this IContentManager manager, string contentType, VersionOptions options, Action<T> initialize) where T : class, IContent {
var content = manager.New<T>(contentType);
if (content == null)
return null;
initialize(content);
manager.Create(content.ContentItem, options);
return content;
}
}
public static class ContentExtensions {
public static T Get<T>(this IContentManager manager, int id) where T : class, IContent {
var contentItem = manager.Get(id);
return contentItem == null ? null : contentItem.Get<T>();
}
public static T Get<T>(this IContentManager manager, int id, VersionOptions options) where T : class, IContent {
var contentItem = manager.Get(id, options);
return contentItem == null ? null : contentItem.Get<T>();
}
/* Display and editor convenience extension methods */

View File

@@ -13,16 +13,19 @@ namespace Orchard.ContentManagement {
private readonly IList<ContentPart> _parts;
ContentItem IContent.ContentItem { get { return this; } }
public int Id { get; set; }
public int Id { get { return Record == null ? 0 : Record.Id; } }
public int Version { get { return VersionRecord == null ? 0 : VersionRecord.Number; } }
public string ContentType { get; set; }
public ContentItemRecord Record { get; set; }
public ContentItemRecord Record { get { return VersionRecord == null ? null : VersionRecord.ContentItemRecord; } }
public ContentItemVersionRecord VersionRecord { get; set; }
public IEnumerable<ContentPart> Parts { get { return _parts; } }
public IContentManager ContentManager { get; set; }
public bool Has(Type partType) {
return partType==typeof(ContentItem) || _parts.Any(part => partType.IsAssignableFrom(part.GetType()));
return partType == typeof(ContentItem) || _parts.Any(part => partType.IsAssignableFrom(part.GetType()));
}
public IContent Get(Type partType) {

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Autofac;
using Orchard.ContentManagement.Handlers;
@@ -11,16 +12,19 @@ using Orchard.UI.Navigation;
namespace Orchard.ContentManagement {
public class DefaultContentManager : IContentManager {
private readonly IContext _context;
private readonly IRepository<ContentItemRecord> _contentItemRepository;
private readonly IRepository<ContentTypeRecord> _contentTypeRepository;
private readonly IRepository<ContentItemRecord> _contentItemRepository;
private readonly IRepository<ContentItemVersionRecord> _contentItemVersionRepository;
public DefaultContentManager(
IContext context,
IRepository<ContentTypeRecord> contentTypeRepository,
IRepository<ContentItemRecord> contentItemRepository,
IRepository<ContentTypeRecord> contentTypeRepository) {
IRepository<ContentItemVersionRecord> contentItemVersionRepository) {
_context = context;
_contentItemRepository = contentItemRepository;
_contentTypeRepository = contentTypeRepository;
_contentItemRepository = contentItemRepository;
_contentItemVersionRepository = contentItemVersionRepository;
}
private IEnumerable<IContentHandler> _handlers;
@@ -67,24 +71,66 @@ namespace Orchard.ContentManagement {
}
public virtual ContentItem Get(int id) {
// obtain root record to determine the model type
var record = _contentItemRepository.Get(id);
return Get(id, VersionOptions.Published);
}
// no record of that id means content item doesn't exist
if (record == null)
public virtual ContentItem Get(int id, VersionOptions options) {
ContentItemVersionRecord versionRecord = null;
var appendLatestVersion = false;
// obtain the root records based on version options
if (options.VersionRecordId != 0) {
// explicit version record known
versionRecord = _contentItemVersionRepository.Get(options.VersionRecordId);
}
else {
var record = _contentItemRepository.Get(id);
if (options.IsPublished) {
versionRecord = _contentItemVersionRepository.Get(x => x.ContentItemRecord == record && x.Published);
}
else if (options.IsLatest) {
versionRecord = _contentItemVersionRepository.Get(x => x.ContentItemRecord == record && x.Latest);
}
else if (options.IsDraft || options.IsDraftRequired) {
versionRecord = _contentItemVersionRepository.Get(x => x.ContentItemRecord == record && x.Latest && !x.Published);
if (versionRecord == null && options.IsDraftRequired) {
versionRecord = _contentItemVersionRepository.Get(x => x.ContentItemRecord == record && x.Latest);
appendLatestVersion = true;
}
}
else if (options.VersionNumber != 0) {
versionRecord = _contentItemVersionRepository.Get(x => x.ContentItemRecord == record && x.Number == options.VersionNumber);
}
//TEMP: this is to transition people with old databases
if (versionRecord == null && !record.Versions.Any() && options.IsPublished) {
versionRecord = new ContentItemVersionRecord {
ContentItemRecord = record,
Latest = true,
Published = true,
Number = 1
};
record.Versions.Add(versionRecord);
_contentItemVersionRepository.Create(versionRecord);
}
}
// no record means content item doesn't exist
if (versionRecord == null) {
return null;
}
// allocate instance and set record property
var contentItem = New(record.ContentType.Name);
contentItem.Id = record.Id;
contentItem.Record = record;
var contentItem = New(versionRecord.ContentItemRecord.ContentType.Name);
contentItem.VersionRecord = versionRecord;
// create a context with a new instance to load
var context = new LoadContentContext {
Id = contentItem.Id,
ContentType = contentItem.ContentType,
ContentItemRecord = record,
ContentItem = contentItem,
ContentItemRecord = contentItem.Record,
ContentItemVersionRecord = contentItem.VersionRecord,
};
// invoke handlers to acquire state, or at least establish lazy loading callbacks
@@ -95,26 +141,100 @@ namespace Orchard.ContentManagement {
handler.Loaded(context);
}
// when draft is required and not currently available a new version is appended
if (appendLatestVersion) {
return AppendLatestVersion(context.ContentItem);
}
return context.ContentItem;
}
public void Create(ContentItem contentItem) {
public virtual ContentItem AppendLatestVersion(ContentItem existingContentItem) {
var contentItemRecord = existingContentItem.Record;
// locate the existing and the current latest versions, allocate building version
var existingItemVersionRecord = existingContentItem.VersionRecord;
var buildingItemVersionRecord = new ContentItemVersionRecord {
ContentItemRecord = contentItemRecord,
Latest = true,
Published = false
};
if (existingItemVersionRecord.Latest == false) {
var latestVersion = _contentItemVersionRepository.Get(x => x.ContentItemRecord == contentItemRecord && x.Latest);
latestVersion.Latest = false;
buildingItemVersionRecord.Number = latestVersion.Number + 1;
}
else {
existingItemVersionRecord.Latest = false;
buildingItemVersionRecord.Number = existingItemVersionRecord.Number + 1;
}
_contentItemVersionRepository.Create(buildingItemVersionRecord);
var buildingContentItem = New(existingContentItem.ContentType);
buildingContentItem.VersionRecord = buildingItemVersionRecord;
var context = new VersionContentContext {
Id = existingContentItem.Id,
ContentType = existingContentItem.ContentType,
ContentItemRecord = contentItemRecord,
ExistingContentItem = existingContentItem,
BuildingContentItem = buildingContentItem,
ExistingItemVersionRecord = existingItemVersionRecord,
BuildingItemVersionRecord = buildingItemVersionRecord,
};
foreach (var handler in Handlers) {
handler.Versioning(context);
}
foreach (var handler in Handlers) {
handler.Versioned(context);
}
return context.BuildingContentItem;
}
public virtual void Create(ContentItem contentItem) {
Create(contentItem, VersionOptions.Published);
}
public virtual void Create(ContentItem contentItem, VersionOptions options) {
// produce root record to determine the model id
var modelRecord = new ContentItemRecord { ContentType = AcquireContentTypeRecord(contentItem.ContentType) };
_contentItemRepository.Create(modelRecord);
contentItem.Record = modelRecord;
contentItem.VersionRecord = new ContentItemVersionRecord {
ContentItemRecord = new ContentItemRecord {
ContentType = AcquireContentTypeRecord(contentItem.ContentType)
},
Number = 1,
Latest = true,
Published = true
};
// add to the collection manually for the created case
contentItem.VersionRecord.ContentItemRecord.Versions.Add(contentItem.VersionRecord);
// version may be specified
if (options.VersionNumber != 0) {
contentItem.VersionRecord.Number = options.VersionNumber;
}
// draft flag on create is required for explicitly-published content items
if (options.IsDraft) {
contentItem.VersionRecord.Published = false;
}
_contentItemRepository.Create(contentItem.Record);
_contentItemVersionRepository.Create(contentItem.VersionRecord);
// build a context with the initialized instance to create
var context = new CreateContentContext {
Id = modelRecord.Id,
ContentType = modelRecord.ContentType.Name,
ContentItemRecord = modelRecord,
Id = contentItem.Id,
ContentType = contentItem.ContentType,
ContentItemRecord = contentItem.Record,
ContentItemVersionRecord = contentItem.VersionRecord,
ContentItem = contentItem
};
// set the id
context.ContentItem.Id = context.Id;
// invoke handlers to add information to persistent stores
foreach (var handler in Handlers) {
@@ -137,7 +257,7 @@ namespace Orchard.ContentManagement {
}
public ItemDisplayModel<TContentPart> BuildDisplayModel<TContentPart>(TContentPart content, string displayType) where TContentPart : IContent {
var itemView = new ItemDisplayModel<TContentPart> {Item = content};
var itemView = new ItemDisplayModel<TContentPart> { Item = content };
var context = new BuildDisplayModelContext(itemView, displayType);
foreach (var handler in Handlers) {
handler.BuildDisplayModel(context);

View File

@@ -36,6 +36,15 @@ namespace Orchard.ContentManagement.Drivers {
void IContentHandler.Loaded(LoadContentContext context) { }
void IContentHandler.Versioning(VersionContentContext context) { }
void IContentHandler.Versioned(VersionContentContext context) { }
void IContentHandler.Removing(RemoveContentContext context) { }
void IContentHandler.Removed(RemoveContentContext context) { }
void IContentHandler.GetItemMetadata(GetItemMetadataContext context) {
_drivers.Invoke(driver => driver.GetItemMetadata(context), Logger);
}
@@ -63,6 +72,8 @@ namespace Orchard.ContentManagement.Drivers {
result.Apply(context);
}, Logger);
}
}
}

View File

@@ -32,6 +32,14 @@ namespace Orchard.ContentManagement.Drivers {
void IContentHandler.Loaded(LoadContentContext context) { }
void IContentHandler.Versioning(VersionContentContext context) { }
void IContentHandler.Versioned(VersionContentContext context) { }
void IContentHandler.Removing(RemoveContentContext context) { }
void IContentHandler.Removed(RemoveContentContext context) { }
void IContentHandler.GetItemMetadata(GetItemMetadataContext context) { }
void IContentHandler.BuildDisplayModel(BuildDisplayModelContext context) {

View File

@@ -33,6 +33,22 @@ namespace Orchard.ContentManagement.Handlers {
Filters.Add(new InlineStorageFilter<TPart> { OnLoaded = handler });
}
protected void OnVersioning<TPart>(Action<VersionContentContext, TPart, TPart> handler) where TPart : class, IContent {
Filters.Add(new InlineStorageFilter<TPart> { OnVersioning = handler });
}
protected void OnVersioned<TPart>(Action<VersionContentContext, TPart, TPart> handler) where TPart : class, IContent {
Filters.Add(new InlineStorageFilter<TPart> { OnVersioned = handler });
}
protected void OnRemoving<TPart>(Action<RemoveContentContext, TPart> handler) where TPart : class, IContent {
Filters.Add(new InlineStorageFilter<TPart> { OnRemoving = handler });
}
protected void OnRemoved<TPart>(Action<RemoveContentContext, TPart> handler) where TPart : class, IContent {
Filters.Add(new InlineStorageFilter<TPart> { OnRemoved = handler });
}
protected void OnGetItemMetadata<TPart>(Action<GetItemMetadataContext, TPart> handler) where TPart : class, IContent {
Filters.Add(new InlineTemplateFilter<TPart> { OnGetItemMetadata = handler });
}
@@ -54,6 +70,10 @@ namespace Orchard.ContentManagement.Handlers {
public Action<CreateContentContext, TPart> OnCreated { get; set; }
public Action<LoadContentContext, TPart> OnLoading { get; set; }
public Action<LoadContentContext, TPart> OnLoaded { get; set; }
public Action<VersionContentContext, TPart, TPart> OnVersioning { get; set; }
public Action<VersionContentContext, TPart, TPart> OnVersioned { get; set; }
public Action<RemoveContentContext, TPart> OnRemoving { get; set; }
public Action<RemoveContentContext, TPart> OnRemoved { get; set; }
protected override void Activated(ActivatedContentContext context, TPart instance) {
if (OnActivated != null) OnActivated(context, instance);
}
@@ -69,6 +89,18 @@ namespace Orchard.ContentManagement.Handlers {
protected override void Loaded(LoadContentContext context, TPart instance) {
if (OnLoaded != null) OnLoaded(context, instance);
}
protected override void Versioning(VersionContentContext context, TPart existing, TPart building) {
if (OnVersioning != null) OnVersioning(context, existing, building);
}
protected override void Versioned(VersionContentContext context, TPart existing, TPart building) {
if (OnVersioned != null) OnVersioned(context, existing, building);
}
protected override void Removing(RemoveContentContext context, TPart instance) {
if (OnRemoving != null) OnRemoving(context, instance);
}
protected override void Removed(RemoveContentContext context, TPart instance) {
if (OnRemoved != null) OnRemoved(context, instance);
}
}
class InlineTemplateFilter<TPart> : TemplateFilterBase<TPart> where TPart : class, IContent {
@@ -129,6 +161,28 @@ namespace Orchard.ContentManagement.Handlers {
filter.Loaded(context);
Loaded(context);
}
void IContentHandler.Versioning(VersionContentContext context) {
foreach (var filter in Filters.OfType<IContentStorageFilter>())
filter.Versioning(context);
Versioning(context);
}
void IContentHandler.Versioned(VersionContentContext context) {
foreach (var filter in Filters.OfType<IContentStorageFilter>())
filter.Versioned(context);
Versioned(context);
}
void IContentHandler.Removing(RemoveContentContext context) {
foreach (var filter in Filters.OfType<IContentStorageFilter>())
filter.Removing(context);
Removing(context);
}
void IContentHandler.Removed(RemoveContentContext context) {
foreach (var filter in Filters.OfType<IContentStorageFilter>())
filter.Removed(context);
Removed(context);
}
void IContentHandler.GetItemMetadata(GetItemMetadataContext context) {
@@ -155,15 +209,21 @@ namespace Orchard.ContentManagement.Handlers {
protected virtual void Activating(ActivatingContentContext context) { }
protected virtual void Activated(ActivatedContentContext context) { }
protected virtual void Creating(CreateContentContext context) { }
protected virtual void Created(CreateContentContext context) { }
protected virtual void Loading(LoadContentContext context) { }
protected virtual void Loaded(LoadContentContext context) { }
protected virtual void Creating(CreateContentContext context) { }
protected virtual void Created(CreateContentContext context) { }
protected virtual void Versioning(VersionContentContext context) { }
protected virtual void Versioned(VersionContentContext context) { }
protected virtual void Removing(RemoveContentContext context) { }
protected virtual void Removed(RemoveContentContext context) { }
protected virtual void GetItemMetadata(GetItemMetadataContext context) { }
protected virtual void BuildDisplayModel(BuildDisplayModelContext context) { }
protected virtual void BuildEditorModel(BuildEditorModelContext context) { }
protected virtual void UpdateEditorModel(UpdateEditorModelContext context) {}
protected virtual void UpdateEditorModel(UpdateEditorModelContext context) { }
}
}

View File

@@ -4,7 +4,9 @@ namespace Orchard.ContentManagement.Handlers {
public class CreateContentContext {
public int Id { get; set; }
public string ContentType { get; set; }
public ContentItemRecord ContentItemRecord { get; set; }
public ContentItem ContentItem { get; set; }
public ContentItemRecord ContentItemRecord { get; set; }
public ContentItemVersionRecord ContentItemVersionRecord { get; set; }
}
}
}

View File

@@ -10,6 +10,10 @@ namespace Orchard.ContentManagement.Handlers {
void Created(CreateContentContext context);
void Loading(LoadContentContext context);
void Loaded(LoadContentContext context);
void Versioning(VersionContentContext context);
void Versioned(VersionContentContext context);
void Removing(RemoveContentContext context);
void Removed(RemoveContentContext context);
void GetItemMetadata(GetItemMetadataContext context);
void BuildDisplayModel(BuildDisplayModelContext context);

View File

@@ -4,6 +4,10 @@ namespace Orchard.ContentManagement.Handlers {
void Creating(CreateContentContext context);
void Created(CreateContentContext context);
void Loading(LoadContentContext context);
void Loaded(LoadContentContext context);
void Loaded(LoadContentContext context);
void Versioning(VersionContentContext context);
void Versioned(VersionContentContext context);
void Removing(RemoveContentContext context);
void Removed(RemoveContentContext context);
}
}

View File

@@ -5,7 +5,29 @@ namespace Orchard.ContentManagement.Handlers {
public class LoadContentContext {
public int Id { get; set; }
public string ContentType { get; set; }
public ContentItemRecord ContentItemRecord { get; set; }
public ContentItem ContentItem { get; set; }
public ContentItemRecord ContentItemRecord { get; set; }
public ContentItemVersionRecord ContentItemVersionRecord { get; set; }
}
}
public class RemoveContentContext {
public int Id { get; set; }
public string ContentType { get; set; }
public ContentItem ContentItem { get; set; }
public ContentItemRecord ContentItemRecord { get; set; }
}
public class VersionContentContext {
public int Id { get; set; }
public string ContentType { get; set; }
public ContentItemRecord ContentItemRecord { get; set; }
public ContentItemVersionRecord ExistingItemVersionRecord { get; set; }
public ContentItemVersionRecord BuildingItemVersionRecord { get; set; }
public ContentItem ExistingContentItem { get; set; }
public ContentItem BuildingContentItem { get; set; }
}
}

View File

@@ -21,7 +21,7 @@ namespace Orchard.ContentManagement.Handlers {
}
protected override void Loading(LoadContentContext context, ContentPart<TRecord> instance) {
var record = _repository.Get(instance.ContentItem.Id);
var record = _repository.Get(context.Id);
if (record != null) {
instance.Record = record;
}
@@ -30,5 +30,52 @@ namespace Orchard.ContentManagement.Handlers {
_repository.Create(instance.Record);
}
}
protected override void Versioning(VersionContentContext context, ContentPart<TRecord> existing, ContentPart<TRecord> building) {
building.Record = existing.Record;
}
}
public class StorageVersionFilter<TRecord> : StorageFilterBase<ContentPart<TRecord>> where TRecord : ContentPartVersionRecord, new() {
private readonly IRepository<TRecord> _repository;
public StorageVersionFilter(IRepository<TRecord> repository) {
_repository = repository;
}
public bool AutomaticallyCreateMissingRecord { get; set; }
protected override void Activated(ActivatedContentContext context, ContentPart<TRecord> instance) {
instance.Record = new TRecord();
}
protected override void Creating(CreateContentContext context, ContentPart<TRecord> instance) {
instance.Record.ContentItemRecord = context.ContentItemRecord;
instance.Record.ContentItemVersionRecord = context.ContentItemVersionRecord;
_repository.Create(instance.Record);
}
protected override void Loading(LoadContentContext context, ContentPart<TRecord> instance) {
var record = _repository.Get(context.ContentItemVersionRecord.Id);
if (record != null) {
instance.Record = record;
}
else if (AutomaticallyCreateMissingRecord) {
instance.Record.ContentItemRecord = context.ContentItemRecord;
instance.Record.ContentItemVersionRecord = context.ContentItemVersionRecord;
_repository.Create(instance.Record);
}
}
protected override void Versioning(VersionContentContext context, ContentPart<TRecord> existing, ContentPart<TRecord> building) {
// move known ORM values over
_repository.Copy(existing.Record, building.Record);
// only the up-reference to the particular version differs at this point
building.Record.ContentItemVersionRecord = context.BuildingItemVersionRecord;
// push the new instance into the transaction and session
_repository.Create(building.Record);
}
}
}

View File

@@ -6,6 +6,10 @@ namespace Orchard.ContentManagement.Handlers {
protected virtual void Created(CreateContentContext context, TPart instance) { }
protected virtual void Loading(LoadContentContext context, TPart instance) { }
protected virtual void Loaded(LoadContentContext context, TPart instance) { }
protected virtual void Versioning(VersionContentContext context, TPart existing, TPart building) { }
protected virtual void Versioned(VersionContentContext context, TPart existing, TPart building) { }
protected virtual void Removing(RemoveContentContext context, TPart instance) { }
protected virtual void Removed(RemoveContentContext context, TPart instance) { }
void IContentStorageFilter.Activated(ActivatedContentContext context) {
@@ -32,5 +36,25 @@ namespace Orchard.ContentManagement.Handlers {
if (context.ContentItem.Is<TPart>())
Loaded(context, context.ContentItem.As<TPart>());
}
void IContentStorageFilter.Versioning(VersionContentContext context) {
if (context.ExistingContentItem.Is<TPart>() || context.BuildingContentItem.Is<TPart>())
Versioning(context, context.ExistingContentItem.As<TPart>(), context.BuildingContentItem.As<TPart>());
}
void IContentStorageFilter.Versioned(VersionContentContext context) {
if (context.ExistingContentItem.Is<TPart>() || context.BuildingContentItem.Is<TPart>())
Versioned(context, context.ExistingContentItem.As<TPart>(), context.BuildingContentItem.As<TPart>());
}
void IContentStorageFilter.Removing(RemoveContentContext context) {
if (context.ContentItem.Is<TPart>())
Removing(context, context.ContentItem.As<TPart>());
}
void IContentStorageFilter.Removed(RemoveContentContext context) {
if (context.ContentItem.Is<TPart>())
Removed(context, context.ContentItem.As<TPart>());
}
}
}

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.ViewModels;
namespace Orchard.ContentManagement {
@@ -7,9 +6,14 @@ namespace Orchard.ContentManagement {
IEnumerable<ContentType> GetContentTypes();
ContentItem New(string contentType);
void Create(ContentItem contentItem);
void Create(ContentItem contentItem, VersionOptions options);
ContentItem Get(int id);
ContentItem Get(int id, VersionOptions options);
ContentItem AppendLatestVersion(ContentItem sourceVersion);
IContentQuery<ContentItem> Query();
@@ -19,4 +23,20 @@ namespace Orchard.ContentManagement {
ItemEditorModel<TContent> BuildEditorModel<TContent>(TContent content) where TContent : IContent;
ItemEditorModel<TContent> UpdateEditorModel<TContent>(TContent content, IUpdateModel updater) where TContent : IContent;
}
public class VersionOptions {
public static VersionOptions Latest { get { return new VersionOptions { IsLatest = true }; } }
public static VersionOptions Published { get { return new VersionOptions { IsPublished = true }; } }
public static VersionOptions Draft { get { return new VersionOptions { IsDraft = true }; } }
public static VersionOptions DraftRequired { get { return new VersionOptions { IsDraft = true, IsDraftRequired = true }; } }
public static VersionOptions Number(int version) { return new VersionOptions { VersionNumber = version }; }
public static VersionOptions VersionRecord(int id) { return new VersionOptions { VersionRecordId = id }; }
public bool IsLatest { get; private set; }
public bool IsPublished { get; private set; }
public bool IsDraft { get; private set; }
public bool IsDraftRequired { get; private set; }
public int VersionNumber { get; private set; }
public int VersionRecordId { get; private set; }
}
}

View File

@@ -1,6 +1,17 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Orchard.ContentManagement.Records {
public class ContentItemRecord {
public ContentItemRecord() {
// ReSharper disable DoNotCallOverridableMethodsInConstructor
Versions = new List<ContentItemVersionRecord>();
// ReSharper restore DoNotCallOverridableMethodsInConstructor
}
public virtual int Id { get; set; }
public virtual ContentTypeRecord ContentType { get; set; }
public virtual IList<ContentItemVersionRecord> Versions { get; set; }
}
}

View File

@@ -36,25 +36,32 @@ namespace Orchard.ContentManagement.Records {
class Alteration<TPartRecord> : IAlteration where TPartRecord : ContentPartRecord {
public void Override(AutoMapping<ContentItemRecord> mapping) {
// public TPartRecord TPartRecord {get;set;}
var name = typeof(TPartRecord).Name;
var syntheticMethod = new DynamicMethod(name, typeof(TPartRecord), null, typeof(ContentItemRecord));
var syntheticProperty = new FakePropertyInfo(syntheticMethod);
var syntheticProperty = new SyntheticPropertyInfo(syntheticMethod);
// record => record.TPartRecord
var parameter = Expression.Parameter(typeof(ContentItemRecord), "record");
var syntheticExpression = (Expression<Func<ContentItemRecord, TPartRecord>>)Expression.Lambda(
typeof(Func<ContentItemRecord, TPartRecord>),
Expression.Property(parameter, syntheticProperty),
parameter);
mapping.HasOne(syntheticExpression).Access.NoOp().Fetch.Select();
mapping.References(syntheticExpression)
.Access.NoOp()
.Column("Id")
.Unique()
.Not.Insert()
.Not.Update()
.Cascade.All();
}
private class FakePropertyInfo : PropertyInfo {
private class SyntheticPropertyInfo : PropertyInfo {
private readonly DynamicMethod _getMethod;
public FakePropertyInfo(DynamicMethod dynamicMethod) {
public SyntheticPropertyInfo(DynamicMethod dynamicMethod) {
_getMethod = dynamicMethod;
}

View File

@@ -0,0 +1,10 @@
namespace Orchard.ContentManagement.Records {
public class ContentItemVersionRecord {
public virtual int Id { get; set; }
public virtual ContentItemRecord ContentItemRecord { get; set; }
public virtual int Number { get; set; }
public virtual bool Published { get; set; }
public virtual bool Latest { get; set; }
}
}

View File

@@ -27,8 +27,12 @@ namespace Orchard.ContentManagement.Records {
class Alteration<T> : IAlteration where T : ContentPartRecord {
public void Override(object mappingObj) {
var mapping = (AutoMapping<T>)mappingObj;
mapping.Id(x => x.Id).GeneratedBy.Foreign("ContentItemRecord");
mapping.HasOne(x => x.ContentItemRecord).Constrained();
mapping.Id(x => x.Id)
.GeneratedBy.Foreign("ContentItemRecord");
mapping.HasOne(x => x.ContentItemRecord)
.Constrained();
}
}
}

View File

@@ -0,0 +1,7 @@
namespace Orchard.ContentManagement.Records {
public abstract class ContentPartVersionRecord {
public virtual int Id { get; set; }
public virtual ContentItemRecord ContentItemRecord { get; set; }
public virtual ContentItemVersionRecord ContentItemVersionRecord { get; set; }
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Linq;
using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;
namespace Orchard.ContentManagement.Records {
public class ContentPartVersionRecordAlteration : IAutoMappingAlteration {
public void Alter(AutoPersistenceModel model) {
model.OverrideAll(mapping => {
var genericArguments = mapping.GetType().GetGenericArguments();
if (!genericArguments.Single().IsSubclassOf(typeof(ContentPartVersionRecord))) {
return;
}
var type = typeof(Alteration<>).MakeGenericType(genericArguments);
var alteration = (IAlteration)Activator.CreateInstance(type);
alteration.Override(mapping);
});
}
interface IAlteration {
void Override(object mapping);
}
class Alteration<T> : IAlteration where T : ContentPartVersionRecord {
public void Override(object mappingObj) {
var mapping = (AutoMapping<T>)mappingObj;
mapping.Id(x => x.Id)
.GeneratedBy.Foreign("ContentItemVersionRecord");
mapping.HasOne(x => x.ContentItemVersionRecord)
.Constrained();
}
}
}
}

View File

@@ -2,12 +2,14 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Orchard.ContentManagement.Records;
namespace Orchard.Data {
public interface IRepository<T> {
void Create(T entity);
void Update(T entity);
void Delete(T entity);
void Copy(T source, T target);
T Get(int id);
T Get(Expression<Func<T, bool>> predicate);
@@ -18,7 +20,8 @@ namespace Orchard.Data {
IEnumerable<T> Fetch(Expression<Func<T, bool>> predicate);
IEnumerable<T> Fetch(Expression<Func<T, bool>> predicate, Action<Orderable<T>> order);
IEnumerable<T> Fetch(Expression<Func<T, bool>> predicate, Action<Orderable<T>> order, int skip, int count);
[Obsolete]
void Transaction(Action action);
}
}

View File

@@ -2,7 +2,7 @@ using System;
using NHibernate;
namespace Orchard.Data {
public interface ISessionLocator {
public interface ISessionLocator {
ISession For(Type entityType);
}
}

View File

@@ -44,6 +44,10 @@ namespace Orchard.Data {
Delete(entity);
}
void IRepository<T>.Copy(T source, T target) {
Copy(source, target);
}
T IRepository<T>.Get(int id) {
return Get(id);
}
@@ -114,6 +118,14 @@ namespace Orchard.Data {
Session.Delete(entity);
}
public virtual void Copy(T source, T target) {
Logger.Debug("Delete {0}", source, target);
var metadata = Session.SessionFactory.GetClassMetadata(typeof (T));
var values = metadata.GetPropertyValues(source, EntityMode.Poco);
metadata.SetPropertyValues(target, values, EntityMode.Poco);
}
public virtual int Count(Expression<Func<T, bool>> predicate) {
return Fetch(predicate).Count();
}

View File

@@ -41,8 +41,12 @@ namespace Orchard.Environment {
public IEnumerable<Type> GetRecordTypes() {
var types = _extensionManager.ActiveExtensions().SelectMany(x => x.ExportedTypes);
var recordTypes = types.Where(IsRecordType)
.Concat(new[] { typeof(ContentItemRecord), typeof(ContentPartRecord), typeof(ContentTypeRecord) });
var coreRecords = new[] {
typeof (ContentTypeRecord),
typeof (ContentItemRecord),
typeof (ContentItemVersionRecord),
};
var recordTypes = types.Where(IsRecordType).Concat(coreRecords);
return recordTypes;
}

View File

@@ -1,22 +1,31 @@
using System.Web.Mvc;
using JetBrains.Annotations;
using Orchard.Security;
using Orchard.Settings;
namespace Orchard.Mvc.Filters {
[UsedImplicitly]
public class AntiForgeryAuthorizationFilter : FilterProvider, IAuthorizationFilter {
private readonly ISiteService _siteService;
private readonly IAuthenticationService _authenticationService;
public AntiForgeryAuthorizationFilter(ISiteService siteService) {
public AntiForgeryAuthorizationFilter(ISiteService siteService, IAuthenticationService authenticationService) {
_siteService = siteService;
_authenticationService = authenticationService;
}
public void OnAuthorization(AuthorizationContext filterContext) {
if (!(filterContext.HttpContext.Request.HttpMethod == "POST" && filterContext.RequestContext.HttpContext.Request.IsAuthenticated))
// not a post: no work to do
if (filterContext.HttpContext.Request.HttpMethod != "POST")
return;
// not logged in: no attack vector
if (_authenticationService.GetAuthenticatedUser() == null)
return;
var siteSalt = _siteService.GetSiteSettings().SiteSalt;
ValidateAntiForgeryTokenAttribute validator = new ValidateAntiForgeryTokenAttribute { Salt = siteSalt };
var validator = new ValidateAntiForgeryTokenAttribute { Salt = siteSalt };
validator.OnAuthorization(filterContext);
}
}
}
}

View File

@@ -66,6 +66,9 @@
<HintPath>..\..\lib\linqnhibernate\NHibernate.Linq.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Configuration" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
@@ -168,8 +171,11 @@
<Compile Include="ContentManagement\IUpdateModel.cs" />
<Compile Include="ContentManagement\Records\ContentItemRecord.cs" />
<Compile Include="ContentManagement\Records\ContentItemRecordAlteration.cs" />
<Compile Include="ContentManagement\Records\ContentItemVersionRecord.cs" />
<Compile Include="ContentManagement\Records\ContentPartRecord.cs" />
<Compile Include="ContentManagement\Records\ContentPartRecordAlteration.cs" />
<Compile Include="ContentManagement\Records\ContentPartVersionRecord.cs" />
<Compile Include="ContentManagement\Records\ContentPartVersionRecordAlteration.cs" />
<Compile Include="ContentManagement\Records\ContentTypeRecord.cs" />
<Compile Include="ContentManagement\ViewModels\ItemDisplayModel.cs" />
<Compile Include="ContentManagement\ViewModels\ItemEditorModel.cs" />