Allow indexing variant nodes when not all variants are published - fixes issues 11383. (#12669)

* This fixes issues 11383.
The introduction of the new Examine library caused an unintended consequence that it stopped indexing of nodes with language variants on them when one of the variants was unpublished.

These changes align ValueSetValidationStatus.Filtered to indicate that a node is intended as filtered out of a search, not that parts of its contents had been excluded from the result.

This brings it inline with how it is used in Umbraco.Examine.Lucene/UmbracoContentIndex

Unit tests changed to indicate the intent of ValueSetValidationStatus.Filtered

Change to UmbracoViewPage to make model variable nullable (because the solution wouldn't build otherwise on 2022)

* revert to use explicit type instead of var

Co-authored-by: Nathan Woulfe <nathan@nathanw.com.au>
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
Jonny Muir
2022-12-06 07:17:58 +00:00
committed by Bjarke Berg
parent 2143d8c3fe
commit 4e823982c7
4 changed files with 33 additions and 21 deletions

View File

@@ -13,7 +13,7 @@ namespace Umbraco.Cms.Infrastructure.Examine;
public class ContentValueSetValidator : ValueSetValidator, IContentValueSetValidator
{
private const string PathKey = "path";
private static readonly IEnumerable<string> ValidCategories = new[] {IndexTypes.Content, IndexTypes.Media};
private static readonly IEnumerable<string> ValidCategories = new[] { IndexTypes.Content, IndexTypes.Media };
private readonly IPublicAccessService? _publicAccessService;
private readonly IScopeProvider? _scopeProvider;
@@ -105,6 +105,12 @@ public class ContentValueSetValidator : ValueSetValidator, IContentValueSetValid
public override ValueSetValidationResult Validate(ValueSet valueSet)
{
// Notes on status on the result:
// A result status of filtered means that this whole value set result is to be filtered from the index
// For example the path is incorrect or it is in the recycle bin
// It does not mean that the values it contains have been through a filtering (for example if an language variant is not published)
// See notes on issue 11383
ValueSetValidationResult baseValidate = base.Validate(valueSet);
valueSet = baseValidate.ValueSet;
if (baseValidate.Status == ValueSetValidationStatus.Failed)
@@ -112,8 +118,6 @@ public class ContentValueSetValidator : ValueSetValidator, IContentValueSetValid
return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
}
var isFiltered = baseValidate.Status == ValueSetValidationStatus.Filtered;
var filteredValues = valueSet.Values.ToDictionary(x => x.Key, x => x.Value.ToList());
//check for published content
if (valueSet.Category == IndexTypes.Content && PublishedValuesOnly)
@@ -133,18 +137,15 @@ public class ContentValueSetValidator : ValueSetValidator, IContentValueSetValid
&& 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 (KeyValuePair<string, IReadOnlyList<object>> publishField in valueSet.Values
.Where(x => x.Key.StartsWith($"{UmbracoExamineFieldNames.PublishedFieldName}_")).ToList())
foreach (KeyValuePair<string, IReadOnlyList<object>> publishField in valueSet.Values.Where(x => x.Key.StartsWith($"{UmbracoExamineFieldNames.PublishedFieldName}_")).ToList())
{
if (publishField.Value.Count <= 0 || !publishField.Value[0].Equals("y"))
{
//this culture is not published, so remove all of these culture values
var cultureSuffix = publishField.Key.Substring(publishField.Key.LastIndexOf('_'));
foreach (KeyValuePair<string, IReadOnlyList<object>> cultureField in valueSet.Values
.Where(x => x.Key.InvariantEndsWith(cultureSuffix)).ToList())
foreach (KeyValuePair<string, IReadOnlyList<object>> cultureField in valueSet.Values.Where(x => x.Key.InvariantEndsWith(cultureSuffix)).ToList())
{
filteredValues.Remove(cultureField.Key);
isFiltered = true;
}
}
}
@@ -175,9 +176,11 @@ public class ContentValueSetValidator : ValueSetValidator, IContentValueSetValid
var path = pathValues[0].ToString();
var filteredValueSet = new ValueSet(valueSet.Id, valueSet.Category, valueSet.ItemType, filteredValues.ToDictionary(x => x.Key, x => (IEnumerable<object>)x.Value));
// 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.
// Therefore we return FILTERED to indicate this whole set needs to be filtered out
if (!ValidatePath(path!, valueSet.Category)
|| !ValidateRecycleBin(path!, valueSet.Category)
|| !ValidateProtectedContent(path!, valueSet.Category))
@@ -185,7 +188,6 @@ public class ContentValueSetValidator : ValueSetValidator, IContentValueSetValid
return new ValueSetValidationResult(ValueSetValidationStatus.Filtered, filteredValueSet);
}
return new ValueSetValidationResult(
isFiltered ? ValueSetValidationStatus.Filtered : ValueSetValidationStatus.Valid, filteredValueSet);
return new ValueSetValidationResult(ValueSetValidationStatus.Valid, filteredValueSet);
}
}

View File

@@ -57,6 +57,12 @@ public class ValueSetValidator : IValueSetValidator
public virtual ValueSetValidationResult Validate(ValueSet valueSet)
{
/* Notes on status on the result:
* A result status of filtered means that this whole value set result is to be filtered from the index
* For example the path is incorrect or it is in the recycle bin
* It does not mean that the values it contains have been through a filtering (for example if an language variant is not published)
* See notes on issue 11383 */
if (ValidIndexCategories != null && !ValidIndexCategories.InvariantContains(valueSet.Category))
{
return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
@@ -74,8 +80,6 @@ public class ValueSetValidator : IValueSetValidator
return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
}
var isFiltered = false;
var filteredValues = valueSet.Values.ToDictionary(x => x.Key, x => x.Value.ToList());
// filter based on the fields provided (if any)
@@ -86,19 +90,17 @@ public class ValueSetValidator : IValueSetValidator
if (IncludeFields != null && !IncludeFields.InvariantContains(key))
{
filteredValues.Remove(key); // remove any value with a key that doesn't match the inclusion list
isFiltered = true;
}
if (ExcludeFields != null && ExcludeFields.InvariantContains(key))
{
filteredValues.Remove(key); // remove any value with a key that matches the exclusion list
isFiltered = true;
}
}
}
var filteredValueSet = new ValueSet(valueSet.Id, valueSet.Category, valueSet.ItemType, filteredValues.ToDictionary(x => x.Key, x => (IEnumerable<object>)x.Value));
return new ValueSetValidationResult(
isFiltered ? ValueSetValidationStatus.Filtered : ValueSetValidationStatus.Valid, filteredValueSet);
return new ValueSetValidationResult(ValueSetValidationStatus.Valid, filteredValueSet);
}
}

View File

@@ -41,7 +41,7 @@ public abstract class UmbracoViewPage<TModel> : RazorPage<TModel>
_helper = Context.RequestServices.GetRequiredService<UmbracoHelper>();
TModel model = ViewData.Model;
TModel? model = ViewData.Model;
var content = model as IPublishedContent;
if (content is null && model is IContentModel contentModel)

View File

@@ -106,7 +106,9 @@ public class UmbracoContentValueSetValidatorTests
"test-content",
new { hello = "world", path = "-1,555", world = "your oyster" });
var result = validator.Validate(valueSet);
Assert.AreEqual(ValueSetValidationStatus.Filtered, result.Status);
// Note - Result is still valid, excluded is not the same as filtered.
Assert.AreEqual(ValueSetValidationStatus.Valid, result.Status);
Assert.IsFalse(result.ValueSet.Values.ContainsKey("path"));
Assert.IsTrue(result.ValueSet.Values.ContainsKey("hello"));
@@ -128,7 +130,9 @@ public class UmbracoContentValueSetValidatorTests
"test-content",
new { hello = "world", path = "-1,555", world = "your oyster" });
var result = validator.Validate(valueSet);
Assert.AreEqual(ValueSetValidationStatus.Filtered, result.Status);
// Note - Result is still valid, excluded is not the same as filtered.
Assert.AreEqual(ValueSetValidationStatus.Valid, result.Status);
Assert.IsTrue(result.ValueSet.Values.ContainsKey("path"));
Assert.IsFalse(result.ValueSet.Values.ContainsKey("hello"));
@@ -150,7 +154,9 @@ public class UmbracoContentValueSetValidatorTests
"test-content",
new { hello = "world", path = "-1,555", world = "your oyster" });
var result = validator.Validate(valueSet);
Assert.AreEqual(ValueSetValidationStatus.Filtered, result.Status);
// Note - Result is still valid, excluded is not the same as filtered.
Assert.AreEqual(ValueSetValidationStatus.Valid, result.Status);
Assert.IsFalse(result.ValueSet.Values.ContainsKey("path"));
Assert.IsTrue(result.ValueSet.Values.ContainsKey("hello"));
@@ -416,7 +422,9 @@ public class UmbracoContentValueSetValidatorTests
Assert.IsTrue(valueSet.Values.ContainsKey("title_es-ES"));
result = validator.Validate(valueSet);
Assert.AreEqual(ValueSetValidationStatus.Filtered, result.Status);
// Note - Result is still valid, excluded is not the same as filtered.
Assert.AreEqual(ValueSetValidationStatus.Valid, result.Status);
Assert.AreEqual(7, result.ValueSet.Values.Count()); // filtered to 7 values (removes es-es values)
Assert.IsFalse(result.ValueSet.Values.ContainsKey($"{UmbracoExamineFieldNames.PublishedFieldName}_es-es"));