diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs
index 9d2cca3e6d..bfe1389921 100644
--- a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs
@@ -21,7 +21,7 @@
/// Other caches that get their raw value from the database would consider that a property has "no
/// value" if it is missing, null, or an empty string (including whitespace-only).
///
- bool HasValue(string culture = null, string segment = null);
+ bool HasValue(string culture = ".", string segment = ".");
///
/// Gets the source value of the property.
@@ -35,7 +35,7 @@
/// If you're using that value, you're probably wrong, unless you're doing some internal
/// Umbraco stuff.
///
- object GetSourceValue(string culture = null, string segment = null);
+ object GetSourceValue(string culture = ".", string segment = ".");
///
/// Gets the object value of the property.
@@ -45,7 +45,7 @@
/// It can be null, or any type of CLR object.
/// It has been fully prepared and processed by the appropriate converter.
///
- object GetValue(string culture = null, string segment = null);
+ object GetValue(string culture = ".", string segment = ".");
///
/// Gets the XPath value of the property.
@@ -55,6 +55,6 @@
/// It must be either null, or a string, or an XPathNavigator.
/// It has been fully prepared and processed by the appropriate converter.
///
- object GetXPathValue(string culture = null, string segment = null);
+ object GetXPathValue(string culture = ".", string segment = ".");
}
}
diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs
new file mode 100644
index 0000000000..d83438ab06
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs
@@ -0,0 +1,35 @@
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Provides a fallback strategy for getting values.
+ ///
+ public interface IPublishedValueFallback
+ {
+ ///
+ /// Gets a value.
+ ///
+ ///
+ /// This is invoked when getting a value for the specified and
+ /// could not return a value, and fallback rules should apply to get the value for another language and/or segment.
+ ///
+ TValue GetValue(IPublishedProperty property, string culture, string segment);
+ }
+
+ // fixme question
+ // this is working at property level at the moment, 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?
+
+ ///
+ /// Provides a default implementation of that does not fall back at all.
+ ///
+ public class NoPublishedValueFallback : IPublishedValueFallback
+ {
+ ///
+ public TValue GetValue(IPublishedProperty property, string culture, string segment)
+ {
+ // we don't implement fallback
+ return default;
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedVariationContextAccessor.cs
new file mode 100644
index 0000000000..2af4230665
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedVariationContextAccessor.cs
@@ -0,0 +1,13 @@
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Gives access to the current .
+ ///
+ public interface IPublishedVariationContextAccessor
+ {
+ ///
+ /// Gets or sets the current .
+ ///
+ PublishedVariationContext Context { get; set; }
+ }
+}
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs
index 7f42c022a5..df3213eb07 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs
@@ -18,6 +18,11 @@ namespace Umbraco.Core.Models.PublishedContent
if (content == null)
return null;
+ // in order to provide a nice, "fluent" experience, this extension method
+ // needs to access Current, which is not always initialized in tests - not
+ // very elegant, but works
+ if (!Current.HasContainer) return content;
+
// get model
// if factory returns nothing, throw
var model = Current.PublishedModelFactory.CreateModel(content);
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs
index 7e2a5b5498..c6626be1b2 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs
@@ -53,15 +53,15 @@ namespace Umbraco.Core.Models.PublishedContent
public string Alias => PropertyType.Alias;
///
- public abstract bool HasValue(string culture = null, string segment = null);
+ public abstract bool HasValue(string culture = ".", string segment = ".");
///
- public abstract object GetSourceValue(string culture = null, string segment = null);
+ public abstract object GetSourceValue(string culture = ".", string segment = ".");
///
- public abstract object GetValue(string culture = null, string segment = null);
+ public abstract object GetValue(string culture = ".", string segment = ".");
///
- public abstract object GetXPathValue(string culture = null, string segment = null);
+ public abstract object GetXPathValue(string culture = ".", string segment = ".");
}
}
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedVariationContext.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedVariationContext.cs
new file mode 100644
index 0000000000..2440b1dc32
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedVariationContext.cs
@@ -0,0 +1,30 @@
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Represents the published variation context.
+ ///
+ ///
+ /// The published variation context indicates which variation is the current default variation.
+ ///
+ public class PublishedVariationContext
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PublishedVariationContext(string culture = null, string segment = null)
+ {
+ Culture = culture;
+ Segment = segment;
+ }
+
+ ///
+ /// Gets the culture.
+ ///
+ public string Culture { get; set; }
+
+ ///
+ /// Gets the segment.
+ ///
+ public string Segment { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs
index e20d8cb49c..00a6b940bb 100644
--- a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs
@@ -20,19 +20,19 @@ namespace Umbraco.Core.Models.PublishedContent
private readonly Lazy _objectValue;
private readonly Lazy _xpathValue;
- public override object GetSourceValue(string culture = null, string segment = null)
+ public override object GetSourceValue(string culture = ".", string segment = ".")
=> culture == null & segment == null ? _sourceValue : null;
- public override bool HasValue(string culture = null, string segment = null)
+ public override bool HasValue(string culture = ".", string segment = ".")
{
var sourceValue = GetSourceValue(culture, segment);
return sourceValue is string s ? !string.IsNullOrWhiteSpace(s) : sourceValue != null;
}
- public override object GetValue(string culture = null, string segment = null)
+ public override object GetValue(string culture = ".", string segment = ".")
=> culture == null & segment == null ? _objectValue.Value : null;
- public override object GetXPathValue(string culture = null, string segment = null)
+ public override object GetXPathValue(string culture = ".", string segment = ".")
=> culture == null & segment == null ? _xpathValue.Value : null;
public RawValueProperty(PublishedPropertyType propertyType, IPublishedElement content, object sourceValue, bool isPreviewing = false)
diff --git a/src/Umbraco.Core/Models/PublishedContent/ThreadCulturePublishedVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/ThreadCulturePublishedVariationContextAccessor.cs
new file mode 100644
index 0000000000..8bf02e3f9b
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/ThreadCulturePublishedVariationContextAccessor.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Concurrent;
+using System.Threading;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Provides a CurrentUICulture-based implementation of .
+ ///
+ ///
+ /// This accessor does not support segments. There is no need to set the current context.
+ ///
+ public class ThreadCulturePublishedVariationContextAccessor : IPublishedVariationContextAccessor
+ {
+ private readonly ConcurrentDictionary _contexts = new ConcurrentDictionary();
+
+ public PublishedVariationContext Context
+ {
+ get => _contexts.GetOrAdd(Thread.CurrentThread.CurrentUICulture.Name, culture => new PublishedVariationContext { Culture = culture });
+ set => throw new NotSupportedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/PublishedContent/ThreadStaticPublishedVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/ThreadStaticPublishedVariationContextAccessor.cs
new file mode 100644
index 0000000000..b7391e8b0d
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/ThreadStaticPublishedVariationContextAccessor.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Provides a ThreadStatic-based implementation of .
+ ///
+ ///
+ /// Something must set the current context.
+ ///
+ public class ThreadStaticPublishedVariationContextAccessor : IPublishedVariationContextAccessor
+ {
+ [ThreadStatic]
+ private static PublishedVariationContext _context;
+
+ ///
+ public PublishedVariationContext Context
+ {
+ get => _context;
+ set => _context = value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 2a4a79180b..7d6f27083b 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -369,6 +369,11 @@
+
+
+
+
+
diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs
index c806930704..2ad5f470ef 100644
--- a/src/Umbraco.Tests/Published/NestedContentTests.cs
+++ b/src/Umbraco.Tests/Published/NestedContentTests.cs
@@ -242,10 +242,10 @@ namespace Umbraco.Tests.Published
_owner = owner;
}
- public override bool HasValue(string culture = null, string segment = null) => _hasValue;
- public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue;
- public override object GetValue(string culture = null, string segment = null) => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview);
- public override object GetXPathValue(string culture = null, string segment = null) => throw new WontImplementException();
+ public override bool HasValue(string culture = ".", string segment = ".") => _hasValue;
+ public override object GetSourceValue(string culture = ".", string segment = ".") => _sourceValue;
+ public override object GetValue(string culture = ".", string segment = ".") => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview);
+ public override object GetXPathValue(string culture = ".", string segment = ".") => throw new WontImplementException();
}
class TestPublishedContent : PublishedContentBase
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs
new file mode 100644
index 0000000000..a131076a3a
--- /dev/null
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs
@@ -0,0 +1,212 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Core;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Events;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.Persistence.Repositories;
+using Umbraco.Core.PropertyEditors;
+using Umbraco.Core.Scoping;
+using Umbraco.Core.Services;
+using Umbraco.Tests.TestHelpers;
+using Umbraco.Tests.TestHelpers.Stubs;
+using Umbraco.Tests.Testing.Objects.Accessors;
+using Umbraco.Web;
+using Umbraco.Web.PublishedCache.NuCache;
+using Umbraco.Web.PublishedCache.NuCache.DataSource;
+
+namespace Umbraco.Tests.PublishedContent
+{
+ [TestFixture]
+ public class PublishedContentOtherTests // FIXME rename!
+ {
+ [Test]
+ public void Test()
+ {
+ SettingsForTests.ConfigureSettings(SettingsForTests.GenerateMockUmbracoSettings());
+ var globalSettings = UmbracoConfig.For.GlobalSettings();
+
+ // fixme - missing variant names here, and what else?
+ var kit = new ContentNodeKit
+ {
+ ContentTypeId = 2,
+ Node = new ContentNode(1, Guid.NewGuid(), 0, "-1,1", 0, -1, DateTime.Now, 0),
+ DraftData = new ContentData { Name="It Works2!", Published = false, TemplateId = 0, VersionId = 2, VersionDate = DateTime.Now, WriterId = 0,
+ Properties = new Dictionary { { "prop", new[]
+ {
+ new PropertyData { Value = "val2" },
+ new PropertyData { Culture = "fr-FR", Value = "val-fr2" },
+ new PropertyData { Culture = "en-UK", Value = "val-uk2" }
+ } } } },
+ PublishedData = new ContentData { Name="It Works1!", Published = true, TemplateId = 0, VersionId = 1, VersionDate = DateTime.Now, WriterId = 0,
+ Properties = new Dictionary { { "prop", new[]
+ {
+ new PropertyData { Value = "val1" },
+ new PropertyData { Culture = "fr-FR", Value = "val-fr1" },
+ new PropertyData { Culture = "en-UK", Value = "val-uk1" }
+ } } } }
+ };
+
+ var dataSource = new TestDataSource(kit);
+
+ var runtime = Mock.Of();
+ Mock.Get(runtime).Setup(x => x.Level).Returns(RuntimeLevel.Run);
+
+ var propertyType = new PropertyType("Umbraco.Void.Editor", ValueStorageType.Nvarchar) { Alias = "prop", DataTypeId = 3, Variations = ContentVariation.InvariantNeutral | ContentVariation.CultureNeutral };
+ var contentType = new ContentType(-1) { Id = 2, Alias = "alias-ct", Variations = ContentVariation.InvariantNeutral | ContentVariation.CultureNeutral };
+ contentType.AddPropertyType(propertyType);
+
+ var contentTypes = new[]
+ {
+ contentType
+ };
+
+ var dataType = new DataType(new VoidEditor("Editor", Mock.Of())) { Id = 3 };
+
+ var dataTypes = new[]
+ {
+ dataType
+ };
+
+ var contentTypeService = Mock.Of();
+ Mock.Get(contentTypeService).Setup(x => x.GetAll()).Returns(contentTypes);
+
+ var dataTypeService = Mock.Of();
+ Mock.Get(dataTypeService).Setup(x => x.GetAll()).Returns(dataTypes);
+
+ var serviceContext = new ServiceContext(
+ dataTypeService : dataTypeService,
+
+ memberTypeService: Mock.Of(),
+ memberService: Mock.Of(),
+
+ contentTypeService : contentTypeService,
+
+ localizationService: Mock.Of()
+ );
+
+ var contentTypeFactory = new PublishedContentTypeFactory(
+ Mock.Of(),
+ new PropertyValueConverterCollection(Array.Empty()),
+ dataTypeService);
+
+ var documentRepository = Mock.Of();
+ var mediaRepository = Mock.Of();
+ var memberRepository = Mock.Of();
+
+ var snapshotAccessor = new TestPublishedSnapshotAccessor();
+
+ var scopeProvider = Mock.Of();
+ Mock.Get(scopeProvider)
+ .Setup(x => x.CreateScope(
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny()))
+ .Returns(() => Mock.Of());
+
+ var variationAccessor = new TestPublishedVariationContextAccessor();
+
+ var options = new PublishedSnapshotService.Options { IgnoreLocalDb = true };
+ var snapshotService = new PublishedSnapshotService(options,
+ null,
+ runtime,
+ serviceContext,
+ contentTypeFactory,
+ null,
+ snapshotAccessor,
+ variationAccessor,
+ Mock.Of(),
+ scopeProvider,
+ documentRepository,
+ mediaRepository,
+ memberRepository,
+ dataSource,
+ globalSettings);
+
+ var snapshot = snapshotService.CreatePublishedSnapshot(previewToken: null);
+ var publishedContent = snapshot.Content.GetById(1);
+
+ // invariant is the current default
+ variationAccessor.Context = null;
+
+ Assert.IsNotNull(publishedContent);
+ Assert.AreEqual("It Works1!", publishedContent.Name);
+ Assert.AreEqual("val1", publishedContent.Value("prop"));
+ Assert.AreEqual("val-fr1", publishedContent.Value("prop", "fr-FR"));
+ Assert.AreEqual("val-uk1", publishedContent.Value("prop", "en-UK"));
+
+ var draftContent = snapshot.Content.GetById(true, 1);
+ Assert.AreEqual("It Works2!", draftContent.Name);
+ Assert.AreEqual("val2", draftContent.Value("prop"));
+ Assert.AreEqual("val-fr2", draftContent.Value("prop", "fr-FR"));
+ Assert.AreEqual("val-uk2", draftContent.Value("prop", "en-UK"));
+
+ variationAccessor.Context = new PublishedVariationContext("fr-FR");
+ Assert.AreEqual("val-fr1", publishedContent.Value("prop"));
+ variationAccessor.Context = new PublishedVariationContext("en-UK");
+ Assert.AreEqual("val-uk1", publishedContent.Value("prop"));
+
+ // invariant needs to be retrieved explicitely, when it's not default
+ Assert.AreEqual("val1", publishedContent.Value("prop", culture: null));
+
+ // then, test fallback
+ }
+
+ internal class TestDataSource : IDataSource
+ {
+ private readonly Dictionary _kits;
+
+ public TestDataSource(params ContentNodeKit[] kits)
+ : this((IEnumerable) kits)
+ { }
+
+ public TestDataSource(IEnumerable kits)
+ {
+ _kits = kits.ToDictionary(x => x.Node.Id, x => x);
+ }
+
+ public ContentNodeKit GetContentSource(IScope scope, int id)
+ => _kits.TryGetValue(id, out var kit) ? kit : default;
+
+ public IEnumerable GetAllContentSources(IScope scope)
+ => _kits.Values;
+
+ public IEnumerable GetBranchContentSources(IScope scope, int id)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IEnumerable GetTypeContentSources(IScope scope, IEnumerable ids)
+ => _kits.Values.Where(x => ids.Contains(x.ContentTypeId));
+
+ public ContentNodeKit GetMediaSource(IScope scope, int id)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IEnumerable GetAllMediaSources(IScope scope)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IEnumerable GetBranchMediaSources(IScope scope, int id)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IEnumerable GetTypeMediaSources(IScope scope, IEnumerable ids)
+ {
+ throw new NotImplementedException();
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs
index 4b7a131bd0..2be86640d7 100644
--- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs
+++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs
@@ -258,10 +258,10 @@ namespace Umbraco.Tests.PublishedContent
public bool SolidHasValue { get; set; }
public object SolidXPathValue { get; set; }
- public object GetSourceValue(string culture = null, string segment = null) => SolidSourceValue;
- public object GetValue(string culture = null, string segment = null) => SolidValue;
- public object GetXPathValue(string culture = null, string segment = null) => SolidXPathValue;
- public bool HasValue(string culture = null, string segment = null) => SolidHasValue;
+ public object GetSourceValue(string culture = ".", string segment = ".") => SolidSourceValue;
+ public object GetValue(string culture = ".", string segment = ".") => SolidValue;
+ public object GetXPathValue(string culture = ".", string segment = ".") => SolidXPathValue;
+ public bool HasValue(string culture = ".", string segment = ".") => SolidHasValue;
}
[PublishedModel("ContentType2")]
diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs
index d0b3622127..f722906053 100644
--- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs
+++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs
@@ -18,6 +18,7 @@ using Umbraco.Web.WebApi;
using Umbraco.Core.Strings;
using Umbraco.Core.Composing;
using Umbraco.Tests.Testing;
+using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web.Runtime;
using Current = Umbraco.Web.Composing.Current;
diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs
index cc9813cdbd..70ff7d8341 100644
--- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs
+++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs
@@ -25,6 +25,7 @@ using Umbraco.Web;
using Umbraco.Web.Cache;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.PublishedCache.NuCache;
+using Umbraco.Web.PublishedCache.NuCache.DataSource;
using Umbraco.Web.Routing;
using Umbraco.Web.Security;
@@ -88,9 +89,11 @@ namespace Umbraco.Tests.Scoping
contentTypeFactory,
null,
publishedSnapshotAccessor,
+ Mock.Of(),
Logger,
ScopeProvider,
documentRepository, mediaRepository, memberRepository,
+ new DatabaseDataSource(),
SystemDefaultCultureProvider,
Container.GetInstance(), new SiteDomainHelper());
}
diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs
index ee0dd38c45..4952360b6a 100644
--- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs
+++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs
@@ -15,6 +15,7 @@ using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers.Stubs;
+using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs
index 0fcd3c9295..cede329bd0 100644
--- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs
+++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs
@@ -33,6 +33,7 @@ using Umbraco.Core.Migrations.Install;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Tests.Testing.Objects.AccessorsAndProviders;
+using Umbraco.Tests.Testing.Objects.Accessors;
namespace Umbraco.Tests.TestHelpers
{
@@ -264,11 +265,12 @@ namespace Umbraco.Tests.TestHelpers
// testing=true so XmlStore will not use the file nor the database
var publishedSnapshotAccessor = new UmbracoContextPublishedSnapshotAccessor(Umbraco.Web.Composing.Current.UmbracoContextAccessor);
+ var variationContextAccessor = new TestPublishedVariationContextAccessor();
var service = new PublishedSnapshotService(
ServiceContext,
Container.GetInstance(),
ScopeProvider,
- cache, publishedSnapshotAccessor,
+ cache, publishedSnapshotAccessor, variationContextAccessor,
Container.GetInstance(), Container.GetInstance(), Container.GetInstance(),
SystemDefaultCultureProvider,
Logger,
diff --git a/src/Umbraco.Tests/TestHelpers/NoHttpContextAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs
similarity index 78%
rename from src/Umbraco.Tests/TestHelpers/NoHttpContextAccessor.cs
rename to src/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs
index b77f8f828c..9b37389241 100644
--- a/src/Umbraco.Tests/TestHelpers/NoHttpContextAccessor.cs
+++ b/src/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs
@@ -1,7 +1,7 @@
using System.Web;
using Umbraco.Web;
-namespace Umbraco.Tests.TestHelpers
+namespace Umbraco.Tests.Testing.Objects.Accessors
{
public class NoHttpContextAccessor : IHttpContextAccessor
{
diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedSnapshotAccessor.cs
similarity index 79%
rename from src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs
rename to src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedSnapshotAccessor.cs
index 3768803de2..c46915e3a0 100644
--- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs
+++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedSnapshotAccessor.cs
@@ -1,6 +1,6 @@
using Umbraco.Web.PublishedCache;
-namespace Umbraco.Tests.TestHelpers.Stubs
+namespace Umbraco.Tests.Testing.Objects.Accessors
{
public class TestPublishedSnapshotAccessor : IPublishedSnapshotAccessor
{
diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs
new file mode 100644
index 0000000000..9327b462f1
--- /dev/null
+++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs
@@ -0,0 +1,13 @@
+using Umbraco.Core.Models.PublishedContent;
+
+namespace Umbraco.Tests.Testing.Objects.Accessors
+{
+ ///
+ /// Provides an implementation of for tests.
+ ///
+ public class TestPublishedVariationContextAccessor : IPublishedVariationContextAccessor
+ {
+ ///
+ public PublishedVariationContext Context { get; set; }
+ }
+}
diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestUmbracoContextAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestUmbracoContextAccessor.cs
similarity index 77%
rename from src/Umbraco.Tests/TestHelpers/Stubs/TestUmbracoContextAccessor.cs
rename to src/Umbraco.Tests/Testing/Objects/Accessors/TestUmbracoContextAccessor.cs
index eaf8912fda..da93218907 100644
--- a/src/Umbraco.Tests/TestHelpers/Stubs/TestUmbracoContextAccessor.cs
+++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestUmbracoContextAccessor.cs
@@ -1,6 +1,6 @@
using Umbraco.Web;
-namespace Umbraco.Tests.TestHelpers.Stubs
+namespace Umbraco.Tests.Testing.Objects.Accessors
{
public class TestUmbracoContextAccessor : IUmbracoContextAccessor
{
diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs
index 59bc24ed10..9f06d0de17 100644
--- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs
+++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs
@@ -10,6 +10,7 @@ using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Stubs;
+using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web;
using Umbraco.Web.Routing;
using Umbraco.Web.Security;
diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs
index 003ba9ff4d..6f803516cf 100644
--- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs
+++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs
@@ -31,6 +31,7 @@ using Umbraco.Tests.TestHelpers.Stubs;
using Umbraco.Web;
using Umbraco.Web.Services;
using Umbraco.Examine;
+using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web.Composing.CompositionRoots;
using Umbraco.Web._Legacy.Actions;
using Current = Umbraco.Core.Composing.Current;
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index 20bc181017..dc38dd54ee 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -129,6 +129,7 @@
+
@@ -176,9 +177,10 @@
-
+
+
@@ -195,10 +197,10 @@
-
+
-
+
diff --git a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs
index 1f5fe1a6e3..6e8b22378c 100644
--- a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs
+++ b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs
@@ -15,6 +15,7 @@ using Umbraco.Core.Profiling;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Stubs;
+using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web;
using Umbraco.Web.Composing;
using Umbraco.Web.Models;
diff --git a/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs
index 3b60f1a7c5..901c737584 100644
--- a/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs
+++ b/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs
@@ -8,6 +8,7 @@ using NUnit.Framework;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Tests.TestHelpers.Stubs;
+using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
using Current = Umbraco.Web.Composing.Current;
diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs
index 931cc57493..e467889831 100644
--- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs
+++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs
@@ -14,6 +14,7 @@ using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Stubs;
using Umbraco.Tests.Testing;
+using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web;
using Umbraco.Web.Composing;
using Umbraco.Web.Mvc;
diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs
index 295f42fee2..8e9e6a3e22 100644
--- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs
+++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs
@@ -424,22 +424,23 @@ namespace Umbraco.Tests.Web.Mvc
//var provider = new ScopeUnitOfWorkProvider(databaseFactory, new RepositoryFactory(Mock.Of()));
var scopeProvider = TestObjects.GetScopeProvider(Mock.Of());
var factory = Mock.Of();
- _service = new PublishedSnapshotService(svcCtx, factory, scopeProvider, cache, Enumerable.Empty(), null,
- null, null, null,
+ _service = new PublishedSnapshotService(svcCtx, factory, scopeProvider, cache, Enumerable.Empty(),
+ null, null,
+ null, null, null,
new TestSystemDefaultCultureProvider(),
Current.Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), null, true, false); // no events
var http = GetHttpContextFactory(url, routeData).HttpContext;
-
- var globalSettings = TestObjects.GetGlobalSettings();
+
+ var globalSettings = TestObjects.GetGlobalSettings();
var ctx = new UmbracoContext(
http,
_service,
new WebSecurity(http, Current.Services.UserService, globalSettings),
TestObjects.GetUmbracoSettings(),
- Enumerable.Empty(),
- globalSettings,
+ Enumerable.Empty(),
+ globalSettings,
Mock.Of());
//if (setSingleton)
diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
index d35b4e5823..165f103d43 100644
--- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
+++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
@@ -13,6 +13,7 @@ using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Stubs;
+using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
diff --git a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs
index cc97633cde..aa1fce8c85 100644
--- a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs
+++ b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs
@@ -8,6 +8,7 @@ using NUnit.Framework;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers.Stubs;
using Umbraco.Tests.Testing;
+using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web;
using Umbraco.Web.Mvc;
using Umbraco.Web.PublishedCache;
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs
index a7cc1e950c..de658daeec 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs
@@ -33,10 +33,11 @@ namespace Umbraco.Web.PublishedCache.NuCache
int parentContentId,
DateTime createDate, int creatorId,
ContentData draftData, ContentData publishedData,
- IPublishedSnapshotAccessor publishedSnapshotAccessor)
+ IPublishedSnapshotAccessor publishedSnapshotAccessor,
+ IPublishedVariationContextAccessor variationContextAccessor)
: this(id, uid, level, path, sortOrder, parentContentId, createDate, creatorId)
{
- SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor);
+ SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor, variationContextAccessor);
}
// 2-phases ctor, phase 1
@@ -58,7 +59,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
}
// two-phase ctor, phase 2
- public void SetContentTypeAndData(PublishedContentType contentType, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor)
+ public void SetContentTypeAndData(PublishedContentType contentType, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor)
{
ContentType = contentType;
@@ -66,9 +67,9 @@ namespace Umbraco.Web.PublishedCache.NuCache
throw new ArgumentException("Both draftData and publishedData cannot be null at the same time.");
if (draftData != null)
- Draft = new PublishedContent(this, draftData, publishedSnapshotAccessor).CreateModel();
+ Draft = new PublishedContent(this, draftData, publishedSnapshotAccessor, variationContextAccessor).CreateModel();
if (publishedData != null)
- Published = new PublishedContent(this, publishedData, publishedSnapshotAccessor).CreateModel();
+ Published = new PublishedContent(this, publishedData, publishedSnapshotAccessor, variationContextAccessor).CreateModel();
}
// clone parent
@@ -97,7 +98,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
}
// clone with new content type
- public ContentNode(ContentNode origin, PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor)
+ public ContentNode(ContentNode origin, PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor)
{
Id = origin.Id;
Uid = origin.Uid;
@@ -112,8 +113,8 @@ namespace Umbraco.Web.PublishedCache.NuCache
var originDraft = origin.Draft == null ? null : PublishedContent.UnwrapIPublishedContent(origin.Draft);
var originPublished = origin.Published == null ? null : PublishedContent.UnwrapIPublishedContent(origin.Published);
- Draft = originDraft == null ? null : new PublishedContent(this, originDraft._contentData, publishedSnapshotAccessor).CreateModel();
- Published = originPublished == null ? null : new PublishedContent(this, originPublished._contentData, publishedSnapshotAccessor).CreateModel();
+ Draft = originDraft == null ? null : new PublishedContent(this, originDraft._contentData, publishedSnapshotAccessor, variationContextAccessor).CreateModel();
+ Published = originPublished == null ? null : new PublishedContent(this, originPublished._contentData, publishedSnapshotAccessor, variationContextAccessor).CreateModel();
ChildContentIds = origin.ChildContentIds; // can be the *same* list FIXME oh really?
}
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs
index 82bfc8766a..19163d5e8d 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs
@@ -17,9 +17,9 @@ namespace Umbraco.Web.PublishedCache.NuCache
public static ContentNodeKit Null { get; } = new ContentNodeKit { ContentTypeId = -1 };
- public void Build(PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor)
+ public void Build(PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor)
{
- Node.SetContentTypeAndData(contentType, DraftData, PublishedData, publishedSnapshotAccessor);
+ Node.SetContentTypeAndData(contentType, DraftData, PublishedData, publishedSnapshotAccessor, variationContextAccessor);
}
}
}
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs
index 74613509ba..95482bb6b1 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs
@@ -19,6 +19,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
// SnapDictionary has unit tests to ensure it all works correctly
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
+ private readonly IPublishedVariationContextAccessor _variationContextAccessor;
private readonly ConcurrentDictionary> _contentNodes;
private readonly ConcurrentDictionary> _contentRootNodes;
private readonly ConcurrentDictionary> _contentTypesById;
@@ -43,9 +44,10 @@ namespace Umbraco.Web.PublishedCache.NuCache
#region Ctor
- public ContentStore(IPublishedSnapshotAccessor publishedSnapshotAccessor, ILogger logger, BPlusTree localDb = null)
+ public ContentStore(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, ILogger logger, BPlusTree localDb = null)
{
_publishedSnapshotAccessor = publishedSnapshotAccessor;
+ _variationContextAccessor = variationContextAccessor;
_logger = logger;
_localDb = localDb;
@@ -277,7 +279,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
if (node == null) continue;
var contentTypeId = node.ContentType.Id;
if (index.TryGetValue(contentTypeId, out PublishedContentType contentType) == false) continue;
- SetValueLocked(_contentNodes, node.Id, new ContentNode(node, contentType, _publishedSnapshotAccessor));
+ SetValueLocked(_contentNodes, node.Id, new ContentNode(node, contentType, _publishedSnapshotAccessor, _variationContextAccessor));
}
}
finally
@@ -391,7 +393,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
_contentNodes.TryGetValue(id, out LinkedNode link);
if (link?.Value == null)
continue;
- var node = new ContentNode(link.Value, contentType, _publishedSnapshotAccessor);
+ var node = new ContentNode(link.Value, contentType, _publishedSnapshotAccessor, _variationContextAccessor);
SetValueLocked(_contentNodes, id, node);
if (_localDb != null) RegisterChange(id, node.ToKit());
}
@@ -414,7 +416,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
return false;
// and use
- kit.Build(link.Value, _publishedSnapshotAccessor);
+ kit.Build(link.Value, _publishedSnapshotAccessor, _variationContextAccessor);
return true;
}
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs
similarity index 97%
rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs
rename to src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs
index 342ad5b59f..16f11aeafd 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs
@@ -18,7 +18,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
// fixme - use SqlTemplate for these queries else it's going to be horribly slow!
// provides efficient database access for NuCache
- internal class Database
+ internal class DatabaseDataSource : IDataSource
{
// we want arrays, we want them all loaded, not an enumerable
@@ -186,7 +186,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
{
if (Debugger.IsAttached)
throw new Exception("Missing cmsContentNu edited content for node " + dto.Id + ", consider rebuilding.");
- Current.Logger.Warn("Missing cmsContentNu edited content for node " + dto.Id + ", consider rebuilding.");
+ Current.Logger.Warn("Missing cmsContentNu edited content for node " + dto.Id + ", consider rebuilding.");
}
else
{
@@ -211,7 +211,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
{
if (Debugger.IsAttached)
throw new Exception("Missing cmsContentNu published content for node " + dto.Id + ", consider rebuilding.");
- Current.Logger.Warn("Missing cmsContentNu published content for node " + dto.Id + ", consider rebuilding.");
+ Current.Logger.Warn("Missing cmsContentNu published content for node " + dto.Id + ", consider rebuilding.");
}
else
{
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDataSource.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDataSource.cs
new file mode 100644
index 0000000000..323d954980
--- /dev/null
+++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDataSource.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using Umbraco.Core.Scoping;
+
+namespace Umbraco.Web.PublishedCache.NuCache.DataSource
+{
+ ///
+ /// Defines a data source for NuCache.
+ ///
+ internal interface IDataSource
+ {
+ ContentNodeKit GetContentSource(IScope scope, int id);
+ IEnumerable GetAllContentSources(IScope scope);
+ IEnumerable GetBranchContentSources(IScope scope, int id);
+ IEnumerable GetTypeContentSources(IScope scope, IEnumerable ids);
+
+ ContentNodeKit GetMediaSource(IScope scope, int id);
+ IEnumerable GetAllMediaSources(IScope scope);
+ IEnumerable GetBranchMediaSources(IScope scope, int id);
+ IEnumerable GetTypeMediaSources(IScope scope, IEnumerable ids);
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs
index a8cf4a97c0..899fbd6eed 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs
@@ -16,6 +16,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
class MemberCache : IPublishedMemberCache, INavigableData
{
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
+ public readonly IPublishedVariationContextAccessor _variationContextAccessor;
private readonly ICacheProvider _snapshotCache;
private readonly IMemberService _memberService;
private readonly IDataTypeService _dataTypeService;
@@ -23,10 +24,11 @@ namespace Umbraco.Web.PublishedCache.NuCache
private readonly PublishedContentTypeCache _contentTypeCache;
private readonly bool _previewDefault;
- public MemberCache(bool previewDefault, ICacheProvider snapshotCache, IMemberService memberService, IDataTypeService dataTypeService, ILocalizationService localizationService, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor)
+ public MemberCache(bool previewDefault, ICacheProvider snapshotCache, IMemberService memberService, IDataTypeService dataTypeService, ILocalizationService localizationService, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor)
{
_snapshotCache = snapshotCache;
_publishedSnapshotAccessor = publishedSnapshotAccessor;
+ _variationContextAccessor = variationContextAccessor;
_memberService = memberService;
_dataTypeService = dataTypeService;
_localizationService = localizationService;
@@ -63,14 +65,14 @@ namespace Umbraco.Web.PublishedCache.NuCache
var member = _memberService.GetById(memberId);
return member == null
? null
- : PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor);
+ : PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, _variationContextAccessor);
});
}
private IPublishedContent /*IPublishedMember*/ GetById(IMember member, bool previewing)
{
return GetCacheItem(CacheKeys.MemberCacheMember("ById", _previewDefault, member.Id), () =>
- PublishedMember.Create(member, GetContentType(member.ContentTypeId), previewing, _publishedSnapshotAccessor));
+ PublishedMember.Create(member, GetContentType(member.ContentTypeId), previewing, _publishedSnapshotAccessor, _variationContextAccessor));
}
public IPublishedContent /*IPublishedMember*/ GetByProviderKey(object key)
@@ -105,7 +107,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
public IPublishedContent /*IPublishedMember*/ GetByMember(IMember member)
{
- return PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor);
+ return PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, _variationContextAccessor);
}
public IEnumerable GetAtRoot(bool preview)
@@ -113,7 +115,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
// because members are flat (not a tree) everything is at root
// because we're loading everything... let's just not cache?
var members = _memberService.GetAllMembers();
- return members.Select(m => PublishedMember.Create(m, GetContentType(m.ContentTypeId), preview, _publishedSnapshotAccessor));
+ return members.Select(m => PublishedMember.Create(m, GetContentType(m.ContentTypeId), preview, _publishedSnapshotAccessor, _variationContextAccessor));
}
public XPathNavigator CreateNavigator()
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs
index 3482a6cf2c..1d497d73e0 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs
@@ -1,11 +1,5 @@
-using Umbraco.Core;
-using Umbraco.Core.Components;
-using Umbraco.Core.Logging;
-using Umbraco.Core.Scoping;
-using Umbraco.Core.Services;
-using LightInject;
-using Umbraco.Core.Models.PublishedContent;
-using Umbraco.Core.Persistence.Repositories;
+using Umbraco.Core.Components;
+using Umbraco.Web.PublishedCache.NuCache.DataSource;
namespace Umbraco.Web.PublishedCache.NuCache
{
@@ -15,6 +9,9 @@ namespace Umbraco.Web.PublishedCache.NuCache
{
base.Compose(composition);
+ // register the NuCache database data source
+ composition.Container.Register();
+
// register the NuCache published snapshot service
// must register default options, required in the service ctor
composition.Container.Register(factory => new PublishedSnapshotService.Options());
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs
index f2e3355750..d2f37a1488 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs
@@ -18,7 +18,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
private readonly Guid _contentUid;
private readonly bool _isPreviewing;
private readonly bool _isMember;
- private readonly IPublishedContent _content;
+ private readonly PublishedContent _content;
private readonly object _locko = new object();
@@ -71,7 +71,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
}
// clone for previewing as draft a published content that is published and has no draft
- public Property(Property origin, IPublishedContent content)
+ public Property(Property origin, PublishedContent content)
: base(origin.PropertyType, origin.ReferenceCacheLevel)
{
_sourceValue = origin._sourceValue;
@@ -84,7 +84,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
_publishedSnapshotAccessor = origin._publishedSnapshotAccessor;
}
- public override bool HasValue(string culture = null, string segment = null) => _sourceValue != null
+ public override bool HasValue(string culture = ".", string segment = ".") => _sourceValue != null
&& (!(_sourceValue is string) || string.IsNullOrWhiteSpace((string) _sourceValue) == false);
// used to cache the recursive *property* for this property
@@ -166,8 +166,10 @@ namespace Umbraco.Web.PublishedCache.NuCache
return vvalue.InterValue;
}
- public override object GetSourceValue(string culture = null, string segment = null)
+ public override object GetSourceValue(string culture = ".", string segment = ".")
{
+ ContextualizeVariation(ref culture, ref segment);
+
if (culture == null && segment == null)
return _sourceValue;
@@ -178,8 +180,21 @@ namespace Umbraco.Web.PublishedCache.NuCache
}
}
- public override object GetValue(string culture = null, string segment = null)
+ private void ContextualizeVariation(ref string culture, ref string segment)
{
+ if (culture != "." && segment != ".") return;
+
+ // use context values
+ var publishedVariationContext = _content.VariationContextAccessor?.Context;
+ if (culture == ".") culture = publishedVariationContext?.Culture;
+ if (segment == ".") segment = publishedVariationContext?.Segment;
+ }
+
+ public override object GetValue(string culture = ".", string segment = ".")
+ {
+ ContextualizeVariation(ref culture, ref segment);
+
+ object value;
lock (_locko)
{
var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment);
@@ -190,12 +205,16 @@ namespace Umbraco.Web.PublishedCache.NuCache
if (cacheValues.ObjectInitialized) return cacheValues.ObjectValue;
cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing);
cacheValues.ObjectInitialized = true;
- return cacheValues.ObjectValue;
+ value = cacheValues.ObjectValue;
}
+
+ return value;
}
- public override object GetXPathValue(string culture = null, string segment = null)
+ public override object GetXPathValue(string culture = ".", string segment = ".")
{
+ ContextualizeVariation(ref culture, ref segment);
+
lock (_locko)
{
var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment);
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs
index b5201716ac..a0ec6e4687 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs
@@ -23,11 +23,12 @@ namespace Umbraco.Web.PublishedCache.NuCache
#region Constructors
- public PublishedContent(ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor)
+ public PublishedContent(ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor)
{
_contentNode = contentNode;
_contentData = contentData;
_publishedSnapshotAccessor = publishedSnapshotAccessor;
+ VariationContextAccessor = variationContextAccessor;
_urlName = _contentData.Name.ToUrlSegment();
IsPreviewing = _contentData.Published == false;
@@ -70,6 +71,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
{
_contentNode = contentNode;
_publishedSnapshotAccessor = origin._publishedSnapshotAccessor;
+ VariationContextAccessor = origin.VariationContextAccessor;
_contentData = origin._contentData;
_urlName = origin._urlName;
@@ -85,6 +87,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
private PublishedContent(PublishedContent origin)
{
_publishedSnapshotAccessor = origin._publishedSnapshotAccessor;
+ VariationContextAccessor = origin.VariationContextAccessor;
_contentNode = origin._contentNode;
_contentData = origin._contentData;
@@ -308,6 +311,8 @@ namespace Umbraco.Web.PublishedCache.NuCache
#region Internal
+ internal IPublishedVariationContextAccessor VariationContextAccessor { get; }
+
// used by navigable content
internal IPublishedProperty[] PropertiesArray { get; }
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs
index 8b3d01f6e9..63ef1ae5aa 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs
@@ -15,13 +15,13 @@ namespace Umbraco.Web.PublishedCache.NuCache
{
private readonly IMember _member;
- private PublishedMember(IMember member, ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor)
- : base(contentNode, contentData, publishedSnapshotAccessor)
+ private PublishedMember(IMember member, ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor)
+ : base(contentNode, contentData, publishedSnapshotAccessor, variationContextAccessor)
{
_member = member;
}
- public static IPublishedContent Create(IMember member, PublishedContentType contentType, bool previewing, IPublishedSnapshotAccessor publishedSnapshotAccessor)
+ public static IPublishedContent Create(IMember member, PublishedContentType contentType, bool previewing, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor)
{
var d = new ContentData
{
@@ -37,7 +37,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
member.Level, member.Path, member.SortOrder,
member.ParentId,
member.CreateDate, member.CreatorId);
- return new PublishedMember(member, n, d, publishedSnapshotAccessor).CreateModel();
+ return new PublishedMember(member, n, d, publishedSnapshotAccessor, variationContextAccessor).CreateModel();
}
private static Dictionary GetPropertyValues(PublishedContentType contentType, IMember member)
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
index b498a1a42b..1ca5770060 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
@@ -28,7 +28,6 @@ using Umbraco.Web.Install;
using Umbraco.Web.PublishedCache.NuCache.DataSource;
using Umbraco.Web.PublishedCache.XmlPublishedCache;
using Umbraco.Web.Routing;
-using Database = Umbraco.Web.PublishedCache.NuCache.DataSource.Database;
namespace Umbraco.Web.PublishedCache.NuCache
{
@@ -37,7 +36,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
private readonly ServiceContext _serviceContext;
private readonly IPublishedContentTypeFactory _publishedContentTypeFactory;
private readonly IScopeProvider _scopeProvider;
- private readonly Database _dataSource;
+ private readonly IDataSource _dataSource;
private readonly ILogger _logger;
private readonly IDocumentRepository _documentRepository;
private readonly IMediaRepository _mediaRepository;
@@ -82,18 +81,19 @@ namespace Umbraco.Web.PublishedCache.NuCache
public PublishedSnapshotService(Options options, MainDom mainDom, IRuntimeState runtime,
ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, IdkMap idkMap,
- IPublishedSnapshotAccessor publishedSnapshotAccessor, ILogger logger, IScopeProvider scopeProvider,
+ IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor,
+ ILogger logger, IScopeProvider scopeProvider,
IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository,
ISystemDefaultCultureProvider systemDefaultCultureProvider,
- IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper)
- : base(publishedSnapshotAccessor)
+ IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper)
+ : base(publishedSnapshotAccessor, variationContextAccessor)
{
//if (Interlocked.Increment(ref _singletonCheck) > 1)
// throw new Exception("Singleton must be instancianted only once!");
_serviceContext = serviceContext;
_publishedContentTypeFactory = publishedContentTypeFactory;
- _dataSource = new Database();
+ _dataSource = dataSource;
_logger = logger;
_scopeProvider = scopeProvider;
_documentRepository = documentRepository;
@@ -145,13 +145,13 @@ namespace Umbraco.Web.PublishedCache.NuCache
// stores are created with a db so they can write to it, but they do not read from it,
// stores need to be populated, happens in OnResolutionFrozen which uses _localDbExists to
// figure out whether it can read the dbs or it should populate them from sql
- _contentStore = new ContentStore(publishedSnapshotAccessor, logger, _localContentDb);
- _mediaStore = new ContentStore(publishedSnapshotAccessor, logger, _localMediaDb);
+ _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localContentDb);
+ _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localMediaDb);
}
else
{
- _contentStore = new ContentStore(publishedSnapshotAccessor, logger);
- _mediaStore = new ContentStore(publishedSnapshotAccessor, logger);
+ _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger);
+ _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger);
}
_domainStore = new SnapDictionary();
@@ -1025,7 +1025,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
{
ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainHelper, _globalSettings, _serviceContext.LocalizationService),
MediaCache = new MediaCache(previewDefault, mediaSnap, snapshotCache, elementsCache),
- MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, _serviceContext.DataTypeService, _serviceContext.LocalizationService, memberTypeCache, PublishedSnapshotAccessor),
+ MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, _serviceContext.DataTypeService, _serviceContext.LocalizationService, memberTypeCache, PublishedSnapshotAccessor, VariationContextAccessor),
DomainCache = domainCache,
SnapshotCache = snapshotCache,
ElementsCache = elementsCache
diff --git a/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs b/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs
index d8db937ca8..9fa16d184c 100644
--- a/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs
+++ b/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs
@@ -36,7 +36,7 @@ namespace Umbraco.Web.PublishedCache
IsMember = propertyType.ContentType.ItemType == PublishedItemType.Member;
}
- public override bool HasValue(string culture = null, string segment = null)
+ public override bool HasValue(string culture = ".", string segment = ".")
=> _sourceValue != null && (!(_sourceValue is string s) || !string.IsNullOrWhiteSpace(s));
// used to cache the CacheValues of this property
@@ -136,9 +136,9 @@ namespace Umbraco.Web.PublishedCache
return _interValue;
}
- public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue;
+ public override object GetSourceValue(string culture = ".", string segment = ".") => _sourceValue;
- public override object GetValue(string culture = null, string segment = null)
+ public override object GetValue(string culture = ".", string segment = ".")
{
GetCacheLevels(out var cacheLevel, out var referenceCacheLevel);
@@ -152,7 +152,7 @@ namespace Umbraco.Web.PublishedCache
}
}
- public override object GetXPathValue(string culture = null, string segment = null)
+ public override object GetXPathValue(string culture = ".", string segment = ".")
{
GetCacheLevels(out var cacheLevel, out var referenceCacheLevel);
diff --git a/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs b/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs
index 685c129224..64dda9f20b 100644
--- a/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs
+++ b/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs
@@ -1,17 +1,20 @@
using System.Collections.Generic;
using Umbraco.Core.Models.Membership;
+using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web.Cache;
namespace Umbraco.Web.PublishedCache
{
abstract class PublishedSnapshotServiceBase : IPublishedSnapshotService
{
- protected PublishedSnapshotServiceBase(IPublishedSnapshotAccessor publishedSnapshotAccessor)
+ protected PublishedSnapshotServiceBase(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor)
{
PublishedSnapshotAccessor = publishedSnapshotAccessor;
+ VariationContextAccessor = variationContextAccessor;
}
public IPublishedSnapshotAccessor PublishedSnapshotAccessor { get; }
+ public IPublishedVariationContextAccessor VariationContextAccessor { get; }
// note: NOT setting _publishedSnapshotAccessor.PublishedSnapshot here because it is the
// responsibility of the caller to manage what the 'current' facade is
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs
index 3b9a97d8cc..a0efd14ba9 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs
@@ -43,7 +43,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
IScopeProvider scopeProvider,
ICacheProvider requestCache,
IEnumerable segmentProviders,
- IPublishedSnapshotAccessor publishedSnapshotAccessor,
+ IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor,
IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository,
ISystemDefaultCultureProvider systemDefaultCultureProvider,
ILogger logger,
@@ -51,7 +51,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
ISiteDomainHelper siteDomainHelper,
MainDom mainDom,
bool testing = false, bool enableRepositoryEvents = true)
- : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, segmentProviders, publishedSnapshotAccessor,
+ : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, segmentProviders,
+ publishedSnapshotAccessor, variationContextAccessor,
documentRepository, mediaRepository, memberRepository,
systemDefaultCultureProvider,
logger, globalSettings, siteDomainHelper, null, mainDom, testing, enableRepositoryEvents)
@@ -62,7 +63,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
IPublishedContentTypeFactory publishedContentTypeFactory,
IScopeProvider scopeProvider,
ICacheProvider requestCache,
- IPublishedSnapshotAccessor publishedSnapshotAccessor,
+ IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor,
IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository,
ISystemDefaultCultureProvider systemDefaultCultureProvider,
ILogger logger,
@@ -71,7 +72,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
PublishedContentTypeCache contentTypeCache,
MainDom mainDom,
bool testing, bool enableRepositoryEvents)
- : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, Enumerable.Empty(), publishedSnapshotAccessor,
+ : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, Enumerable.Empty(),
+ publishedSnapshotAccessor, variationContextAccessor,
documentRepository, mediaRepository, memberRepository,
systemDefaultCultureProvider,
logger, globalSettings, siteDomainHelper, contentTypeCache, mainDom, testing, enableRepositoryEvents)
@@ -82,7 +84,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
IScopeProvider scopeProvider,
ICacheProvider requestCache,
IEnumerable segmentProviders,
- IPublishedSnapshotAccessor publishedSnapshotAccessor,
+ IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor,
IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository,
ISystemDefaultCultureProvider systemDefaultCultureProvider,
ILogger logger,
@@ -91,7 +93,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
PublishedContentTypeCache contentTypeCache,
MainDom mainDom,
bool testing, bool enableRepositoryEvents)
- : base(publishedSnapshotAccessor)
+ : base(publishedSnapshotAccessor, variationContextAccessor)
{
_routesCache = new RoutesCache();
_publishedContentTypeFactory = publishedContentTypeFactory;
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs
index c9794eb99a..aea2fc60c6 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs
@@ -29,6 +29,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
factory.GetInstance().RequestCache,
factory.GetInstance(),
factory.GetInstance(),
+ factory.GetInstance(),
factory.GetInstance(),
factory.GetInstance(),
factory.GetInstance(),
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs
index ea8ab925c6..57b81b8c73 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs
@@ -27,13 +27,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
///
/// Gets the raw value of the property.
///
- public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue;
+ public override object GetSourceValue(string culture = ".", string segment = ".") => _sourceValue;
// in the Xml cache, everything is a string, and to have a value
// you want to have a non-null, non-empty string.
- public override bool HasValue(string culture = null, string segment = null) => _sourceValue.Trim().Length > 0;
+ public override bool HasValue(string culture = ".", string segment = ".") => _sourceValue.Trim().Length > 0;
- public override object GetValue(string culture = null, string segment = null)
+ public override object GetValue(string culture = ".", string segment = ".")
{
// NOT caching the source (intermediate) value since we'll never need it
// everything in Xml cache is per-request anyways
diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs
index e4a3a85d13..4ae03bfc22 100644
--- a/src/Umbraco.Web/PublishedContentExtensions.cs
+++ b/src/Umbraco.Web/PublishedContentExtensions.cs
@@ -122,6 +122,8 @@ namespace Umbraco.Web
#endregion
#region Value
+
+ // fixme missing variations, but recurse/variations/fallback = ?
///
/// Recursively gets the value of a content's property identified by its alias.
@@ -148,6 +150,8 @@ namespace Umbraco.Web
///
/// The content.
/// The property alias.
+ /// The variation language.
+ /// The variation segment.
/// A value indicating whether to recurse.
/// The default value.
/// The value of the content's property identified by the alias, if it exists, otherwise a default value.
@@ -158,22 +162,26 @@ namespace Umbraco.Web
/// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
/// The alias is case-insensitive.
///
- public static object Value(this IPublishedContent content, string alias, bool recurse, object defaultValue)
- {
+ public static object Value(this IPublishedContent content, string alias, string culture = ".", string segment = ".", object defaultValue = default, bool recurse = false)
+ {
+ // fixme - variations+recurse not implemented here
var property = content.GetProperty(alias, recurse);
- return property == null || property.HasValue() == false ? defaultValue : property.GetValue();
+ return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue();
}
#endregion
#region Value
-
+
///
/// Recursively gets the value of a content's property identified by its alias, converted to a specified type.
///
/// The target property type.
/// The content.
/// The property alias.
+ /// The variation language.
+ /// The variation segment.
+ /// The default value.
/// A value indicating whether to recurse.
/// The value of the content's property identified by the alias, converted to the specified type.
///
@@ -183,38 +191,13 @@ namespace Umbraco.Web
/// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
/// The alias is case-insensitive.
///
- public static T Value(this IPublishedContent content, string alias, bool recurse)
- {
- return content.Value(alias, recurse, false, default(T));
- }
-
- ///
- /// Recursively gets the value of a content's property identified by its alias, converted to a specified type, if it exists, otherwise a default value.
- ///
- /// The target property type.
- /// The content.
- /// The property alias.
- /// A value indicating whether to recurse.
- /// The default value.
- /// The value of the content's property identified by the alias, converted to the specified type, if it exists, otherwise a default value.
- ///
- /// Recursively means: walking up the tree from , get the first value that can be found.
- /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content.
- /// If no property with the specified alias exists, or if the property has no value, or if it could not be converted, returns .
- /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
- /// The alias is case-insensitive.
- ///
- public static T Value(this IPublishedContent content, string alias, bool recurse, T defaultValue)
- {
- return content.Value(alias, recurse, true, defaultValue);
- }
-
- internal static T Value(this IPublishedContent content, string alias, bool recurse, bool withDefaultValue, T defaultValue)
+ public static T Value(this IPublishedContent content, string alias, string culture = ".", string segment = ".", T defaultValue = default, bool recurse = false)
{
+ // fixme - variations+recurse not implemented here
var property = content.GetProperty(alias, recurse);
if (property == null) return defaultValue;
- return property.Value(withDefaultValue, defaultValue);
+ return property.Value(culture, segment, defaultValue);
}
#endregion
diff --git a/src/Umbraco.Web/PublishedContentPropertyExtension.cs b/src/Umbraco.Web/PublishedContentPropertyExtension.cs
index 6d397ffaa3..e460445c6c 100644
--- a/src/Umbraco.Web/PublishedContentPropertyExtension.cs
+++ b/src/Umbraco.Web/PublishedContentPropertyExtension.cs
@@ -10,21 +10,16 @@ namespace Umbraco.Web
{
#region Value
- public static T Value(this IPublishedProperty property, string culture = null, string segment = null)
- {
- return property.Value(false, default(T), culture, segment);
- }
-
- public static T Value(this IPublishedProperty property, T defaultValue, string culture = null, string segment = null)
- {
- return property.Value(true, defaultValue, culture, segment);
- }
-
- internal static T Value(this IPublishedProperty property, bool withDefaultValue, T defaultValue, string culture = null, string segment = null)
- {
- if (property.HasValue(culture, segment) == false && withDefaultValue) return defaultValue;
-
- // else we use .Value so we give the converter a chance to handle the default value differently
+ public static T Value(this IPublishedProperty property, string culture = ".", string segment = ".", T defaultValue = default)
+ {
+ // for Value when defaultValue is not specified, and HasValue() is false, we still want to convert the result (see below)
+ // but we have no way to tell whether default value is specified or not - we could do it with overloads, but then defaultValue
+ // comes right after property and conflicts with culture when T is string - so we're just not doing it - if defaultValue is
+ // default, whether specified or not, we give a chance to the converter
+ //
+ //if (!property.HasValue(culture, segment) && 'defaultValue is explicitely specified') return defaultValue;
+
+ // 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);
@@ -34,7 +29,7 @@ namespace Umbraco.Web
// failed attempt. So, no need to care for value being null here.
// if already the requested type, return
- if (value is T) return (T)value;
+ if (value is T variable) return variable;
// if can convert to requested type, return
var convert = value.TryConvertTo();
diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs
index bde74c0ab1..57e71573b5 100644
--- a/src/Umbraco.Web/PublishedElementExtensions.cs
+++ b/src/Umbraco.Web/PublishedElementExtensions.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
using System.Web;
using Umbraco.Core.Models.PublishedContent;
@@ -48,12 +50,13 @@ namespace Umbraco.Web
/// Gets a value indicating whether the content has a value for a property identified by its alias.
///
/// Returns true if GetProperty(alias) is not null and GetProperty(alias).HasValue is true .
- public static bool HasValue(this IPublishedElement content, string alias, string culture = null, string segment = null)
+ public static bool HasValue(this IPublishedElement content, string alias, string culture = ".", string segment = ".")
{
var prop = content.GetProperty(alias);
return prop != null && prop.HasValue(culture, segment);
}
+ // fixme - that one is missing variations
///
/// Returns one of two strings depending on whether the content has a value for a property identified by its alias.
///
@@ -63,7 +66,7 @@ namespace Umbraco.Web
/// The value to return if the content has no value for the property.
/// Either or depending on whether the content
/// has a value for the property identified by the alias.
- public static IHtmlString IfHasValue(this IPublishedElement content, string alias, string valueIfTrue, string valueIfFalse = null)
+ public static IHtmlString IfValue(this IPublishedElement content, string alias, string valueIfTrue, string valueIfFalse = null)
{
return content.HasValue(alias)
? new HtmlString(valueIfTrue)
@@ -81,27 +84,7 @@ namespace Umbraco.Web
/// The property alias.
/// The variation language.
/// The variation segment.
- /// The value of the content's property identified by the alias.
- ///
- /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content.
- /// If no property with the specified alias exists, or if the property has no value, returns null .
- /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
- /// The alias is case-insensitive.
- ///
- public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null)
- {
- var property = content.GetProperty(alias);
- return property?.GetValue(culture, segment);
- }
-
- ///
- /// Gets the value of a content's property identified by its alias, if it exists, otherwise a default value.
- ///
- /// The content.
- /// The property alias.
/// The default value.
- /// The variation language.
- /// The variation segment.
/// The value of the content's property identified by the alias, if it exists, otherwise a default value.
///
/// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content.
@@ -109,31 +92,13 @@ namespace Umbraco.Web
/// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
/// The alias is case-insensitive.
///
- public static object Value(this IPublishedElement content, string alias, string defaultValue, string culture = null, string segment = null) // fixme - kill
+ public static object Value(this IPublishedElement content, string alias, string culture = ".", string segment = ".", object defaultValue = default)
{
var property = content.GetProperty(alias);
- return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue(culture, segment);
- }
+ if (property == null || !property.HasValue(culture, segment)) return defaultValue;
- ///
- /// Gets the value of a content's property identified by its alias, if it exists, otherwise a default value.
- ///
- /// The content.
- /// The property alias.
- /// The default value.
- /// The variation language.
- /// The variation segment.
- /// The value of the content's property identified by the alias, if it exists, otherwise a default value.
- ///
- /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content.
- /// If no property with the specified alias exists, or if the property has no value, returns .
- /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
- /// The alias is case-insensitive.
- ///
- public static object Value(this IPublishedElement content, string alias, object defaultValue, string culture = null, string segment = null)
- {
- var property = content.GetProperty(alias);
- return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue(culture, segment);
+ // note: supporting the "." notation for 'current' is the responsibility of the IPublishedProperty
+ return property.GetValue(culture, segment); // tested HasValue() right above
}
#endregion
@@ -148,6 +113,7 @@ namespace Umbraco.Web
/// The property alias.
/// The variation language.
/// The variation segment.
+ /// The default value.
/// The value of the content's property identified by the alias, converted to the specified type.
///
/// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content.
@@ -155,44 +121,20 @@ namespace Umbraco.Web
/// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
/// The alias is case-insensitive.
///
- public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null)
- {
- return content.Value(alias, false, default(T), culture, segment);
- }
-
- ///
- /// Gets the value of a content's property identified by its alias, converted to a specified type, if it exists, otherwise a default value.
- ///
- /// The target property type.
- /// The content.
- /// The property alias.
- /// The default value.
- /// The variation language.
- /// The variation segment.
- /// The value of the content's property identified by the alias, converted to the specified type, if it exists, otherwise a default value.
- ///
- /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content.
- /// If no property with the specified alias exists, or if the property has no value, or if it could not be converted, returns .
- /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
- /// The alias is case-insensitive.
- ///
- public static T Value(this IPublishedElement content, string alias, T defaultValue, string culture = null, string segment = null)
- {
- return content.Value(alias, true, defaultValue, culture, segment);
- }
-
- internal static T Value(this IPublishedElement content, string alias, bool withDefaultValue, T defaultValue, string culture = null, string segment = null) // fixme uh?
+ public static T Value(this IPublishedElement content, string alias, string culture = ".", string segment = ".", T defaultValue = default)
{
var property = content.GetProperty(alias);
if (property == null) return defaultValue;
- return property.Value(withDefaultValue, defaultValue, culture, segment);
+ // note: supporting the "." notation for 'current' is the responsibility of the IPublishedProperty
+ return property.Value(culture, segment, defaultValue);
}
#endregion
#region Value or Umbraco.Field - WORK IN PROGRESS
+ // fixme - more work-in-progress for element.Value() and element.Value() here
// trying to reproduce Umbraco.Field so we can get rid of it
//
// what we want:
@@ -211,6 +153,18 @@ namespace Umbraco.Web
// TODO: strongly typed properties howto?
// there is no strongly typed recurse, etc => needs to be in ModelsBuilder?
+ // todo - that one can only happen in ModelsBuilder as that's where the attributes are defined
+ // the attribute that carries the alias is in ModelsBuilder!
+ //public static TValue Value(this TModel content, Expression> propertySelector, ...)
+ // where TModel : IPublishedElement
+ //{
+ // PropertyInfo pi = GetPropertyFromExpression(propertySelector);
+ // var attr = pi.GetCustomAttribute();
+ // var alias = attr.Alias;
+ // return content.Value(alias, ...)
+ //}
+
+ // todo - that one should be refactored, missing culture and so many things
public static IHtmlString Value(this IPublishedElement content, string aliases, Func format, string alt = "")
{
if (format == null) format = x => x.ToString();
@@ -225,7 +179,7 @@ namespace Umbraco.Web
: new HtmlString(alt);
}
- // fixme - move that one!
+ // 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();
diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs
index 35e5ab1af4..9437a8147d 100644
--- a/src/Umbraco.Web/Routing/PublishedRouter.cs
+++ b/src/Umbraco.Web/Routing/PublishedRouter.cs
@@ -533,7 +533,7 @@ namespace Umbraco.Web.Routing
var redirect = false;
var valid = false;
IPublishedContent internalRedirectNode = null;
- var internalRedirectId = request.PublishedContent.Value(Constants.Conventions.Content.InternalRedirectId, -1);
+ var internalRedirectId = request.PublishedContent.Value(Constants.Conventions.Content.InternalRedirectId, defaultValue: -1);
if (internalRedirectId > 0)
{
@@ -739,7 +739,7 @@ namespace Umbraco.Web.Routing
if (request.PublishedContent.HasProperty(Constants.Conventions.Content.Redirect) == false)
return;
- var redirectId = request.PublishedContent.Value(Constants.Conventions.Content.Redirect, -1);
+ var redirectId = request.PublishedContent.Value(Constants.Conventions.Content.Redirect, defaultValue: -1);
var redirectUrl = "#";
if (redirectId > 0)
{
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index a39495430b..2760229030 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -350,6 +350,7 @@
+
@@ -371,7 +372,7 @@
-
+
diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs
index 89ac52949f..61f263f02c 100644
--- a/src/Umbraco.Web/umbraco.presentation/page.cs
+++ b/src/Umbraco.Web/umbraco.presentation/page.cs
@@ -374,17 +374,17 @@ namespace umbraco
_content = content;
}
- public override bool HasValue(string culture = null, string segment = null)
+ public override bool HasValue(string culture = ".", string segment = ".")
{
return _sourceValue != null && ((_sourceValue is string) == false || string.IsNullOrWhiteSpace((string)_sourceValue) == false);
}
- public override object GetSourceValue(string culture = null, string segment = null)
+ public override object GetSourceValue(string culture = ".", string segment = ".")
{
return _sourceValue;
}
- public override object GetValue(string culture = null, string segment = null)
+ public override object GetValue(string culture = ".", string segment = ".")
{
// isPreviewing is true here since we want to preview anyway...
const bool isPreviewing = true;
@@ -392,7 +392,7 @@ namespace umbraco
return PropertyType.ConvertInterToObject(_content, PropertyCacheLevel.Unknown, source, isPreviewing);
}
- public override object GetXPathValue(string culture = null, string segment = null)
+ public override object GetXPathValue(string culture = ".", string segment = ".")
{
throw new NotImplementedException();
}