diff --git a/src/Umbraco.Core/Services/Implement/PublicAccessService.cs b/src/Umbraco.Core/Services/Implement/PublicAccessService.cs index ab9ea64292..06570da725 100644 --- a/src/Umbraco.Core/Services/Implement/PublicAccessService.cs +++ b/src/Umbraco.Core/Services/Implement/PublicAccessService.cs @@ -62,12 +62,10 @@ namespace Umbraco.Core.Services.Implement //start with the deepest id ids.Reverse(); - using (var scope = ScopeProvider.CreateScope()) + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { //This will retrieve from cache! - var entries = _publicAccessRepository.GetMany().ToArray(); - - scope.Complete(); + var entries = _publicAccessRepository.GetMany().ToList(); foreach (var id in ids) { var found = entries.FirstOrDefault(x => x.ProtectedNodeId == id); diff --git a/src/Umbraco.Examine/ContentValueSetValidator.cs b/src/Umbraco.Examine/ContentValueSetValidator.cs index 9555566c53..f702e8197d 100644 --- a/src/Umbraco.Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Examine/ContentValueSetValidator.cs @@ -4,7 +4,9 @@ using System.Linq; using Examine; using Examine.LuceneEngine.Providers; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Models; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; namespace Umbraco.Examine @@ -15,9 +17,9 @@ namespace Umbraco.Examine public class ContentValueSetValidator : ValueSetValidator, IContentValueSetValidator { private readonly IPublicAccessService _publicAccessService; - + private readonly IScopeProvider _scopeProvider; private const string PathKey = "path"; - private static readonly IEnumerable ValidCategories = new[] {IndexTypes.Content, IndexTypes.Media}; + private static readonly IEnumerable ValidCategories = new[] { IndexTypes.Content, IndexTypes.Media }; protected override IEnumerable ValidIndexCategories => ValidCategories; public bool PublishedValuesOnly { get; } @@ -53,25 +55,38 @@ namespace Umbraco.Examine public bool ValidateProtectedContent(string path, string category) { - if (category == IndexTypes.Content - && !SupportProtectedContent - //if the service is null we can't look this up so we'll return false - && (_publicAccessService == null || _publicAccessService.IsProtected(path))) + if (category == IndexTypes.Content && !SupportProtectedContent) { - return false; + //if the service is null we can't look this up so we'll return false + if (_publicAccessService == null || _scopeProvider == null) + { + return false; + } + + // explicit scope since we may be in a background thread + using (_scopeProvider.CreateScope(autoComplete: true)) + { + if (_publicAccessService.IsProtected(path)) + { + return false; + } + } } return true; } + // used for tests public ContentValueSetValidator(bool publishedValuesOnly, int? parentId = null, IEnumerable includeItemTypes = null, IEnumerable excludeItemTypes = null) - : this(publishedValuesOnly, true, null, parentId, includeItemTypes, excludeItemTypes) + : this(publishedValuesOnly, true, null, null, parentId, includeItemTypes, excludeItemTypes) { } public ContentValueSetValidator(bool publishedValuesOnly, bool supportProtectedContent, - IPublicAccessService publicAccessService, int? parentId = null, + IPublicAccessService publicAccessService, + IScopeProvider scopeProvider, + int? parentId = null, IEnumerable includeItemTypes = null, IEnumerable excludeItemTypes = null) : base(includeItemTypes, excludeItemTypes, null, null) { @@ -79,6 +94,16 @@ namespace Umbraco.Examine SupportProtectedContent = supportProtectedContent; ParentId = parentId; _publicAccessService = publicAccessService; + _scopeProvider = scopeProvider; + } + + [Obsolete("Use the ctor with all parameters instead")] + public ContentValueSetValidator(bool publishedValuesOnly, bool supportProtectedContent, + IPublicAccessService publicAccessService, int? parentId = null, + IEnumerable includeItemTypes = null, IEnumerable excludeItemTypes = null) + : this(publishedValuesOnly, supportProtectedContent, publicAccessService, Current.ScopeProvider, + parentId, includeItemTypes, excludeItemTypes) + { } public override ValueSetValidationResult Validate(ValueSet valueSet) @@ -103,7 +128,7 @@ namespace Umbraco.Examine && variesByCulture.Count > 0 && variesByCulture[0].Equals("y")) { //so this valueset is for a content that varies by culture, now check for non-published cultures and remove those values - foreach(var publishField in valueSet.Values.Where(x => x.Key.StartsWith($"{UmbracoExamineIndex.PublishedFieldName}_")).ToList()) + foreach (var publishField in valueSet.Values.Where(x => x.Key.StartsWith($"{UmbracoExamineIndex.PublishedFieldName}_")).ToList()) { if (publishField.Value.Count <= 0 || !publishField.Value[0].Equals("y")) { @@ -134,7 +159,7 @@ namespace Umbraco.Examine || !ValidateProtectedContent(path, valueSet.Category)) return ValueSetValidationResult.Filtered; - return isFiltered ? ValueSetValidationResult.Filtered: ValueSetValidationResult.Valid; + return isFiltered ? ValueSetValidationResult.Filtered : ValueSetValidationResult.Valid; } } } diff --git a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs index 8bdb0c71c7..330529a5db 100644 --- a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs @@ -8,6 +8,7 @@ using Umbraco.Core; using Umbraco.Core.Models; using System; using System.Linq; +using Umbraco.Core.Scoping; namespace Umbraco.Tests.UmbracoExamine { @@ -17,10 +18,14 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Invalid_Category() { - var validator = new ContentValueSetValidator(false, true, Mock.Of()); + var validator = new ContentValueSetValidator( + false, + true, + Mock.Of(), + Mock.Of()); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.AreEqual(ValueSetValidationResult.Valid, result); + Assert.AreEqual(ValueSetValidationResult.Valid, result); result = validator.Validate(ValueSet.FromObject("777", IndexTypes.Media, new { hello = "world", path = "-1,555" })); Assert.AreEqual(ValueSetValidationResult.Valid, result); @@ -33,7 +38,11 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Must_Have_Path() { - var validator = new ContentValueSetValidator(false, true, Mock.Of()); + var validator = new ContentValueSetValidator( + false, + true, + Mock.Of(), + Mock.Of()); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world" })); Assert.AreEqual(ValueSetValidationResult.Failed, result); @@ -45,7 +54,12 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Parent_Id() { - var validator = new ContentValueSetValidator(false, true, Mock.Of(), 555); + var validator = new ContentValueSetValidator( + false, + true, + Mock.Of(), + Mock.Of(), + 555); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); Assert.AreEqual(ValueSetValidationResult.Filtered, result); @@ -63,7 +77,9 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Inclusion_Field_List() { - var validator = new ValueSetValidator(null, null, + var validator = new ValueSetValidator( + null, + null, new[] { "hello", "world" }, null); @@ -79,7 +95,9 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Exclusion_Field_List() { - var validator = new ValueSetValidator(null, null, + var validator = new ValueSetValidator( + null, + null, null, new[] { "hello", "world" }); @@ -95,7 +113,9 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Inclusion_Exclusion_Field_List() { - var validator = new ValueSetValidator(null, null, + var validator = new ValueSetValidator( + null, + null, new[] { "hello", "world" }, new[] { "world" }); @@ -111,7 +131,11 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Inclusion_Type_List() { - var validator = new ContentValueSetValidator(false, true, Mock.Of(), + var validator = new ContentValueSetValidator( + false, + true, + Mock.Of(), + Mock.Of(), includeItemTypes: new List { "include-content" }); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); @@ -127,7 +151,11 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Exclusion_Type_List() { - var validator = new ContentValueSetValidator(false, true, Mock.Of(), + var validator = new ContentValueSetValidator( + false, + true, + Mock.Of(), + Mock.Of(), excludeItemTypes: new List { "exclude-content" }); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); @@ -143,7 +171,11 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Inclusion_Exclusion_Type_List() { - var validator = new ContentValueSetValidator(false, true, Mock.Of(), + var validator = new ContentValueSetValidator( + false, + true, + Mock.Of(), + Mock.Of(), includeItemTypes: new List { "include-content", "exclude-content" }, excludeItemTypes: new List { "exclude-content" }); @@ -163,7 +195,11 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Recycle_Bin_Content() { - var validator = new ContentValueSetValidator(true, false, Mock.Of()); + var validator = new ContentValueSetValidator( + true, + false, + Mock.Of(), + Mock.Of()); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555" })); Assert.AreEqual(ValueSetValidationResult.Failed, result); @@ -187,7 +223,11 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Recycle_Bin_Media() { - var validator = new ContentValueSetValidator(true, false, Mock.Of()); + var validator = new ContentValueSetValidator( + true, + false, + Mock.Of(), + Mock.Of()); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Media, new { hello = "world", path = "-1,-21,555" })); Assert.AreEqual(ValueSetValidationResult.Filtered, result); @@ -203,7 +243,11 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Published_Only() { - var validator = new ContentValueSetValidator(true, true, Mock.Of()); + var validator = new ContentValueSetValidator( + true, + true, + Mock.Of(), + Mock.Of()); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); Assert.AreEqual(ValueSetValidationResult.Failed, result); @@ -230,7 +274,10 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Published_Only_With_Variants() { - var validator = new ContentValueSetValidator(true, true, Mock.Of()); + var validator = new ContentValueSetValidator(true, + true, + Mock.Of(), + Mock.Of()); var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new Dictionary @@ -288,7 +335,11 @@ namespace Umbraco.Tests.UmbracoExamine .Returns(Attempt.Succeed(new PublicAccessEntry(Guid.NewGuid(), 555, 444, 333, Enumerable.Empty()))); publicAccessService.Setup(x => x.IsProtected("-1,777")) .Returns(Attempt.Fail()); - var validator = new ContentValueSetValidator(false, false, publicAccessService.Object); + var validator = new ContentValueSetValidator( + false, + false, + publicAccessService.Object, + Mock.Of()); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); Assert.AreEqual(ValueSetValidationResult.Filtered, result); diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index c9d7b7cf56..bb3f30d4cf 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -629,22 +629,27 @@ namespace Umbraco.Web.Search // perform the ValueSet lookup on a background thread examineComponent._indexItemTaskRunner.Add(new SimpleTask(() => { - // for content we have a different builder for published vs unpublished - // we don't want to build more value sets than is needed so we'll lazily build 2 one for published one for non-published - var builders = new Dictionary>> + // Background thread, wrap the whole thing in an explicit scope since we know + // DB services are used within this logic. + using (examineComponent._scopeProvider.CreateScope(autoComplete: true)) { - [true] = new Lazy>(() => examineComponent._publishedContentValueSetBuilder.GetValueSets(content).ToList()), - [false] = new Lazy>(() => examineComponent._contentValueSetBuilder.GetValueSets(content).ToList()) - }; + // for content we have a different builder for published vs unpublished + // we don't want to build more value sets than is needed so we'll lazily build 2 one for published one for non-published + var builders = new Dictionary>> + { + [true] = new Lazy>(() => examineComponent._publishedContentValueSetBuilder.GetValueSets(content).ToList()), + [false] = new Lazy>(() => examineComponent._contentValueSetBuilder.GetValueSets(content).ToList()) + }; - foreach (var index in examineComponent._examineManager.Indexes.OfType() - //filter the indexers - .Where(x => isPublished || !x.PublishedValuesOnly) - .Where(x => x.EnableDefaultEventHandler)) - { - var valueSet = builders[index.PublishedValuesOnly].Value; - index.IndexItems(valueSet); - } + foreach (var index in examineComponent._examineManager.Indexes.OfType() + //filter the indexers + .Where(x => isPublished || !x.PublishedValuesOnly) + .Where(x => x.EnableDefaultEventHandler)) + { + var valueSet = builders[index.PublishedValuesOnly].Value; + index.IndexItems(valueSet); + } + } })); } } @@ -675,14 +680,19 @@ namespace Umbraco.Web.Search // perform the ValueSet lookup on a background thread examineComponent._indexItemTaskRunner.Add(new SimpleTask(() => { - var valueSet = examineComponent._mediaValueSetBuilder.GetValueSets(media).ToList(); - - foreach (var index in examineComponent._examineManager.Indexes.OfType() - //filter the indexers - .Where(x => isPublished || !x.PublishedValuesOnly) - .Where(x => x.EnableDefaultEventHandler)) + // Background thread, wrap the whole thing in an explicit scope since we know + // DB services are used within this logic. + using (examineComponent._scopeProvider.CreateScope(autoComplete: true)) { - index.IndexItems(valueSet); + var valueSet = examineComponent._mediaValueSetBuilder.GetValueSets(media).ToList(); + + foreach (var index in examineComponent._examineManager.Indexes.OfType() + //filter the indexers + .Where(x => isPublished || !x.PublishedValuesOnly) + .Where(x => x.EnableDefaultEventHandler)) + { + index.IndexItems(valueSet); + } } })); } @@ -712,12 +722,17 @@ namespace Umbraco.Web.Search // perform the ValueSet lookup on a background thread examineComponent._indexItemTaskRunner.Add(new SimpleTask(() => { - var valueSet = examineComponent._memberValueSetBuilder.GetValueSets(member).ToList(); - foreach (var index in examineComponent._examineManager.Indexes.OfType() - //filter the indexers - .Where(x => x.EnableDefaultEventHandler)) + // Background thread, wrap the whole thing in an explicit scope since we know + // DB services are used within this logic. + using (examineComponent._scopeProvider.CreateScope(autoComplete: true)) { - index.IndexItems(valueSet); + var valueSet = examineComponent._memberValueSetBuilder.GetValueSets(member).ToList(); + foreach (var index in examineComponent._examineManager.Indexes.OfType() + //filter the indexers + .Where(x => x.EnableDefaultEventHandler)) + { + index.IndexItems(valueSet); + } } })); }