diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index e1071334d6..124dc93140 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -17,4 +17,4 @@ using System.Resources; // these are FYI and changed automatically [assembly: AssemblyFileVersion("8.0.0")] -[assembly: AssemblyInformationalVersion("8.0.0-alpha.33")] +[assembly: AssemblyInformationalVersion("8.0.0-alpha.34")] diff --git a/src/Umbraco.Core/Composing/Current.cs b/src/Umbraco.Core/Composing/Current.cs index f462c1b6ea..a763b22e36 100644 --- a/src/Umbraco.Core/Composing/Current.cs +++ b/src/Umbraco.Core/Composing/Current.cs @@ -33,6 +33,7 @@ namespace Umbraco.Core.Composing private static ILogger _logger; private static IProfiler _profiler; private static ProfilingLogger _profilingLogger; + private static IPublishedValueFallback _publishedValueFallback; /// /// Gets or sets the DI container. @@ -63,6 +64,7 @@ namespace Umbraco.Core.Composing _logger = null; _profiler = null; _profilingLogger = null; + _publishedValueFallback = null; Resetted?.Invoke(null, EventArgs.Empty); } @@ -153,6 +155,9 @@ namespace Umbraco.Core.Composing public static IPublishedContentTypeFactory PublishedContentTypeFactory => Container.GetInstance(); + public static IPublishedValueFallback PublishedValueFallback + => _publishedValueFallback ?? Container.GetInstance() ?? new NoopPublishedValueFallback(); + #endregion } } diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 0e8e473cf0..063125e462 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core.Configuration /// /// Gets the version comment of the executing code (eg "beta"). /// - public static string CurrentComment => "alpha.33"; + public static string CurrentComment => "alpha.34"; /// /// Gets the assembly version of Umbraco.Code.dll. diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index a8db8a98a7..d9c0b8a35a 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -173,23 +173,5 @@ namespace Umbraco.Core.Models.PublishedContent IEnumerable Children { get; } #endregion - - #region Properties - - /// - /// Gets a property identified by its alias. - /// - /// The property alias. - /// A value indicating whether to navigate the tree upwards until a property with a value is found. - /// The property identified by the alias. - /// - /// Navigate the tree upwards and look for a property with that alias and with a value (ie HasValue is true). - /// If found, return the property. If no property with that alias is found, having a value or not, return null. Otherwise - /// return the first property that was found with the alias but had no value (ie HasValue is false). - /// The alias is case-insensitive. - /// - IPublishedProperty GetProperty(string alias, bool recurse); - - #endregion } } diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs index db6470e3cc..8e1dcfd543 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -1,14 +1,37 @@ -namespace Umbraco.Core.Models.PublishedContent +using Umbraco.Core.Composing; + +namespace Umbraco.Core.Models.PublishedContent { /// /// Provides a fallback strategy for getting values. /// + // fixme - IPublishedValueFallback is still WorkInProgress + // todo - properly document methods, etc + // todo - understand caching vs fallback (recurse etc) public interface IPublishedValueFallback { - // todo - define & implement + // note that at property level, property.GetValue() does NOT implement fallback, and one has + // to get property.Value() or property.Value() to trigger fallback - // property level ... should we move it up to element, - // so that the decision can be made based upon the entire element, other properties, etc? - // or, would we need the *two* levels? + // this method is called whenever property.Value(culture, segment, defaultValue) is called, and + // property.HasValue(culture, segment) is false. it can only fallback at property level (no recurse). + + object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue); + + // this method is called whenever property.Value(culture, segment, defaultValue) is called, and + // property.HasValue(culture, segment) is false. it can only fallback at property level (no recurse). + + T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue); + + // these methods to be called whenever getting the property value for the specified alias, culture and segment, + // either returned no property at all, or a property that does not HasValue for the specified culture and segment. + + object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue); + + T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue); + + object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse); + + T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs new file mode 100644 index 0000000000..b99b4ad415 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs @@ -0,0 +1,29 @@ +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Provides a noop implementation for . + /// + /// + /// This is for tests etc - does not implement fallback at all. + /// + public class NoopPublishedValueFallback : IPublishedValueFallback + { + /// + public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) => defaultValue; + + /// + public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) => defaultValue; + + /// + public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) => defaultValue; + + /// + public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) => defaultValue; + + /// + public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse) => defaultValue; + + /// + public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse) => defaultValue; + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 8187b7498e..dd3e7b6d9f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -134,12 +134,6 @@ namespace Umbraco.Core.Models.PublishedContent return _content.GetProperty(alias); } - /// - public virtual IPublishedProperty GetProperty(string alias, bool recurse) - { - return _content.GetProperty(alias, recurse); - } - #endregion } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 2d4b59cf3f..d0d633211c 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -368,6 +368,7 @@ + diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 2816af7a90..9a0034a0ef 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -16,6 +16,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.PropertyEditors; namespace Umbraco.Tests.PublishedContent @@ -32,7 +33,8 @@ namespace Umbraco.Tests.PublishedContent base.Compose(); Container.RegisterSingleton(f => new PublishedModelFactory(f.GetInstance().GetTypes())); - Container.RegisterSingleton(); + Container.RegisterSingleton(); + Container.RegisterSingleton(); var logger = Mock.Of(); var dataTypeService = new TestObjects.TestDataTypeService( diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index d2a1ee6f80..0869cdbb68 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -142,7 +142,7 @@ namespace Umbraco.Web.Composing internal static IPublishedSnapshotService PublishedSnapshotService => Container.GetInstance(); - + public static ThumbnailProviderCollection ThumbnailProviders => Container.GetInstance(); @@ -257,6 +257,8 @@ namespace Umbraco.Web.Composing public static IPublishedContentTypeFactory PublishedContentTypeFactory => CoreCurrent.PublishedContentTypeFactory; + public static IPublishedValueFallback PublishedValueFallback => CoreCurrent.PublishedValueFallback; + #endregion } } diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs new file mode 100644 index 0000000000..47e4b3d872 --- /dev/null +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -0,0 +1,92 @@ +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Web.Models.PublishedContent +{ + /// + /// Provides a default implementation for . + /// + public class PublishedValueFallback : IPublishedValueFallback + { + // this is our default implementation + // kinda reproducing what was available in v7 + + /// + public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) + { + // no fallback here + return defaultValue; + } + + /// + public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) + { + // no fallback here + return defaultValue; + } + + /// + public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) + { + // no fallback here + return defaultValue; + } + + /// + public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) + { + // no fallback here + return defaultValue; + } + + /// + public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse) + { + // no fallback here + if (!recurse) return defaultValue; + + // is that ok? + return GetValue(content, alias, culture, segment, defaultValue, recurse); + } + + /// + public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse) + { + // no fallback here + if (!recurse) return defaultValue; + + // otherwise, 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 + { + content = content.Parent; + property = content?.GetProperty(alias); + if (property != null) noValueProperty = property; + } while (content != null && (property == null || property.HasValue(culture, segment) == false)); + + // if we found a content with the property having a value, return that property value + if (property != null && property.HasValue(culture, segment)) + return property.Value(culture, segment); + + // if we found a property, even though with no value, return that property value + // because the converter may want to handle the missing value. ie if defaultValue is default, + // either specified or by default, the converter may want to substitute something else. + if (noValueProperty != null) + return noValueProperty.Value(culture, segment, defaultValue: defaultValue); + + // else return default + return defaultValue; + } + } +} diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 60fb746423..ffa72796a9 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -164,31 +164,6 @@ namespace Umbraco.Web.Models /// public abstract IPublishedProperty GetProperty(string alias); - /// - public virtual IPublishedProperty GetProperty(string alias, bool recurse) - { - // fixme - refactor with fallback - - var property = GetProperty(alias); - if (recurse == false) return property; - - IPublishedContent content = this; - var firstNonNullProperty = property; - while (content != null && (property == null || property.HasValue() == false)) - { - content = content.Parent; - property = content?.GetProperty(alias); - if (firstNonNullProperty == null && property != null) firstNonNullProperty = property; - } - - // if we find a content with the property with a value, return that property - // if we find no content with the property, return null - // if we find a content with the property without a value, return that property - // have to save that first property while we look further up, hence firstNonNullProperty - - return property != null && property.HasValue() ? property : firstNonNullProperty; - } - #endregion } } diff --git a/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs b/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs index 628a67b561..0636eb808c 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs @@ -33,11 +33,18 @@ namespace Umbraco.Web.PublishedCache /// /// Gets the snapshot-level cache. /// + /// + /// The snapshot-level cache belongs to this snapshot only. + /// ICacheProvider SnapshotCache { get; } /// /// Gets the elements-level cache. /// + /// + /// The elements-level cache is shared by all snapshots relying on the same elements, + /// ie all snapshots built on top of unchanging content / media / etc. + /// ICacheProvider ElementsCache { get; } /// diff --git a/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs b/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs index 5bdee5a416..0b67c3a1e2 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs @@ -43,11 +43,6 @@ namespace Umbraco.Web.PublishedCache.NuCache return "NuCache.Profile.Name[" + userId + "]"; } - public static string PropertyRecurse(Guid contentUid, string typeAlias, bool previewing) - { - return "NuCache.Property.Recurse[" + DraftOrPub(previewing) + contentUid + ":" + typeAlias + "]"; - } - public static string PropertyCacheValues(Guid contentUid, string typeAlias, bool previewing) { return "NuCache.Property.CacheValues[" + DraftOrPub(previewing) + contentUid + ":" + typeAlias + "]"; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs index 63e82ffa6c..1de7724706 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs @@ -91,10 +91,6 @@ namespace Umbraco.Web.PublishedCache.NuCache public override bool HasValue(string culture = null, string segment = null) => _sourceValue != null && (!(_sourceValue is string) || string.IsNullOrWhiteSpace((string) _sourceValue) == false); - // used to cache the recursive *property* for this property - internal string RecurseCacheKey => _recurseCacheKey - ?? (_recurseCacheKey = CacheKeys.PropertyRecurse(_contentUid, Alias, _isPreviewing)); - // used to cache the CacheValues of this property internal string ValuesCacheKey => _valuesCacheKey ?? (_valuesCacheKey = CacheKeys.PropertyCacheValues(_contentUid, Alias, _isPreviewing)); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index d5294c723f..d944c5f824 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -359,20 +359,6 @@ namespace Umbraco.Web.PublishedCache.NuCache return property; } - /// - public override IPublishedProperty GetProperty(string alias, bool recurse) - { - var property = GetProperty(alias); - if (recurse == false) return property; - - var cache = GetAppropriateCache(); - if (cache == null) - return base.GetProperty(alias, true); - - var key = ((Property)property).RecurseCacheKey; - return (Property)cache.GetCacheItem(key, () => base.GetProperty(alias, true)); - } - #endregion #region Caching diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs index 741e4d09f5..9b8982c69c 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs @@ -17,7 +17,6 @@ namespace Umbraco.Web.PublishedCache.NuCache { _service = service; _defaultPreview = defaultPreview; - SnapshotCache = new ObjectCacheRuntimeCacheProvider(); } public class PublishedSnapshotElements : IDisposable @@ -49,7 +48,7 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Caches - public ICacheProvider SnapshotCache { get; } + public ICacheProvider SnapshotCache => Elements.SnapshotCache; public ICacheProvider ElementsCache => Elements.ElementsCache; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 8a7f956e56..2b7615b5cc 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -937,6 +937,9 @@ namespace Umbraco.Web.PublishedCache.NuCache return new PublishedSnapshot(this, preview); } + // gets a new set of elements + // always creates a new set of elements, + // even though the underlying elements may not change (store snapshots) public PublishedSnapshot.PublishedSnapshotElements GetElements(bool previewDefault) { // note: using ObjectCacheRuntimeCacheProvider for elements and snapshot caches diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index c480e5f4ae..2064bf51ea 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -87,15 +87,6 @@ namespace Umbraco.Web.PublishedCache public override IEnumerable Properties => _properties; - public override IPublishedProperty GetProperty(string alias, bool recurse) - { - if (recurse) - { - throw new NotSupportedException(); - } - return GetProperty(alias); - } - public override IPublishedProperty GetProperty(string alias) { return _properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs index 45a8ebbb7a..5cd9dd0069 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs @@ -189,18 +189,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache public override PublishedContentType ContentType => _contentType; - // override to implement cache - // cache at context level, ie once for the whole request - // but cache is not shared by requests because we wouldn't know how to clear it - public override IPublishedProperty GetProperty(string alias, bool recurse) - { - if (recurse == false) return GetProperty(alias); - - var key = $"XmlPublishedCache.PublishedMediaCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}"; - var cacheProvider = _cacheProvider; - return cacheProvider.GetCacheItem(key, () => base.GetProperty(alias, true)); - } - private readonly List _keysAdded = new List(); private int _id; private Guid _key; diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs index f85f8f8640..2535f61996 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs @@ -79,20 +79,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return _properties.TryGetValue(alias, out property) ? property : null; } - // override to implement cache - // cache at context level, ie once for the whole request - // but cache is not shared by requests because we wouldn't know how to clear it - public override IPublishedProperty GetProperty(string alias, bool recurse) - { - if (recurse == false) return GetProperty(alias); - - var key = $"XmlPublishedCache.PublishedContentCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}"; - var cacheProvider = _cacheProvider; - return cacheProvider.GetCacheItem(key, () => base.GetProperty(alias, true)); - - // note: cleared by PublishedContentCache.Resync - any change here must be applied there - } - public override PublishedItemType ItemType => PublishedItemType.Content; public override IPublishedContent Parent diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 4edd6b3016..017f248f70 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -20,6 +20,10 @@ namespace Umbraco.Web /// public static class PublishedContentExtensions { + // see notes in PublishedElementExtensions + // + private static IPublishedValueFallback PublishedValueFallback => Current.PublishedValueFallback; + #region Urls /// @@ -84,6 +88,8 @@ namespace Umbraco.Web #endregion + // fixme - .HasValue() and .Value() refactoring - in progress - see exceptions below + #region HasValue /// @@ -96,8 +102,10 @@ namespace Umbraco.Web /// Returns true if GetProperty(alias, recurse) is not null and GetProperty(alias, recurse).HasValue is true. public static bool HasValue(this IPublishedContent content, string alias, bool recurse) { - var prop = content.GetProperty(alias, recurse); - return prop != null && prop.HasValue(); + throw new NotImplementedException("WorkInProgress"); + + //var prop = content.GetProperty(alias, recurse); + //return prop != null && prop.HasValue(); } /// @@ -113,9 +121,11 @@ namespace Umbraco.Web public static IHtmlString HasValue(this IPublishedContent content, string alias, bool recurse, string valueIfTrue, string valueIfFalse = null) { - return content.HasValue(alias, recurse) - ? new HtmlString(valueIfTrue) - : new HtmlString(valueIfFalse ?? string.Empty); + throw new NotImplementedException("WorkInProgress"); + + //return content.HasValue(alias, recurse) + // ? new HtmlString(valueIfTrue) + // : new HtmlString(valueIfFalse ?? string.Empty); } #endregion @@ -141,9 +151,12 @@ namespace Umbraco.Web /// public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, bool recurse = false) { - // fixme - refactor with fallback - var property = content.GetProperty(alias, recurse); - return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue(); + 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, recurse); } #endregion @@ -170,11 +183,35 @@ namespace Umbraco.Web /// public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, bool recurse = false) { - // fixme - refactor with fallback - var property = content.GetProperty(alias, recurse); - if (property == null) return defaultValue; + var property = content.GetProperty(alias); - return property.Value(culture, segment, defaultValue); + if (property != null && property.HasValue(culture, segment)) + return property.Value(culture, segment); + + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse); + } + + // fixme - .Value() refactoring - in progress + public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", bool recurse = false) + { + var aliasesA = aliases.Split(','); + if (aliasesA.Length == 0) + return new HtmlString(string.Empty); + + throw new NotImplementedException("WorkInProgress"); + + var property = content.GetProperty(aliasesA[0]); + + //var property = aliases.Split(',') + // .Where(x => string.IsNullOrWhiteSpace(x) == false) + // .Select(x => content.GetProperty(x.Trim(), recurse)) + // .FirstOrDefault(x => x != null); + + //if (format == null) format = x => x.ToString(); + + //return property != null + // ? new HtmlString(format(property.Value())) + // : new HtmlString(alt); } #endregion diff --git a/src/Umbraco.Web/PublishedContentPropertyExtension.cs b/src/Umbraco.Web/PublishedContentPropertyExtension.cs index d57c2437a8..de9321888f 100644 --- a/src/Umbraco.Web/PublishedContentPropertyExtension.cs +++ b/src/Umbraco.Web/PublishedContentPropertyExtension.cs @@ -1,4 +1,5 @@ using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web @@ -8,8 +9,24 @@ namespace Umbraco.Web /// public static class PublishedPropertyExtension { - #region Value + // see notes in PublishedElementExtensions + // + private static IPublishedValueFallback PublishedValueFallback => Current.PublishedValueFallback; + + #region Value + 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); + } + + #endregion + + #region Value + public static T Value(this IPublishedProperty property, string culture = null, string segment = null, T defaultValue = default) { // for Value when defaultValue is not specified, and HasValue() is false, we still want to convert the result (see below) @@ -21,7 +38,7 @@ namespace Umbraco.Web // give the converter a chance to handle the default value differently // eg for IEnumerable it may return Enumerable.Empty instead of null - + var value = property.GetValue(culture, segment); // if value is null (strange but why not) it still is OK to call TryConvertTo diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index 2529146b17..945270cb9e 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Web; +using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web @@ -11,6 +12,19 @@ namespace Umbraco.Web /// public static class PublishedElementExtensions { + // lots of debates about accessing dependencies (IPublishedValueFallback) from extension methods, ranging + // from "don't do it" i.e. if an extension method is relying on dependencies, a proper service should be + // created instead, to discussing method injection vs service locator vs other subtleties, see for example + // this post http://marisks.net/2016/12/19/dependency-injection-with-extension-methods/ + // + // point is, we do NOT want a service, we DO want to write model.Value("alias", "fr-FR") and hit + // fallback somehow - which pretty much rules out method injection, and basically anything but service + // locator - bah, let's face it, it works + // + // besides, for tests, Current support setting a fallback without even a container + // + private static IPublishedValueFallback PublishedValueFallback => Current.PublishedValueFallback; + #region IsComposedOf /// @@ -95,10 +109,11 @@ namespace Umbraco.Web 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 defaultValue; - // note: supporting the "." notation for 'current' is the responsibility of the IPublishedProperty - return property.GetValue(culture, segment); // tested HasValue() right above + if (property != null && property.HasValue(culture, segment)) + return property.GetValue(culture, segment); + + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); } #endregion @@ -124,10 +139,11 @@ namespace Umbraco.Web public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default) { var property = content.GetProperty(alias); - if (property == null) return defaultValue; - // note: supporting the "." notation for 'current' is the responsibility of the IPublishedProperty - return property.Value(culture, segment, defaultValue); + if (property != null && property.HasValue(culture, segment)) + return property.Value(culture, segment); + + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); } #endregion @@ -181,21 +197,6 @@ namespace Umbraco.Web : new HtmlString(alt); } - // todo - that one should move to PublishedContentExtensions - public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", bool recurse = false) - { - if (format == null) format = x => x.ToString(); - - var property = aliases.Split(',') - .Where(x => string.IsNullOrWhiteSpace(x) == false) - .Select(x => content.GetProperty(x.Trim(), recurse)) - .FirstOrDefault(x => x != null); - - return property != null - ? new HtmlString(format(property.Value())) - : new HtmlString(alt); - } - #endregion #region ToIndexedArray diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index e472286902..811dd7358b 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -40,6 +40,7 @@ using Umbraco.Web.HealthCheck; using Umbraco.Web.Install; using Umbraco.Web.Media; using Umbraco.Web.Media.ThumbnailProviders; +using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -206,6 +207,9 @@ namespace Umbraco.Web.Runtime // register preview SignalR hub composition.Container.Register(_ => GlobalHost.ConnectionManager.GetHubContext(), new PerContainerLifetime()); + + // register properties fallback + composition.Container.RegisterSingleton(); } internal void Initialize( @@ -408,7 +412,7 @@ namespace Umbraco.Web.Runtime // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not // utilizing an old path appDomainHash); - + //set the file map and composite file default location to the %temp% location BaseCompositeFileProcessingProvider.CompositeFilePathDefaultFolder = XmlFileMapper.FileMapDefaultFolder diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 362802787f..85150e3079 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -267,6 +267,7 @@ +