From a383309e46912e3f434c3852acf4c2b2d576d1b7 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 30 Oct 2018 17:32:27 +1100 Subject: [PATCH 01/18] Gets indexes updating based on content type changes --- .../Implement/DocumentRepository.cs | 2 +- src/Umbraco.Core/Services/IContentService.cs | 10 +- .../Services/Implement/ContentService.cs | 26 ++---- .../Services/ContentServiceTests.cs | 2 +- .../Services/PerformanceTests.cs | 34 ------- src/Umbraco.Web/Search/ExamineComponent.cs | 91 ++++++++++++++++++- 6 files changed, 105 insertions(+), 60 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index f3afe99b28..b3a7c31e54 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -99,7 +99,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private string VariantNameSqlExpression => SqlContext.VisitDto((ccv, node) => ccv.Name ?? node.Text, "ccv").Sql; - protected virtual Sql GetBaseQuery(QueryType queryType, bool current) + protected Sql GetBaseQuery(QueryType queryType, bool current) { var sql = SqlContext.Sql(); diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 7371686c7c..0cfa0bb601 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -79,9 +79,15 @@ namespace Umbraco.Core.Services IEnumerable GetByIds(IEnumerable ids); /// - /// Gets documents of a given document type. + /// Gets paged documents of a content content /// - IEnumerable GetByType(int documentTypeId); + /// The page number. + /// The page number. + /// The page size. + /// Total number of documents. + /// Search text filter. + /// Ordering infos. + IEnumerable GetPagedOfType(int contentType, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null); /// /// Gets documents at a given level. diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 3953e7a640..64c6a23aff 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -405,28 +405,20 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by the Id of the - /// - /// Id of the - /// An Enumerable list of objects - public IEnumerable GetByType(int id) + public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); - var query = Query().Where(x => x.ContentTypeId == id); - return _documentRepository.Get(query); - } - } + if(pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); + if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + + if (ordering == null) + ordering = Ordering.By("sortOrder"); - internal IEnumerable GetPublishedContentOfContentType(int id) - { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.ContentTree); - var query = Query().Where(x => x.ContentTypeId == id); - return _documentRepository.Get(query); + return _documentRepository.GetPage( + Query().Where(x => x.ContentTypeId == contentTypeId), + pageIndex, pageSize, out totalRecords, filter, ordering); } } diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 5aad7c4d90..5b06037f90 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1738,7 +1738,7 @@ namespace Umbraco.Tests.Services // Act contentService.DeleteOfType(contentType.Id); var rootContent = contentService.GetRootContent(); - var contents = contentService.GetByType(contentType.Id); + var contents = contentService.GetPagedOfType(contentType.Id, 0, int.MaxValue, out var _); // Assert Assert.That(rootContent.Any(), Is.False); diff --git a/src/Umbraco.Tests/Services/PerformanceTests.cs b/src/Umbraco.Tests/Services/PerformanceTests.cs index 9b0117c266..900a466a1d 100644 --- a/src/Umbraco.Tests/Services/PerformanceTests.cs +++ b/src/Umbraco.Tests/Services/PerformanceTests.cs @@ -108,40 +108,6 @@ namespace Umbraco.Tests.Services } } - [Test] - public void Get_All_Published_Content_Of_Type() - { - var result = PrimeDbWithLotsOfContent(); - var contentSvc = (ContentService)ServiceContext.ContentService; - - var countOfPublished = result.Count(x => x.Published); - var contentTypeId = result.First().ContentTypeId; - - var proflog = GetTestProfilingLogger(); - using (proflog.DebugDuration("Getting published content of type normally")) - { - //do this 10x! - for (var i = 0; i < 10; i++) - { - - //get all content items that are published of this type - var published = contentSvc.GetByType(contentTypeId).Where(content => content.Published); - Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId)); - } - } - - using (proflog.DebugDuration("Getting published content of type optimized")) - { - - //do this 10x! - for (var i = 0; i < 10; i++) - { - //get all content items that are published of this type - var published = contentSvc.GetPublishedContentOfContentType(contentTypeId); - Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId)); - } - } - } [Test] public void Truncate_Insert_Vs_Update_Insert() diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index ff7d0c8dc4..530e8ba449 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -102,13 +102,10 @@ namespace Umbraco.Web.Search // bind to distributed cache events - this ensures that this logic occurs on ALL servers // that are taking part in a load balanced environment. ContentCacheRefresher.CacheUpdated += ContentCacheRefresherUpdated; + ContentTypeCacheRefresher.CacheUpdated += ContentTypeCacheRefresherUpdated; ; MediaCacheRefresher.CacheUpdated += MediaCacheRefresherUpdated; MemberCacheRefresher.CacheUpdated += MemberCacheRefresherUpdated; - // fixme - content type? - // events handling removed in ef013f9d3b945d0a48a306ff1afbd49c10c3fff8 - // because, could not make sense of it? - EnsureUnlocked(profilingLogger.Logger, examineManager); RebuildIndexesOnStartup(profilingLogger.Logger); @@ -317,6 +314,88 @@ namespace Umbraco.Web.Search } } + private void ContentTypeCacheRefresherUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs args) + { + + //before content type changes just caused full blown re-indexing: + // https://github.com/umbraco/Umbraco-CMS/commit/ef013f9d3b945d0a48a306ff1afbd49c10c3fff8 + + + if (Suspendable.ExamineEvents.CanIndex == false) + return; + + if (args.MessageType != MessageType.RefreshByPayload) + throw new NotSupportedException(); + + var contentService = _services.ContentService; + + var removedIds = new List(); + var refreshedIds = new List(); + + //TODO: What do we do about these? + var otherIds = new List(); + + foreach (var payload in (ContentTypeCacheRefresher.JsonPayload[])args.MessageObject) + { + if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.Remove)) + removedIds.Add(payload.Id); + else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshMain)) + refreshedIds.Add(payload.Id); + else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshOther)) + otherIds.Add(payload.Id); + } + + const int pageSize = 500; + + //Re-index all content of these types + foreach(var id in refreshedIds) + { + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var contentToRefresh = _services.ContentService.GetPagedOfType(id, page++, pageSize, out total); + foreach(var c in contentToRefresh) + { + //TODO: We might have to order by Path ascending or something since we're going to need to check + // contentService.IsPathPublished(content) but we don't want to make that check for every content item + IContent published = null; + if (c.Published && contentService.IsPathPublished(c)) + published = c; + + ReIndexForContent(c, published); + } + } + } + + //Delete all content of this content type that is in any content indexer by looking up matched examine docs + foreach(var id in removedIds) + { + foreach(var index in _examineManager.IndexProviders.Values.OfType()) + { + var searcher = index.GetSearcher(); + + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + //paging with examine, see https://shazwazza.com/post/paging-with-examine/ + var results = searcher.Search( + searcher.CreateCriteria().Field("nodeType", id).Compile(), + maxResults: pageSize * (page + 1)); + total = results.TotalItemCount; + var paged = results.Skip(page * pageSize); + + foreach(var item in paged) + if (int.TryParse(item.Id, out var contentId)) + DeleteIndexForEntity(contentId, false); + } + } + + + } + } + private void ContentCacheRefresherUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) { if (Suspendable.ExamineEvents.CanIndex == false) @@ -340,6 +419,8 @@ namespace Umbraco.Web.Search // ExamineEvents does not support RefreshAll // just ignore that payload // so what?! + + //fixme: Rebuild the index at this point? } else // RefreshNode or RefreshBranch (maybe trashed) { @@ -355,7 +436,7 @@ namespace Umbraco.Web.Search } IContent published = null; - if (content.Published && ((ContentService)contentService).IsPathPublished(content)) + if (content.Published && contentService.IsPathPublished(content)) published = content; // just that content From 23860e58b93bfc9ecb1ce41a0b5ec3fddcd360e4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 31 Oct 2018 16:33:10 +1100 Subject: [PATCH 02/18] Update re-indexing logic for content types including updating content types affected by composition changes --- src/Umbraco.Examine/UmbracoExamineIndexer.cs | 2 +- src/Umbraco.Web/Search/ExamineComponent.cs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index 64fd7b5c71..a4c1fb4336 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -418,7 +418,7 @@ namespace Umbraco.Examine //icon if (e.IndexItem.ValueSet.Values.TryGetValue("icon", out var icon) && e.IndexItem.ValueSet.Values.ContainsKey(IconFieldName) == false) { - e.IndexItem.ValueSet.Values[IconFieldName] = new List { icon }; + e.IndexItem.ValueSet.Values[IconFieldName] = icon; } } diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 530e8ba449..a1625206d1 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -317,7 +317,8 @@ namespace Umbraco.Web.Search private void ContentTypeCacheRefresherUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs args) { - //before content type changes just caused full blown re-indexing: + //before content type changes just didn't do anything for indexing, simply just updated the + //definitions to index: // https://github.com/umbraco/Umbraco-CMS/commit/ef013f9d3b945d0a48a306ff1afbd49c10c3fff8 @@ -331,8 +332,6 @@ namespace Umbraco.Web.Search var removedIds = new List(); var refreshedIds = new List(); - - //TODO: What do we do about these? var otherIds = new List(); foreach (var payload in (ContentTypeCacheRefresher.JsonPayload[])args.MessageObject) @@ -348,7 +347,7 @@ namespace Umbraco.Web.Search const int pageSize = 500; //Re-index all content of these types - foreach(var id in refreshedIds) + foreach(var id in refreshedIds.Concat(otherIds).Distinct()) { var page = 0; var total = long.MaxValue; From 18f6e7ba789702fcf2d9434fa0c4bbe1f3d73e21 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 31 Oct 2018 17:57:35 +1100 Subject: [PATCH 03/18] Updates ExamineComponent to page over descendants when caches are updated --- src/Umbraco.Web/Search/ExamineComponent.cs | 42 +++++++++++++--------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index a1625206d1..b1a302785d 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -314,14 +314,13 @@ namespace Umbraco.Web.Search } } + /// + /// Updates indexes based on content type changes + /// + /// + /// private void ContentTypeCacheRefresherUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs args) { - - //before content type changes just didn't do anything for indexing, simply just updated the - //definitions to index: - // https://github.com/umbraco/Umbraco-CMS/commit/ef013f9d3b945d0a48a306ff1afbd49c10c3fff8 - - if (Suspendable.ExamineEvents.CanIndex == false) return; @@ -395,6 +394,11 @@ namespace Umbraco.Web.Search } } + /// + /// Updates indexes based on content changes + /// + /// + /// private void ContentCacheRefresherUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) { if (Suspendable.ExamineEvents.CanIndex == false) @@ -445,19 +449,25 @@ namespace Umbraco.Web.Search if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) { var masked = published == null ? null : new List(); - var descendants = contentService.GetDescendants(content); - foreach (var descendant in descendants) + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) { - published = null; - if (masked != null) // else everything is masked + var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total); + foreach (var descendant in descendants) { - if (masked.Contains(descendant.ParentId) || !descendant.Published) - masked.Add(descendant.Id); - else - published = descendant; - } + published = null; + if (masked != null) // else everything is masked + { + if (masked.Contains(descendant.ParentId) || !descendant.Published) + masked.Add(descendant.Id); + else + published = descendant; + } - ReIndexForContent(descendant, published); + ReIndexForContent(descendant, published); + } } } } From 437bf978a1e67c95d09de629c527564157f0c643 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 31 Oct 2018 18:01:39 +1100 Subject: [PATCH 04/18] Removes non paged GetChildren and GetDescendants from content service --- src/Umbraco.Core/ContentExtensions.cs | 46 +--- .../Services/EntityXmlSerializer.cs | 31 ++- src/Umbraco.Core/Services/IContentService.cs | 21 +- .../Services/Implement/ContentService.cs | 214 ++++++++---------- .../Integration/ContentEventsTests.cs | 2 +- .../NPocoTests/PetaPocoCachesTest.cs | 4 - .../Services/ContentServicePerformanceTest.cs | 2 +- .../Services/ContentServiceTests.cs | 85 ++++--- .../Umbraco/dialogs/ChangeDocType.aspx.cs | 3 +- src/Umbraco.Web/Editors/ContentController.cs | 5 +- 10 files changed, 164 insertions(+), 249 deletions(-) diff --git a/src/Umbraco.Core/ContentExtensions.cs b/src/Umbraco.Core/ContentExtensions.cs index 4f88c2b803..f0fa6cdf17 100644 --- a/src/Umbraco.Core/ContentExtensions.cs +++ b/src/Umbraco.Core/ContentExtensions.cs @@ -48,28 +48,6 @@ namespace Umbraco.Core return contentService.GetAncestors(content); } - /// - /// Returns a list of the current contents children. - /// - /// Current content - /// - /// An enumerable list of objects - public static IEnumerable Children(this IContent content, IContentService contentService) - { - return contentService.GetChildren(content.Id); - } - - /// - /// Returns a list of the current contents descendants, not including the content itself. - /// - /// Current content - /// - /// An enumerable list of objects - public static IEnumerable Descendants(this IContent content, IContentService contentService) - { - return contentService.GetDescendants(content); - } - /// /// Returns the parent of the current content. /// @@ -179,29 +157,7 @@ namespace Umbraco.Core } return false; } - - /// - /// Returns the children for the content base item - /// - /// - /// - /// - /// - /// This is a bit of a hack because we need to type check! - /// - internal static IEnumerable Children(IContentBase content, ServiceContext services) - { - if (content is IContent) - { - return services.ContentService.GetChildren(content.Id); - } - if (content is IMedia) - { - return services.MediaService.GetChildren(content.Id); - } - return null; - } - + /// /// Returns properties that do not belong to a group /// diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/EntityXmlSerializer.cs index e418c8d3e6..ebb35a43ed 100644 --- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/EntityXmlSerializer.cs @@ -30,7 +30,7 @@ namespace Umbraco.Core.Services IEnumerable urlSegmentProviders, IContent content, bool published, - bool withDescendants = false) // fixme take care of usage! + bool withDescendants = false) //fixme take care of usage! only used for the packager { if (contentService == null) throw new ArgumentNullException(nameof(contentService)); if (dataTypeService == null) throw new ArgumentNullException(nameof(dataTypeService)); @@ -58,9 +58,15 @@ namespace Umbraco.Core.Services if (withDescendants) { - var descendants = contentService.GetDescendants(content).ToArray(); - var currentChildren = descendants.Where(x => x.ParentId == content.Id); - SerializeDescendants(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, descendants, currentChildren, xml, published); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) + { + var children = contentService.GetPagedChildren(content.Id, page++, pageSize, out total); + SerializeChildren(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, children, xml, published); + } + } return xml; @@ -451,7 +457,7 @@ namespace Umbraco.Core.Services } // exports an IContent item descendants. - private static void SerializeDescendants(IContentService contentService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable urlSegmentProviders, IContent[] originalDescendants, IEnumerable children, XElement xml, bool published) + private static void SerializeChildren(IContentService contentService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable urlSegmentProviders, IEnumerable children, XElement xml, bool published) { foreach (var child in children) { @@ -459,12 +465,15 @@ namespace Umbraco.Core.Services var childXml = Serialize(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, child, published); xml.Add(childXml); - // capture id (out of closure) and get the grandChildren (children of the child) - var parentId = child.Id; - var grandChildren = originalDescendants.Where(x => x.ParentId == parentId); - - // recurse - SerializeDescendants(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, originalDescendants, grandChildren, childXml, published); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) + { + var grandChildren = contentService.GetPagedChildren(child.Id, page++, pageSize, out total); + // recurse + SerializeChildren(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, grandChildren, childXml, published); + } } } diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 0cfa0bb601..b821ae1eb0 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -94,16 +94,6 @@ namespace Umbraco.Core.Services /// IEnumerable GetByLevel(int level); - /// - /// Gets child documents of a given parent. - /// - IEnumerable GetChildren(int parentId); - - /// - /// Gets child documents of a document, (partially) matching a name. - /// - IEnumerable GetChildren(int parentId, string name); - /// /// Gets the parent of a document. /// @@ -124,16 +114,6 @@ namespace Umbraco.Core.Services /// IEnumerable GetAncestors(IContent content); - /// - /// Gets descendant documents of a document. - /// - IEnumerable GetDescendants(int id); - - /// - /// Gets descendant documents of a document. - /// - IEnumerable GetDescendants(IContent content); - /// /// Gets all versions of a document. /// @@ -172,6 +152,7 @@ namespace Umbraco.Core.Services /// IEnumerable GetContentForRelease(); + //fixme: should be paged /// /// Gets documents in the recycle bin. /// diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 64c6a23aff..1f28988fa6 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -528,21 +528,6 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// An Enumerable list of objects - public IEnumerable GetChildren(int id) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); - var query = Query().Where(x => x.ParentId == id); - return _documentRepository.Get(query).OrderBy(x => x.SortOrder); - } - } - /// /// Gets a collection of published objects by Parent Id /// @@ -626,60 +611,6 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by its name or partial name - /// - /// Id of the Parent to retrieve Children from - /// Full or partial name of the children - /// An Enumerable list of objects - public IEnumerable GetChildren(int parentId, string name) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); - var query = Query().Where(x => x.ParentId == parentId && x.Name.Contains(name)); - return _documentRepository.Get(query); - } - } - - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Descendants from - /// An Enumerable list of objects - public IEnumerable GetDescendants(int id) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); - var content = GetById(id); - if (content == null) - { - scope.Complete(); // else causes rollback - return Enumerable.Empty(); - } - var pathMatch = content.Path + ","; - var query = Query().Where(x => x.Id != content.Id && x.Path.StartsWith(pathMatch)); - return _documentRepository.Get(query); - } - } - - /// - /// Gets a collection of objects by Parent Id - /// - /// item to retrieve Descendants from - /// An Enumerable list of objects - public IEnumerable GetDescendants(IContent content) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); - var pathMatch = content.Path + ","; - var query = Query().Where(x => x.Id != content.Id && x.Path.StartsWith(pathMatch)); - return _documentRepository.Get(query); - } - } - /// /// Gets the parent of the current content as an item. /// @@ -1347,27 +1278,35 @@ namespace Umbraco.Core.Services.Implement // if one fails, abort its branch var exclude = new HashSet(); - //fixme: should be paged to not overwhelm the database (timeouts) - foreach (var d in GetDescendants(document)) + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) { - // if parent is excluded, exclude document and ignore - // if not forcing, and not publishing, exclude document and ignore - if (exclude.Contains(d.ParentId) || !force && !d.Published) + var descendants = GetPagedDescendants(document.Id, page++, pageSize, out total); + + foreach (var d in descendants) { + // if parent is excluded, exclude document and ignore + // if not forcing, and not publishing, exclude document and ignore + if (exclude.Contains(d.ParentId) || !force && !d.Published) + { + exclude.Add(d.Id); + continue; + } + + // no need to check path here, + // 1. because we know the parent is path-published (we just published it) + // 2. because it would not work as nothing's been written out to the db until the uow completes + result = SaveAndPublishBranchOne(scope, d, editing, publishCultures, false, publishedDocuments, evtMsgs, userId); + results.Add(result); + if (result.Success) continue; + + // abort branch exclude.Add(d.Id); - continue; } - - // no need to check path here, - // 1. because we know the parent is path-published (we just published it) - // 2. because it would not work as nothing's been written out to the db until the uow completes - result = SaveAndPublishBranchOne(scope, d, editing, publishCultures, false, publishedDocuments, evtMsgs, userId); - results.Add(result); - if (result.Success) continue; - - // abort branch - exclude.Add(d.Id); } + scope.Events.Dispatch(TreeChanged, this, new TreeChange(document, TreeChangeTypes.RefreshBranch).ToEventArgs()); scope.Events.Dispatch(Published, this, new PublishEventArgs(publishedDocuments, false, false), "Published"); @@ -1450,6 +1389,8 @@ namespace Umbraco.Core.Services.Implement private void DeleteLocked(IScope scope, IContent content) { + //TODO: Test this + // then recursively delete descendants, bottom-up // just repository.Delete + an event var stack = new Stack(); @@ -1458,14 +1399,37 @@ namespace Umbraco.Core.Services.Implement while (stack.Count > 0) { var c = stack.Peek(); - IContent[] cc; if (c.Level == level) - while ((cc = c.Children(this).ToArray()).Length > 0) + { + var hasChildren = true; + while (hasChildren) { - foreach (var ci in cc) - stack.Push(ci); - c = cc[cc.Length - 1]; + IContent last = null; + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + var countChildren = 0; + + //get all children of c in pages + while (page * pageSize < total) + { + var children = GetPagedChildren(c.Id, page++, pageSize, out total); + + foreach (var ci in children) + { + countChildren++; + stack.Push(ci); + last = ci; + } + + if (countChildren == 0) + hasChildren = false; //exit, there are no children left to load + else + c = last; //recurse with the last child + } } + } + c = stack.Pop(); level = c.Level; @@ -1686,9 +1650,6 @@ namespace Umbraco.Core.Services.Implement moves.Add(Tuple.Create(content, content.Path)); // capture original path - // get before moving, in case uow is immediate - var descendants = GetDescendants(content); - // these will be updated by the repo because we changed parentId //content.Path = (parent == null ? "-1" : parent.Path) + "," + content.Id; //content.SortOrder = ((ContentRepository) repository).NextChildSortOrder(parentId); @@ -1700,18 +1661,26 @@ namespace Umbraco.Core.Services.Implement //paths[content.Id] = content.Path; paths[content.Id] = (parent == null ? (parentId == Constants.System.RecycleBinContent ? "-1,-20" : "-1") : parent.Path) + "," + content.Id; - foreach (var descendant in descendants) + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) { - moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path + var descendants = GetPagedDescendants(content.Id, page++, pageSize, out total); + foreach (var descendant in descendants) + { + moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path - // update path and level since we do not update parentId - if (paths.ContainsKey(descendant.ParentId) == false) - Console.WriteLine("oops on " + descendant.ParentId + " for " + content.Path + " " + parent?.Path); - descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id; - Console.WriteLine("path " + descendant.Id + " = " + paths[descendant.Id]); - descendant.Level += levelDelta; - PerformMoveContentLocked(descendant, userId, trash); + // update path and level since we do not update parentId + if (paths.ContainsKey(descendant.ParentId) == false) + Console.WriteLine("oops on " + descendant.ParentId + " for " + content.Path + " " + parent?.Path); + descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id; + Console.WriteLine("path " + descendant.Id + " = " + paths[descendant.Id]); + descendant.Level += levelDelta; + PerformMoveContentLocked(descendant, userId, trash); + } } + } private void PerformMoveContentLocked(IContent content, int userId, bool? trash) @@ -1844,29 +1813,36 @@ namespace Umbraco.Core.Services.Implement if (recursive) // process descendants { - foreach (var descendant in GetDescendants(content)) + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) { - // if parent has not been copied, skip, else gets its copy id - if (idmap.TryGetValue(descendant.ParentId, out parentId) == false) continue; + var descendants = GetPagedDescendants(content.Id, page++, pageSize, out total); + foreach (var descendant in descendants) + { + // if parent has not been copied, skip, else gets its copy id + if (idmap.TryGetValue(descendant.ParentId, out parentId) == false) continue; - var descendantCopy = descendant.DeepCloneWithResetIdentities(); - descendantCopy.ParentId = parentId; + var descendantCopy = descendant.DeepCloneWithResetIdentities(); + descendantCopy.ParentId = parentId; - if (scope.Events.DispatchCancelable(Copying, this, new CopyEventArgs(descendant, descendantCopy, parentId))) - continue; + if (scope.Events.DispatchCancelable(Copying, this, new CopyEventArgs(descendant, descendantCopy, parentId))) + continue; - // a copy is not published (but not really unpublishing either) - // update the create author and last edit author - if (descendantCopy.Published) - ((Content) descendantCopy).Published = false; - descendantCopy.CreatorId = userId; - descendantCopy.WriterId = userId; + // a copy is not published (but not really unpublishing either) + // update the create author and last edit author + if (descendantCopy.Published) + ((Content)descendantCopy).Published = false; + descendantCopy.CreatorId = userId; + descendantCopy.WriterId = userId; - // save and flush (see above) - _documentRepository.Save(descendantCopy); + // save and flush (see above) + _documentRepository.Save(descendantCopy); - copies.Add(Tuple.Create(descendant, descendantCopy)); - idmap[descendant.Id] = descendantCopy.Id; + copies.Add(Tuple.Create(descendant, descendantCopy)); + idmap[descendant.Id] = descendantCopy.Id; + } } } diff --git a/src/Umbraco.Tests/Integration/ContentEventsTests.cs b/src/Umbraco.Tests/Integration/ContentEventsTests.cs index 4ca63e9e96..655d44e86c 100644 --- a/src/Umbraco.Tests/Integration/ContentEventsTests.cs +++ b/src/Umbraco.Tests/Integration/ContentEventsTests.cs @@ -459,7 +459,7 @@ namespace Umbraco.Tests.Integration #region Utils private IEnumerable Children(IContent content) - => ServiceContext.ContentService.GetChildren(content.Id); + => ServiceContext.ContentService.GetPagedChildren(content.Id, 0, int.MaxValue, out var total); #endregion diff --git a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs index 21a75b2e24..b1ebee108a 100644 --- a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs +++ b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs @@ -125,10 +125,6 @@ namespace Umbraco.Tests.Persistence.NPocoTests contentService.GetByLevel(2); - contentService.GetChildren(id1); - - contentService.GetDescendants(id2); - contentService.GetVersions(id3); contentService.GetRootContent(); diff --git a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs index 28021f1e22..4326eee273 100644 --- a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs +++ b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs @@ -105,7 +105,7 @@ namespace Umbraco.Tests.Services total.AddRange(ServiceContext.ContentService.GetRootContent()); foreach (var content in total.ToArray()) { - total.AddRange(ServiceContext.ContentService.GetDescendants(content)); + total.AddRange(ServiceContext.ContentService.GetPagedDescendants(content.Id, 0, int.MaxValue, out var _)); } TestProfiler.Disable(); Current.Logger.Info("Returned " + total.Count + " items"); diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 5b06037f90..952c8f9dc1 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1038,37 +1038,6 @@ namespace Umbraco.Tests.Services Assert.That(contents.Count(), Is.GreaterThanOrEqualTo(2)); } - [Test] - public void Can_Get_Children_Of_Content_Id() - { - // Arrange - var contentService = ServiceContext.ContentService; - - // Act - var contents = contentService.GetChildren(NodeDto.NodeIdSeed + 2).ToList(); - - // Assert - Assert.That(contents, Is.Not.Null); - Assert.That(contents.Any(), Is.True); - Assert.That(contents.Count(), Is.GreaterThanOrEqualTo(2)); - } - - [Test] - public void Can_Get_Descendents_Of_Content() - { - // Arrange - var contentService = ServiceContext.ContentService; - var hierarchy = CreateContentHierarchy(); - contentService.Save(hierarchy, 0); - - // Act - var contents = contentService.GetDescendants(NodeDto.NodeIdSeed + 2).ToList(); - - // Assert - Assert.That(contents, Is.Not.Null); - Assert.That(contents.Any(), Is.True); - Assert.That(contents.Count(), Is.EqualTo(52)); - } [Test] public void Can_Get_All_Versions_Of_Content() @@ -1476,8 +1445,16 @@ namespace Umbraco.Tests.Services var parent = contentService.GetById(parentId); Console.WriteLine(" " + parent.Id); - foreach (var x in contentService.GetDescendants(parent)) - Console.WriteLine(" ".Substring(0, x.Level) + x.Id); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) + { + var descendants = contentService.GetPagedDescendants(parent.Id, page++, pageSize, out total); + foreach (var x in descendants) + Console.WriteLine(" ".Substring(0, x.Level) + x.Id); + } + Console.WriteLine(); // publish parent & its branch @@ -1492,7 +1469,7 @@ namespace Umbraco.Tests.Services Assert.IsTrue(parentPublished.All(x => x.Success)); Assert.IsTrue(parent.Published); - var children = contentService.GetChildren(parentId); + var children = contentService.GetPagedChildren(parentId, 0, 500, out var totalChildren); //we only want the first so page size, etc.. is abitrary // children are published including ... that was released 5 mins ago Assert.IsTrue(children.First(x => x.Id == NodeDto.NodeIdSeed + 4).Published); @@ -1785,7 +1762,13 @@ namespace Umbraco.Tests.Services contentService.Save(subsubpage, 0); var content = contentService.GetById(NodeDto.NodeIdSeed + 2); - var descendants = contentService.GetDescendants(content).ToList(); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + var descendants = new List(); + while(page * pageSize < total) + descendants.AddRange(contentService.GetPagedDescendants(content.Id, page++, pageSize, out total)); + Assert.AreNotEqual(-20, content.ParentId); Assert.IsFalse(content.Trashed); Assert.AreEqual(3, descendants.Count); @@ -1793,7 +1776,11 @@ namespace Umbraco.Tests.Services Assert.IsFalse(descendants.Any(x => x.Trashed)); contentService.MoveToRecycleBin(content, 0); - descendants = contentService.GetDescendants(content).ToList(); + + descendants.Clear(); + page = 0; + while (page * pageSize < total) + descendants.AddRange(contentService.GetPagedDescendants(content.Id, page++, pageSize, out total)); Assert.AreEqual(-20, content.ParentId); Assert.IsTrue(content.Trashed); @@ -1883,8 +1870,13 @@ namespace Umbraco.Tests.Services ServiceContext.ContentService.Save(childPage3); //Verify that the children have the inherited permissions - var descendants = ServiceContext.ContentService.GetDescendants(parentPage).ToArray(); - Assert.AreEqual(3, descendants.Length); + var descendants = new List(); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) + descendants.AddRange(ServiceContext.ContentService.GetPagedDescendants(parentPage.Id, page++, pageSize, out total)); + Assert.AreEqual(3, descendants.Count); foreach (var descendant in descendants) { @@ -1902,8 +1894,11 @@ namespace Umbraco.Tests.Services //Now copy, what should happen is the child pages will now have permissions inherited from the new parent var copy = ServiceContext.ContentService.Copy(childPage1, parentPage2.Id, false, true); - descendants = ServiceContext.ContentService.GetDescendants(parentPage2).ToArray(); - Assert.AreEqual(3, descendants.Length); + descendants.Clear(); + page = 0; + while (page * pageSize < total) + descendants.AddRange(ServiceContext.ContentService.GetPagedDescendants(parentPage2.Id, page++, pageSize, out total)); + Assert.AreEqual(3, descendants.Count); foreach (var descendant in descendants) { @@ -2035,7 +2030,7 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; var temp = contentService.GetById(NodeDto.NodeIdSeed + 2); Assert.AreEqual("Home", temp.Name); - Assert.AreEqual(2, temp.Children(contentService).Count()); + Assert.AreEqual(2, contentService.CountChildren(temp.Id)); // Act var copy = contentService.Copy(temp, temp.ParentId, false, true, 0); @@ -2045,10 +2040,10 @@ namespace Umbraco.Tests.Services Assert.That(copy, Is.Not.Null); Assert.That(copy.Id, Is.Not.EqualTo(content.Id)); Assert.AreNotSame(content, copy); - Assert.AreEqual(2, copy.Children(contentService).Count()); + Assert.AreEqual(2, contentService.CountChildren(copy.Id)); var child = contentService.GetById(NodeDto.NodeIdSeed + 3); - var childCopy = copy.Children(contentService).First(); + var childCopy = contentService.GetPagedChildren(copy.Id, 0, 500, out var total).First(); Assert.AreEqual(childCopy.Name, child.Name); Assert.AreNotEqual(childCopy.Id, child.Id); Assert.AreNotEqual(childCopy.Key, child.Key); @@ -2061,7 +2056,7 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; var temp = contentService.GetById(NodeDto.NodeIdSeed + 2); Assert.AreEqual("Home", temp.Name); - Assert.AreEqual(2, temp.Children(contentService).Count()); + Assert.AreEqual(2, contentService.CountChildren(temp.Id)); // Act var copy = contentService.Copy(temp, temp.ParentId, false, false, 0); @@ -2071,7 +2066,7 @@ namespace Umbraco.Tests.Services Assert.That(copy, Is.Not.Null); Assert.That(copy.Id, Is.Not.EqualTo(content.Id)); Assert.AreNotSame(content, copy); - Assert.AreEqual(0, copy.Children(contentService).Count()); + Assert.AreEqual(0, contentService.CountChildren(copy.Id)); } [Test] diff --git a/src/Umbraco.Web.UI/Umbraco/dialogs/ChangeDocType.aspx.cs b/src/Umbraco.Web.UI/Umbraco/dialogs/ChangeDocType.aspx.cs index 2297ea1aa7..8111410edd 100644 --- a/src/Umbraco.Web.UI/Umbraco/dialogs/ChangeDocType.aspx.cs +++ b/src/Umbraco.Web.UI/Umbraco/dialogs/ChangeDocType.aspx.cs @@ -113,7 +113,8 @@ namespace Umbraco.Web.UI.Umbraco.Dialogs private IEnumerable RemoveInvalidByChildrenDocumentTypesFromAlternatives(IEnumerable documentTypes) { - var docTypeIdsOfChildren = _content.Children(Services.ContentService) + //fixme Should do proper paging here ... when this is refactored we will + var docTypeIdsOfChildren = Services.ContentService.GetPagedChildren(_content.Id, 0, int.MaxValue, out var total) .Select(x => x.ContentType.Id) .Distinct() .ToList(); diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 9681a79ed1..e5d9e8fc3c 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -456,7 +456,7 @@ namespace Umbraco.Web.Editors public PagedResult> GetChildren( int id, string includeProperties, - int pageNumber = 0, //TODO: This should be '1' as it's not the index + int pageNumber = 0, int pageSize = 0, string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, @@ -483,7 +483,8 @@ namespace Umbraco.Web.Editors } else { - children = Services.ContentService.GetChildren(id).ToList(); + //better to not use this without paging where possible, currently only the sort dialog does + children = Services.ContentService.GetPagedChildren(id, 0, int.MaxValue, out var total).ToList(); totalChildren = children.Count; } From 40eafe57b1f489266e4f570aea0887b067f12b54 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 31 Oct 2018 22:38:58 +1100 Subject: [PATCH 05/18] Fixes tests --- src/Umbraco.Core/Services/IContentService.cs | 2 +- .../Services/Implement/ContentService.cs | 30 ++++- .../Integration/ContentEventsTests.cs | 113 +++++++++--------- .../Services/ContentServiceTests.cs | 23 ++-- 4 files changed, 93 insertions(+), 75 deletions(-) diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index b821ae1eb0..c6efe926c2 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -193,7 +193,7 @@ namespace Umbraco.Core.Services /// The ordering direction. /// Search text filter. IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = ""); + string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = ""); /// /// Gets descendant documents of a given parent. diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 1f28988fa6..d2a74ba37e 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -593,8 +593,6 @@ namespace Umbraco.Core.Services.Implement { scope.ReadLock(Constants.Locks.ContentTree); - var query = Query(); - //if the id is System Root, then just get all if (id != Constants.System.Root) { @@ -604,8 +602,24 @@ namespace Umbraco.Core.Services.Implement totalChildren = 0; return Enumerable.Empty(); } - query.Where(x => x.Path.SqlStartsWith($"{contentPath[0].Path},", TextColumnType.NVarchar)); + return GetPagedDescendants(contentPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); } + return GetPagedDescendants(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + } + } + + private IEnumerable GetPagedDescendants(string contentPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + { + if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); + if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + scope.ReadLock(Constants.Locks.ContentTree); + + var query = Query(); + if (!contentPath.IsNullOrWhiteSpace()) + query.Where(x => x.Path.SqlStartsWith($"{contentPath},", TextColumnType.NVarchar)); return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); } @@ -794,7 +808,7 @@ namespace Umbraco.Core.Services.Implement var langs = string.Join(", ", _languageRepository.GetMany() .Where(x => culturesChanging.InvariantContains(x.IsoCode)) .Select(x => x.CultureName)); - Audit(AuditType.SaveVariant, userId, content.Id, $"Saved languagues: {langs}", langs); + Audit(AuditType.SaveVariant, userId, content.Id, $"Saved languages: {langs}", langs); } else Audit(AuditType.Save, userId, content.Id); @@ -1125,7 +1139,7 @@ namespace Umbraco.Core.Services.Implement var langs = string.Join(", ", _languageRepository.GetMany() .Where(x => culturesChanging.InvariantContains(x.IsoCode)) .Select(x => x.CultureName)); - Audit(AuditType.PublishVariant, userId, content.Id, $"Published languagues: {langs}", langs); + Audit(AuditType.PublishVariant, userId, content.Id, $"Published languages: {langs}", langs); } else Audit(AuditType.Publish, userId, content.Id); @@ -1650,6 +1664,9 @@ namespace Umbraco.Core.Services.Implement moves.Add(Tuple.Create(content, content.Path)); // capture original path + //need to store the original path to lookup descendants based on it below + var originalPath = content.Path; + // these will be updated by the repo because we changed parentId //content.Path = (parent == null ? "-1" : parent.Path) + "," + content.Id; //content.SortOrder = ((ContentRepository) repository).NextChildSortOrder(parentId); @@ -1666,7 +1683,7 @@ namespace Umbraco.Core.Services.Implement var total = long.MaxValue; while(page * pageSize < total) { - var descendants = GetPagedDescendants(content.Id, page++, pageSize, out total); + var descendants = GetPagedDescendants(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null); foreach (var descendant in descendants) { moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path @@ -1685,6 +1702,7 @@ namespace Umbraco.Core.Services.Implement private void PerformMoveContentLocked(IContent content, int userId, bool? trash) { + //fixme no casting if (trash.HasValue) ((ContentBase) content).Trashed = trash.Value; content.WriterId = userId; _documentRepository.Save(content); diff --git a/src/Umbraco.Tests/Integration/ContentEventsTests.cs b/src/Umbraco.Tests/Integration/ContentEventsTests.cs index 655d44e86c..af188c6a09 100644 --- a/src/Umbraco.Tests/Integration/ContentEventsTests.cs +++ b/src/Umbraco.Tests/Integration/ContentEventsTests.cs @@ -846,22 +846,18 @@ namespace Umbraco.Tests.Integration // force:true => all nodes are republished, refreshing all nodes - but only with changes - published w/out changes are not repub Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.u+p", _events[i++].ToString()); - //Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p+p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u+p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p+p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u+p", _events[i++].ToString()); - - // remember: ordered by level, sortOrder //Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p+p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u+p", _events[i++].ToString()); - //Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p+p", _events[i++].ToString()); - //Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u+p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u+p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u+p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p+p", _events[i++].ToString()); + //Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u+p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u+p", _events[i++].ToString()); + //Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[1].Id}.u+p", _events[i++].ToString()); - Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); // repub content1 } @@ -1073,17 +1069,18 @@ namespace Umbraco.Tests.Integration Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); + m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i].ToString()); } @@ -1706,16 +1703,16 @@ namespace Umbraco.Tests.Integration var content5C = Children(content1C[3]).ToArray(); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); @@ -1759,16 +1756,16 @@ namespace Umbraco.Tests.Integration var content5C = Children(content1C[3]).ToArray(); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); @@ -1816,16 +1813,16 @@ namespace Umbraco.Tests.Integration var content5C = Children(content1C[3]).ToArray(); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); @@ -1871,16 +1868,16 @@ namespace Umbraco.Tests.Integration var content5C = Children(content1C[3]).ToArray(); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); @@ -1925,16 +1922,16 @@ namespace Umbraco.Tests.Integration var content5C = Children(content1C[3]).ToArray(); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); @@ -1984,16 +1981,16 @@ namespace Umbraco.Tests.Integration var content5C = Children(content1C[3]).ToArray(); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); @@ -2098,16 +2095,16 @@ namespace Umbraco.Tests.Integration var m = 0; Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy.Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[2].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy2C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy4C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy5C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[2].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy4C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy5C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{copy5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{copy.Id}", _events[i].ToString()); diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 952c8f9dc1..1f99bd1127 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -35,7 +35,10 @@ namespace Umbraco.Tests.Services /// as well as configuration. /// [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true, WithApplication = true)] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, + PublishedRepositoryEvents = true, + WithApplication = true, + Logger = UmbracoTestOptions.Logger.Console)] public class ContentServiceTests : TestWithSomeContentBase { //TODO Add test to verify there is only ONE newest document/content in {Constants.DatabaseSchema.Tables.Document} table after updating. @@ -1209,7 +1212,7 @@ namespace Umbraco.Tests.Services { // Arrange - var langUk = new Language("en-UK") { IsDefault = true }; + var langUk = new Language("en-GB") { IsDefault = true }; var langFr = new Language("fr-FR"); ServiceContext.LocalizationService.Save(langFr); @@ -1262,7 +1265,7 @@ namespace Umbraco.Tests.Services { // Arrange - var langUk = new Language("en-UK") { IsDefault = true }; + var langUk = new Language("en-GB") { IsDefault = true }; var langFr = new Language("fr-FR"); ServiceContext.LocalizationService.Save(langFr); @@ -1278,7 +1281,7 @@ namespace Umbraco.Tests.Services var published = ServiceContext.ContentService.SavePublishing(content); //audit log will only show that french was published var lastLog = ServiceContext.AuditService.GetLogs(content.Id).Last(); - Assert.AreEqual($"Published cultures: fr-fr", lastLog.Comment); + Assert.AreEqual($"Published languages: French (France)", lastLog.Comment); //re-get content = ServiceContext.ContentService.GetById(content.Id); @@ -1287,7 +1290,7 @@ namespace Umbraco.Tests.Services published = ServiceContext.ContentService.SavePublishing(content); //audit log will only show that english was published lastLog = ServiceContext.AuditService.GetLogs(content.Id).Last(); - Assert.AreEqual($"Published cultures: en-uk", lastLog.Comment); + Assert.AreEqual($"Published languages: English (United Kingdom)", lastLog.Comment); } [Test] @@ -2775,7 +2778,7 @@ namespace Umbraco.Tests.Services { var languageService = ServiceContext.LocalizationService; - var langUk = new Language("en-UK") { IsDefault = true }; + var langUk = new Language("en-GB") { IsDefault = true }; var langFr = new Language("fr-FR"); languageService.Save(langFr); @@ -2810,7 +2813,7 @@ namespace Umbraco.Tests.Services { var languageService = ServiceContext.LocalizationService; - var langUk = new Language("en-UK") { IsDefault = true }; + var langUk = new Language("en-GB") { IsDefault = true }; var langFr = new Language("fr-FR"); languageService.Save(langFr); @@ -2847,7 +2850,7 @@ namespace Umbraco.Tests.Services { var languageService = ServiceContext.LocalizationService; - var langUk = new Language("en-UK") { IsDefault = true }; + var langUk = new Language("en-GB") { IsDefault = true }; var langFr = new Language("fr-FR"); var langDa = new Language("da-DK"); @@ -2939,7 +2942,7 @@ namespace Umbraco.Tests.Services private void WriteList(List list) { foreach (var content in list) - Console.WriteLine("[{0}] {1} {2} {3} {4}", content.Id, content.Name, content.GetCultureName("en-UK"), content.GetCultureName("fr-FR"), content.GetCultureName("da-DK")); + Console.WriteLine("[{0}] {1} {2} {3} {4}", content.Id, content.Name, content.GetCultureName("en-GB"), content.GetCultureName("fr-FR"), content.GetCultureName("da-DK")); Console.WriteLine("-"); } @@ -2951,7 +2954,7 @@ namespace Umbraco.Tests.Services //var langFr = new Language("fr-FR") { IsDefaultVariantLanguage = true }; var langXx = new Language("pt-PT") { IsDefault = true }; var langFr = new Language("fr-FR"); - var langUk = new Language("en-UK"); + var langUk = new Language("en-GB"); var langDe = new Language("de-DE"); languageService.Save(langFr); From 25dcc3f2c185d84fe9b08f0e6c887f0a43ad3079 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 31 Oct 2018 23:11:37 +1100 Subject: [PATCH 06/18] Removes GetChildren and GetDescendants from IMediaService, paging is now required --- src/Umbraco.Core/ContentExtensions.cs | 73 ----------- .../Services/EntityXmlSerializer.cs | 28 +++-- src/Umbraco.Core/Services/IMediaService.cs | 23 +--- .../Services/Implement/ContentService.cs | 83 ++++--------- .../Services/Implement/MediaService.cs | 117 ++++++------------ src/Umbraco.Web/Editors/ContentController.cs | 2 +- src/Umbraco.Web/Editors/MediaController.cs | 29 ++++- .../Routing/UrlProviderExtensions.cs | 2 +- src/Umbraco.Web/Search/ExamineComponent.cs | 12 +- 9 files changed, 112 insertions(+), 257 deletions(-) diff --git a/src/Umbraco.Core/ContentExtensions.cs b/src/Umbraco.Core/ContentExtensions.cs index f0fa6cdf17..910717304c 100644 --- a/src/Umbraco.Core/ContentExtensions.cs +++ b/src/Umbraco.Core/ContentExtensions.cs @@ -37,79 +37,6 @@ namespace Umbraco.Core return dirty.WasPropertyDirty("Published") && entity.Published; } - /// - /// Returns a list of the current contents ancestors, not including the content itself. - /// - /// Current content - /// - /// An enumerable list of objects - public static IEnumerable Ancestors(this IContent content, IContentService contentService) - { - return contentService.GetAncestors(content); - } - - /// - /// Returns the parent of the current content. - /// - /// Current content - /// - /// An object - public static IContent Parent(this IContent content, IContentService contentService) - { - return contentService.GetById(content.ParentId); - } - - #endregion - - #region IMedia - - /// - /// Returns a list of the current medias ancestors, not including the media itself. - /// - /// Current media - /// - /// An enumerable list of objects - public static IEnumerable Ancestors(this IMedia media, IMediaService mediaService) - { - return mediaService.GetAncestors(media); - } - - - /// - /// Returns a list of the current medias children. - /// - /// Current media - /// - /// An enumerable list of objects - public static IEnumerable Children(this IMedia media, IMediaService mediaService) - { - return mediaService.GetChildren(media.Id); - } - - - /// - /// Returns a list of the current medias descendants, not including the media itself. - /// - /// Current media - /// - /// An enumerable list of objects - public static IEnumerable Descendants(this IMedia media, IMediaService mediaService) - { - return mediaService.GetDescendants(media); - } - - - /// - /// Returns the parent of the current media. - /// - /// Current media - /// - /// An object - public static IMedia Parent(this IMedia media, IMediaService mediaService) - { - return mediaService.GetById(media.ParentId); - } - #endregion /// diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/EntityXmlSerializer.cs index ebb35a43ed..5b64584dc6 100644 --- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/EntityXmlSerializer.cs @@ -109,9 +109,14 @@ namespace Umbraco.Core.Services if (withDescendants) { - var descendants = mediaService.GetDescendants(media).ToArray(); - var currentChildren = descendants.Where(x => x.ParentId == media.Id); - SerializeDescendants(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, descendants, currentChildren, xml); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var children = mediaService.GetPagedChildren(media.Id, page++, pageSize, out total); + SerializeChildren(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, children, xml); + } } return xml; @@ -478,7 +483,7 @@ namespace Umbraco.Core.Services } // exports an IMedia item descendants. - private static void SerializeDescendants(IMediaService mediaService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable urlSegmentProviders, IMedia[] originalDescendants, IEnumerable children, XElement xml) + private static void SerializeChildren(IMediaService mediaService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable urlSegmentProviders, IEnumerable children, XElement xml) { foreach (var child in children) { @@ -486,12 +491,15 @@ namespace Umbraco.Core.Services var childXml = Serialize(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, child); xml.Add(childXml); - // capture id (out of closure) and get the grandChildren (children of the child) - var parentId = child.Id; - var grandChildren = originalDescendants.Where(x => x.ParentId == parentId); - - // recurse - SerializeDescendants(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, originalDescendants, grandChildren, childXml); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var grandChildren = mediaService.GetPagedChildren(child.Id, page++, pageSize, out total); + // recurse + SerializeChildren(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, grandChildren, childXml); + } } } } diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 31c2e74fd4..6976a09b76 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -78,13 +78,6 @@ namespace Umbraco.Core.Services /// IMedia GetById(int id); - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// An Enumerable list of objects - IEnumerable GetChildren(int id); - /// /// Gets a collection of objects by Parent Id /// @@ -158,14 +151,7 @@ namespace Umbraco.Core.Services /// An Enumerable list of objects IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter); - - /// - /// Gets descendants of a object by its Id - /// - /// Id of the Parent to retrieve descendants from - /// An Enumerable flat list of objects - IEnumerable GetDescendants(int id); - + /// /// Gets a collection of objects by the Id of the /// @@ -321,13 +307,6 @@ namespace Umbraco.Core.Services /// An Enumerable list of objects IEnumerable GetAncestors(IMedia media); - /// - /// Gets descendants of a object by its Id - /// - /// The Parent object to retrieve descendants from - /// An Enumerable flat list of objects - IEnumerable GetDescendants(IMedia media); - /// /// Gets the parent of the current media as an item. /// diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index d2a74ba37e..2c8e38777b 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -586,9 +586,6 @@ namespace Umbraco.Core.Services.Implement /// public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) { - if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); - if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.ContentTree); @@ -602,27 +599,22 @@ namespace Umbraco.Core.Services.Implement totalChildren = 0; return Enumerable.Empty(); } - return GetPagedDescendants(contentPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(contentPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); } - return GetPagedDescendants(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); } } - private IEnumerable GetPagedDescendants(string contentPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + private IEnumerable GetPagedDescendantsLocked(string contentPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); + var query = Query(); + if (!contentPath.IsNullOrWhiteSpace()) + query.Where(x => x.Path.SqlStartsWith($"{contentPath},", TextColumnType.NVarchar)); - var query = Query(); - if (!contentPath.IsNullOrWhiteSpace()) - query.Where(x => x.Path.SqlStartsWith($"{contentPath},", TextColumnType.NVarchar)); - - return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); - } + return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); } /// @@ -1403,50 +1395,8 @@ namespace Umbraco.Core.Services.Implement private void DeleteLocked(IScope scope, IContent content) { - //TODO: Test this - - // then recursively delete descendants, bottom-up - // just repository.Delete + an event - var stack = new Stack(); - stack.Push(content); - var level = 1; - while (stack.Count > 0) + void DoDelete(IContent c) { - var c = stack.Peek(); - if (c.Level == level) - { - var hasChildren = true; - while (hasChildren) - { - IContent last = null; - const int pageSize = 500; - var page = 0; - var total = long.MaxValue; - var countChildren = 0; - - //get all children of c in pages - while (page * pageSize < total) - { - var children = GetPagedChildren(c.Id, page++, pageSize, out total); - - foreach (var ci in children) - { - countChildren++; - stack.Push(ci); - last = ci; - } - - if (countChildren == 0) - hasChildren = false; //exit, there are no children left to load - else - c = last; //recurse with the last child - } - } - } - - c = stack.Pop(); - level = c.Level; - _documentRepository.Delete(c); var args = new DeleteEventArgs(c, false); // raise event & get flagged files scope.Events.Dispatch(Deleted, this, args, nameof(Deleted)); @@ -1455,6 +1405,18 @@ namespace Umbraco.Core.Services.Implement _mediaFileSystem.DeleteFiles(args.MediaFilesToDelete, // remove flagged files (file, e) => Logger.Error(e, "An error occurred while deleting file attached to nodes: {File}", file)); } + + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + //get descendants - ordered from deepest to shallowest + var descendants = GetPagedDescendants(content.Id, page, pageSize, out total, "Path", Direction.Descending); + foreach (var c in descendants) + DoDelete(c); + } + DoDelete(content); } //TODO: @@ -1683,16 +1645,13 @@ namespace Umbraco.Core.Services.Implement var total = long.MaxValue; while(page * pageSize < total) { - var descendants = GetPagedDescendants(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null); + var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null); foreach (var descendant in descendants) { moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path // update path and level since we do not update parentId - if (paths.ContainsKey(descendant.ParentId) == false) - Console.WriteLine("oops on " + descendant.ParentId + " for " + content.Path + " " + parent?.Path); descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id; - Console.WriteLine("path " + descendant.Id + " = " + paths[descendant.Id]); descendant.Level += levelDelta; PerformMoveContentLocked(descendant, userId, trash); } diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index da04f41e18..25aa02befa 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -460,21 +460,6 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// An Enumerable list of objects - public IEnumerable GetChildren(int id) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.MediaTree); - var query = Query().Where(x => x.ParentId == id); - return _mediaRepository.Get(query).OrderBy(x => x.SortOrder); - } - } - /// /// Gets a collection of objects by Parent Id /// @@ -594,15 +579,10 @@ namespace Umbraco.Core.Services.Implement /// An Enumerable list of objects public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) { - if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); - if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.MediaTree); - var query = Query(); - //if the id is System Root, then just get all if (id != Constants.System.Root) { @@ -612,47 +592,22 @@ namespace Umbraco.Core.Services.Implement totalChildren = 0; return Enumerable.Empty(); } - query.Where(x => x.Path.SqlStartsWith(mediaPath[0].Path + ",", TextColumnType.NVarchar)); + return GetPagedDescendantsLocked(mediaPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); } - - return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); + return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); } } - /// - /// Gets descendants of a object by its Id - /// - /// Id of the Parent to retrieve descendants from - /// An Enumerable flat list of objects - public IEnumerable GetDescendants(int id) + private IEnumerable GetPagedDescendantsLocked(string mediaPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.MediaTree); - var media = GetById(id); - if (media == null) - return Enumerable.Empty(); + if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); + if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - var pathMatch = media.Path + ","; - var query = Query().Where(x => x.Id != media.Id && x.Path.StartsWith(pathMatch)); - return _mediaRepository.Get(query); - } - } + var query = Query(); + if (!mediaPath.IsNullOrWhiteSpace()) + query.Where(x => x.Path.SqlStartsWith(mediaPath + ",", TextColumnType.NVarchar)); - /// - /// Gets descendants of a object by its Id - /// - /// The Parent object to retrieve descendants from - /// An Enumerable flat list of objects - public IEnumerable GetDescendants(IMedia media) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.MediaTree); - var pathMatch = media.Path + ","; - var query = Query().Where(x => x.Id != media.Id && x.Path.StartsWith(pathMatch)); - return _mediaRepository.Get(query); - } + return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); } /// @@ -865,25 +820,8 @@ namespace Umbraco.Core.Services.Implement private void DeleteLocked(IScope scope, IMedia media) { - // then recursively delete descendants, bottom-up - // just repository.Delete + an event - var stack = new Stack(); - stack.Push(media); - var level = 1; - while (stack.Count > 0) + void DoDelete(IMedia c) { - var c = stack.Peek(); - IMedia[] cc; - if (c.Level == level) - while ((cc = c.Children(this).ToArray()).Length > 0) - { - foreach (var ci in cc) - stack.Push(ci); - c = cc[cc.Length - 1]; - } - c = stack.Pop(); - level = c.Level; - _mediaRepository.Delete(c); var args = new DeleteEventArgs(c, false); // raise event & get flagged files scope.Events.Dispatch(Deleted, this, args); @@ -891,6 +829,18 @@ namespace Umbraco.Core.Services.Implement _mediaFileSystem.DeleteFiles(args.MediaFilesToDelete, // remove flagged files (file, e) => Logger.Error(e, "An error occurred while deleting file attached to nodes: {File}", file)); } + + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) + { + //get descendants - ordered from deepest to shallowest + var descendants = GetPagedDescendants(media.Id, page, pageSize, out total, "Path", Direction.Descending); + foreach (var c in descendants) + DoDelete(c); + } + DoDelete(media); } //TODO: @@ -1100,8 +1050,8 @@ namespace Umbraco.Core.Services.Implement moves.Add(Tuple.Create(media, media.Path)); // capture original path - // get before moving, in case uow is immediate - var descendants = GetDescendants(media); + //need to store the original path to lookup descendants based on it below + var originalPath = media.Path; // these will be updated by the repo because we changed parentId //media.Path = (parent == null ? "-1" : parent.Path) + "," + media.Id; @@ -1114,14 +1064,21 @@ namespace Umbraco.Core.Services.Implement //paths[media.Id] = media.Path; paths[media.Id] = (parent == null ? (parentId == Constants.System.RecycleBinMedia ? "-1,-21" : "-1") : parent.Path) + "," + media.Id; - foreach (var descendant in descendants) + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) { - moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path + var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null); + foreach (var descendant in descendants) + { + moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path - // update path and level since we do not update parentId - descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id; - descendant.Level += levelDelta; - PerformMoveMediaLocked(descendant, userId, trash); + // update path and level since we do not update parentId + descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id; + descendant.Level += levelDelta; + PerformMoveMediaLocked(descendant, userId, trash); + } } } diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index e5d9e8fc3c..ed87f770ca 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -1318,7 +1318,7 @@ namespace Umbraco.Web.Editors xnames.Add(xcontent.Name); if (xcontent.ParentId < -1) xnames.Add("Recycle Bin"); - xcontent = xcontent.Parent(Services.ContentService); + xcontent = Services.ContentService.GetParent(xcontent); } xnames.Reverse(); domainModel.Other = "/" + string.Join("/", xnames); diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index dc744ea361..3dfd435d8f 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -271,7 +271,7 @@ namespace Umbraco.Web.Editors // else proceed as usual long totalChildren; - IMedia[] children; + List children; if (pageNumber > 0 && pageSize > 0) { IQuery queryFilter = null; @@ -287,12 +287,13 @@ namespace Umbraco.Web.Editors id, (pageNumber - 1), pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, - queryFilter).ToArray(); + queryFilter).ToList(); } else { - children = Services.MediaService.GetChildren(id).ToArray(); - totalChildren = children.Length; + //better to not use this without paging where possible, currently only the sort dialog does + children = Services.MediaService.GetPagedChildren(id, 0, int.MaxValue, out var total).ToList(); + totalChildren = children.Count; } if (totalChildren == 0) @@ -663,7 +664,8 @@ namespace Umbraco.Web.Editors " returned null"); //look for matching folder - folderMediaItem = mediaRoot.Children(Services.MediaService).FirstOrDefault(x => x.Name == folderName && x.ContentType.Alias == Constants.Conventions.MediaTypes.Folder); + folderMediaItem = FindInChildren(mediaRoot.Id, folderName, Constants.Conventions.MediaTypes.Folder); + if (folderMediaItem == null) { //if null, create a folder @@ -753,6 +755,23 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(HttpStatusCode.OK, tempFiles); } + private IMedia FindInChildren(int mediaId, string nameToFind, string contentTypeAlias) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var children = Services.MediaService.GetPagedChildren(mediaId, page, pageSize, out total); + foreach (var c in children) + { + if (c.Name == nameToFind && c.ContentType.Alias == contentTypeAlias) + return c; + } + } + return null; + } + /// /// Given a parent id which could be a GUID, UDI or an INT, this will resolve the INT /// diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs index 2be0dce096..14b9c0a465 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs @@ -129,7 +129,7 @@ namespace Umbraco.Web.Routing var parent = content; do { - parent = parent.ParentId > 0 ? parent.Parent(contentService) : null; + parent = parent.ParentId > 0 ? contentService.GetParent(parent) : null; } while (parent != null && parent.Published && (!parent.ContentType.VariesByCulture() || parent.IsCulturePublished(culture))); diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index b1a302785d..05be969f92 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -304,10 +304,16 @@ namespace Umbraco.Web.Search // branch if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) { - var descendants = mediaService.GetDescendants(media); - foreach (var descendant in descendants) + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) { - ReIndexForMedia(descendant, descendant.Trashed == false); + var descendants = mediaService.GetPagedDescendants(media.Id, page++, pageSize, out total); + foreach (var descendant in descendants) + { + ReIndexForMedia(descendant, descendant.Trashed == false); + } } } } From 4d594e17aafa01a07a105a038f6431b50195f15e Mon Sep 17 00:00:00 2001 From: elitsa Date: Wed, 31 Oct 2018 13:40:23 +0100 Subject: [PATCH 07/18] Adding a check to verify if there are any children which will resolve the problem with showing a right-pointing arrow in front of empty trees in the Settings section. --- src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs | 3 +++ src/Umbraco.Web/Trees/FileSystemTreeController.cs | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs b/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs index 5b45888dca..f3be39b6f8 100644 --- a/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs @@ -32,6 +32,9 @@ namespace Umbraco.Web.Trees //this will load in a custom UI instead of the dashboard for the root node root.RoutePath = $"{Constants.Applications.Settings}/{Constants.Trees.ContentBlueprints}/intro"; + //check if there are any content blueprints + root.HasChildren = Services.ContentService.GetBlueprintsForContentTypes().Any(); + return root; } protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) diff --git a/src/Umbraco.Web/Trees/FileSystemTreeController.cs b/src/Umbraco.Web/Trees/FileSystemTreeController.cs index 21e81a58af..33c5c360b2 100644 --- a/src/Umbraco.Web/Trees/FileSystemTreeController.cs +++ b/src/Umbraco.Web/Trees/FileSystemTreeController.cs @@ -74,6 +74,14 @@ namespace Umbraco.Web.Trees return nodes; } + protected override TreeNode CreateRootNode(FormDataCollection queryStrings) + { + var root = base.CreateRootNode(queryStrings); + //check if there are any children + root.HasChildren = GetTreeNodes(Constants.System.Root.ToInvariantString(), queryStrings).Any(); + return root; + } + protected virtual MenuItemCollection GetMenuForRootNode(FormDataCollection queryStrings) { var menu = new MenuItemCollection(); From b8d1dd7684a5dabb3359ba8fbfee926e2ae7126d Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 00:05:17 +1100 Subject: [PATCH 08/18] Cleans up paged methods of IContentService to use the Ordering parameter for consistency --- src/Umbraco.Core/Services/IContentService.cs | 39 ++++++++++----- .../Services/Implement/ContentService.cs | 47 +++++++++++++++---- src/Umbraco.Examine/UmbracoContentIndexer.cs | 3 +- .../Services/ContentServiceTests.cs | 2 +- .../UmbracoExamine/IndexInitializer.cs | 4 +- .../UmbracoExamine/SearchTests.cs | 2 +- src/Umbraco.Web/Search/ExamineComponent.cs | 45 ++++++++++++++---- 7 files changed, 105 insertions(+), 37 deletions(-) diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index c6efe926c2..c609a7493d 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -78,17 +78,6 @@ namespace Umbraco.Core.Services /// IEnumerable GetByIds(IEnumerable ids); - /// - /// Gets paged documents of a content content - /// - /// The page number. - /// The page number. - /// The page size. - /// Total number of documents. - /// Search text filter. - /// Ordering infos. - IEnumerable GetPagedOfType(int contentType, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null); - /// /// Gets documents at a given level. /// @@ -193,7 +182,7 @@ namespace Umbraco.Core.Services /// The ordering direction. /// Search text filter. IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = ""); + string filter = null, Ordering ordering = null); /// /// Gets descendant documents of a given parent. @@ -207,7 +196,31 @@ namespace Umbraco.Core.Services /// A flag indicating whether the ordering field is a system field. /// Query filter. IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter); + IQuery filter, Ordering ordering = null); + + /// + /// Gets paged documents of a content content + /// + /// The page number. + /// The page number. + /// The page size. + /// Total number of documents. + /// Search text filter. + /// Ordering infos. + IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, + IQuery filter, Ordering ordering = null); + + /// + /// Gets paged documents for specified content types + /// + /// The page number. + /// The page number. + /// The page size. + /// Total number of documents. + /// Search text filter. + /// Ordering infos. + IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, + IQuery filter, Ordering ordering = null); /// /// Counts documents of a given document type. diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 2c8e38777b..1c3f5203f9 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -405,6 +405,7 @@ namespace Umbraco.Core.Services.Implement } } + /// public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) { if(pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); @@ -422,6 +423,24 @@ namespace Umbraco.Core.Services.Implement } } + /// + public IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) + { + if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); + if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + + if (ordering == null) + ordering = Ordering.By("sortOrder"); + + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + scope.ReadLock(Constants.Locks.ContentTree); + return _documentRepository.GetPage( + Query().Where(x => contentTypeIds.Contains(x.ContentTypeId)), + pageIndex, pageSize, out totalRecords, filter, ordering); + } + } + /// /// Gets a collection of objects by Level /// @@ -574,18 +593,24 @@ namespace Umbraco.Core.Services.Implement } /// - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") + public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, + string filter = null, Ordering ordering = null) + //string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") { var filterQuery = filter.IsNullOrWhiteSpace() ? null : Query().Where(x => x.Name.Contains(filter)); - return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filterQuery); + return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); } /// - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, + IQuery filter, Ordering ordering = null) { + if (ordering == null) + ordering = Ordering.By("Path"); + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.ContentTree); @@ -599,22 +624,26 @@ namespace Umbraco.Core.Services.Implement totalChildren = 0; return Enumerable.Empty(); } - return GetPagedDescendantsLocked(contentPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(contentPath[0].Path, pageIndex, pageSize, out totalChildren, filter, ordering); } - return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, filter, ordering); } } - private IEnumerable GetPagedDescendantsLocked(string contentPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + private IEnumerable GetPagedDescendantsLocked(string contentPath, long pageIndex, int pageSize, out long totalChildren, + IQuery filter, Ordering ordering) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + if (filter == null) throw new ArgumentNullException(nameof(filter)); + + if (ordering == null) throw new ArgumentNullException(nameof(ordering)); var query = Query(); if (!contentPath.IsNullOrWhiteSpace()) query.Where(x => x.Path.SqlStartsWith($"{contentPath},", TextColumnType.NVarchar)); - return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); + return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, ordering); } /// @@ -1412,7 +1441,7 @@ namespace Umbraco.Core.Services.Implement while (page * pageSize < total) { //get descendants - ordered from deepest to shallowest - var descendants = GetPagedDescendants(content.Id, page, pageSize, out total, "Path", Direction.Descending); + var descendants = GetPagedDescendants(content.Id, page, pageSize, out total, ordering: Ordering.By("Path", Direction.Descending)); foreach (var c in descendants) DoDelete(c); } @@ -1645,7 +1674,7 @@ namespace Umbraco.Core.Services.Implement var total = long.MaxValue; while(page * pageSize < total) { - var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null); + var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, null, Ordering.By("Path", Direction.Ascending)); foreach (var descendant in descendants) { moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index 94982c8591..fab9f226a4 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -258,7 +258,8 @@ namespace Umbraco.Examine else { //add the published filter - descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "Path", Direction.Ascending, true, _publishedQuery); + descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, + _publishedQuery, Ordering.By("Path", Direction.Ascending)); } //if specific types are declared we need to post filter them diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 1f99bd1127..c2b1cdc52d 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1718,7 +1718,7 @@ namespace Umbraco.Tests.Services // Act contentService.DeleteOfType(contentType.Id); var rootContent = contentService.GetRootContent(); - var contents = contentService.GetPagedOfType(contentType.Id, 0, int.MaxValue, out var _); + var contents = contentService.GetPagedOfType(contentType.Id, 0, int.MaxValue, out var _, null); // Assert Assert.That(rootContent.Any(), Is.False); diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index dedf04488c..b197cea5bc 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -71,11 +71,11 @@ namespace Umbraco.Tests.UmbracoExamine contentService = Mock.Of( x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny(), It.IsAny()) + It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny()) == allRecs && x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()) + It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny>(), It.IsAny()) == allRecs); } diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index 2d440b8453..768d1c735c 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -49,7 +49,7 @@ namespace Umbraco.Tests.UmbracoExamine .ToArray(); var contentService = Mock.Of( x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()) + It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny>(), It.IsAny()) == allRecs); diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 05be969f92..b5cfac7e99 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -26,6 +26,7 @@ using Umbraco.Web.Cache; using Umbraco.Web.Composing; using Umbraco.Web.PropertyEditors; using Umbraco.Examine; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Web.Search { @@ -351,21 +352,42 @@ namespace Umbraco.Web.Search const int pageSize = 500; - //Re-index all content of these types - foreach(var id in refreshedIds.Concat(otherIds).Distinct()) - { + if (refreshedIds.Count > 0 || otherIds.Count > 0) + { var page = 0; var total = long.MaxValue; while (page * pageSize < total) { - var contentToRefresh = _services.ContentService.GetPagedOfType(id, page++, pageSize, out total); - foreach(var c in contentToRefresh) + var contentToRefresh = _services.ContentService.GetPagedOfTypes( + //Re-index all content of these types + refreshedIds.Concat(otherIds).Distinct().ToArray(), + page++, pageSize, out total, null, + //order by shallowest to deepest, this allows us to check it's published state without checking every item + Ordering.By("Path", Direction.Ascending)); + + //track which Ids have their paths are published + var publishChecked = new Dictionary(); + + foreach (var c in contentToRefresh) { - //TODO: We might have to order by Path ascending or something since we're going to need to check - // contentService.IsPathPublished(content) but we don't want to make that check for every content item IContent published = null; - if (c.Published && contentService.IsPathPublished(c)) - published = c; + if (c.Published) + { + if (publishChecked.TryGetValue(c.ParentId, out var isPublished)) + { + //if the parent's published path has already been verified then this is published + if (isPublished) + published = c; + } + else + { + //nothing by parent id, so query the service and cache the result for the next child to check against + isPublished = contentService.IsPathPublished(c); + publishChecked[c.Id] = isPublished; + if (isPublished) + published = c; + } + } ReIndexForContent(c, published); } @@ -460,7 +482,10 @@ namespace Umbraco.Web.Search var total = long.MaxValue; while(page * pageSize < total) { - var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total); + var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total, + //order by shallowest to deepest, this allows us to check it's published state without checking every item + ordering: Ordering.By("Path", Direction.Ascending)); + foreach (var descendant in descendants) { published = null; From ae5bda0910e98144cfde1624ca8cbd3ad1889a5d Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 00:21:52 +1100 Subject: [PATCH 09/18] Streamlines IMediaService to be consistent with IContentService for paging and using the Ordering class --- src/Umbraco.Core/Services/IMediaService.cs | 26 ++----- .../Services/Implement/ContentService.cs | 2 - .../Services/Implement/MediaService.cs | 78 +++++++------------ .../Services/MediaServiceTests.cs | 8 +- .../UmbracoExamine/IndexInitializer.cs | 4 +- src/Umbraco.Web/Editors/MediaController.cs | 17 ++-- 6 files changed, 48 insertions(+), 87 deletions(-) diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 6976a09b76..6fa211f4f3 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -90,7 +90,7 @@ namespace Umbraco.Core.Services /// Search text filter /// An Enumerable list of objects IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, string filter = ""); + string filter = null, Ordering ordering = null); /// /// Gets a collection of objects by Parent Id @@ -105,23 +105,7 @@ namespace Umbraco.Core.Services /// /// An Enumerable list of objects IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter); - - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Flag to indicate when ordering by system field - /// Search text filter - /// A list of content type Ids to filter the list by - /// An Enumerable list of objects - IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, string filter, int[] contentTypeFilter); + IQuery filter, Ordering ordering = null); /// /// Gets a collection of objects by Parent Id @@ -135,7 +119,7 @@ namespace Umbraco.Core.Services /// Search text filter /// An Enumerable list of objects IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = ""); + string filter = null, Ordering ordering = null); /// /// Gets a collection of objects by Parent Id @@ -150,8 +134,8 @@ namespace Umbraco.Core.Services /// /// An Enumerable list of objects IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter); - + IQuery filter, Ordering ordering = null); + /// /// Gets a collection of objects by the Id of the /// diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 1c3f5203f9..debf7dcb34 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -595,7 +595,6 @@ namespace Umbraco.Core.Services.Implement /// public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string filter = null, Ordering ordering = null) - //string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") { var filterQuery = filter.IsNullOrWhiteSpace() ? null @@ -636,7 +635,6 @@ namespace Umbraco.Core.Services.Implement if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); if (filter == null) throw new ArgumentNullException(nameof(filter)); - if (ordering == null) throw new ArgumentNullException(nameof(ordering)); var query = Query(); diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index 25aa02befa..a584521c33 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -471,13 +471,14 @@ namespace Umbraco.Core.Services.Implement /// Direction to order by /// Search text filter /// An Enumerable list of objects - public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, string filter = "") + public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, + string filter = null, Ordering ordering = null) { var filterQuery = filter.IsNullOrWhiteSpace() ? null : Query().Where(x => x.Name.Contains(filter)); - return GetPagedChildren(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filterQuery); + return GetPagedChildren(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); } /// @@ -492,56 +493,21 @@ namespace Umbraco.Core.Services.Implement /// Flag to indicate when ordering by system field /// /// An Enumerable list of objects - public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, + IQuery filter, Ordering ordering = null) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + if (ordering == null) + ordering = Ordering.By("sortOrder"); + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.MediaTree); var query = Query().Where(x => x.ParentId == id); - return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); - } - } - - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Flag to indicate when ordering by system field - /// Search text filter - /// A list of content type Ids to filter the list by - /// An Enumerable list of objects - public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, string filter, int[] contentTypeFilter) - { - if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); - if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.MediaTree); - - var query = Query(); - // always check for a parent - else it will also get decendants (and then you should use the GetPagedDescendants method) - - query.Where(x => x.ParentId == id); - - if (contentTypeFilter != null && contentTypeFilter.Length > 0) - { - query.Where(x => contentTypeFilter.Contains(x.ContentTypeId)); - } - - var filterQuery = filter.IsNullOrWhiteSpace() - ? null - : Query().Where(x => x.Name.Contains(filter)); - return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filterQuery, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); + return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, ordering); } } @@ -556,13 +522,14 @@ namespace Umbraco.Core.Services.Implement /// Direction to order by /// Search text filter /// An Enumerable list of objects - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") + public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, + string filter = null, Ordering ordering = null) { var filterQuery = filter.IsNullOrWhiteSpace() ? null : Query().Where(x => x.Name.Contains(filter)); - return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filterQuery); + return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); } /// @@ -577,8 +544,12 @@ namespace Umbraco.Core.Services.Implement /// Flag to indicate when ordering by system field /// /// An Enumerable list of objects - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, + IQuery filter, Ordering ordering = null) { + if (ordering == null) + ordering = Ordering.By("Path"); + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.MediaTree); @@ -592,22 +563,25 @@ namespace Umbraco.Core.Services.Implement totalChildren = 0; return Enumerable.Empty(); } - return GetPagedDescendantsLocked(mediaPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(mediaPath[0].Path, pageIndex, pageSize, out totalChildren, filter, ordering); } - return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, filter, ordering); } } - private IEnumerable GetPagedDescendantsLocked(string mediaPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + private IEnumerable GetPagedDescendantsLocked(string mediaPath, long pageIndex, int pageSize, out long totalChildren, + IQuery filter, Ordering ordering) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + if (filter == null) throw new ArgumentNullException(nameof(filter)); + if (ordering == null) throw new ArgumentNullException(nameof(ordering)); var query = Query(); if (!mediaPath.IsNullOrWhiteSpace()) query.Where(x => x.Path.SqlStartsWith(mediaPath + ",", TextColumnType.NVarchar)); - return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); + return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, ordering); } /// @@ -836,7 +810,7 @@ namespace Umbraco.Core.Services.Implement while(page * pageSize < total) { //get descendants - ordered from deepest to shallowest - var descendants = GetPagedDescendants(media.Id, page, pageSize, out total, "Path", Direction.Descending); + var descendants = GetPagedDescendants(media.Id, page, pageSize, out total, ordering: Ordering.By("Path", Direction.Descending)); foreach (var c in descendants) DoDelete(c); } @@ -1069,7 +1043,7 @@ namespace Umbraco.Core.Services.Implement var total = long.MaxValue; while (page * pageSize < total) { - var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null); + var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, null, Ordering.By("Path", Direction.Ascending)); foreach (var descendant in descendants) { moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path diff --git a/src/Umbraco.Tests/Services/MediaServiceTests.cs b/src/Umbraco.Tests/Services/MediaServiceTests.cs index 68fd2c3e11..b9e1fee0db 100644 --- a/src/Umbraco.Tests/Services/MediaServiceTests.cs +++ b/src/Umbraco.Tests/Services/MediaServiceTests.cs @@ -69,11 +69,15 @@ namespace Umbraco.Tests.Services } long total; - var result = ServiceContext.MediaService.GetPagedChildren(-1, 0, 11, out total, "SortOrder", Direction.Ascending, true, null, new[] { mediaType1.Id, mediaType2.Id }); + var result = ServiceContext.MediaService.GetPagedChildren(-1, 0, 11, out total, + SqlContext.Query().Where(x => new[] { mediaType1.Id, mediaType2.Id }.Contains(x.ContentTypeId)), + Ordering.By("SortOrder", Direction.Ascending)); Assert.AreEqual(11, result.Count()); Assert.AreEqual(20, total); - result = ServiceContext.MediaService.GetPagedChildren(-1, 1, 11, out total, "SortOrder", Direction.Ascending, true, null, new[] { mediaType1.Id, mediaType2.Id }); + result = ServiceContext.MediaService.GetPagedChildren(-1, 1, 11, out total, + SqlContext.Query().Where(x => new[] { mediaType1.Id, mediaType2.Id }.Contains(x.ContentTypeId)), + Ordering.By("SortOrder", Direction.Ascending)); Assert.AreEqual(9, result.Count()); Assert.AreEqual(20, total); } diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index b197cea5bc..8aec854441 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -116,12 +116,12 @@ namespace Umbraco.Tests.UmbracoExamine mediaServiceMock .Setup(x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny(), It.IsAny(), It.IsAny()) + It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny(), It.IsAny()) ).Returns(() => allRecs); mediaServiceMock .Setup(x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()) + It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny>(), It.IsAny()) ).Returns(() => allRecs); //mediaServiceMock.Setup(service => service.GetPagedXmlEntries(It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs)) diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 3dfd435d8f..f61cbc5952 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -209,7 +209,10 @@ namespace Umbraco.Web.Editors } long total; - var children = Services.MediaService.GetPagedChildren(id, pageNumber - 1, pageSize, out total, "Name", Direction.Ascending, true, null, folderTypes.ToArray()); + var children = Services.MediaService.GetPagedChildren(id, pageNumber - 1, pageSize, out total, + //lookup these content types + SqlContext.Query().Where(x => folderTypes.Contains(x.ContentTypeId)), + Ordering.By("Name", Direction.Ascending)); return new PagedResult>(total, pageNumber, pageSize) { @@ -286,8 +289,8 @@ namespace Umbraco.Web.Editors .GetPagedChildren( id, (pageNumber - 1), pageSize, out totalChildren, - orderBy, orderDirection, orderBySystemField, - queryFilter).ToList(); + queryFilter, + Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)).ToList(); } else { @@ -762,12 +765,10 @@ namespace Umbraco.Web.Editors var total = long.MaxValue; while (page * pageSize < total) { - var children = Services.MediaService.GetPagedChildren(mediaId, page, pageSize, out total); + var children = Services.MediaService.GetPagedChildren(mediaId, page, pageSize, out total, + SqlContext.Query().Where(x => x.Name == nameToFind)); foreach (var c in children) - { - if (c.Name == nameToFind && c.ContentType.Alias == contentTypeAlias) - return c; - } + return c; //return first one if any are found } return null; } From 6a1d6668c4e42651dc834483c6d980e8dc5c9a91 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 01:01:50 +1100 Subject: [PATCH 10/18] Gets both media and member indexing working for when media/member types are changed --- src/Umbraco.Core/Services/IMediaService.cs | 25 ++- .../Services/Implement/MediaService.cs | 39 +++- .../Services/Implement/MemberService.cs | 6 +- src/Umbraco.Web/Search/ExamineComponent.cs | 191 ++++++++++++------ 4 files changed, 182 insertions(+), 79 deletions(-) diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 6fa211f4f3..0c6e528dee 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -137,11 +137,28 @@ namespace Umbraco.Core.Services IQuery filter, Ordering ordering = null); /// - /// Gets a collection of objects by the Id of the + /// Gets paged documents of a content content /// - /// Id of the - /// An Enumerable list of objects - IEnumerable GetMediaOfMediaType(int id); + /// The page number. + /// The page number. + /// The page size. + /// Total number of documents. + /// Search text filter. + /// Ordering infos. + IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, + IQuery filter, Ordering ordering = null); + + /// + /// Gets paged documents for specified content types + /// + /// The page number. + /// The page number. + /// The page size. + /// Total number of documents. + /// Search text filter. + /// Ordering infos. + IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, + IQuery filter, Ordering ordering = null); /// /// Gets a collection of objects, which reside at the first level / root diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index a584521c33..a69b39943b 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -364,18 +364,39 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by the Id of the - /// - /// Id of the - /// An Enumerable list of objects - public IEnumerable GetMediaOfMediaType(int id) + /// + public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) { + if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); + if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + + if (ordering == null) + ordering = Ordering.By("sortOrder"); + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { - scope.ReadLock(Constants.Locks.MediaTree); - var query = Query().Where(x => x.ContentTypeId == id); - return _mediaRepository.Get(query); + scope.ReadLock(Constants.Locks.ContentTree); + return _mediaRepository.GetPage( + Query().Where(x => x.ContentTypeId == contentTypeId), + pageIndex, pageSize, out totalRecords, filter, ordering); + } + } + + /// + public IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) + { + if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); + if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + + if (ordering == null) + ordering = Ordering.By("sortOrder"); + + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + scope.ReadLock(Constants.Locks.ContentTree); + return _mediaRepository.GetPage( + Query().Where(x => contentTypeIds.Contains(x.ContentTypeId)), + pageIndex, pageSize, out totalRecords, filter, ordering); } } diff --git a/src/Umbraco.Core/Services/Implement/MemberService.cs b/src/Umbraco.Core/Services/Implement/MemberService.cs index 3fd714f974..5a644cfec1 100644 --- a/src/Umbraco.Core/Services/Implement/MemberService.cs +++ b/src/Umbraco.Core/Services/Implement/MemberService.cs @@ -393,12 +393,14 @@ namespace Umbraco.Core.Services.Implement // fixme get rid of string filter? - public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, string memberTypeAlias = null, string filter = "") + public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords, + string orderBy, Direction orderDirection, string memberTypeAlias = null, string filter = "") { return GetAll(pageIndex, pageSize, out totalRecords, orderBy, orderDirection, true, memberTypeAlias, filter); } - public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, bool orderBySystemField, string memberTypeAlias, string filter) + public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords, + string orderBy, Direction orderDirection, bool orderBySystemField, string memberTypeAlias, string filter) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index b5cfac7e99..7a036ef712 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -333,92 +333,155 @@ namespace Umbraco.Web.Search if (args.MessageType != MessageType.RefreshByPayload) throw new NotSupportedException(); - - var contentService = _services.ContentService; - - var removedIds = new List(); - var refreshedIds = new List(); - var otherIds = new List(); - + + var changedIds = new Dictionary removedIds, List refreshedIds, List otherIds)>(); + foreach (var payload in (ContentTypeCacheRefresher.JsonPayload[])args.MessageObject) { + if (!changedIds.TryGetValue(payload.ItemType, out var idLists)) + { + idLists = (removedIds: new List(), refreshedIds: new List(), otherIds: new List()); + changedIds.Add(payload.ItemType, idLists); + } + if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.Remove)) - removedIds.Add(payload.Id); + idLists.removedIds.Add(payload.Id); else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshMain)) - refreshedIds.Add(payload.Id); + idLists.refreshedIds.Add(payload.Id); else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshOther)) - otherIds.Add(payload.Id); + idLists.otherIds.Add(payload.Id); } const int pageSize = 500; - if (refreshedIds.Count > 0 || otherIds.Count > 0) - { + foreach(var ci in changedIds) + { + if (ci.Value.refreshedIds.Count > 0 || ci.Value.otherIds.Count > 0) + { + switch(ci.Key) + { + case var itemType when itemType == typeof(IContentType).Name: + RefreshContentOfContentTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + case var itemType when itemType == typeof(IMediaType).Name: + RefreshMediaOfMediaTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + case var itemType when itemType == typeof(IMemberType).Name: + RefreshMemberOfMemberTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + } + } + + //Delete all content of this content/media/member type that is in any content indexer by looking up matched examine docs + foreach (var id in ci.Value.removedIds) + { + foreach (var index in _examineManager.IndexProviders.Values.OfType()) + { + var searcher = index.GetSearcher(); + + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + //paging with examine, see https://shazwazza.com/post/paging-with-examine/ + var results = searcher.Search( + searcher.CreateCriteria().Field("nodeType", id).Compile(), + maxResults: pageSize * (page + 1)); + total = results.TotalItemCount; + var paged = results.Skip(page * pageSize); + + foreach (var item in paged) + if (int.TryParse(item.Id, out var contentId)) + DeleteIndexForEntity(contentId, false); + } + } + } + } + } + + private void RefreshMemberOfMemberTypes(int[] memberTypeIds) + { + const int pageSize = 500; + + var memberTypes = _services.MemberTypeService.GetAll(memberTypeIds); + foreach(var memberType in memberTypes) + { var page = 0; var total = long.MaxValue; while (page * pageSize < total) { - var contentToRefresh = _services.ContentService.GetPagedOfTypes( - //Re-index all content of these types - refreshedIds.Concat(otherIds).Distinct().ToArray(), - page++, pageSize, out total, null, - //order by shallowest to deepest, this allows us to check it's published state without checking every item - Ordering.By("Path", Direction.Ascending)); + var memberToRefresh = _services.MemberService.GetAll( + page++, pageSize, out total, "LoginName", Direction.Ascending, + memberType.Alias); - //track which Ids have their paths are published - var publishChecked = new Dictionary(); - - foreach (var c in contentToRefresh) + foreach (var c in memberToRefresh) { - IContent published = null; - if (c.Published) - { - if (publishChecked.TryGetValue(c.ParentId, out var isPublished)) - { - //if the parent's published path has already been verified then this is published - if (isPublished) - published = c; - } - else - { - //nothing by parent id, so query the service and cache the result for the next child to check against - isPublished = contentService.IsPathPublished(c); - publishChecked[c.Id] = isPublished; - if (isPublished) - published = c; - } - } - - ReIndexForContent(c, published); + ReIndexForMember(c); } } } + } - //Delete all content of this content type that is in any content indexer by looking up matched examine docs - foreach(var id in removedIds) + private void RefreshMediaOfMediaTypes(int[] mediaTypeIds) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) { - foreach(var index in _examineManager.IndexProviders.Values.OfType()) + var mediaToRefresh = _services.MediaService.GetPagedOfTypes( + //Re-index all content of these types + mediaTypeIds, + page++, pageSize, out total, null, + Ordering.By("Path", Direction.Ascending)); + + foreach (var c in mediaToRefresh) { - var searcher = index.GetSearcher(); - - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - //paging with examine, see https://shazwazza.com/post/paging-with-examine/ - var results = searcher.Search( - searcher.CreateCriteria().Field("nodeType", id).Compile(), - maxResults: pageSize * (page + 1)); - total = results.TotalItemCount; - var paged = results.Skip(page * pageSize); - - foreach(var item in paged) - if (int.TryParse(item.Id, out var contentId)) - DeleteIndexForEntity(contentId, false); - } + ReIndexForMedia(c, c.Trashed == false); + } + } + } + + private void RefreshContentOfContentTypes(int[] contentTypeIds) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var contentToRefresh = _services.ContentService.GetPagedOfTypes( + //Re-index all content of these types + contentTypeIds, + page++, pageSize, out total, null, + //order by shallowest to deepest, this allows us to check it's published state without checking every item + Ordering.By("Path", Direction.Ascending)); + + //track which Ids have their paths are published + var publishChecked = new Dictionary(); + + foreach (var c in contentToRefresh) + { + IContent published = null; + if (c.Published) + { + if (publishChecked.TryGetValue(c.ParentId, out var isPublished)) + { + //if the parent's published path has already been verified then this is published + if (isPublished) + published = c; + } + else + { + //nothing by parent id, so query the service and cache the result for the next child to check against + isPublished = _services.ContentService.IsPathPublished(c); + publishChecked[c.Id] = isPublished; + if (isPublished) + published = c; + } + } + + ReIndexForContent(c, published); } - - } } From 082d860a5b2f97319d25db61afdaf0c34fca2f9b Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 01:28:12 +1100 Subject: [PATCH 11/18] fixes tests --- src/Umbraco.Core/Services/Implement/ContentService.cs | 1 - src/Umbraco.Core/Services/Implement/MediaService.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index debf7dcb34..64e536b7e2 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -634,7 +634,6 @@ namespace Umbraco.Core.Services.Implement { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - if (filter == null) throw new ArgumentNullException(nameof(filter)); if (ordering == null) throw new ArgumentNullException(nameof(ordering)); var query = Query(); diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index a69b39943b..a41d12ac59 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -595,7 +595,6 @@ namespace Umbraco.Core.Services.Implement { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - if (filter == null) throw new ArgumentNullException(nameof(filter)); if (ordering == null) throw new ArgumentNullException(nameof(ordering)); var query = Query(); From 76a8cd6dcf59ed61d4674d6385fb74ffa68cff82 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 10:22:45 +1100 Subject: [PATCH 12/18] Changes recycle bin service queries to paged --- src/Umbraco.Core/Services/IContentService.cs | 4 ++-- src/Umbraco.Core/Services/IMediaService.cs | 3 ++- .../Services/Implement/ContentService.cs | 8 ++++++-- src/Umbraco.Core/Services/Implement/MediaService.cs | 13 +++++++------ .../Persistence/NPocoTests/PetaPocoCachesTest.cs | 2 -- src/Umbraco.Tests/Services/ContentServiceTests.cs | 8 ++++---- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index c609a7493d..ccf88a7b1e 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -141,11 +141,11 @@ namespace Umbraco.Core.Services /// IEnumerable GetContentForRelease(); - //fixme: should be paged /// /// Gets documents in the recycle bin. /// - IEnumerable GetContentInRecycleBin(); + IEnumerable GetPagedContentInRecycleBin(long pageIndex, int pageSize, out long totalRecords, + IQuery filter = null, Ordering ordering = null); /// /// Gets child documents of a parent. diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 0c6e528dee..2476af94dd 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -170,7 +170,8 @@ namespace Umbraco.Core.Services /// Gets a collection of an objects, which resides in the Recycle Bin /// /// An Enumerable list of objects - IEnumerable GetMediaInRecycleBin(); + IEnumerable GetPagedMediaInRecycleBin(long pageIndex, int pageSize, out long totalRecords, + IQuery filter = null, Ordering ordering = null); /// /// Moves an object to a new location diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 64e536b7e2..63d6167f39 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -727,13 +727,17 @@ namespace Umbraco.Core.Services.Implement /// Gets a collection of an objects, which resides in the Recycle Bin /// /// An Enumerable list of objects - public IEnumerable GetContentInRecycleBin() + public IEnumerable GetPagedContentInRecycleBin(long pageIndex, int pageSize, out long totalRecords, + IQuery filter = null, Ordering ordering = null) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { + if (ordering == null) + ordering = Ordering.By("Path"); + scope.ReadLock(Constants.Locks.ContentTree); var query = Query().Where(x => x.Path.StartsWith(Constants.System.RecycleBinContentPathPrefix)); - return _documentRepository.Get(query); + return _documentRepository.GetPage(query, pageIndex, pageSize, out totalRecords, filter, ordering); } } diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index a41d12ac59..4be75487b7 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -643,17 +643,18 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of an objects, which resides in the Recycle Bin - /// - /// An Enumerable list of objects - public IEnumerable GetMediaInRecycleBin() + /// + public IEnumerable GetPagedMediaInRecycleBin(long pageIndex, int pageSize, out long totalRecords, + IQuery filter = null, Ordering ordering = null) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { + if (ordering == null) + ordering = Ordering.By("Path"); + scope.ReadLock(Constants.Locks.MediaTree); var query = Query().Where(x => x.Path.StartsWith(Constants.System.RecycleBinMediaPathPrefix)); - return _mediaRepository.Get(query); + return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalRecords, filter, ordering); } } diff --git a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs index b1ebee108a..570fbfb7e3 100644 --- a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs +++ b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs @@ -133,8 +133,6 @@ namespace Umbraco.Tests.Persistence.NPocoTests contentService.GetContentForRelease(); - contentService.GetContentInRecycleBin(); - ((ContentService)contentService).GetPublishedDescendants(new Content("Test", -1, new ContentType(-1)) { Id = id1, diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index c2b1cdc52d..b22a1dfd6a 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1172,7 +1172,7 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; // Act - var contents = contentService.GetContentInRecycleBin().ToList(); + var contents = contentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList(); // Assert Assert.That(contents, Is.Not.Null); @@ -1792,7 +1792,7 @@ namespace Umbraco.Tests.Services Assert.True(descendants.All(x => x.Trashed)); contentService.EmptyRecycleBin(); - var trashed = contentService.GetContentInRecycleBin(); + var trashed = contentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList(); Assert.IsEmpty(trashed); } @@ -1804,7 +1804,7 @@ namespace Umbraco.Tests.Services // Act contentService.EmptyRecycleBin(); - var contents = contentService.GetContentInRecycleBin(); + var contents = contentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList(); // Assert Assert.That(contents.Any(), Is.False); @@ -1982,7 +1982,7 @@ namespace Umbraco.Tests.Services // Act ServiceContext.ContentService.MoveToRecycleBin(content1); ServiceContext.ContentService.EmptyRecycleBin(); - var contents = ServiceContext.ContentService.GetContentInRecycleBin(); + var contents = ServiceContext.ContentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList(); // Assert Assert.That(contents.Any(), Is.False); From 047bce17bf3dc85ae9522b63ac2abe87586577b1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 10:28:53 +1100 Subject: [PATCH 13/18] Cleans up more IContentService and IMediaService and removes any string filtering methods --- src/Umbraco.Core/Services/IContentService.cs | 29 +------ src/Umbraco.Core/Services/IMediaService.cs | 36 +-------- .../Services/Implement/ContentService.cs | 29 +------ .../Services/Implement/MediaService.cs | 76 ++----------------- .../UmbracoExamine/IndexInitializer.cs | 9 --- 5 files changed, 16 insertions(+), 163 deletions(-) diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index ccf88a7b1e..6cfc923ebe 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -147,18 +147,6 @@ namespace Umbraco.Core.Services IEnumerable GetPagedContentInRecycleBin(long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null); - /// - /// Gets child documents of a parent. - /// - /// The parent identifier. - /// The page number. - /// The page size. - /// Total number of documents. - /// Search text filter. - /// Ordering infos. - IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - string filter = null, Ordering ordering = null); - /// /// Gets child documents of a parent. /// @@ -169,20 +157,7 @@ namespace Umbraco.Core.Services /// Query filter. /// Ordering infos. IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - IQuery filter, Ordering ordering = null); - - /// - /// Gets descendant documents of a given parent. - /// - /// The parent identifier. - /// The page number. - /// The page size. - /// Total number of documents. - /// A field to order by. - /// The ordering direction. - /// Search text filter. - IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string filter = null, Ordering ordering = null); + IQuery filter = null, Ordering ordering = null); /// /// Gets descendant documents of a given parent. @@ -196,7 +171,7 @@ namespace Umbraco.Core.Services /// A flag indicating whether the ordering field is a system field. /// Query filter. IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - IQuery filter, Ordering ordering = null); + IQuery filter = null, Ordering ordering = null); /// /// Gets paged documents of a content content diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 2476af94dd..9cc559ccd5 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -78,20 +78,6 @@ namespace Umbraco.Core.Services /// IMedia GetById(int id); - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Search text filter - /// An Enumerable list of objects - IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - string filter = null, Ordering ordering = null); - /// /// Gets a collection of objects by Parent Id /// @@ -105,21 +91,7 @@ namespace Umbraco.Core.Services /// /// An Enumerable list of objects IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - IQuery filter, Ordering ordering = null); - - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Descendants from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Search text filter - /// An Enumerable list of objects - IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string filter = null, Ordering ordering = null); + IQuery filter = null, Ordering ordering = null); /// /// Gets a collection of objects by Parent Id @@ -134,7 +106,7 @@ namespace Umbraco.Core.Services /// /// An Enumerable list of objects IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - IQuery filter, Ordering ordering = null); + IQuery filter = null, Ordering ordering = null); /// /// Gets paged documents of a content content @@ -146,7 +118,7 @@ namespace Umbraco.Core.Services /// Search text filter. /// Ordering infos. IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, - IQuery filter, Ordering ordering = null); + IQuery filter = null, Ordering ordering = null); /// /// Gets paged documents for specified content types @@ -158,7 +130,7 @@ namespace Umbraco.Core.Services /// Search text filter. /// Ordering infos. IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, - IQuery filter, Ordering ordering = null); + IQuery filter = null, Ordering ordering = null); /// /// Gets a collection of objects, which reside at the first level / root diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 63d6167f39..386c76db88 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -406,7 +406,8 @@ namespace Umbraco.Core.Services.Implement } /// - public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) + public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords + , IQuery filter = null, Ordering ordering = null) { if(pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); @@ -564,18 +565,7 @@ namespace Umbraco.Core.Services.Implement /// public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, - string filter = null, Ordering ordering = null) - { - var filterQuery = filter.IsNullOrWhiteSpace() - ? null - : Query().Where(x => x.Name.Contains(filter)); - - return GetPagedChildren(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); - } - - /// - public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, - IQuery filter, Ordering ordering = null) + IQuery filter = null, Ordering ordering = null) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); @@ -594,18 +584,7 @@ namespace Umbraco.Core.Services.Implement /// public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, - string filter = null, Ordering ordering = null) - { - var filterQuery = filter.IsNullOrWhiteSpace() - ? null - : Query().Where(x => x.Name.Contains(filter)); - - return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); - } - - /// - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, - IQuery filter, Ordering ordering = null) + IQuery filter = null, Ordering ordering = null) { if (ordering == null) ordering = Ordering.By("Path"); diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index 4be75487b7..5f35e35acf 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -365,7 +365,7 @@ namespace Umbraco.Core.Services.Implement } /// - public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) + public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); @@ -383,7 +383,7 @@ namespace Umbraco.Core.Services.Implement } /// - public IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) + public IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); @@ -481,41 +481,9 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// Page index (zero based) - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Search text filter - /// An Enumerable list of objects + /// public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, - string filter = null, Ordering ordering = null) - { - var filterQuery = filter.IsNullOrWhiteSpace() - ? null - : Query().Where(x => x.Name.Contains(filter)); - - return GetPagedChildren(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); - } - - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// Page index (zero based) - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Flag to indicate when ordering by system field - /// - /// An Enumerable list of objects - public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, - IQuery filter, Ordering ordering = null) + IQuery filter = null, Ordering ordering = null) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); @@ -532,41 +500,9 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Descendants from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Search text filter - /// An Enumerable list of objects + /// public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, - string filter = null, Ordering ordering = null) - { - var filterQuery = filter.IsNullOrWhiteSpace() - ? null - : Query().Where(x => x.Name.Contains(filter)); - - return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); - } - - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Descendants from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Flag to indicate when ordering by system field - /// - /// An Enumerable list of objects - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, - IQuery filter, Ordering ordering = null) + IQuery filter = null, Ordering ordering = null) { if (ordering == null) ordering = Ordering.By("Path"); diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 8aec854441..8b57e10849 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -71,10 +71,6 @@ namespace Umbraco.Tests.UmbracoExamine contentService = Mock.Of( x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny()) - == - allRecs - && x.GetPagedDescendants( It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny>(), It.IsAny()) == allRecs); @@ -114,11 +110,6 @@ namespace Umbraco.Tests.UmbracoExamine // MOCK! var mediaServiceMock = new Mock(); - mediaServiceMock - .Setup(x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny(), It.IsAny()) - ).Returns(() => allRecs); - mediaServiceMock .Setup(x => x.GetPagedDescendants( It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny>(), It.IsAny()) From 0e8bac0b0907456da24e517fa36497450a71f237 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 11:24:03 +1100 Subject: [PATCH 14/18] fixes member indexing with list object --- src/Umbraco.Examine/UmbracoMemberIndexer.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Examine/UmbracoMemberIndexer.cs b/src/Umbraco.Examine/UmbracoMemberIndexer.cs index b7cbcc19bc..82bf3b9cf6 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndexer.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndexer.cs @@ -175,13 +175,17 @@ namespace Umbraco.Examine if (e.IndexItem.ValueSet.Values.TryGetValue("key", out var key) && e.IndexItem.ValueSet.Values.ContainsKey("__key") == false) { //double __ prefix means it will be indexed as culture invariant - e.IndexItem.ValueSet.Values["__key"] = new List { key }; + e.IndexItem.ValueSet.Values["__key"] = key; } if (e.IndexItem.ValueSet.Values.TryGetValue("email", out var email) && e.IndexItem.ValueSet.Values.ContainsKey("_searchEmail") == false) { - //will be indexed as full text (the default anaylyzer) - e.IndexItem.ValueSet.Values["_searchEmail"] = new List { email?.ToString().Replace(".", " ").Replace("@", " ") }; + if (email.Count > 0) + { + //will be indexed as full text (the default anaylyzer) + e.IndexItem.ValueSet.Values["_searchEmail"] = new List { email[0]?.ToString().Replace(".", " ").Replace("@", " ") }; + } + } } From 2badacb8a846557902805c6402f6f7c8a50d4682 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Fri, 2 Nov 2018 15:33:43 +0000 Subject: [PATCH 15/18] Adds UmbracoFeatures to the Composition to allow user components to do something like this - composition.Features().Disabled.Controllers.Add() --- src/Umbraco.Web/CompositionExtensions.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Umbraco.Web/CompositionExtensions.cs b/src/Umbraco.Web/CompositionExtensions.cs index 38b0be3103..fe2ea4b8a7 100644 --- a/src/Umbraco.Web/CompositionExtensions.cs +++ b/src/Umbraco.Web/CompositionExtensions.cs @@ -11,6 +11,7 @@ using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.ContentApps; +using Umbraco.Web.Features; // the namespace here is intentional - although defined in Umbraco.Web assembly, // this class should be visible when using Umbraco.Core.Components, alongside @@ -58,6 +59,9 @@ namespace Umbraco.Core.Components internal static EditorValidatorCollectionBuilder EditorValidators(this Composition composition) => composition.Container.GetInstance(); + public static UmbracoFeatures Features(this Composition composition) + => composition.Container.GetInstance(); + /// /// Gets the filtered controller factories collection builder. /// From b9e30604a4f5fc667fb2ba0c1abff628ad943157 Mon Sep 17 00:00:00 2001 From: elitsa Date: Sun, 4 Nov 2018 22:54:25 +0100 Subject: [PATCH 16/18] Fix for chekcing the children belonging to Templates tree. --- src/Umbraco.Web/Trees/TemplatesTreeController.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Umbraco.Web/Trees/TemplatesTreeController.cs b/src/Umbraco.Web/Trees/TemplatesTreeController.cs index 2339d92d96..a84baa76c4 100644 --- a/src/Umbraco.Web/Trees/TemplatesTreeController.cs +++ b/src/Umbraco.Web/Trees/TemplatesTreeController.cs @@ -25,6 +25,14 @@ namespace Umbraco.Web.Trees [CoreTree(TreeGroup = Constants.Trees.Groups.Templating)] public class TemplatesTreeController : TreeController, ISearchableTree { + protected override TreeNode CreateRootNode(FormDataCollection queryStrings) + { + var root = base.CreateRootNode(queryStrings); + //check if there are any templates + root.HasChildren = Services.MacroService.GetAll().Any(); + return root; + } + /// /// The method called to render the contents of the tree structure /// From 666a78f80381f2c8e3e8117791dd338a8b096d85 Mon Sep 17 00:00:00 2001 From: elitsa Date: Mon, 5 Nov 2018 10:36:17 +0100 Subject: [PATCH 17/18] Fix for getting the right collection. --- src/Umbraco.Web/Trees/TemplatesTreeController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Trees/TemplatesTreeController.cs b/src/Umbraco.Web/Trees/TemplatesTreeController.cs index a84baa76c4..3fc005d670 100644 --- a/src/Umbraco.Web/Trees/TemplatesTreeController.cs +++ b/src/Umbraco.Web/Trees/TemplatesTreeController.cs @@ -29,7 +29,7 @@ namespace Umbraco.Web.Trees { var root = base.CreateRootNode(queryStrings); //check if there are any templates - root.HasChildren = Services.MacroService.GetAll().Any(); + root.HasChildren = Services.FileService.GetTemplates(-1).Any(); return root; } From 2b33c5cfd75fc979f259a2eceab3f5ff5a8ae191 Mon Sep 17 00:00:00 2001 From: elitsa Date: Mon, 5 Nov 2018 10:46:50 +0100 Subject: [PATCH 18/18] Fix for chekcing the children belonging to MemberType tree. --- src/Umbraco.Web/Trees/MemberTypeTreeController.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Trees/MemberTypeTreeController.cs b/src/Umbraco.Web/Trees/MemberTypeTreeController.cs index 77eb6e0b24..7bf04010f2 100644 --- a/src/Umbraco.Web/Trees/MemberTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTypeTreeController.cs @@ -7,11 +7,18 @@ using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Trees { - [CoreTree(TreeGroup =Constants.Trees.Groups.Settings)] + [CoreTree(TreeGroup = Constants.Trees.Groups.Settings)] [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] [Tree(Constants.Applications.Settings, Constants.Trees.MemberTypes, null, sortOrder: 2)] public class MemberTypeTreeController : MemberTypeAndGroupTreeControllerBase { + protected override TreeNode CreateRootNode(FormDataCollection queryStrings) + { + var root = base.CreateRootNode(queryStrings); + //check if there are any member types + root.HasChildren = Services.MemberTypeService.GetAll().Any(); + return root; + } protected override IEnumerable GetTreeNodesFromService(string id, FormDataCollection queryStrings) { return Services.MemberTypeService.GetAll()