Refactoring
This commit is contained in:
@@ -11,11 +11,27 @@
|
||||
/// Defines core supported content fall-back options when retrieving content property values.
|
||||
/// Defined as constants rather than enum to allow solution or package defined fall-back methods.
|
||||
/// </summary>
|
||||
public static class FallbackMethods
|
||||
public static class ValueFallback
|
||||
{
|
||||
public const int None = 0;
|
||||
public const int RecursiveTree = 1;
|
||||
public const int FallbackLanguage = 2;
|
||||
/// <summary>
|
||||
/// No fallback at all.
|
||||
/// </summary>
|
||||
public const int None = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Default fallback.
|
||||
/// </summary>
|
||||
public const int Default = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Recurse up the tree.
|
||||
/// </summary>
|
||||
public const int Recurse = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Fallback to other languages.
|
||||
/// </summary>
|
||||
public const int Language = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,44 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a fallback strategy for getting <see cref="IPublishedElement"/> values.
|
||||
/// </summary>
|
||||
// fixme - IPublishedValueFallback is still WorkInProgress
|
||||
// todo - properly document methods, etc
|
||||
// todo - understand caching vs fallback (recurse etc)
|
||||
public interface IPublishedValueFallback
|
||||
{
|
||||
// fixme discussions & challenges
|
||||
//
|
||||
// - what's with visitedLanguage? should be internal to fallback implementation
|
||||
// so that should be the case now, with latest changes
|
||||
//
|
||||
// - should be as simple as
|
||||
// model.Value("price", fallback: ValueFallback.Language);
|
||||
// model.Value("name", fallback: ValueFallback.Recurse);
|
||||
//
|
||||
// so chaining things through an array of ints is not... convenient
|
||||
// it feels like ppl could have ValueFallback.LanguageAndRecurse or something?
|
||||
//
|
||||
// - the fallback: parameter value must be open, so about anything can be passed to the IPublishedValueFallback
|
||||
// we have it now, it's an integer + constants, cool
|
||||
//
|
||||
// - we need to be able to configure (via code for now) a default fallback policy?
|
||||
// not! the default value of the fallback: parameter is 'default', not 'none', and if people
|
||||
// want to implement a different default behavior, they have to override the fallback provider
|
||||
//
|
||||
// - currently, no policies on IPublishedProperty nor IPublishedElement, but some may apply (language)
|
||||
// todo: implement
|
||||
//
|
||||
// - general defaultValue discussion:
|
||||
// when HasValue is false, the converter may return something, eg an empty enumerable, even though
|
||||
// defaultValue is null, so should we respect defaultValue only when it is not 'default'?
|
||||
// todo: when defaultValue==default, and HasValue is false, still return GetValue to ensure this
|
||||
//
|
||||
// - (and...)
|
||||
// ModelsBuilder model.Value(x => x.Price, ...) extensions need to be adjusted too
|
||||
//
|
||||
// - cache & perfs
|
||||
// soon as ppl implement custom fallbacks, caching is a problem, so better just not cache
|
||||
// OTOH we need to implement the readonly thing for languages
|
||||
|
||||
/// <summary>
|
||||
/// Gets a fallback value for a property.
|
||||
/// </summary>
|
||||
@@ -25,7 +54,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// <para>At property level, property.GetValue() does *not* implement fallback, and one has to
|
||||
/// get property.Value() or property.Value{T}() to trigger fallback.</para>
|
||||
/// </remarks>
|
||||
object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection<int> visitedLanguages);
|
||||
object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a fallback value for a property.
|
||||
@@ -43,7 +72,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// <para>At property level, property.GetValue() does *not* implement fallback, and one has to
|
||||
/// get property.Value() or property.Value{T}() to trigger fallback.</para>
|
||||
/// </remarks>
|
||||
T GetValue<T>(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection<int> visitedLanguages);
|
||||
T GetValue<T>(IPublishedProperty property, string culture, string segment, T defaultValue);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a fallback value for a published element property.
|
||||
@@ -59,7 +88,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// segment, either returned no property at all, or a property with HasValue(culture, segment) being false.</para>
|
||||
/// <para>It can only fallback at element level (no recurse).</para>
|
||||
/// </remarks>
|
||||
object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection<int> visitedLanguages);
|
||||
object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a fallback value for a published element property.
|
||||
@@ -76,7 +105,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// segment, either returned no property at all, or a property with HasValue(culture, segment) being false.</para>
|
||||
/// <para>It can only fallback at element level (no recurse).</para>
|
||||
/// </remarks>
|
||||
T GetValue<T>(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection<int> visitedLanguages);
|
||||
T GetValue<T>(IPublishedElement content, string alias, string culture, string segment, T defaultValue);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a fallback value for a published content property.
|
||||
@@ -92,7 +121,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// segment, either returned no property at all, or a property with HasValue(culture, segment) being false.</para>
|
||||
/// fixme explain & document priority + merge w/recurse?
|
||||
/// </remarks>
|
||||
object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, IEnumerable<int> fallbackMethods, ICollection<int> visitedLanguages);
|
||||
object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, int fallback);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a fallback value for a published content property.
|
||||
@@ -109,6 +138,6 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// segment, either returned no property at all, or a property with HasValue(culture, segment) being false.</para>
|
||||
/// fixme explain & document priority + merge w/recurse?
|
||||
/// </remarks>
|
||||
T GetValue<T>(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable<int> fallbackMethods, ICollection<int> visitedLanguages);
|
||||
T GetValue<T>(IPublishedContent content, string alias, string culture, string segment, T defaultValue, int fallback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a noop implementation for <see cref="IPublishedValueFallback"/>.
|
||||
@@ -11,21 +9,21 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
public class NoopPublishedValueFallback : IPublishedValueFallback
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection<int> visitedLanguages) => defaultValue;
|
||||
public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) => defaultValue;
|
||||
|
||||
/// <inheritdoc />
|
||||
public T GetValue<T>(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection<int> visitedLanguages) => defaultValue;
|
||||
public T GetValue<T>(IPublishedProperty property, string culture, string segment, T defaultValue) => defaultValue;
|
||||
|
||||
/// <inheritdoc />
|
||||
public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection<int> visitedLanguages) => defaultValue;
|
||||
public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) => defaultValue;
|
||||
|
||||
/// <inheritdoc />
|
||||
public T GetValue<T>(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection<int> visitedLanguages) => defaultValue;
|
||||
public T GetValue<T>(IPublishedElement content, string alias, string culture, string segment, T defaultValue) => defaultValue;
|
||||
|
||||
/// <inheritdoc />
|
||||
public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, IEnumerable<int> fallbackMethods, ICollection<int> visitedLanguages) => defaultValue;
|
||||
public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, int fallback) => defaultValue;
|
||||
|
||||
/// <inheritdoc />
|
||||
public T GetValue<T>(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable<int> fallbackMethods, ICollection<int> visitedLanguages) => defaultValue;
|
||||
public T GetValue<T>(IPublishedContent content, string alias, string culture, string segment, T defaultValue, int fallback) => defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback()
|
||||
{
|
||||
var content = UmbracoContext.Current.ContentCache.GetAtRoot().First();
|
||||
var value = content.Value("welcomeText", "es", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage });
|
||||
var value = content.Value("welcomeText", "es", fallback: Core.Constants.Content.ValueFallback.Language);
|
||||
Assert.AreEqual("Welcome", value);
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Over_Two_Levels()
|
||||
{
|
||||
var content = UmbracoContext.Current.ContentCache.GetAtRoot().First();
|
||||
var value = content.Value("welcomeText", "it", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage });
|
||||
var value = content.Value("welcomeText", "it", fallback: Core.Constants.Content.ValueFallback.Language);
|
||||
Assert.AreEqual("Welcome", value);
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
public void Do_Not_GetContent_For_Unpopulated_Requested_Language_With_Fallback_Over_That_Loops()
|
||||
{
|
||||
var content = UmbracoContext.Current.ContentCache.GetAtRoot().First();
|
||||
var value = content.Value("welcomeText", "no", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage });
|
||||
var value = content.Value("welcomeText", "no", fallback: Core.Constants.Content.ValueFallback.Language);
|
||||
Assert.IsNull(value);
|
||||
}
|
||||
|
||||
@@ -199,27 +199,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
public void Can_Get_Content_Recursively()
|
||||
{
|
||||
var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First();
|
||||
var value = content.Value("welcomeText2", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.RecursiveTree });
|
||||
Assert.AreEqual("Welcome", value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Get_Content_With_Recursive_Priority()
|
||||
{
|
||||
var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First();
|
||||
var value = content.Value("welcomeText", "nl", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.RecursiveTree, Core.Constants.Content.FallbackMethods.FallbackLanguage });
|
||||
|
||||
// No Dutch value is directly assigned. Check has fallen back to Dutch value from parent.
|
||||
Assert.AreEqual("Welkom", value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Get_Content_With_Fallback_Language_Priority()
|
||||
{
|
||||
var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First();
|
||||
var value = content.Value("welcomeText", "nl", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage, Core.Constants.Content.FallbackMethods.RecursiveTree });
|
||||
|
||||
// No Dutch value is directly assigned. Check has fallen back to English value from language variant.
|
||||
var value = content.Value("welcomeText2", fallback: Core.Constants.Content.ValueFallback.Recurse);
|
||||
Assert.AreEqual("Welcome", value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
|
||||
Container.RegisterSingleton<IPublishedModelFactory>(f => new PublishedModelFactory(f.GetInstance<TypeLoader>().GetTypes<PublishedContentModel>()));
|
||||
Container.RegisterSingleton<IPublishedContentTypeFactory, PublishedContentTypeFactory>();
|
||||
Container.RegisterSingleton<IPublishedValueFallback, PublishedValueLanguageFallback>();
|
||||
Container.RegisterSingleton<IPublishedValueFallback, PublishedValueFallback>();
|
||||
|
||||
var logger = Mock.Of<ILogger>();
|
||||
var dataTypeService = new TestObjects.TestDataTypeService(
|
||||
@@ -336,8 +336,8 @@ namespace Umbraco.Tests.PublishedContent
|
||||
public void Get_Property_Value_Recursive()
|
||||
{
|
||||
var doc = GetNode(1174);
|
||||
var rVal = doc.Value("testRecursive", fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree } );
|
||||
var nullVal = doc.Value("DoNotFindThis", fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree });
|
||||
var rVal = doc.Value("testRecursive", fallback: Constants.Content.ValueFallback.Recurse);
|
||||
var nullVal = doc.Value("DoNotFindThis", fallback: Constants.Content.ValueFallback.Recurse);
|
||||
Assert.AreEqual("This is the recursive val", rVal);
|
||||
Assert.AreEqual(null, nullVal);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
{
|
||||
base.Compose();
|
||||
|
||||
Container.RegisterSingleton<IPublishedValueFallback, PublishedValueLanguageFallback>();
|
||||
Container.RegisterSingleton<IPublishedValueFallback, PublishedValueFallback>();
|
||||
Container.RegisterSingleton<ProfilingLogger>();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Services;
|
||||
using ValueFallback = Umbraco.Core.Constants.Content.ValueFallback;
|
||||
|
||||
namespace Umbraco.Web.Models.PublishedContent
|
||||
{
|
||||
@@ -9,90 +12,72 @@ namespace Umbraco.Web.Models.PublishedContent
|
||||
/// </summary>
|
||||
public class PublishedValueFallback : IPublishedValueFallback
|
||||
{
|
||||
// kinda reproducing what was available in v7
|
||||
private readonly ILocalizationService _localizationService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedValueFallback"/> class.
|
||||
/// </summary>
|
||||
/// <param name="localizationService"></param>
|
||||
public PublishedValueFallback(ILocalizationService localizationService)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection<int> visitedLanguages)
|
||||
public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue)
|
||||
{
|
||||
// no fallback here
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public T GetValue<T>(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection<int> visitedLanguages)
|
||||
public T GetValue<T>(IPublishedProperty property, string culture, string segment, T defaultValue)
|
||||
{
|
||||
// no fallback here
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection<int> visitedLanguages)
|
||||
public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue)
|
||||
{
|
||||
// no fallback here
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public T GetValue<T>(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection<int> visitedLanguages)
|
||||
public T GetValue<T>(IPublishedElement content, string alias, string culture, string segment, T defaultValue)
|
||||
{
|
||||
// no fallback here
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, IEnumerable<int> fallbackMethods, ICollection<int> visitedLanguages)
|
||||
public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, int fallback)
|
||||
{
|
||||
// is that ok?
|
||||
return GetValue<object>(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages);
|
||||
return GetValue<object>(content, alias, culture, segment, defaultValue, fallback);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public T GetValue<T>(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable<int> fallbackMethods, ICollection<int> visitedLanguages)
|
||||
public virtual T GetValue<T>(IPublishedContent content, string alias, string culture, string segment, T defaultValue, int fallback)
|
||||
{
|
||||
if (fallbackMethods == null)
|
||||
switch (fallback)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
foreach (var fallbackMethod in fallbackMethods)
|
||||
{
|
||||
if (TryGetValueWithFallbackMethod(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages, fallbackMethod, out T value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
protected virtual bool TryGetValueWithFallbackMethod<T>(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable<int> fallbackMethods, ICollection<int> visitedLanguages, int fallbackMethod, out T value)
|
||||
{
|
||||
value = defaultValue;
|
||||
switch (fallbackMethod)
|
||||
{
|
||||
case Core.Constants.Content.FallbackMethods.None:
|
||||
return false;
|
||||
case Core.Constants.Content.FallbackMethods.RecursiveTree:
|
||||
return TryGetValueWithRecursiveTree(content, alias, culture, segment, defaultValue, out value);
|
||||
case ValueFallback.None:
|
||||
case ValueFallback.Default:
|
||||
return defaultValue;
|
||||
case ValueFallback.Recurse:
|
||||
return TryGetValueWithRecursiveFallback(content, alias, culture, segment, defaultValue, out var value1) ? value1 : defaultValue;
|
||||
case ValueFallback.Language:
|
||||
return TryGetValueWithLanguageFallback(content, alias, culture, segment, defaultValue, out var value2) ? value2 : defaultValue;
|
||||
default:
|
||||
throw new NotSupportedException($"Fallback method with indentifying number {fallbackMethod} is not supported within {GetType().Name}.");
|
||||
throw new NotSupportedException($"Fallback {GetType().Name} does not support policy code '{fallback}'.");
|
||||
}
|
||||
}
|
||||
|
||||
protected static bool TryGetValueWithRecursiveTree<T>(IPublishedContent content, string alias, string culture, string segment, T defaultValue, out T value)
|
||||
// tries to get a value, recursing the tree
|
||||
protected static bool TryGetValueWithRecursiveFallback<T>(IPublishedContent content, string alias, string culture, string segment, T defaultValue, out T value)
|
||||
{
|
||||
// Implement recursion as it was implemented in PublishedContentBase
|
||||
|
||||
// fixme caching?
|
||||
//
|
||||
// all caches were using PublishedContentBase.GetProperty(alias, recurse) to get the property,
|
||||
// then,
|
||||
// NuCache.PublishedContent was storing the property in GetAppropriateCache() with key "NuCache.Property.Recurse[" + DraftOrPub(previewing) + contentUid + ":" + typeAlias + "]";
|
||||
// XmlPublishedContent was storing the property in _cacheProvider with key $"XmlPublishedCache.PublishedContentCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}";
|
||||
// DictionaryPublishedContent was storing the property in _cacheProvider with key $"XmlPublishedCache.PublishedMediaCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}";
|
||||
//
|
||||
// at the moment, caching has been entirely removed, until we better understand caching + fallback
|
||||
|
||||
IPublishedProperty property = null; // if we are here, content's property has no value
|
||||
IPublishedProperty noValueProperty = null;
|
||||
do
|
||||
@@ -125,5 +110,44 @@ namespace Umbraco.Web.Models.PublishedContent
|
||||
value = defaultValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
// tries to get a value, falling back onto other languages
|
||||
private bool TryGetValueWithLanguageFallback<T>(IPublishedContent content, string alias, string culture, string segment, T defaultValue, out T value)
|
||||
{
|
||||
value = defaultValue;
|
||||
|
||||
if (culture.IsNullOrWhiteSpace()) return false;
|
||||
|
||||
var visited = new HashSet<int>();
|
||||
|
||||
// fixme
|
||||
// _localizationService.GetXxx() is expensive, it deep clones objects
|
||||
// we want _localizationService.GetReadOnlyXxx() returning IReadOnlyLanguage which cannot be saved back = no need to clone
|
||||
|
||||
var language = _localizationService.GetLanguageByIsoCode(culture);
|
||||
if (language == null) return false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (language.FallbackLanguageId == null) return false;
|
||||
|
||||
var language2Id = language.FallbackLanguageId.Value;
|
||||
if (visited.Contains(language2Id)) return false;
|
||||
visited.Add(language2Id);
|
||||
|
||||
var language2 = _localizationService.GetLanguageById(language2Id);
|
||||
if (language2 == null) return false;
|
||||
var culture2 = language2.IsoCode;
|
||||
|
||||
if (content.HasValue(alias, culture2, segment))
|
||||
{
|
||||
value = content.Value<T>(alias, culture2, segment);
|
||||
return true;
|
||||
}
|
||||
|
||||
language = language2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a default implementation for <see cref="IPublishedValueFallback"/> that allows
|
||||
/// for use of fall-back languages
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Inherits from <see cref="PublishedValueFallback" /> that implments what was available in v7.
|
||||
/// </remarks>
|
||||
public class PublishedValueLanguageFallback : PublishedValueFallback
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
|
||||
public PublishedValueLanguageFallback(ServiceContext services)
|
||||
{
|
||||
_localizationService = services.LocalizationService;
|
||||
}
|
||||
|
||||
protected override bool TryGetValueWithFallbackMethod<T>(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable<int> fallbackMethods, ICollection<int> visitedLanguages, int fallbackMethod, out T value)
|
||||
{
|
||||
value = defaultValue;
|
||||
switch (fallbackMethod)
|
||||
{
|
||||
case Core.Constants.Content.FallbackMethods.None:
|
||||
return false;
|
||||
case Core.Constants.Content.FallbackMethods.RecursiveTree:
|
||||
return TryGetValueWithRecursiveTree(content, alias, culture, segment, defaultValue, out value);
|
||||
case Core.Constants.Content.FallbackMethods.FallbackLanguage:
|
||||
return TryGetValueWithFallbackLanguage(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages, out value);
|
||||
default:
|
||||
throw new NotSupportedException($"Fallback method with indentifying number {fallbackMethod} is not supported within {GetType().Name}.");
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetValueWithFallbackLanguage<T>(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable<int> fallbackMethods, ICollection<int> visitedLanguages, out T value)
|
||||
{
|
||||
value = defaultValue;
|
||||
if (string.IsNullOrEmpty(culture))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var language = _localizationService.GetLanguageByIsoCode(culture);
|
||||
if (language.FallbackLanguageId.HasValue == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AlreadyVisitedLanguage(visitedLanguages, language.FallbackLanguageId.Value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
visitedLanguages.Add(language.FallbackLanguageId.Value);
|
||||
|
||||
var fallbackLanguage = GetLanguageById(language.FallbackLanguageId.Value);
|
||||
value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue, fallbackMethods.ToArray(), visitedLanguages);
|
||||
if (ValueIsNotNullEmptyOrDefault(value, defaultValue))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool AlreadyVisitedLanguage(ICollection<int> visitedLanguages, int fallbackLanguageId)
|
||||
{
|
||||
return visitedLanguages.Contains(fallbackLanguageId);
|
||||
}
|
||||
|
||||
private ILanguage GetLanguageById(int id)
|
||||
{
|
||||
return _localizationService.GetLanguageById(id);
|
||||
}
|
||||
|
||||
private static bool ValueIsNotNullEmptyOrDefault<T>(T value, T defaultValue)
|
||||
{
|
||||
return value != null &&
|
||||
string.IsNullOrEmpty(value.ToString()) == false &&
|
||||
value.Equals(defaultValue) == false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,18 +153,16 @@ namespace Umbraco.Web
|
||||
/// <param name="culture">The variation language.</param>
|
||||
/// <param name="segment">The variation segment.</param>
|
||||
/// <param name="defaultValue">The default value.</param>
|
||||
/// <param name="fallbackMethods">Options for fall-back if content not found.</param>
|
||||
/// <param name="visitedLanguages">A list of cultures already visited in looking for a value via a fall-back method.</param>
|
||||
/// <param name="fallback">Optional fallback strategy.</param>
|
||||
/// <returns>The value of the content's property identified by the alias, if it exists, otherwise a default value.</returns>
|
||||
public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default,
|
||||
int[] fallbackMethods = null, ICollection<int> visitedLanguages = null)
|
||||
public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, int fallback = 0)
|
||||
{
|
||||
var property = content.GetProperty(alias);
|
||||
|
||||
if (property != null && property.HasValue(culture, segment))
|
||||
return property.GetValue(culture, segment);
|
||||
|
||||
return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages ?? new List<int>());
|
||||
return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallback);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -180,24 +178,20 @@ namespace Umbraco.Web
|
||||
/// <param name="culture">The variation language.</param>
|
||||
/// <param name="segment">The variation segment.</param>
|
||||
/// <param name="defaultValue">The default value.</param>
|
||||
/// <param name="fallbackMethods">Options for fall-back if content not found.</param>
|
||||
/// <param name="visitedLanguages">A list of cultures already visited in looking for a value via a fall-back method.</param>
|
||||
/// <param name="fallback">Optional fallback strategy.</param>
|
||||
/// <returns>The value of the content's property identified by the alias, converted to the specified type.</returns>
|
||||
/// <remarks>
|
||||
public static T Value<T>(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default,
|
||||
int[] fallbackMethods = null, ICollection<int> visitedLanguages = null)
|
||||
public static T Value<T>(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, int fallback = 0)
|
||||
{
|
||||
var property = content.GetProperty(alias);
|
||||
|
||||
if (property != null && property.HasValue(culture, segment))
|
||||
return property.Value<T>(culture, segment);
|
||||
|
||||
return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallbackMethods, visitedLanguages ?? new List<int>());
|
||||
return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, fallback);
|
||||
}
|
||||
|
||||
// fixme - .Value() refactoring - in progress
|
||||
public static IHtmlString Value<T>(this IPublishedContent content, string aliases, Func<T, string> format, string alt = "",
|
||||
int[] fallbackMethods = null, ICollection<int> visitedLanguages = null)
|
||||
public static IHtmlString Value<T>(this IPublishedContent content, string aliases, Func<T, string> format, string alt = "", int fallback = 0)
|
||||
{
|
||||
var aliasesA = aliases.Split(',');
|
||||
if (aliasesA.Length == 0)
|
||||
|
||||
@@ -100,7 +100,6 @@ namespace Umbraco.Web
|
||||
/// <param name="culture">The variation language.</param>
|
||||
/// <param name="segment">The variation segment.</param>
|
||||
/// <param name="defaultValue">The default value.</param>
|
||||
/// <param name="visitedLanguages">A list of cultures already visited in looking for a value via a fall-back method.</param>
|
||||
/// <returns>The value of the content's property identified by the alias, if it exists, otherwise a default value.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The value comes from <c>IPublishedProperty</c> field <c>Value</c> ie it is suitable for use when rendering content.</para>
|
||||
@@ -108,14 +107,19 @@ namespace Umbraco.Web
|
||||
/// <para>If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.</para>
|
||||
/// <para>The alias is case-insensitive.</para>
|
||||
/// </remarks>
|
||||
public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default, ICollection<int> visitedLanguages = null)
|
||||
public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default)
|
||||
{
|
||||
var property = content.GetProperty(alias);
|
||||
|
||||
if (property != null && property.HasValue(culture, segment))
|
||||
return property.GetValue(culture, segment);
|
||||
|
||||
return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, visitedLanguages ?? new List<int>());
|
||||
// fixme defaultValue is a problem here
|
||||
// assuming the value may return as an IEnumerable<int> and no defaultValue is provided, then defaultValue is null
|
||||
// and if HasValue is false, what we get is 'null' - but the converter may instead have been able to return an
|
||||
// empty enumerable, which would be way nicer - so we need a way to tell that 'no defaultValue has been provided'?
|
||||
|
||||
return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -131,7 +135,6 @@ namespace Umbraco.Web
|
||||
/// <param name="culture">The variation language.</param>
|
||||
/// <param name="segment">The variation segment.</param>
|
||||
/// <param name="defaultValue">The default value.</param>
|
||||
/// <param name="visitedLanguages">A list of cultures already visited in looking for a value via a fall-back method.</param>
|
||||
/// <returns>The value of the content's property identified by the alias, converted to the specified type.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The value comes from <c>IPublishedProperty</c> field <c>Value</c> ie it is suitable for use when rendering content.</para>
|
||||
@@ -139,14 +142,14 @@ namespace Umbraco.Web
|
||||
/// <para>If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.</para>
|
||||
/// <para>The alias is case-insensitive.</para>
|
||||
/// </remarks>
|
||||
public static T Value<T>(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default, ICollection<int> visitedLanguages = null)
|
||||
public static T Value<T>(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default)
|
||||
{
|
||||
var property = content.GetProperty(alias);
|
||||
|
||||
if (property != null && property.HasValue(culture, segment))
|
||||
return property.Value<T>(culture, segment);
|
||||
|
||||
return PublishedValueFallback.GetValue<T>(content, alias, culture, segment, defaultValue, visitedLanguages ?? new List<int>());
|
||||
return PublishedValueFallback.GetValue<T>(content, alias, culture, segment, defaultValue);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -16,12 +16,12 @@ namespace Umbraco.Web
|
||||
|
||||
#region Value
|
||||
|
||||
public static object Value(this IPublishedProperty property, string culture = null, string segment = null, object defaultValue = default, ICollection<int> visitedLanguages = null)
|
||||
public static object Value(this IPublishedProperty property, string culture = null, string segment = null, object defaultValue = default)
|
||||
{
|
||||
if (property.HasValue(culture, segment))
|
||||
return property.GetValue(culture, segment);
|
||||
|
||||
return PublishedValueFallback.GetValue(property, culture, segment, defaultValue, visitedLanguages ?? new List<int>());
|
||||
return PublishedValueFallback.GetValue(property, culture, segment, defaultValue);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -199,7 +199,7 @@ namespace Umbraco.Web.Runtime
|
||||
composition.Container.Register(_ => GlobalHost.ConnectionManager.GetHubContext<PreviewHub>(), new PerContainerLifetime());
|
||||
|
||||
// register properties fallback
|
||||
composition.Container.RegisterSingleton<IPublishedValueFallback, PublishedValueLanguageFallback>();
|
||||
composition.Container.RegisterSingleton<IPublishedValueFallback, PublishedValueFallback>();
|
||||
}
|
||||
|
||||
internal void Initialize(
|
||||
|
||||
@@ -263,7 +263,6 @@
|
||||
<Compile Include="Models\Mapping\UserGroupDefaultPermissionsResolver.cs" />
|
||||
<Compile Include="Models\Mapping\ContentItemDisplayVariationResolver.cs" />
|
||||
<Compile Include="Models\PublishedContent\HttpContextVariationContextAccessor.cs" />
|
||||
<Compile Include="Models\PublishedContent\PublishedValueLanguageFallback.cs" />
|
||||
<Compile Include="Models\PublishedContent\PublishedValueFallback.cs" />
|
||||
<Compile Include="Models\RelatedLink.cs" />
|
||||
<Compile Include="Models\RelatedLinkBase.cs" />
|
||||
@@ -838,7 +837,7 @@
|
||||
<Compile Include="Models\LoginStatusModel.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\MarkdownEditorValueConverter.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\TextStringValueConverter.cs" />
|
||||
<Compile Include="PublishedContentPropertyExtension.cs" />
|
||||
<Compile Include="PublishedPropertyExtension.cs" />
|
||||
<Compile Include="Models\UmbracoProperty.cs" />
|
||||
<Compile Include="Mvc\MergeParentContextViewDataAttribute.cs" />
|
||||
<Compile Include="Mvc\ViewDataDictionaryExtensions.cs" />
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace umbraco
|
||||
//check for published content and get its value using that
|
||||
if (publishedContent != null && (publishedContent.HasProperty(_fieldName) || recursive))
|
||||
{
|
||||
var pval = publishedContent.Value(_fieldName, fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree });
|
||||
var pval = publishedContent.Value(_fieldName, fallback: Constants.Content.ValueFallback.Recurse);
|
||||
var rval = pval == null ? string.Empty : pval.ToString();
|
||||
_fieldContent = rval.IsNullOrWhiteSpace() ? _fieldContent : rval;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ namespace umbraco
|
||||
{
|
||||
if (publishedContent != null && (publishedContent.HasProperty(altFieldName) || recursive))
|
||||
{
|
||||
var pval = publishedContent.Value(altFieldName, fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree });
|
||||
var pval = publishedContent.Value(altFieldName, fallback: Constants.Content.ValueFallback.Recurse);
|
||||
var rval = pval == null ? string.Empty : pval.ToString();
|
||||
_fieldContent = rval.IsNullOrWhiteSpace() ? _fieldContent : rval;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user