diff --git a/src/Orchard/ContentManagement/DefaultContentManager.cs b/src/Orchard/ContentManagement/DefaultContentManager.cs index f08983f1f..b1317c871 100644 --- a/src/Orchard/ContentManagement/DefaultContentManager.cs +++ b/src/Orchard/ContentManagement/DefaultContentManager.cs @@ -5,12 +5,17 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Xml.Linq; using Autofac; +using NHibernate; +using NHibernate.Criterion; +using NHibernate.Metadata; +using NHibernate.SqlCommand; using Orchard.ContentManagement.Handlers; using Orchard.ContentManagement.MetaData; using Orchard.ContentManagement.MetaData.Builders; using Orchard.ContentManagement.MetaData.Models; using Orchard.ContentManagement.Records; using Orchard.Data; +using Orchard.Environment; using Orchard.Indexing; using Orchard.Logging; using Orchard.UI; @@ -24,6 +29,7 @@ namespace Orchard.ContentManagement { private readonly IContentDefinitionManager _contentDefinitionManager; private readonly Func _contentManagerSession; private readonly Lazy _contentDisplay; + private readonly Work _sessionLocator; private const string Published = "Published"; private const string Draft = "Draft"; @@ -34,7 +40,8 @@ namespace Orchard.ContentManagement { IRepository contentItemVersionRepository, IContentDefinitionManager contentDefinitionManager, Func contentManagerSession, - Lazy contentDisplay) { + Lazy contentDisplay, + Work sessionLocator) { _context = context; _contentTypeRepository = contentTypeRepository; _contentItemRepository = contentItemRepository; @@ -42,6 +49,7 @@ namespace Orchard.ContentManagement { _contentDefinitionManager = contentDefinitionManager; _contentManagerSession = contentManagerSession; _contentDisplay = contentDisplay; + _sessionLocator = sessionLocator; Logger = NullLogger.Instance; } @@ -50,8 +58,10 @@ namespace Orchard.ContentManagement { private IEnumerable _handlers; public IEnumerable Handlers { get { - if (_handlers == null) + if (_handlers == null) { _handlers = _context.Resolve>(); + } + return _handlers; } } @@ -110,8 +120,9 @@ namespace Orchard.ContentManagement { // obtain the root records based on version options if (options.VersionRecordId != 0) { // short-circuit if item held in session - if (session.RecallVersionRecordId(options.VersionRecordId, out contentItem)) + if (session.RecallVersionRecordId(options.VersionRecordId, out contentItem)) { return contentItem; + } // locate explicit version record versionRecord = _contentItemVersionRepository.Get(options.VersionRecordId); @@ -160,25 +171,25 @@ namespace Orchard.ContentManagement { private ContentItemVersionRecord GetVersionRecord(VersionOptions options, ContentItemRecord itemRecord) { if (options.IsPublished) { return itemRecord.Versions.FirstOrDefault( - x => x.Published) ?? + x => x.Published) ?? _contentItemVersionRepository.Get( x => x.ContentItemRecord == itemRecord && x.Published); } if (options.IsLatest || options.IsDraftRequired) { return itemRecord.Versions.FirstOrDefault( - x => x.Latest) ?? + x => x.Latest) ?? _contentItemVersionRepository.Get( x => x.ContentItemRecord == itemRecord && x.Latest); } if (options.IsDraft) { return itemRecord.Versions.FirstOrDefault( - x => x.Latest && !x.Published) ?? + x => x.Latest && !x.Published) ?? _contentItemVersionRepository.Get( x => x.ContentItemRecord == itemRecord && x.Latest && !x.Published); } if (options.VersionNumber != 0) { return itemRecord.Versions.FirstOrDefault( - x => x.Number == options.VersionNumber) ?? + x => x.Number == options.VersionNumber) ?? _contentItemVersionRepository.Get( x => x.ContentItemRecord == itemRecord && x.Number == options.VersionNumber); } @@ -192,6 +203,71 @@ namespace Orchard.ContentManagement { .Select(x => Get(x.ContentItemRecord.Id, VersionOptions.VersionRecord(x.Id))); } + public IEnumerable GetMany(IEnumerable ids, VersionOptions options, QueryHints hints) where T : class, IContent { + var contentItemVersionRecords = GetManyImplementation(hints, (contentItemCriteria, contentItemVersionCriteria) => { + contentItemCriteria.Add(Restrictions.In("Id", ids.ToArray())); + if (options.IsPublished) { + contentItemVersionCriteria.Add(Restrictions.Eq("Published", true)); + } + else if (options.IsLatest) { + contentItemVersionCriteria.Add(Restrictions.Eq("Latest", true)); + } + else if (options.IsDraft) { + contentItemVersionCriteria.Add( + Restrictions.And(Restrictions.Eq("Published", false), + Restrictions.Eq("Latest", true))); + } + }); + var itemsById = contentItemVersionRecords + .Select(r => Get(r.ContentItemRecord.Id, VersionOptions.VersionRecord(r.Id))) + .GroupBy(ci => ci.Id) + .ToDictionary(g => g.Key); + + return ids.SelectMany(id => { + IGrouping values; + return itemsById.TryGetValue(id, out values) ? values : Enumerable.Empty(); + }).AsPart().ToArray(); + } + + public IEnumerable GetManyByVersionId(IEnumerable versionRecordIds, QueryHints hints) where T : class, IContent { + var contentItemVersionRecords = GetManyImplementation(hints, (contentItemCriteria, contentItemVersionCriteria) => + contentItemVersionCriteria.Add(Restrictions.In("Id", versionRecordIds.ToArray()))); + + var itemsById = contentItemVersionRecords + .Select(r => Get(r.ContentItemRecord.Id, VersionOptions.VersionRecord(r.Id))) + .GroupBy(ci => ci.VersionRecord.Id) + .ToDictionary(g => g.Key); + + return versionRecordIds.SelectMany(id => { + IGrouping values; + return itemsById.TryGetValue(id, out values) ? values : Enumerable.Empty(); + }).AsPart().ToArray(); + } + + private IEnumerable GetManyImplementation(QueryHints hints, Action predicate) { + var session = _sessionLocator.Value.For(typeof (ContentItemRecord)); + var contentItemVersionCriteria = session.CreateCriteria(typeof (ContentItemVersionRecord)); + var contentItemCriteria = contentItemVersionCriteria.CreateCriteria("ContentItemRecord"); + predicate(contentItemCriteria, contentItemVersionCriteria); + + var hintDictionary = hints.Records.Distinct().ToDictionary(value => value); + var contentItemMetadata = session.SessionFactory.GetClassMetadata(typeof (ContentItemRecord)); + var contentItemVersionMetadata = session.SessionFactory.GetClassMetadata(typeof (ContentItemVersionRecord)); + + ExpandHints(hintDictionary, contentItemMetadata, contentItemCriteria); + ExpandHints(hintDictionary, contentItemVersionMetadata, contentItemVersionCriteria); + + return contentItemVersionCriteria.List(); + } + + private static void ExpandHints(Dictionary hintDictionary, IClassMetadata recordMetadata, ICriteria recordCriteria) { + foreach (var associationPath in recordMetadata.PropertyNames.Where(hintDictionary.ContainsKey)) { + if (recordCriteria.GetCriteriaByPath(associationPath) == null) { + recordCriteria.CreateCriteria(associationPath, JoinType.LeftOuterJoin); + } + } + } + public virtual void Publish(ContentItem contentItem) { if (contentItem.VersionRecord.Published) { return; @@ -407,8 +483,9 @@ namespace Orchard.ContentManagement { // Call content item handlers. public void Import(XElement element, ImportContentSession importContentSession) { var elementId = element.Attribute("Id"); - if (elementId == null) + if (elementId == null) { return; + } var identity = elementId.Value; var status = element.Attribute("Status"); @@ -487,8 +564,8 @@ namespace Orchard.ContentManagement { } } - class CallSiteCollection : ConcurrentDictionary>> { - readonly Func>> _valueFactory; + internal class CallSiteCollection : ConcurrentDictionary>> { + private readonly Func>> _valueFactory; public CallSiteCollection(Func>> callSiteFactory) { _valueFactory = callSiteFactory; diff --git a/src/Orchard/ContentManagement/IContentManager.cs b/src/Orchard/ContentManagement/IContentManager.cs index ea1920b6d..7379a8482 100644 --- a/src/Orchard/ContentManagement/IContentManager.cs +++ b/src/Orchard/ContentManagement/IContentManager.cs @@ -16,6 +16,9 @@ namespace Orchard.ContentManagement { ContentItem Get(int id, VersionOptions options); IEnumerable GetAllVersions(int id); + IEnumerable GetMany(IEnumerable ids, VersionOptions options, QueryHints hints) where T : class, IContent; + IEnumerable GetManyByVersionId(IEnumerable versionRecordIds, QueryHints hints) where T : class, IContent; + void Publish(ContentItem contentItem); void Unpublish(ContentItem contentItem); void Remove(ContentItem contentItem); diff --git a/src/Orchard/ContentManagement/QueryHints.cs b/src/Orchard/ContentManagement/QueryHints.cs new file mode 100644 index 000000000..7e932bbb1 --- /dev/null +++ b/src/Orchard/ContentManagement/QueryHints.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Orchard.ContentManagement.Records; + +namespace Orchard.ContentManagement +{ + public class QueryHints + { + private readonly List _records = new List(); + private static readonly QueryHints _empty = new QueryHints(); + + public IEnumerable Records + { + get { return _records; } + } + + public static QueryHints Empty + { + get { return _empty; } + } + + public QueryHints ExpandRecords(IEnumerable records) + { + _records.AddRange(records); + return this; + } + + public QueryHints ExpandRecords(params string[] records) + { + _records.AddRange(records); + return this; + } + + public QueryHints ExpandRecords() where TRecord1 : ContentPartRecord + { + _records.AddRange(new[] { typeof(TRecord1).Name }); + return this; + } + public QueryHints ExpandRecords() + where TRecord1 : ContentPartRecord + where TRecord2 : ContentPartRecord + { + _records.AddRange(new[] { typeof(TRecord1).Name, typeof(TRecord2).Name }); + return this; + } + public QueryHints ExpandRecords() + where TRecord1 : ContentPartRecord + where TRecord2 : ContentPartRecord + where TRecord3 : ContentPartRecord + { + _records.AddRange(new[] { typeof(TRecord1).Name, typeof(TRecord2).Name, typeof(TRecord3).Name }); + return this; + } + public QueryHints ExpandRecords() + where TRecord1 : ContentPartRecord + where TRecord2 : ContentPartRecord + where TRecord3 : ContentPartRecord + where TRecord4 : ContentPartRecord + { + _records.AddRange(new[] { typeof(TRecord1).Name, typeof(TRecord2).Name, typeof(TRecord3).Name, typeof(TRecord4).Name }); + return this; + } + public QueryHints ExpandRecords() + where TRecord1 : ContentPartRecord + where TRecord2 : ContentPartRecord + where TRecord3 : ContentPartRecord + where TRecord4 : ContentPartRecord + where TRecord5 : ContentPartRecord + { + _records.AddRange(new[] { typeof(TRecord1).Name, typeof(TRecord2).Name, typeof(TRecord3).Name, typeof(TRecord4).Name, typeof(TRecord5).Name }); + return this; + } + public QueryHints ExpandRecords() + where TRecord1 : ContentPartRecord + where TRecord2 : ContentPartRecord + where TRecord3 : ContentPartRecord + where TRecord4 : ContentPartRecord + where TRecord5 : ContentPartRecord + where TRecord6 : ContentPartRecord + { + _records.AddRange(new[] { typeof(TRecord1).Name, typeof(TRecord2).Name, typeof(TRecord3).Name, typeof(TRecord4).Name, typeof(TRecord5).Name, typeof(TRecord6).Name }); + return this; + } + public QueryHints ExpandRecords() + where TRecord1 : ContentPartRecord + where TRecord2 : ContentPartRecord + where TRecord3 : ContentPartRecord + where TRecord4 : ContentPartRecord + where TRecord5 : ContentPartRecord + where TRecord6 : ContentPartRecord + where TRecord7 : ContentPartRecord + { + _records.AddRange(new[] { typeof(TRecord1).Name, typeof(TRecord2).Name, typeof(TRecord3).Name, typeof(TRecord4).Name, typeof(TRecord5).Name, typeof(TRecord6).Name, typeof(TRecord7).Name }); + return this; + } + public QueryHints ExpandRecords() + where TRecord1 : ContentPartRecord + where TRecord2 : ContentPartRecord + where TRecord3 : ContentPartRecord + where TRecord4 : ContentPartRecord + where TRecord5 : ContentPartRecord + where TRecord6 : ContentPartRecord + where TRecord7 : ContentPartRecord + where TRecord8 : ContentPartRecord + { + _records.AddRange(new[] { typeof(TRecord1).Name, typeof(TRecord2).Name, typeof(TRecord3).Name, typeof(TRecord4).Name, typeof(TRecord5).Name, typeof(TRecord6).Name, typeof(TRecord7).Name, typeof(TRecord8).Name }); + return this; + } + + public QueryHints ExpandParts() where TPart1 : ContentPart + { + return ExpandPartsImpl(typeof(TPart1)); + } + + public QueryHints ExpandParts() + where TPart1 : ContentPart + where TPart2 : ContentPart + { + return ExpandPartsImpl(typeof(TPart1), typeof(TPart2)); + } + + public QueryHints ExpandParts() + where TPart1 : ContentPart + where TPart2 : ContentPart + where TPart3 : ContentPart + { + return ExpandPartsImpl(typeof(TPart1), typeof(TPart2), typeof(TPart3)); + } + + public QueryHints ExpandParts() + where TPart1 : ContentPart + where TPart2 : ContentPart + where TPart3 : ContentPart + where TPart4 : ContentPart + { + return ExpandPartsImpl(typeof(TPart1), typeof(TPart2), typeof(TPart3), typeof(TPart4)); + } + + public QueryHints ExpandParts() + where TPart1 : ContentPart + where TPart2 : ContentPart + where TPart3 : ContentPart + where TPart4 : ContentPart + where TPart5 : ContentPart + { + return ExpandPartsImpl(typeof(TPart1), typeof(TPart2), typeof(TPart3), typeof(TPart4), typeof(TPart5)); + } + + public QueryHints ExpandParts() + where TPart1 : ContentPart + where TPart2 : ContentPart + where TPart3 : ContentPart + where TPart4 : ContentPart + where TPart5 : ContentPart + where TPart6 : ContentPart + { + return ExpandPartsImpl(typeof(TPart1), typeof(TPart2), typeof(TPart3), typeof(TPart4), typeof(TPart5), typeof(TPart6)); + } + + public QueryHints ExpandParts() + where TPart1 : ContentPart + where TPart2 : ContentPart + where TPart3 : ContentPart + where TPart4 : ContentPart + where TPart5 : ContentPart + where TPart6 : ContentPart + where TPart7 : ContentPart + { + return ExpandPartsImpl(typeof(TPart1), typeof(TPart2), typeof(TPart3), typeof(TPart4), typeof(TPart5), typeof(TPart6), typeof(TPart7)); + } + + public QueryHints ExpandParts() + where TPart1 : ContentPart + where TPart2 : ContentPart + where TPart3 : ContentPart + where TPart4 : ContentPart + where TPart5 : ContentPart + where TPart6 : ContentPart + where TPart7 : ContentPart + where TPart8 : ContentPart + { + return ExpandPartsImpl(typeof(TPart1), typeof(TPart2), typeof(TPart3), typeof(TPart4), typeof(TPart5), typeof(TPart6), typeof(TPart7), typeof(TPart8)); + } + + private QueryHints ExpandPartsImpl(params Type[] parts) + { + foreach (var part in parts) + { + for (var scan = part; scan != typeof(Object); scan = scan.BaseType) + { + if (scan.IsGenericType && scan.GetGenericTypeDefinition() == typeof(ContentPart<>)) + { + _records.Add(scan.GetGenericArguments().Single().Name); + break; + } + } + } + return this; + } + } +} \ No newline at end of file diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 0989db416..b15730e53 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -175,6 +175,7 @@ + @@ -186,6 +187,7 @@ + @@ -252,6 +254,7 @@ +