From f8d203abff9f2ab18b3063714e21a2336d47ce13 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 3 Dec 2018 22:10:56 +1100 Subject: [PATCH] Updates latest examine + api updates + fixes tests, gets different validation working (valid, failed, filtered) --- .../ContentValueSetValidator.cs | 96 +++++++----- .../IContentValueSetValidator.cs | 18 +++ src/Umbraco.Examine/IndexRebuilder.cs | 4 +- src/Umbraco.Examine/Umbraco.Examine.csproj | 3 +- src/Umbraco.Examine/UmbracoContentIndexer.cs | 51 +++++-- src/Umbraco.Examine/ValueSetValidator.cs | 20 ++- .../TestHelpers/Stubs/TestExamineManager.cs | 12 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- .../UmbracoExamine/IndexInitializer.cs | 2 +- .../UmbracoContentValueSetValidatorTests.cs | 138 +++++++++--------- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 3 +- src/Umbraco.Web/Editors/EntityController.cs | 28 ++-- .../Editors/ExamineManagementController.cs | 50 ++----- src/Umbraco.Web/IPublishedContentQuery.cs | 2 +- .../XmlPublishedCache/PublishedMediaCache.cs | 5 +- src/Umbraco.Web/PublishedContentExtensions.cs | 43 +++--- src/Umbraco.Web/PublishedContentQuery.cs | 33 +++-- .../Runtime/WebRuntimeComponent.cs | 2 + src/Umbraco.Web/Search/ExamineComponent.cs | 16 +- .../Search/ExamineSearcherModel.cs | 15 -- .../Search/UmbracoIndexesCreator.cs | 4 +- src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 48 +++--- .../Trees/ContentTreeController.cs | 9 +- src/Umbraco.Web/Trees/MediaTreeController.cs | 9 +- src/Umbraco.Web/Trees/MemberTreeController.cs | 7 +- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- 26 files changed, 343 insertions(+), 279 deletions(-) create mode 100644 src/Umbraco.Examine/IContentValueSetValidator.cs diff --git a/src/Umbraco.Examine/ContentValueSetValidator.cs b/src/Umbraco.Examine/ContentValueSetValidator.cs index e78fe749db..46e4f48c9a 100644 --- a/src/Umbraco.Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Examine/ContentValueSetValidator.cs @@ -12,7 +12,7 @@ namespace Umbraco.Examine /// /// Used to validate a ValueSet for content/media - based on permissions, parent id, etc.... /// - public class ContentValueSetValidator : ValueSetValidator + public class ContentValueSetValidator : ValueSetValidator, IContentValueSetValidator { private readonly IPublicAccessService _publicAccessService; @@ -24,6 +24,46 @@ namespace Umbraco.Examine public bool SupportProtectedContent { get; } public int? ParentId { get; } + public bool ValidatePath(string path, string category) + { + //check if this document is a descendent of the parent + if (ParentId.HasValue && ParentId.Value > 0) + { + // we cannot return FAILED here because we need the value set to get into the indexer and then deal with it from there + // because we need to remove anything that doesn't pass by parent Id in the cases that umbraco data is moved to an illegal parent. + if (!path.Contains(string.Concat(",", ParentId.Value, ","))) + return false; + } + + return true; + } + + public bool ValidateRecycleBin(string path, string category) + { + var recycleBinId = category == IndexTypes.Content ? Constants.System.RecycleBinContent : Constants.System.RecycleBinMedia; + + //check for recycle bin + if (!SupportUnpublishedContent) + { + if (path.Contains(string.Concat(",", recycleBinId, ","))) + return false; + } + return true; + } + + 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))) + { + return false; + } + + return true; + } + public ContentValueSetValidator(bool supportUnpublishedContent, int? parentId = null, IEnumerable includeItemTypes = null, IEnumerable excludeItemTypes = null) : this(supportUnpublishedContent, true, null, parentId, includeItemTypes, excludeItemTypes) @@ -41,19 +81,22 @@ namespace Umbraco.Examine _publicAccessService = publicAccessService; } - public override bool Validate(ValueSet valueSet) + public override ValueSetValidationResult Validate(ValueSet valueSet) { - if (!base.Validate(valueSet)) - return false; + var baseValidate = base.Validate(valueSet); + if (baseValidate == ValueSetValidationResult.Failed) + return ValueSetValidationResult.Failed; + + var isFiltered = baseValidate == ValueSetValidationResult.Filtered; //check for published content if (valueSet.Category == IndexTypes.Content && !SupportUnpublishedContent) { if (!valueSet.Values.TryGetValue(UmbracoExamineIndexer.PublishedFieldName, out var published)) - return false; + return ValueSetValidationResult.Failed; if (!published[0].Equals(1)) - return false; + return ValueSetValidationResult.Failed; //deal with variants, if there are unpublished variants than we need to remove them from the value set if (valueSet.Values.TryGetValue(UmbracoContentIndexer.VariesByCultureFieldName, out var variesByCulture) @@ -69,6 +112,7 @@ namespace Umbraco.Examine foreach (var cultureField in valueSet.Values.Where(x => x.Key.InvariantEndsWith(cultureSuffix)).ToList()) { valueSet.Values.Remove(cultureField.Key); + isFiltered = true; } } } @@ -76,37 +120,21 @@ namespace Umbraco.Examine } //must have a 'path' - if (!valueSet.Values.TryGetValue(PathKey, out var pathValues)) return false; - if (pathValues.Count == 0) return false; - if (pathValues[0] == null) return false; - if (pathValues[0].ToString().IsNullOrWhiteSpace()) return false; + if (!valueSet.Values.TryGetValue(PathKey, out var pathValues)) return ValueSetValidationResult.Failed; + if (pathValues.Count == 0) return ValueSetValidationResult.Failed; + if (pathValues[0] == null) return ValueSetValidationResult.Failed; + if (pathValues[0].ToString().IsNullOrWhiteSpace()) return ValueSetValidationResult.Failed; var path = pathValues[0].ToString(); - // return nothing if we're not supporting protected content and it is protected, and we're not supporting unpublished content - if (valueSet.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))) - { - return false; - } + // We need to validate the path of the content based on ParentId, protected content and recycle bin rules. + // We cannot return FAILED here because we need the value set to get into the indexer and then deal with it from there + // because we need to remove anything that doesn't pass by protected content in the cases that umbraco data is moved to an illegal parent. + if (!ValidatePath(path, valueSet.Category) + || !ValidateRecycleBin(path, valueSet.Category) + || !ValidateProtectedContent(path, valueSet.Category)) + return ValueSetValidationResult.Filtered; - //check if this document is a descendent of the parent - if (ParentId.HasValue && ParentId.Value > 0) - { - if (!path.Contains(string.Concat(",", ParentId.Value, ","))) - return false; - } - - //check for recycle bin - if (!SupportUnpublishedContent) - { - var recycleBinId = valueSet.Category == IndexTypes.Content ? Constants.System.RecycleBinContent : Constants.System.RecycleBinMedia; - if (path.Contains(string.Concat(",", recycleBinId, ","))) - return false; - } - - return true; + return isFiltered ? ValueSetValidationResult.Filtered: ValueSetValidationResult.Valid; } } } diff --git a/src/Umbraco.Examine/IContentValueSetValidator.cs b/src/Umbraco.Examine/IContentValueSetValidator.cs new file mode 100644 index 0000000000..a7164773bb --- /dev/null +++ b/src/Umbraco.Examine/IContentValueSetValidator.cs @@ -0,0 +1,18 @@ +using Examine; + +namespace Umbraco.Examine +{ + /// + /// An extended for content indexes + /// + public interface IContentValueSetValidator : IValueSetValidator + { + bool SupportUnpublishedContent { get; } + bool SupportProtectedContent { get; } + int? ParentId { get; } + + bool ValidatePath(string path, string category); + bool ValidateRecycleBin(string path, string category); + bool ValidateProtectedContent(string path, string category); + } +} diff --git a/src/Umbraco.Examine/IndexRebuilder.cs b/src/Umbraco.Examine/IndexRebuilder.cs index 4c21f8416f..525b57cc7d 100644 --- a/src/Umbraco.Examine/IndexRebuilder.cs +++ b/src/Umbraco.Examine/IndexRebuilder.cs @@ -21,8 +21,8 @@ namespace Umbraco.Examine public void RebuildIndexes(bool onlyEmptyIndexes) { var indexes = (onlyEmptyIndexes - ? ExamineManager.IndexProviders.Values.Where(x => !x.IndexExists()) - : ExamineManager.IndexProviders.Values).ToArray(); + ? ExamineManager.Indexes.Where(x => !x.IndexExists()) + : ExamineManager.Indexes).ToArray(); foreach(var index in indexes) index.CreateIndex(); // clear the index diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index cae45edcdb..d2336eedf4 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + @@ -64,6 +64,7 @@ + diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index ae6d607c63..1387357463 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -57,7 +57,7 @@ namespace Umbraco.Examine Analyzer defaultAnalyzer, ProfilingLogger profilingLogger, ILocalizationService languageService, - IValueSetValidator validator, + IContentValueSetValidator validator, IReadOnlyDictionary> indexValueTypes = null) : base(name, fieldDefinitions, luceneDirectory, defaultAnalyzer, profilingLogger, validator, indexValueTypes) { @@ -121,14 +121,50 @@ namespace Umbraco.Examine //Using a singleton here, we can't inject this when using config based providers and we don't use this //anywhere else in this class Current.Services.PublicAccessService, - parentId, ConfigIndexCriteria.IncludeItemTypes, ConfigIndexCriteria.ExcludeItemTypes); + parentId, + ConfigIndexCriteria.IncludeItemTypes, ConfigIndexCriteria.ExcludeItemTypes); SupportSoftDelete = supportUnpublished; } #endregion - #region Public methods + /// + /// Special check for invalid paths + /// + /// + /// + protected override void PerformIndexItems(IEnumerable values, Action onComplete) + { + var valid = true; + + // ReSharper disable once PossibleMultipleEnumeration + foreach (var v in values) + { + if (v.Values.TryGetValue("path", out var paths) && paths.Count > 0 && paths[0] != null) + { + //we know this is an IContentValueSetValidator + var validator = (IContentValueSetValidator) ValueSetValidator; + var path = paths[0].ToString(); + + if (!validator.ValidatePath(path, v.Category) + || !validator.ValidateRecycleBin(path, v.Category) + || !validator.ValidateProtectedContent(path, v.Category)) + { + //since the path is not valid we need to delete this item in case it exists in the index already and has now + //been moved to an invalid parent. + PerformDeleteFromIndex(v.Id, x => { /*noop*/ }); + valid = false; + } + } + } + + if (valid) + { + // ReSharper disable once PossibleMultipleEnumeration + base.PerformIndexItems(values, onComplete); + } + } /// /// @@ -155,14 +191,11 @@ namespace Umbraco.Examine //need to queue a delete item for each one found foreach (var r in results) { - QueueIndexOperation(new IndexOperation(new ValueSet(r.Id, null), IndexOperationType.Delete)); + QueueIndexOperation(new IndexOperation(new ValueSet(r.Id), IndexOperationType.Delete)); } base.PerformDeleteFromIndex(nodeId, onComplete); } - #endregion - - #region Protected /// /// Overridden to ensure that the variant system fields have the right value types @@ -184,8 +217,6 @@ namespace Umbraco.Examine return base.CreateFieldValueTypes(indexValueTypesFactory); } - - - #endregion + } } diff --git a/src/Umbraco.Examine/ValueSetValidator.cs b/src/Umbraco.Examine/ValueSetValidator.cs index a0e926fed0..4db251c0f1 100644 --- a/src/Umbraco.Examine/ValueSetValidator.cs +++ b/src/Umbraco.Examine/ValueSetValidator.cs @@ -59,18 +59,20 @@ namespace Umbraco.Examine /// public IEnumerable ExcludeFields { get; } - public virtual bool Validate(ValueSet valueSet) + public virtual ValueSetValidationResult Validate(ValueSet valueSet) { if (ValidIndexCategories != null && !ValidIndexCategories.InvariantContains(valueSet.Category)) - return false; + return ValueSetValidationResult.Failed; //check if this document is of a correct type of node type alias if (IncludeItemTypes != null && !IncludeItemTypes.InvariantContains(valueSet.ItemType)) - return false; + return ValueSetValidationResult.Failed; //if this node type is part of our exclusion list if (ExcludeItemTypes != null && ExcludeItemTypes.InvariantContains(valueSet.ItemType)) - return false; + return ValueSetValidationResult.Failed; + + var isFiltered = false; //filter based on the fields provided (if any) if (IncludeFields != null || ExcludeFields != null) @@ -78,15 +80,21 @@ namespace Umbraco.Examine foreach (var key in valueSet.Values.Keys.ToList()) { if (IncludeFields != null && !IncludeFields.InvariantContains(key)) + { valueSet.Values.Remove(key); //remove any value with a key that doesn't match the inclusion list + isFiltered = true; + } if (ExcludeFields != null && ExcludeFields.InvariantContains(key)) + { valueSet.Values.Remove(key); //remove any value with a key that matches the exclusion list + isFiltered = true; + } + } } - - return true; + return isFiltered ? ValueSetValidationResult.Filtered : ValueSetValidationResult.Valid; } } } diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs index ac5671fceb..f8d48c5703 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs @@ -24,16 +24,20 @@ namespace Umbraco.Tests.TestHelpers.Stubs //noop } - public IIndex GetIndex(string indexerName) + public bool TryGetIndex(string indexName, out IIndex index) { - return _indexers.TryGetValue(indexerName, out var indexer) ? indexer : null; + return _indexers.TryGetValue(indexName, out index); } - public ISearcher GetSearcher(string searcherName) + public bool TryGetSearcher(string searcherName, out ISearcher searcher) { - return _searchers.TryGetValue(searcherName, out var indexer) ? indexer : null; + return _searchers.TryGetValue(searcherName, out searcher); } + public IEnumerable Indexes => _indexers.Values; + + public IEnumerable RegisteredSearchers => _searchers.Values; + public IReadOnlyDictionary IndexProviders => _indexers; } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 9e6a7734f3..6ddf99822b 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -77,7 +77,7 @@ - + 1.8.9 diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index e5d7598080..b478b8f8fc 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -151,7 +151,7 @@ namespace Umbraco.Tests.UmbracoExamine Directory luceneDir, Analyzer analyzer = null, ILocalizationService languageService = null, - IValueSetValidator validator = null) + IContentValueSetValidator validator = null) { if (languageService == null) languageService = GetMockLocalizationService(); diff --git a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs index 0df4750051..6458cd2a80 100644 --- a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs @@ -19,14 +19,14 @@ namespace Umbraco.Tests.UmbracoExamine { var validator = new ContentValueSetValidator(true, true, Mock.Of()); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); - result = validator.Validate(new ValueSet("777", IndexTypes.Media, new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("777", IndexTypes.Media, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); - result = validator.Validate(new ValueSet("555", "invalid-category", new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", "invalid-category", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); } @@ -35,11 +35,11 @@ namespace Umbraco.Tests.UmbracoExamine { var validator = new ContentValueSetValidator(true, true, Mock.Of()); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } [Test] @@ -47,17 +47,17 @@ namespace Umbraco.Tests.UmbracoExamine { var validator = new ContentValueSetValidator(true, true, Mock.Of(), 555); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,444" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,444" })); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555,777" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555,777" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555,777,999" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555,777,999" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } [Test] @@ -67,9 +67,9 @@ namespace Umbraco.Tests.UmbracoExamine new[] { "hello", "world" }, null); - var valueSet = new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); + var valueSet = ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); var result = validator.Validate(valueSet); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); Assert.IsFalse(valueSet.Values.ContainsKey("path")); Assert.IsTrue(valueSet.Values.ContainsKey("hello")); @@ -83,9 +83,9 @@ namespace Umbraco.Tests.UmbracoExamine null, new[] { "hello", "world" }); - var valueSet = new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); + var valueSet = ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); var result = validator.Validate(valueSet); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); Assert.IsTrue(valueSet.Values.ContainsKey("path")); Assert.IsFalse(valueSet.Values.ContainsKey("hello")); @@ -99,9 +99,9 @@ namespace Umbraco.Tests.UmbracoExamine new[] { "hello", "world" }, new[] { "world" }); - var valueSet = new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); + var valueSet = ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); var result = validator.Validate(valueSet); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); Assert.IsFalse(valueSet.Values.ContainsKey("path")); Assert.IsTrue(valueSet.Values.ContainsKey("hello")); @@ -114,14 +114,14 @@ namespace Umbraco.Tests.UmbracoExamine var validator = new ContentValueSetValidator(true, true, Mock.Of(), includeItemTypes: new List { "include-content" }); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, "include-content", new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "include-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } [Test] @@ -130,14 +130,14 @@ namespace Umbraco.Tests.UmbracoExamine var validator = new ContentValueSetValidator(true, true, Mock.Of(), excludeItemTypes: new List { "exclude-content" }); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, "exclude-content", new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "exclude-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); } [Test] @@ -147,17 +147,17 @@ namespace Umbraco.Tests.UmbracoExamine includeItemTypes: new List { "include-content", "exclude-content" }, excludeItemTypes: new List { "exclude-content" }); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, "exclude-content", new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "exclude-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, "include-content", new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "include-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } [Test] @@ -165,14 +165,14 @@ namespace Umbraco.Tests.UmbracoExamine { var validator = new ContentValueSetValidator(false, false, Mock.Of()); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555,777" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555,777" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); result = validator.Validate(new ValueSet("555", IndexTypes.Content, new Dictionary @@ -181,7 +181,7 @@ namespace Umbraco.Tests.UmbracoExamine ["path"] = "-1,555", [UmbracoExamineIndexer.PublishedFieldName] = 1 })); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } [Test] @@ -189,15 +189,15 @@ namespace Umbraco.Tests.UmbracoExamine { var validator = new ContentValueSetValidator(false, false, Mock.Of()); - var result = validator.Validate(new ValueSet("555", IndexTypes.Media, new { hello = "world", path = "-1,-21,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Media, new { hello = "world", path = "-1,-21,555" })); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Media, new { hello = "world", path = "-1,-21,555,777" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Media, new { hello = "world", path = "-1,-21,555,777" })); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); + + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Media, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Media, new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); - } [Test] @@ -205,8 +205,8 @@ namespace Umbraco.Tests.UmbracoExamine { var validator = new ContentValueSetValidator(false, true, Mock.Of()); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); result = validator.Validate(new ValueSet("555", IndexTypes.Content, new Dictionary @@ -215,7 +215,7 @@ namespace Umbraco.Tests.UmbracoExamine ["path"] = "-1,555", [UmbracoExamineIndexer.PublishedFieldName] = 0 })); - Assert.IsFalse(result); + Assert.AreEqual(ValueSetValidationResult.Failed, result); result = validator.Validate(new ValueSet("555", IndexTypes.Content, new Dictionary @@ -224,7 +224,7 @@ namespace Umbraco.Tests.UmbracoExamine ["path"] = "-1,555", [UmbracoExamineIndexer.PublishedFieldName] = 1 })); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } [Test] @@ -240,7 +240,7 @@ namespace Umbraco.Tests.UmbracoExamine [UmbracoContentIndexer.VariesByCultureFieldName] = 1, [UmbracoExamineIndexer.PublishedFieldName] = 0 })); - Assert.IsFalse(result); + Assert.AreEqual(ValueSetValidationResult.Failed, result); result = validator.Validate(new ValueSet("555", IndexTypes.Content, new Dictionary @@ -250,7 +250,7 @@ namespace Umbraco.Tests.UmbracoExamine [UmbracoContentIndexer.VariesByCultureFieldName] = 1, [UmbracoExamineIndexer.PublishedFieldName] = 1 })); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Valid, result); var valueSet = new ValueSet("555", IndexTypes.Content, new Dictionary @@ -272,7 +272,7 @@ namespace Umbraco.Tests.UmbracoExamine Assert.IsTrue(valueSet.Values.ContainsKey("title_es-ES")); result = validator.Validate(valueSet); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); Assert.AreEqual(7, valueSet.Values.Count()); //filtered to 7 values (removes es-es values) Assert.IsFalse(valueSet.Values.ContainsKey($"{UmbracoExamineIndexer.PublishedFieldName}_es-es")); @@ -290,11 +290,11 @@ namespace Umbraco.Tests.UmbracoExamine .Returns(Attempt.Fail()); var validator = new ContentValueSetValidator(true, false, publicAccessService.Object); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); - result = validator.Validate(new ValueSet("777", IndexTypes.Content, new { hello = "world", path = "-1,777" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("777", IndexTypes.Content, new { hello = "world", path = "-1,777" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } } } diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 5f3b95844c..8278dac171 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,10 +88,9 @@ - + - diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index bf5181ba5e..31109485f6 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Models; using Constants = Umbraco.Core.Constants; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using System.Web.Http.Controllers; +using Examine; using Umbraco.Core.Models.Entities; using Umbraco.Core.Xml; using Umbraco.Web.Models.Mapping; @@ -53,12 +54,13 @@ namespace Umbraco.Web.Editors } } - private readonly UmbracoTreeSearcher _treeSearcher = new UmbracoTreeSearcher(); + private readonly UmbracoTreeSearcher _treeSearcher; private readonly SearchableTreeCollection _searchableTreeCollection; - public EntityController(SearchableTreeCollection searchableTreeCollection) + public EntityController(SearchableTreeCollection searchableTreeCollection, UmbracoTreeSearcher treeSearcher) { _searchableTreeCollection = searchableTreeCollection; + _treeSearcher = treeSearcher; } /// @@ -410,22 +412,18 @@ namespace Umbraco.Web.Editors Direction orderDirection = Direction.Ascending, string filter = "") { - int intId; - - if (int.TryParse(id, out intId)) + if (int.TryParse(id, out var intId)) { return GetPagedChildren(intId, type, pageNumber, pageSize, orderBy, orderDirection, filter); } - Guid guidId; - if (Guid.TryParse(id, out guidId)) + if (Guid.TryParse(id, out _)) { //Not supported currently throw new HttpResponseException(HttpStatusCode.NotFound); } - Udi udiId; - if (Udi.TryParse(id, out udiId)) + if (Udi.TryParse(id, out _)) { //Not supported currently throw new HttpResponseException(HttpStatusCode.NotFound); @@ -443,8 +441,7 @@ namespace Umbraco.Web.Editors //the EntityService cannot search members of a certain type, this is currently not supported and would require //quite a bit of plumbing to do in the Services/Repository, we'll revert to a paged search - long total; - var searchResult = _treeSearcher.ExamineSearch(Umbraco, filter ?? "", type, pageSize, pageNumber - 1, out total, id); + var searchResult = _treeSearcher.ExamineSearch(filter ?? "", type, pageSize, pageNumber - 1, out long total, id); return new PagedResult(total, pageNumber, pageSize) { @@ -480,8 +477,7 @@ namespace Umbraco.Web.Editors var objectType = ConvertToObjectType(type); if (objectType.HasValue) { - long totalRecords; - var entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out totalRecords, orderBy, orderDirection, filter); + var entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out var totalRecords, orderBy, orderDirection, filter); if (totalRecords == 0) { @@ -596,13 +592,9 @@ namespace Umbraco.Web.Editors /// private IEnumerable ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null) { - long total; - return _treeSearcher.ExamineSearch(Umbraco, query, entityType, 200, 0, out total, searchFrom); + return _treeSearcher.ExamineSearch(query, entityType, 200, 0, out _, searchFrom); } - - - private IEnumerable GetResultForChildren(int id, UmbracoEntityTypes entityType) { var objectType = ConvertToObjectType(entityType); diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs index 999b5bd41e..c166162930 100644 --- a/src/Umbraco.Web/Editors/ExamineManagementController.cs +++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs @@ -43,7 +43,7 @@ namespace Umbraco.Web.Editors /// public IEnumerable GetIndexerDetails() { - return _examineManager.IndexProviders.Select(CreateModel).OrderBy(x => x.Name.TrimEnd("Indexer")); + return _examineManager.Indexes.Select(CreateModel).OrderBy(x => x.Name.TrimEnd("Indexer")); } /// @@ -53,32 +53,8 @@ namespace Umbraco.Web.Editors public IEnumerable GetSearcherDetails() { var model = new List( - _examineManager.IndexProviders.Select(indexer => - { - var searcher = indexer.Value.GetSearcher(); - var searcherName = (searcher as BaseLuceneSearcher)?.Name ?? string.Concat(indexer.Key, "Searcher"); - - var indexerModel = new ExamineSearcherModel - { - Name = searcherName - }; - var props = TypeHelper.CachedDiscoverableProperties(searcher.GetType(), mustWrite: false) - //ignore these properties - .Where(x => new[] { "Description" }.InvariantContains(x.Name) == false) - .Where(x => x.GetCustomAttribute() - ?.State != EditorBrowsableState.Never) - .OrderBy(x => x.Name); - foreach (var p in props) - { - indexerModel.ProviderProperties.Add(p.Name, p.GetValue(searcher, null)?.ToString()); - } - - return indexerModel; - }).OrderBy(x => - { - //order by name , but strip the "Searcher" from the end if it exists - return x.Name.TrimEnd("Searcher"); - })); + _examineManager.RegisteredSearchers.Select(searcher => new ExamineSearcherModel{Name = searcher.Name}) + .OrderBy(x => x.Name.TrimEnd("Searcher"))); //order by name , but strip the "Searcher" from the end if it exists return model; } @@ -138,7 +114,7 @@ namespace Umbraco.Web.Editors //if its still there then it's not done return found != null ? null - : CreateModel(new KeyValuePair(indexName, index)); + : CreateModel(index); } @@ -197,13 +173,12 @@ namespace Umbraco.Web.Editors - private ExamineIndexModel CreateModel(KeyValuePair indexerKeyVal) + private ExamineIndexModel CreateModel(IIndex index) { - var indexer = indexerKeyVal.Value; - var indexName = indexerKeyVal.Key; + var indexName = index.Name; - if (!(indexer is IIndexDiagnostics indexDiag)) - indexDiag = new GenericIndexDiagnostics(indexer); + if (!(index is IIndexDiagnostics indexDiag)) + indexDiag = new GenericIndexDiagnostics(index); var isHealth = indexDiag.IsHealthy(); @@ -229,10 +204,10 @@ namespace Umbraco.Web.Editors private HttpResponseMessage ValidateLuceneSearcher(string searcherName, out LuceneSearcher searcher) { - foreach (var indexer in _examineManager.IndexProviders) + foreach (var indexer in _examineManager.Indexes) { - var s = indexer.Value.GetSearcher(); - var sName = (s as BaseLuceneSearcher)?.Name ?? string.Concat(indexer.Key, "Searcher"); + var s = indexer.GetSearcher(); + var sName = (s as BaseLuceneSearcher)?.Name ?? string.Concat(indexer.Name, "Searcher"); if (sName != searcherName) { continue; @@ -277,10 +252,9 @@ namespace Umbraco.Web.Editors { index = null; - if (_examineManager.IndexProviders.ContainsKey(indexName)) + if (_examineManager.TryGetIndex(indexName, out index)) { //return Ok! - index = _examineManager.GetIndex(indexName); return Request.CreateResponse(HttpStatusCode.OK); } diff --git a/src/Umbraco.Web/IPublishedContentQuery.cs b/src/Umbraco.Web/IPublishedContentQuery.cs index 055dcbe181..80af19c941 100644 --- a/src/Umbraco.Web/IPublishedContentQuery.cs +++ b/src/Umbraco.Web/IPublishedContentQuery.cs @@ -49,6 +49,6 @@ namespace Umbraco.Web /// /// Searches content. /// - IEnumerable Search(int skip, int take, out int totalRecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.ISearcher searchProvider = null); + IEnumerable Search(int skip, int take, out int totalRecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.ISearcher searcher = null); } } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 45f32353da..ccfe947515 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -240,8 +240,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache try { - //by default use the internal index - return eMgr.GetSearcher(Constants.Examine.InternalIndexer); + if (eMgr.TryGetIndex(Constants.Examine.InternalIndexer, out var index)) + return index.GetSearcher(); + throw new InvalidOperationException($"No index found by name {Constants.Examine.InternalIndexer}"); } catch (FileNotFoundException) { diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index f0ddf62074..2bc0d7be3f 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -282,16 +282,15 @@ namespace Umbraco.Web #region Search - public static IEnumerable Search(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null) + public static IEnumerable SearchDescendants(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null) { - //TODO: we should pass in the IExamineManager? + //fixme: pass in the IExamineManager - var searcher = string.IsNullOrEmpty(indexName) - ? ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer) - : ExamineManager.Instance.GetSearcher(indexName); + indexName = string.IsNullOrEmpty(indexName) ? Constants.Examine.ExternalIndexer : indexName; + if (!ExamineManager.Instance.TryGetIndex(indexName, out var index)) + throw new InvalidOperationException("No index found with name " + indexName); - if (searcher == null) - throw new InvalidOperationException("No searcher found for index " + indexName); + var searcher = index.GetSearcher(); var t = term.Escape().Value; if (useWildCards) @@ -303,21 +302,15 @@ namespace Umbraco.Web return content.Search(crit, searcher); } - public static IEnumerable SearchDescendants(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null) - { - return content.Search(term, useWildCards, indexName); - } - public static IEnumerable SearchChildren(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null) { - //TODO: we should pass in the IExamineManager? + //fixme: pass in the IExamineManager - var searcher = string.IsNullOrEmpty(indexName) - ? ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer) - : ExamineManager.Instance.GetSearcher(indexName); + indexName = string.IsNullOrEmpty(indexName) ? Constants.Examine.ExternalIndexer : indexName; + if (!ExamineManager.Instance.TryGetIndex(indexName, out var index)) + throw new InvalidOperationException("No index found with name " + indexName); - if (searcher == null) - throw new InvalidOperationException("No searcher found for index " + indexName); + var searcher = index.GetSearcher(); var t = term.Escape().Value; if (useWildCards) @@ -329,13 +322,17 @@ namespace Umbraco.Web return content.Search(crit, searcher); } - public static IEnumerable Search(this IPublishedContent content, Examine.SearchCriteria.ISearchCriteria criteria, Examine.ISearcher searchProvider = null) + public static IEnumerable Search(this IPublishedContent content, Examine.SearchCriteria.ISearchCriteria criteria, ISearcher searchProvider = null) { - //TODO: we should pass in the IExamineManager? + //fixme: pass in the IExamineManager - var s = searchProvider ?? ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer); - - var results = s.Search(criteria); + if (searchProvider == null) + { + if (!ExamineManager.Instance.TryGetIndex(Constants.Examine.ExternalIndexer, out var index)) + throw new InvalidOperationException("No index found with name " + Constants.Examine.ExternalIndexer); + searchProvider = index.GetSearcher(); + } + var results = searchProvider.Search(criteria); return results.ToPublishedSearchResults(UmbracoContext.Current.ContentCache); } diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs index 8ad40887cc..ed42181234 100644 --- a/src/Umbraco.Web/PublishedContentQuery.cs +++ b/src/Umbraco.Web/PublishedContentQuery.cs @@ -229,17 +229,18 @@ namespace Umbraco.Web /// public IEnumerable Search(int skip, int take, out int totalRecords, string term, bool useWildCards = true, string indexName = null) { - //TODO: Can we inject IExamineManager? + //fixme: inject IExamineManager if (_query != null) return _query.Search(skip, take, out totalRecords, term, useWildCards, indexName); - var indexer = string.IsNullOrEmpty(indexName) - ? Examine.ExamineManager.Instance.GetIndex(Constants.Examine.ExternalIndexer) - : Examine.ExamineManager.Instance.GetIndex(indexName); + indexName = string.IsNullOrEmpty(indexName) + ? Constants.Examine.ExternalIndexer + : indexName; - if (indexer == null) throw new InvalidOperationException("No index found by name " + indexName); + if (!ExamineManager.Instance.TryGetIndex(indexName, out var index)) + throw new InvalidOperationException($"No index found by name {indexName}"); - var searcher = indexer.GetSearcher(); + var searcher = index.GetSearcher(); if (skip == 0 && take == 0) { @@ -248,24 +249,28 @@ namespace Umbraco.Web return results.ToPublishedSearchResults(_contentCache); } - var criteria = SearchAllFields(term, useWildCards, searcher, indexer); + var criteria = SearchAllFields(term, useWildCards, searcher, index); return Search(skip, take, out totalRecords, criteria, searcher); } /// - public IEnumerable Search(ISearchCriteria criteria, Examine.ISearcher searchProvider = null) + public IEnumerable Search(ISearchCriteria criteria, ISearcher searchProvider = null) { return Search(0, 0, out _, criteria, searchProvider); } /// - public IEnumerable Search(int skip, int take, out int totalRecords, ISearchCriteria criteria, Examine.ISearcher searchProvider = null) + public IEnumerable Search(int skip, int take, out int totalRecords, ISearchCriteria criteria, ISearcher searcher = null) { - if (_query != null) return _query.Search(skip, take, out totalRecords, criteria, searchProvider); + if (_query != null) return _query.Search(skip, take, out totalRecords, criteria, searcher); - //TODO: Can we inject IExamineManager? - - var searcher = searchProvider ?? Examine.ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer); + //fixme: inject IExamineManager + if (searcher == null) + { + if (!ExamineManager.Instance.TryGetIndex(Constants.Examine.ExternalIndexer, out var index)) + throw new InvalidOperationException($"No index found by name {Constants.Examine.ExternalIndexer}"); + searcher = index.GetSearcher(); + } var results = skip == 0 && take == 0 ? searcher.Search(criteria) @@ -278,7 +283,7 @@ namespace Umbraco.Web /// /// Creates an ISearchCriteria for searching all fields in a . /// - private ISearchCriteria SearchAllFields(string searchText, bool useWildcards, Examine.ISearcher searcher, Examine.IIndex indexer) + private ISearchCriteria SearchAllFields(string searchText, bool useWildcards, ISearcher searcher, IIndex indexer) { var sc = searcher.CreateCriteria(); diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 1af24db636..1f6210b6a1 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -129,6 +129,8 @@ namespace Umbraco.Web.Runtime composition.Container.RegisterCollectionBuilder() .Add(() => typeLoader.GetTypes()); // fixme which searchable trees?! + composition.Container.Register(new PerRequestLifeTime()); + composition.Container.RegisterCollectionBuilder() .Add(() => typeLoader.GetTypes()); diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index d6d5a5f444..fa22da5ae1 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -125,13 +125,11 @@ namespace Umbraco.Web.Search //create the indexes and register them with the manager foreach(var index in indexCreator.Create()) - { _examineManager.AddIndex(index); - } profilingLogger.Logger.Debug("Examine shutdown registered with MainDom"); - var registeredIndexers = examineManager.IndexProviders.Values.OfType().Count(x => x.EnableDefaultEventHandler); + var registeredIndexers = examineManager.Indexes.OfType().Count(x => x.EnableDefaultEventHandler); profilingLogger.Logger.Info("Adding examine event handlers for {RegisteredIndexers} index providers.", registeredIndexers); @@ -205,7 +203,7 @@ namespace Umbraco.Web.Search _isConfigured = true; - foreach (var luceneIndexer in examineManager.IndexProviders.Values.OfType()) + foreach (var luceneIndexer in examineManager.Indexes.OfType()) { //We now need to disable waiting for indexing for Examine so that the appdomain is shutdown immediately and doesn't wait for pending //indexing operations. We used to wait for indexing operations to complete but this can cause more problems than that is worth because @@ -377,7 +375,7 @@ namespace Umbraco.Web.Search //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()) + foreach (var index in _examineManager.Indexes.OfType()) { var searcher = index.GetSearcher(); @@ -707,7 +705,7 @@ namespace Umbraco.Web.Search { var valueSet = examineComponent._contentValueSetBuilder.GetValueSets(content).ToList(); - foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + foreach (var index in examineComponent._examineManager.Indexes.OfType() // only for the specified indexers .Where(x => supportUnpublished.HasValue == false || supportUnpublished.Value == x.SupportSoftDelete) .Where(x => x.EnableDefaultEventHandler)) @@ -739,7 +737,7 @@ namespace Umbraco.Web.Search { var valueSet = examineComponent._mediaValueSetBuilder.GetValueSets(media).ToList(); - foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + foreach (var index in examineComponent._examineManager.Indexes.OfType() // index this item for all indexers if the media is not trashed, otherwise if the item is trashed // then only index this for indexers supporting unpublished media .Where(x => isPublished || (x.SupportSoftDelete)) @@ -769,7 +767,7 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, IMember member) { var valueSet = examineComponent._memberValueSetBuilder.GetValueSets(member).ToList(); - foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + foreach (var index in examineComponent._examineManager.Indexes.OfType() //ensure that only the providers are flagged to listen execute .Where(x => x.EnableDefaultEventHandler)) { @@ -799,7 +797,7 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, int id, bool keepIfUnpublished) { var strId = id.ToString(CultureInfo.InvariantCulture); - foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + foreach (var index in examineComponent._examineManager.Indexes.OfType() // if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content, // otherwise if keepIfUnpublished == false then remove from all indexes .Where(x => keepIfUnpublished == false || x.SupportSoftDelete == false) diff --git a/src/Umbraco.Web/Search/ExamineSearcherModel.cs b/src/Umbraco.Web/Search/ExamineSearcherModel.cs index 3ad932efa2..8b9badfcfa 100644 --- a/src/Umbraco.Web/Search/ExamineSearcherModel.cs +++ b/src/Umbraco.Web/Search/ExamineSearcherModel.cs @@ -11,26 +11,11 @@ namespace Umbraco.Web.Search { public ExamineSearcherModel() { - ProviderProperties = new Dictionary(); } - /// - /// If the index is not healthy this represents the index error state - /// - [DataMember(Name = "error")] - public string Error { get; set; } - - /// - /// If the index can be open/read - /// - [DataMember(Name = "isHealthy")] - public bool IsHealthy { get; set; } - [DataMember(Name = "name")] public string Name { get; set; } - [DataMember(Name = "providerProperties")] - public IDictionary ProviderProperties { get; private set; } } } diff --git a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs index 53bac7769c..dd64d9b142 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs @@ -110,12 +110,12 @@ namespace Umbraco.Web.Search return luceneDir; } - public virtual IValueSetValidator GetContentValueSetValidator() + public virtual IContentValueSetValidator GetContentValueSetValidator() { return new ContentValueSetValidator(true, true, PublicAccessService); } - public virtual IValueSetValidator GetPublishedContentValueSetValidator() + public virtual IContentValueSetValidator GetPublishedContentValueSetValidator() { return new ContentValueSetValidator(false, false, PublicAccessService); } diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index 6290307926..9b98d02a10 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -11,15 +11,27 @@ using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Trees; namespace Umbraco.Web.Search { - internal class UmbracoTreeSearcher + /// + /// Used for internal Umbraco implementations of + /// + public class UmbracoTreeSearcher { + private readonly IExamineManager _examineManager; + private readonly UmbracoHelper _umbracoHelper; + + public UmbracoTreeSearcher(IExamineManager examineManager, UmbracoHelper umbracoHelper) + { + _examineManager = examineManager ?? throw new ArgumentNullException(nameof(examineManager)); + _umbracoHelper = umbracoHelper ?? throw new ArgumentNullException(nameof(umbracoHelper)); + } + /// /// Searches for results based on the entity type /// - /// /// /// /// @@ -30,7 +42,6 @@ namespace Umbraco.Web.Search /// /// public IEnumerable ExamineSearch( - UmbracoHelper umbracoHelper, string query, UmbracoEntityTypes entityType, int pageSize, @@ -39,16 +50,16 @@ namespace Umbraco.Web.Search var sb = new StringBuilder(); string type; - var indexer = Constants.Examine.InternalIndexer; + var indexName = Constants.Examine.InternalIndexer; var fields = new[] { "id", "__NodeId" }; - var umbracoContext = umbracoHelper.UmbracoContext; + var umbracoContext = _umbracoHelper.UmbracoContext; //TODO: WE should really just allow passing in a lucene raw query switch (entityType) { case UmbracoEntityTypes.Member: - indexer = Constants.Examine.InternalMemberIndexer; + indexName = Constants.Examine.InternalMemberIndexer; type = "member"; fields = new[] { "id", "__NodeId", "email", "loginName" }; if (searchFrom != null && searchFrom != Constants.Conventions.MemberTypes.AllMembersListId && searchFrom.Trim() != "-1") @@ -72,7 +83,10 @@ namespace Umbraco.Web.Search throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType); } - var internalSearcher = ExamineManager.Instance.GetSearcher(indexer); + if (!_examineManager.TryGetIndex(indexName, out var index)) + throw new InvalidOperationException("No index found by name " + indexName); + + var internalSearcher = index.GetSearcher(); //build a lucene query: // the __nodeName will be boosted 10x without wildcards @@ -197,7 +211,7 @@ namespace Umbraco.Web.Search case UmbracoEntityTypes.Media: return MediaFromSearchResults(pagedResult); case UmbracoEntityTypes.Document: - return ContentFromSearchResults(umbracoHelper, pagedResult); + return ContentFromSearchResults(pagedResult); default: throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType); } @@ -205,15 +219,13 @@ namespace Umbraco.Web.Search private void AppendPath(StringBuilder sb, UmbracoObjectTypes objectType, int[] startNodeIds, string searchFrom, IEntityService entityService) { - if (sb == null) throw new ArgumentNullException("sb"); - if (entityService == null) throw new ArgumentNullException("entityService"); + if (sb == null) throw new ArgumentNullException(nameof(sb)); + if (entityService == null) throw new ArgumentNullException(nameof(entityService)); - Udi udi; - Udi.TryParse(searchFrom, true, out udi); + Udi.TryParse(searchFrom, true, out var udi); searchFrom = udi == null ? searchFrom : entityService.GetId(udi).Result.ToString(); - int searchFromId; - var entityPath = int.TryParse(searchFrom, out searchFromId) && searchFromId > 0 + var entityPath = int.TryParse(searchFrom, out var searchFromId) && searchFromId > 0 ? entityService.GetAllPaths(objectType, searchFromId).FirstOrDefault() : null; if (entityPath != null) @@ -284,8 +296,7 @@ namespace Umbraco.Web.Search } if (searchResult.Values.ContainsKey("__key") && searchResult.Values["__key"] != null) { - Guid key; - if (Guid.TryParse(searchResult.Values["__key"], out key)) + if (Guid.TryParse(searchResult.Values["__key"], out var key)) { m.Key = key; } @@ -317,10 +328,9 @@ namespace Umbraco.Web.Search /// /// Returns a collection of entities for content based on search results /// - /// /// /// - private IEnumerable ContentFromSearchResults(UmbracoHelper umbracoHelper, IEnumerable results) + private IEnumerable ContentFromSearchResults(IEnumerable results) { var mapped = Mapper.Map>(results).ToArray(); //add additional data @@ -329,7 +339,7 @@ namespace Umbraco.Web.Search var intId = m.Id.TryConvertTo(); if (intId.Success) { - m.AdditionalData["Url"] = umbracoHelper.Url(intId.Result); + m.AdditionalData["Url"] = _umbracoHelper.Url(intId.Result); } } return mapped; diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index c0b2126d5e..35c335f06d 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -35,7 +35,12 @@ namespace Umbraco.Web.Trees [SearchableTree("searchResultFormatter", "configureContentResult")] public class ContentTreeController : ContentTreeControllerBase, ISearchableTree { - private readonly UmbracoTreeSearcher _treeSearcher = new UmbracoTreeSearcher(); + private readonly UmbracoTreeSearcher _treeSearcher; + + public ContentTreeController(UmbracoTreeSearcher treeSearcher) + { + _treeSearcher = treeSearcher; + } protected override int RecycleBinId => Constants.System.RecycleBinContent; @@ -316,7 +321,7 @@ namespace Umbraco.Web.Trees public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) { - return _treeSearcher.ExamineSearch(Umbraco, query, UmbracoEntityTypes.Document, pageSize, pageIndex, out totalFound, searchFrom); + return _treeSearcher.ExamineSearch(query, UmbracoEntityTypes.Document, pageSize, pageIndex, out totalFound, searchFrom); } } } diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs index 20804d4cf8..4efddfb4b3 100644 --- a/src/Umbraco.Web/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTreeController.cs @@ -35,7 +35,12 @@ namespace Umbraco.Web.Trees [SearchableTree("searchResultFormatter", "configureMediaResult")] public class MediaTreeController : ContentTreeControllerBase, ISearchableTree { - private readonly UmbracoTreeSearcher _treeSearcher = new UmbracoTreeSearcher(); + private readonly UmbracoTreeSearcher _treeSearcher; + + public MediaTreeController(UmbracoTreeSearcher treeSearcher) + { + _treeSearcher = treeSearcher; + } protected override int RecycleBinId => Constants.System.RecycleBinMedia; @@ -161,7 +166,7 @@ namespace Umbraco.Web.Trees public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) { - return _treeSearcher.ExamineSearch(Umbraco, query, UmbracoEntityTypes.Media, pageSize, pageIndex, out totalFound, searchFrom); + return _treeSearcher.ExamineSearch(query, UmbracoEntityTypes.Media, pageSize, pageIndex, out totalFound, searchFrom); } internal override IEnumerable GetChildrenFromEntityService(int entityId) diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs index 68819351c0..3317bfbdf9 100644 --- a/src/Umbraco.Web/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTreeController.cs @@ -33,13 +33,14 @@ namespace Umbraco.Web.Trees [SearchableTree("searchResultFormatter", "configureMemberResult")] public class MemberTreeController : TreeController, ISearchableTree { - public MemberTreeController() + public MemberTreeController(UmbracoTreeSearcher treeSearcher) { + _treeSearcher = treeSearcher; _provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); _isUmbracoProvider = _provider.IsUmbracoMembershipProvider(); } - private readonly UmbracoTreeSearcher _treeSearcher = new UmbracoTreeSearcher(); + private readonly UmbracoTreeSearcher _treeSearcher; private readonly MembershipProvider _provider; private readonly bool _isUmbracoProvider; @@ -193,7 +194,7 @@ namespace Umbraco.Web.Trees public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) { - return _treeSearcher.ExamineSearch(Umbraco, query, UmbracoEntityTypes.Member, pageSize, pageIndex, out totalFound, searchFrom); + return _treeSearcher.ExamineSearch(query, UmbracoEntityTypes.Member, pageSize, pageIndex, out totalFound, searchFrom); } } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 19c1e59bd1..bca87b02ac 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -62,7 +62,7 @@ - + 2.6.2.25