From db867c66a3508591211871cd1341929885df2a8c Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 3 Apr 2019 13:32:21 +1100 Subject: [PATCH 01/75] The ImageCropperPropertyValueEditor doesn't convert values in ConvertDbToString correctly --- .../PropertyEditors/ImageCropperPropertyValueEditor.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs index 7bea542521..e5d98804be 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs @@ -174,7 +174,12 @@ namespace Umbraco.Web.PropertyEditors // more magic here ;-( var configuration = dataTypeService.GetDataType(propertyType.DataTypeId).ConfigurationAs(); var crops = configuration?.Crops ?? Array.Empty(); - return "{src: '" + val + "', crops: " + crops + "}"; + + return JsonConvert.SerializeObject(new + { + src = val, + crops = crops + }); } } } From 34ad8dfb8d726d6d9bb2b3e2791dcfdb231357b4 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 15 Apr 2019 13:04:14 +0200 Subject: [PATCH 02/75] Introduce IPublishedContentType --- .../ContentVariationExtensions.cs | 8 +-- .../PublishedContent/IPublishedContentType.cs | 63 +++++++++++++++++++ .../IPublishedContentTypeFactory.cs | 6 +- .../PublishedContent/IPublishedElement.cs | 2 +- .../PublishedContent/PublishedContentType.cs | 45 ++++--------- .../PublishedContentTypeFactory.cs | 10 +-- .../PublishedContentWrapped.cs | 2 +- .../PublishedElementWrapped.cs | 2 +- .../PublishedContent/PublishedPropertyType.cs | 6 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../DictionaryPublishedContent.cs | 4 +- .../PublishedContentCache.cs | 4 +- .../PublishedMediaCache.cs | 6 +- .../PublishedMemberCache.cs | 4 +- .../XmlPublishedContent.cs | 8 +-- .../Published/NestedContentTests.cs | 6 +- .../PublishedContentDataTableTests.cs | 2 +- .../PublishedContent/PublishedContentTests.cs | 2 +- .../SolidPublishedSnapshot.cs | 10 +-- .../TestHelpers/Stubs/TestPublishedContent.cs | 2 +- .../PublishedContentHashtableConverter.cs | 2 +- .../Models/PublishedContentBase.cs | 2 +- .../PublishedCache/IPublishedCache.cs | 6 +- .../PublishedCache/IPublishedMemberCache.cs | 4 +- .../PublishedCache/NuCache/ContentCache.cs | 4 +- .../PublishedCache/NuCache/ContentNode.cs | 10 +-- .../PublishedCache/NuCache/ContentNodeKit.cs | 2 +- .../PublishedCache/NuCache/ContentStore.cs | 30 ++++----- .../PublishedCache/NuCache/MediaCache.cs | 4 +- .../PublishedCache/NuCache/MemberCache.cs | 4 +- .../NuCache/Navigable/NavigableContentType.cs | 8 +-- .../NuCache/PublishedContent.cs | 2 +- .../PublishedCache/NuCache/PublishedMember.cs | 6 +- .../NuCache/PublishedSnapshotService.cs | 6 +- .../PublishedCache/PublishedCacheBase.cs | 6 +- .../PublishedContentTypeCache.cs | 22 +++---- .../PublishedCache/PublishedElement.cs | 6 +- .../PublishedCache/PublishedMember.cs | 6 +- 38 files changed, 183 insertions(+), 140 deletions(-) create mode 100644 src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs diff --git a/src/Umbraco.Core/ContentVariationExtensions.cs b/src/Umbraco.Core/ContentVariationExtensions.cs index d25997b5f0..c5e4a67fe2 100644 --- a/src/Umbraco.Core/ContentVariationExtensions.cs +++ b/src/Umbraco.Core/ContentVariationExtensions.cs @@ -66,24 +66,24 @@ namespace Umbraco.Core /// /// Determines whether the content type is invariant. /// - public static bool VariesByNothing(this PublishedContentType contentType) => contentType.Variations.VariesByNothing(); + public static bool VariesByNothing(this IPublishedContentType contentType) => contentType.Variations.VariesByNothing(); /// /// Determines whether the content type varies by culture. /// /// And then it could also vary by segment. - public static bool VariesByCulture(this PublishedContentType contentType) => contentType.Variations.VariesByCulture(); + public static bool VariesByCulture(this IPublishedContentType contentType) => contentType.Variations.VariesByCulture(); /// /// Determines whether the content type varies by segment. /// /// And then it could also vary by culture. - public static bool VariesBySegment(this PublishedContentType contentType) => contentType.Variations.VariesBySegment(); + public static bool VariesBySegment(this IPublishedContentType contentType) => contentType.Variations.VariesBySegment(); /// /// Determines whether the content type varies by culture and segment. /// - public static bool VariesByCultureAndSegment(this PublishedContentType contentType) => contentType.Variations.VariesByCultureAndSegment(); + public static bool VariesByCultureAndSegment(this IPublishedContentType contentType) => contentType.Variations.VariesByCultureAndSegment(); /// /// Determines whether the property type is invariant. diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs new file mode 100644 index 0000000000..3c28ca1508 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Represents an type. + /// + /// Instances implementing the interface should be + /// immutable, ie if the content type changes, then a new instance needs to be created. + public interface IPublishedContentType + { + /// + /// Gets the content type identifier. + /// + int Id { get; } + + /// + /// Gets the content type alias. + /// + string Alias { get; } + + /// + /// Gets the content item type. + /// + PublishedItemType ItemType { get; } + + /// + /// Gets the aliases of the content types participating in the composition. + /// + HashSet CompositionAliases { get; } + + /// + /// Gets the content variations of the content type. + /// + ContentVariation Variations { get; } + + /// + /// Gets a value indicating whether this content type is for an element. + /// + bool IsElement { get; } + + /// + /// Gets the content type properties. + /// + IEnumerable PropertyTypes { get; } + + /// + /// Gets a property type index. + /// + /// The alias is case-insensitive. This is the only place where alias strings are compared. + int GetPropertyIndex(string alias); + + /// + /// Gets a property type. + /// + PublishedPropertyType GetPropertyType(string alias); + + /// + /// Gets a property type. + /// + PublishedPropertyType GetPropertyType(int index); + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs index e75e8a4eb9..a43f572a74 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs @@ -10,7 +10,7 @@ /// /// An content type. /// A published content type corresponding to the item type and content type. - PublishedContentType CreateContentType(IContentTypeComposition contentType); + IPublishedContentType CreateContentType(IContentTypeComposition contentType); /// /// Creates a published property type. @@ -18,7 +18,7 @@ /// The published content type owning the property. /// A property type. /// Is used by constructor to create property types. - PublishedPropertyType CreatePropertyType(PublishedContentType contentType, PropertyType propertyType); + PublishedPropertyType CreatePropertyType(IPublishedContentType contentType, PropertyType propertyType); /// /// Creates a published property type. @@ -28,7 +28,7 @@ /// The datatype identifier. /// The variations. /// Is used by constructor to create special property types. - PublishedPropertyType CreatePropertyType(PublishedContentType contentType, string propertyTypeAlias, int dataTypeId, ContentVariation variations); + PublishedPropertyType CreatePropertyType(IPublishedContentType contentType, string propertyTypeAlias, int dataTypeId, ContentVariation variations); /// /// Gets a published datatype. diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs index 4b579d824b..4c72dc914a 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Gets the content type. /// - PublishedContentType ContentType { get; } + IPublishedContentType ContentType { get; } #endregion diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs index 0798e9a4e0..c6639b4109 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs @@ -5,11 +5,11 @@ using System.Linq; namespace Umbraco.Core.Models.PublishedContent { /// - /// Represents an type. + /// Represents an type. /// /// Instances of the class are immutable, ie /// if the content type changes, then a new class needs to be created. - public class PublishedContentType + public class PublishedContentType : IPublishedContentType { private readonly PublishedPropertyType[] _propertyTypes; @@ -103,44 +103,29 @@ namespace Umbraco.Core.Models.PublishedContent #region Content type - /// - /// Gets the content type identifier. - /// + /// public int Id { get; } - /// - /// Gets the content type alias. - /// + /// public string Alias { get; } - /// - /// Gets the content item type. - /// + /// public PublishedItemType ItemType { get; } - /// - /// Gets the aliases of the content types participating in the composition. - /// + /// public HashSet CompositionAliases { get; } - /// - /// Gets the content variations of the content type. - /// + /// public ContentVariation Variations { get; } #endregion #region Properties - /// - /// Gets the content type properties. - /// + /// public IEnumerable PropertyTypes => _propertyTypes; - /// - /// Gets a property type index. - /// - /// The alias is case-insensitive. This is the only place where alias strings are compared. + /// public int GetPropertyIndex(string alias) { if (_indexes.TryGetValue(alias, out var index)) return index; // fastest @@ -150,9 +135,7 @@ namespace Umbraco.Core.Models.PublishedContent // virtual for unit tests // TODO: explain why - /// - /// Gets a property type. - /// + /// public virtual PublishedPropertyType GetPropertyType(string alias) { var index = GetPropertyIndex(alias); @@ -161,17 +144,13 @@ namespace Umbraco.Core.Models.PublishedContent // virtual for unit tests // TODO: explain why - /// - /// Gets a property type. - /// + /// public virtual PublishedPropertyType GetPropertyType(int index) { return index >= 0 && index < _propertyTypes.Length ? _propertyTypes[index] : null; } - /// - /// Gets a value indicating whether this content type is for an element. - /// + /// public bool IsElement { get; } #endregion diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs index 2ca3593b55..01f01743de 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs @@ -26,31 +26,31 @@ namespace Umbraco.Core.Models.PublishedContent } /// - public PublishedContentType CreateContentType(IContentTypeComposition contentType) + public IPublishedContentType CreateContentType(IContentTypeComposition contentType) { return new PublishedContentType(contentType, this); } // for tests - internal PublishedContentType CreateContentType(int id, string alias, IEnumerable propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) + internal IPublishedContentType CreateContentType(int id, string alias, IEnumerable propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) { return new PublishedContentType(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, variations, isElement); } // for tests - internal PublishedContentType CreateContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) + internal IPublishedContentType CreateContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) { return new PublishedContentType(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, variations, isElement); } /// - public PublishedPropertyType CreatePropertyType(PublishedContentType contentType, PropertyType propertyType) + public PublishedPropertyType CreatePropertyType(IPublishedContentType contentType, PropertyType propertyType) { return new PublishedPropertyType(contentType, propertyType, _propertyValueConverters, _publishedModelFactory, this); } /// - public PublishedPropertyType CreatePropertyType(PublishedContentType contentType, string propertyTypeAlias, int dataTypeId, ContentVariation variations = ContentVariation.Nothing) + public PublishedPropertyType CreatePropertyType(IPublishedContentType contentType, string propertyTypeAlias, int dataTypeId, ContentVariation variations = ContentVariation.Nothing) { return new PublishedPropertyType(contentType, propertyTypeAlias, dataTypeId, true, variations, _propertyValueConverters, _publishedModelFactory, this); } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 8bf8cec244..7f3f38f629 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -41,7 +41,7 @@ namespace Umbraco.Core.Models.PublishedContent #region ContentType /// - public virtual PublishedContentType ContentType => _content.ContentType; + public virtual IPublishedContentType ContentType => _content.ContentType; #endregion diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedElementWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedElementWrapped.cs index 1989ac2caf..481b9bd5d2 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedElementWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedElementWrapped.cs @@ -28,7 +28,7 @@ namespace Umbraco.Core.Models.PublishedContent public IPublishedElement Unwrap() => _content; /// - public PublishedContentType ContentType => _content.ContentType; + public IPublishedContentType ContentType => _content.ContentType; /// public Guid Key => _content.Key; diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs index 68892fd79a..8dd5003582 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs @@ -32,7 +32,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// The new published property type belongs to the published content type. /// - public PublishedPropertyType(PublishedContentType contentType, PropertyType propertyType, PropertyValueConverterCollection propertyValueConverters, IPublishedModelFactory publishedModelFactory, IPublishedContentTypeFactory factory) + public PublishedPropertyType(IPublishedContentType contentType, PropertyType propertyType, PropertyValueConverterCollection propertyValueConverters, IPublishedModelFactory publishedModelFactory, IPublishedContentTypeFactory factory) : this(propertyType.Alias, propertyType.DataTypeId, true, propertyType.Variations, propertyValueConverters, publishedModelFactory, factory) { ContentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); @@ -45,7 +45,7 @@ namespace Umbraco.Core.Models.PublishedContent /// Values are assumed to be consisted and are not checked. /// The new published property type belongs to the published content type. /// - public PublishedPropertyType(PublishedContentType contentType, string propertyTypeAlias, int dataTypeId, bool isUserProperty, ContentVariation variations, PropertyValueConverterCollection propertyValueConverters, IPublishedModelFactory publishedModelFactory, IPublishedContentTypeFactory factory) + public PublishedPropertyType(IPublishedContentType contentType, string propertyTypeAlias, int dataTypeId, bool isUserProperty, ContentVariation variations, PropertyValueConverterCollection propertyValueConverters, IPublishedModelFactory publishedModelFactory, IPublishedContentTypeFactory factory) : this(propertyTypeAlias, dataTypeId, isUserProperty, variations, propertyValueConverters, publishedModelFactory, factory) { ContentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); @@ -78,7 +78,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Gets the published content type containing the property type. /// - public PublishedContentType ContentType { get; internal set; } // internally set by PublishedContentType constructor + public IPublishedContentType ContentType { get; internal set; } // internally set by PublishedContentType constructor /// /// Gets the data type. diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 68091fb3a9..d6d0a1d9e5 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -221,6 +221,7 @@ + diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs index d3cbf1f183..e472de85dd 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs @@ -194,7 +194,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache return _getProperty(this, alias); } - public override PublishedContentType ContentType => _contentType; + public override IPublishedContentType ContentType => _contentType; private readonly List _keysAdded = new List(); private int _id; @@ -215,7 +215,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache //private Guid _version; private int _level; private readonly ICollection _properties; - private readonly PublishedContentType _contentType; + private readonly IPublishedContentType _contentType; private void ValidateAndSetProperty(IReadOnlyDictionary valueDictionary, Action setProperty, params string[] potentialKeys) { diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs index 5fc0d628c9..d69799dfdf 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs @@ -536,12 +536,12 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache #region Content types - public override PublishedContentType GetContentType(int id) + public override IPublishedContentType GetContentType(int id) { return _contentTypeCache.Get(PublishedItemType.Content, id); } - public override PublishedContentType GetContentType(string alias) + public override IPublishedContentType GetContentType(string alias) { return _contentTypeCache.Get(PublishedItemType.Content, alias); } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs index 71490465d0..8cdab6b2ae 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs @@ -609,17 +609,17 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache #region Content types - public override PublishedContentType GetContentType(int id) + public override IPublishedContentType GetContentType(int id) { return _contentTypeCache.Get(PublishedItemType.Media, id); } - public override PublishedContentType GetContentType(string alias) + public override IPublishedContentType GetContentType(string alias) { return _contentTypeCache.Get(PublishedItemType.Media, alias); } - public override IEnumerable GetByContentType(PublishedContentType contentType) + public override IEnumerable GetByContentType(IPublishedContentType contentType) { throw new NotSupportedException(); } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs index c882488f20..c28575f83d 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs @@ -138,12 +138,12 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache #region Content types - public PublishedContentType GetContentType(int id) + public IPublishedContentType GetContentType(int id) { return _contentTypeCache.Get(PublishedItemType.Member, id); } - public PublishedContentType GetContentType(string alias) + public IPublishedContentType GetContentType(string alias) { return _contentTypeCache.Get(PublishedItemType.Member, alias); } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index e1819bf0be..43c47ec569 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -53,7 +53,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private IEnumerable _children = Enumerable.Empty(); private IPublishedContent _parent; - private PublishedContentType _contentType; + private IPublishedContentType _contentType; private Dictionary _properties; private int _id; @@ -254,7 +254,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache } } - public override PublishedContentType ContentType + public override IPublishedContentType ContentType { get { @@ -308,8 +308,8 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache out int id, out Guid key, out int template, out int sortOrder, out string name, out string writerName, out string urlName, out string creatorName, out int creatorId, out int writerId, out string docTypeAlias, out int docTypeId, out string path, out DateTime createDate, out DateTime updateDate, out int level, out bool isDraft, - out PublishedContentType contentType, out Dictionary properties, - Func getPublishedContentType) + out IPublishedContentType contentType, out Dictionary properties, + Func getPublishedContentType) { //initialize the out params with defaults: writerName = null; diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 8f3b9a1df9..35be4b0bf2 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -23,7 +23,7 @@ namespace Umbraco.Tests.Published [TestFixture] public class NestedContentTests { - private (PublishedContentType, PublishedContentType) CreateContentTypes() + private (IPublishedContentType, IPublishedContentType) CreateContentTypes() { Current.Reset(); @@ -250,7 +250,7 @@ namespace Umbraco.Tests.Published class TestPublishedContent : PublishedContentBase { - public TestPublishedContent(PublishedContentType contentType, Guid key, IEnumerable properties, IUmbracoContextAccessor umbracoContextAccessor): base(umbracoContextAccessor) + public TestPublishedContent(IPublishedContentType contentType, Guid key, IEnumerable properties, IUmbracoContextAccessor umbracoContextAccessor): base(umbracoContextAccessor) { ContentType = contentType; Key = key; @@ -266,7 +266,7 @@ namespace Umbraco.Tests.Published public override bool IsPublished(string culture = null) => true; public override IPublishedContent Parent { get; } public override IEnumerable Children { get; } - public override PublishedContentType ContentType { get; } + public override IPublishedContentType ContentType { get; } // ReSharper restore UnassignedGetOnlyAutoProperty // ReSharper disable UnassignedGetOnlyAutoProperty diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 283ed1edd9..74b9619845 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -243,7 +243,7 @@ namespace Umbraco.Tests.PublishedContent return property; } - public PublishedContentType ContentType { get; set; } + public IPublishedContentType ContentType { get; set; } } } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index de641a99a2..605fae7a1b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -930,7 +930,7 @@ namespace Umbraco.Tests.PublishedContent class ImageWithLegendModel : PublishedElement { - public ImageWithLegendModel(PublishedContentType contentType, Guid fragmentKey, Dictionary values, bool previewing) + public ImageWithLegendModel(IPublishedContentType contentType, Guid fragmentKey, Dictionary values, bool previewing) : base(contentType, fragmentKey, values, previewing) { } diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 86017be820..c14a8c1740 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -137,17 +137,17 @@ namespace Umbraco.Tests.PublishedContent return _content.Count > 0; } - public override PublishedContentType GetContentType(int id) + public override IPublishedContentType GetContentType(int id) { throw new NotImplementedException(); } - public override PublishedContentType GetContentType(string alias) + public override IPublishedContentType GetContentType(string alias) { throw new NotImplementedException(); } - public override IEnumerable GetByContentType(PublishedContentType contentType) + public override IEnumerable GetByContentType(IPublishedContentType contentType) { throw new NotImplementedException(); } @@ -157,7 +157,7 @@ namespace Umbraco.Tests.PublishedContent { #region Constructor - public SolidPublishedContent(PublishedContentType contentType) + public SolidPublishedContent(IPublishedContentType contentType) { // initialize boring stuff TemplateId = 0; @@ -211,7 +211,7 @@ namespace Umbraco.Tests.PublishedContent #region ContentType - public PublishedContentType ContentType { get; private set; } + public IPublishedContentType ContentType { get; private set; } #endregion diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index a9abe96232..206660b904 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -7,7 +7,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs { internal class TestPublishedContent : PublishedElement, IPublishedContent { - public TestPublishedContent(PublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultures = null) + public TestPublishedContent(IPublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultures = null) : base(contentType, key, values, previewing) { Id = id; diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index 9b3bc62cbf..0ff2a41867 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -218,7 +218,7 @@ namespace Umbraco.Web.Macros Parent = new PagePublishedContent(_inner.ParentId); } - public PublishedContentType ContentType { get; } + public IPublishedContentType ContentType { get; } public int Id { get; } diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 39933b49be..d62b8c6665 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.Models #region ContentType - public abstract PublishedContentType ContentType { get; } + public abstract IPublishedContentType ContentType { get; } #endregion diff --git a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs index ff459a2d9b..0a597a7138 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs @@ -199,7 +199,7 @@ namespace Umbraco.Web.PublishedCache /// /// The content type unique identifier. /// The content type, or null. - PublishedContentType GetContentType(int id); + IPublishedContentType GetContentType(int id); /// /// Gets a content type identified by its alias. @@ -207,13 +207,13 @@ namespace Umbraco.Web.PublishedCache /// The content type alias. /// The content type, or null. /// The alias is case-insensitive. - PublishedContentType GetContentType(string alias); + IPublishedContentType GetContentType(string alias); /// /// Gets contents of a given content type. /// /// The content type. /// The contents. - IEnumerable GetByContentType(PublishedContentType contentType); + IEnumerable GetByContentType(IPublishedContentType contentType); } } diff --git a/src/Umbraco.Web/PublishedCache/IPublishedMemberCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedMemberCache.cs index 53d37a8d31..0ea812db83 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedMemberCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedMemberCache.cs @@ -22,7 +22,7 @@ namespace Umbraco.Web.PublishedCache /// /// The content type unique identifier. /// The content type, or null. - PublishedContentType GetContentType(int id); + IPublishedContentType GetContentType(int id); /// /// Gets a content type identified by its alias. @@ -30,6 +30,6 @@ namespace Umbraco.Web.PublishedCache /// The content type alias. /// The content type, or null. /// The alias is case-insensitive. - PublishedContentType GetContentType(string alias); + IPublishedContentType GetContentType(string alias); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index 0e74ea919f..d070b959ed 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -376,12 +376,12 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Content types - public override PublishedContentType GetContentType(int id) + public override IPublishedContentType GetContentType(int id) { return _snapshot.GetContentType(id); } - public override PublishedContentType GetContentType(string alias) + public override IPublishedContentType GetContentType(string alias) { return _snapshot.GetContentType(alias); } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs index db7aa0d5d1..f7eb2ca19e 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web.PublishedCache.NuCache internal class ContentNode { // special ctor with no content data - for members - public ContentNode(int id, Guid uid, PublishedContentType contentType, + public ContentNode(int id, Guid uid, IPublishedContentType contentType, int level, string path, int sortOrder, int parentContentId, DateTime createDate, int creatorId) @@ -28,7 +28,7 @@ namespace Umbraco.Web.PublishedCache.NuCache ChildContentIds = new List(); } - public ContentNode(int id, Guid uid, PublishedContentType contentType, + public ContentNode(int id, Guid uid, IPublishedContentType contentType, int level, string path, int sortOrder, int parentContentId, DateTime createDate, int creatorId, @@ -60,7 +60,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } // two-phase ctor, phase 2 - public void SetContentTypeAndData(PublishedContentType contentType, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IUmbracoContextAccessor umbracoContextAccessor) + public void SetContentTypeAndData(IPublishedContentType contentType, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IUmbracoContextAccessor umbracoContextAccessor) { ContentType = contentType; @@ -109,7 +109,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } // clone with new content type - public ContentNode(ContentNode origin, PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IUmbracoContextAccessor umbracoContextAccessor) + public ContentNode(ContentNode origin, IPublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IUmbracoContextAccessor umbracoContextAccessor) { Id = origin.Id; Uid = origin.Uid; @@ -136,7 +136,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // keep this as small as possible public readonly int Id; public readonly Guid Uid; - public PublishedContentType ContentType; + public IPublishedContentType ContentType; public readonly int Level; public readonly string Path; public readonly int SortOrder; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs index 753ba5cc94..08557fe3db 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public static ContentNodeKit Null { get; } = new ContentNodeKit { ContentTypeId = -1 }; public void Build( - PublishedContentType contentType, + IPublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, bool canBePublished, diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 48c68ab9bf..5693bd3204 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -24,8 +24,8 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly ConcurrentDictionary> _contentNodes; private readonly ConcurrentDictionary> _contentRootNodes; - private readonly ConcurrentDictionary> _contentTypesById; - private readonly ConcurrentDictionary> _contentTypesByAlias; + private readonly ConcurrentDictionary> _contentTypesById; + private readonly ConcurrentDictionary> _contentTypesByAlias; private readonly ConcurrentDictionary _xmap; private readonly ILogger _logger; @@ -61,8 +61,8 @@ namespace Umbraco.Web.PublishedCache.NuCache _contentNodes = new ConcurrentDictionary>(); _contentRootNodes = new ConcurrentDictionary>(); - _contentTypesById = new ConcurrentDictionary>(); - _contentTypesByAlias = new ConcurrentDictionary>(StringComparer.InvariantCultureIgnoreCase); + _contentTypesById = new ConcurrentDictionary>(); + _contentTypesByAlias = new ConcurrentDictionary>(StringComparer.InvariantCultureIgnoreCase); _xmap = new ConcurrentDictionary(); _genObjs = new ConcurrentQueue(); @@ -249,7 +249,7 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Content types - public void NewContentTypes(IEnumerable types) + public void NewContentTypes(IEnumerable types) { var lockInfo = new WriteLockInfo(); try @@ -268,7 +268,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - public void UpdateContentTypes(IEnumerable types) + public void UpdateContentTypes(IEnumerable types) { var lockInfo = new WriteLockInfo(); try @@ -288,7 +288,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var node = link.Value; if (node == null) continue; var contentTypeId = node.ContentType.Id; - if (index.TryGetValue(contentTypeId, out PublishedContentType contentType) == false) continue; + if (index.TryGetValue(contentTypeId, out var contentType) == false) continue; SetValueLocked(_contentNodes, node.Id, new ContentNode(node, contentType, _publishedSnapshotAccessor, _variationContextAccessor, _umbracoContextAccessor)); } } @@ -298,10 +298,10 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - public void UpdateContentTypes(IEnumerable removedIds, IEnumerable refreshedTypes, IEnumerable kits) + public void UpdateContentTypes(IEnumerable removedIds, IEnumerable refreshedTypes, IEnumerable kits) { var removedIdsA = removedIds?.ToArray() ?? Array.Empty(); - var refreshedTypesA = refreshedTypes?.ToArray() ?? Array.Empty(); + var refreshedTypesA = refreshedTypes?.ToArray() ?? Array.Empty(); var refreshedIdsA = refreshedTypesA.Select(x => x.Id).ToArray(); kits = kits ?? Array.Empty(); @@ -377,7 +377,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - public void UpdateDataTypes(IEnumerable dataTypeIds, Func getContentType) + public void UpdateDataTypes(IEnumerable dataTypeIds, Func getContentType) { var lockInfo = new WriteLockInfo(); try @@ -434,7 +434,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return false; // unknown = bad - if (_contentTypesById.TryGetValue(kit.ContentTypeId, out LinkedNode link) == false || link.Value == null) + if (_contentTypesById.TryGetValue(kit.ContentTypeId, out var link) == false || link.Value == null) return false; // check whether parent is published @@ -830,12 +830,12 @@ namespace Umbraco.Web.PublishedCache.NuCache return has == false; } - public PublishedContentType GetContentType(int id, long gen) + public IPublishedContentType GetContentType(int id, long gen) { return GetValue(_contentTypesById, id, gen); } - public PublishedContentType GetContentType(string alias, long gen) + public IPublishedContentType GetContentType(string alias, long gen) { return GetValue(_contentTypesByAlias, alias, gen); } @@ -1151,14 +1151,14 @@ namespace Umbraco.Web.PublishedCache.NuCache return _store.GetAll(_gen); } - public PublishedContentType GetContentType(int id) + public IPublishedContentType GetContentType(int id) { if (_gen < 0) throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); return _store.GetContentType(id, _gen); } - public PublishedContentType GetContentType(string alias) + public IPublishedContentType GetContentType(string alias) { if (_gen < 0) throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs index f7bdb4400f..d63035b219 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs @@ -156,12 +156,12 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Content types - public override PublishedContentType GetContentType(int id) + public override IPublishedContentType GetContentType(int id) { return _snapshot.GetContentType(id); } - public override PublishedContentType GetContentType(string alias) + public override IPublishedContentType GetContentType(string alias) { return _snapshot.GetContentType(alias); } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs index f7ffe73109..5164b2b3bf 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs @@ -151,12 +151,12 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Content types - public PublishedContentType GetContentType(int id) + public IPublishedContentType GetContentType(int id) { return _contentTypeCache.Get(PublishedItemType.Member, id); } - public PublishedContentType GetContentType(string alias) + public IPublishedContentType GetContentType(string alias) { return _contentTypeCache.Get(PublishedItemType.Member, alias); } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContentType.cs b/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContentType.cs index 18bf3ead13..310dae9dd2 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContentType.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContentType.cs @@ -22,10 +22,10 @@ namespace Umbraco.Web.PublishedCache.NuCache.Navigable // changes, but they are replaced by a new instance, so our map here will clean itself automatically and // we don't have to manage cache - ConditionalWeakTable does not prevent keys from being GCed - private static readonly ConditionalWeakTable TypesMap - = new ConditionalWeakTable(); + private static readonly ConditionalWeakTable TypesMap + = new ConditionalWeakTable(); - public static NavigableContentType GetContentType(PublishedContentType contentType) + public static NavigableContentType GetContentType(IPublishedContentType contentType) { return TypesMap.GetOrCreateValue(contentType).EnsureInitialized(contentType); } @@ -49,7 +49,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.Navigable }; } - private NavigableContentType EnsureInitialized(PublishedContentType contentType) + private NavigableContentType EnsureInitialized(IPublishedContentType contentType) { lock (_locko) { diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 057823f29c..5712d55973 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -166,7 +166,7 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Content Type /// - public override PublishedContentType ContentType => _contentNode.ContentType; + public override IPublishedContentType ContentType => _contentNode.ContentType; #endregion diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs index 11ca169300..4bfcbb2a3d 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs @@ -30,7 +30,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public static IPublishedContent Create( IMember member, - PublishedContentType contentType, + IPublishedContentType contentType, bool previewing, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, @@ -53,7 +53,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return new PublishedMember(member, n, d, publishedSnapshotAccessor, variationContextAccessor, umbracoContextAccessor).CreateModel(); } - private static Dictionary GetPropertyValues(PublishedContentType contentType, IMember member) + private static Dictionary GetPropertyValues(IPublishedContentType contentType, IMember member) { // see node in PublishedSnapshotService // we do not (want to) support ConvertDbToXml/String @@ -91,7 +91,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return properties; } - private static void AddIf(PublishedContentType contentType, IDictionary properties, string alias, object value) + private static void AddIf(IPublishedContentType contentType, IDictionary properties, string alias, object value) { var propertyType = contentType.GetPropertyType(alias); if (propertyType == null || propertyType.IsUserProperty) return; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index bf16074040..3b683cdd4e 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -898,11 +898,11 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Content Types - private IEnumerable CreateContentTypes(PublishedItemType itemType, int[] ids) + private IEnumerable CreateContentTypes(PublishedItemType itemType, int[] ids) { // XxxTypeService.GetAll(empty) returns everything! if (ids.Length == 0) - return Enumerable.Empty(); + return Enumerable.Empty(); IEnumerable contentTypes; switch (itemType) @@ -925,7 +925,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return contentTypes.Select(x => _publishedContentTypeFactory.CreateContentType(x)); } - private PublishedContentType CreateContentType(PublishedItemType itemType, int id) + private IPublishedContentType CreateContentType(PublishedItemType itemType, int id) { IContentTypeComposition contentType; switch (itemType) diff --git a/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs b/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs index b0fe1a4240..44f0499a63 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs @@ -88,11 +88,11 @@ namespace Umbraco.Web.PublishedCache return HasContent(PreviewDefault); } - public abstract PublishedContentType GetContentType(int id); + public abstract IPublishedContentType GetContentType(int id); - public abstract PublishedContentType GetContentType(string alias); + public abstract IPublishedContentType GetContentType(string alias); - public virtual IEnumerable GetByContentType(PublishedContentType contentType) + public virtual IEnumerable GetByContentType(IPublishedContentType contentType) { // this is probably not super-efficient, but works // some cache implementation may want to override it, though diff --git a/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs b/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs index ca30370598..e453471bb8 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs @@ -15,8 +15,8 @@ namespace Umbraco.Web.PublishedCache /// This cache is not snapshotted, so it refreshes any time things change. public class PublishedContentTypeCache { - private readonly Dictionary _typesByAlias = new Dictionary(); - private readonly Dictionary _typesById = new Dictionary(); + private readonly Dictionary _typesByAlias = new Dictionary(); + private readonly Dictionary _typesById = new Dictionary(); private readonly IContentTypeService _contentTypeService; private readonly IMediaTypeService _mediaTypeService; private readonly IMemberTypeService _memberTypeService; @@ -136,7 +136,7 @@ namespace Umbraco.Web.PublishedCache /// An item type. /// An alias. /// The published content type corresponding to the item type and alias. - public PublishedContentType Get(PublishedItemType itemType, string alias) + public IPublishedContentType Get(PublishedItemType itemType, string alias) { var aliasKey = GetAliasKey(itemType, alias); @@ -174,7 +174,7 @@ namespace Umbraco.Web.PublishedCache /// An item type. /// An identifier. /// The published content type corresponding to the item type and identifier. - public PublishedContentType Get(PublishedItemType itemType, int id) + public IPublishedContentType Get(PublishedItemType itemType, int id) { try { @@ -204,7 +204,7 @@ namespace Umbraco.Web.PublishedCache } } - private PublishedContentType CreatePublishedContentType(PublishedItemType itemType, string alias) + private IPublishedContentType CreatePublishedContentType(PublishedItemType itemType, string alias) { if (GetPublishedContentTypeByAlias != null) return GetPublishedContentTypeByAlias(alias); @@ -231,7 +231,7 @@ namespace Umbraco.Web.PublishedCache return _publishedContentTypeFactory.CreateContentType(contentType); } - private PublishedContentType CreatePublishedContentType(PublishedItemType itemType, int id) + private IPublishedContentType CreatePublishedContentType(PublishedItemType itemType, int id) { if (GetPublishedContentTypeById != null) return GetPublishedContentTypeById(id); @@ -259,8 +259,8 @@ namespace Umbraco.Web.PublishedCache } // for unit tests - changing the callback must reset the cache obviously - private Func _getPublishedContentTypeByAlias; - internal Func GetPublishedContentTypeByAlias + private Func _getPublishedContentTypeByAlias; + internal Func GetPublishedContentTypeByAlias { get => _getPublishedContentTypeByAlias; set @@ -282,8 +282,8 @@ namespace Umbraco.Web.PublishedCache } // for unit tests - changing the callback must reset the cache obviously - private Func _getPublishedContentTypeById; - internal Func GetPublishedContentTypeById + private Func _getPublishedContentTypeById; + internal Func GetPublishedContentTypeById { get => _getPublishedContentTypeById; set @@ -326,7 +326,7 @@ namespace Umbraco.Web.PublishedCache return k + ":" + alias; } - private static string GetAliasKey(PublishedContentType contentType) + private static string GetAliasKey(IPublishedContentType contentType) { return GetAliasKey(contentType.ItemType, contentType.Alias); } diff --git a/src/Umbraco.Web/PublishedCache/PublishedElement.cs b/src/Umbraco.Web/PublishedCache/PublishedElement.cs index 41902e3e26..618c075b9b 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedElement.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedElement.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.PublishedCache { // initializes a new instance of the PublishedElement class // within the context of a published snapshot service (eg a published content property value) - public PublishedElement(PublishedContentType contentType, Guid key, Dictionary values, bool previewing, + public PublishedElement(IPublishedContentType contentType, Guid key, Dictionary values, bool previewing, PropertyCacheLevel referenceCacheLevel, IPublishedSnapshotAccessor publishedSnapshotAccessor) { if (key == Guid.Empty) throw new ArgumentException("Empty guid."); @@ -46,7 +46,7 @@ namespace Umbraco.Web.PublishedCache // + using an initial reference cache level of .None ensures that everything will be // cached at .Content level - and that reference cache level will propagate to all // properties - public PublishedElement(PublishedContentType contentType, Guid key, Dictionary values, bool previewing) + public PublishedElement(IPublishedContentType contentType, Guid key, Dictionary values, bool previewing) : this(contentType, key, values, previewing, PropertyCacheLevel.None, null) { } @@ -60,7 +60,7 @@ namespace Umbraco.Web.PublishedCache #region ContentType - public PublishedContentType ContentType { get; } + public IPublishedContentType ContentType { get; } #endregion diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index 419c279d46..d954411f5e 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -17,11 +17,11 @@ namespace Umbraco.Web.PublishedCache private readonly IMember _member; private readonly IMembershipUser _membershipUser; private readonly IPublishedProperty[] _properties; - private readonly PublishedContentType _publishedMemberType; + private readonly IPublishedContentType _publishedMemberType; public PublishedMember( IMember member, - PublishedContentType publishedMemberType, + IPublishedContentType publishedMemberType, IUmbracoContextAccessor umbracoContextAccessor) :base(umbracoContextAccessor) { @@ -126,7 +126,7 @@ namespace Umbraco.Web.PublishedCache properties.Add(property); } - public override PublishedContentType ContentType => _publishedMemberType; + public override IPublishedContentType ContentType => _publishedMemberType; public override int Id => _member.Id; From f039b00a44a57b0d32e956852fa878ac59e4badc Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 15 Apr 2019 17:14:45 +0200 Subject: [PATCH 03/75] Introduce IPublishedPropertyType --- .../ContentVariationExtensions.cs | 8 +- .../PublishedContent/IPublishedContentType.cs | 6 +- .../IPublishedContentTypeFactory.cs | 4 +- .../PublishedContent/IPublishedProperty.cs | 2 +- .../IPublishedPropertyType.cs | 108 ++++++++++++++++++ .../PublishedContent/PublishedContentType.cs | 23 ++-- .../PublishedContentTypeFactory.cs | 17 ++- .../PublishedContent/PublishedPropertyBase.cs | 4 +- .../PublishedContent/PublishedPropertyType.cs | 88 +++----------- .../PublishedContent/RawValueProperty.cs | 2 +- .../IPropertyValueConverter.cs | 12 +- .../PropertyValueConverterBase.cs | 12 +- .../CheckboxListValueConverter.cs | 8 +- .../ColorPickerValueConverter.cs | 10 +- .../DatePickerValueConverter.cs | 10 +- .../ValueConverters/DecimalValueConverter.cs | 8 +- .../EmailAddressValueConverter.cs | 8 +- .../ValueConverters/GridValueConverter.cs | 8 +- .../ImageCropperValueConverter.cs | 8 +- .../ValueConverters/IntegerValueConverter.cs | 8 +- .../ValueConverters/JsonValueConverter.cs | 8 +- .../ValueConverters/LabelValueConverter.cs | 8 +- .../MarkdownEditorValueConverter.cs | 12 +- .../MemberGroupPickerValueConverter.cs | 8 +- .../MultipleTextStringValueConverter.cs | 10 +- .../MustBeStringValueConverter.cs | 8 +- .../RadioButtonListValueConverter.cs | 8 +- .../ValueConverters/SliderValueConverter.cs | 8 +- .../ValueConverters/TagsValueConverter.cs | 10 +- .../TextStringValueConverter.cs | 12 +- .../ValueConverters/TinyMceValueConverter.cs | 12 +- .../UploadPropertyConverter.cs | 8 +- .../ValueConverters/YesNoValueConverter.cs | 10 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../XmlPublishedProperty.cs | 6 +- .../Published/ConvertersTests.cs | 80 ++++++------- .../Published/NestedContentTests.cs | 27 +++-- .../Published/PropertyCacheLevelTests.cs | 39 ++++--- .../PublishedContentLanguageVariantTests.cs | 27 +++-- .../PublishedContentMoreTests.cs | 18 +-- .../PublishedContentTestBase.cs | 14 +-- .../PublishedContent/PublishedContentTests.cs | 41 ++++--- .../SolidPublishedSnapshot.cs | 14 ++- .../PublishedContentHashtableConverter.cs | 4 +- src/Umbraco.Web/Models/PublishedProperty.cs | 4 +- .../ContentPickerValueConverter.cs | 12 +- .../FlexibleDropdownPropertyValueConverter.cs | 8 +- .../MacroContainerValueConverter.cs | 8 +- .../MarkdownEditorValueConverter.cs | 10 +- .../MediaPickerValueConverter.cs | 10 +- .../MemberPickerValueConverter.cs | 10 +- .../MultiNodeTreePickerValueConverter.cs | 10 +- .../MultiUrlPickerValueConverter.cs | 10 +- .../NestedContentManyValueConverter.cs | 12 +- .../NestedContentSingleValueConverter.cs | 12 +- .../NestedContentValueConverterBase.cs | 6 +- .../RteMacroRenderingValueConverter.cs | 4 +- .../TextStringValueConverter.cs | 12 +- .../PublishedCache/NuCache/Property.cs | 4 +- .../PublishedElementPropertyBase.cs | 2 +- 60 files changed, 497 insertions(+), 384 deletions(-) create mode 100644 src/Umbraco.Core/Models/PublishedContent/IPublishedPropertyType.cs diff --git a/src/Umbraco.Core/ContentVariationExtensions.cs b/src/Umbraco.Core/ContentVariationExtensions.cs index c5e4a67fe2..9fdc5f0b90 100644 --- a/src/Umbraco.Core/ContentVariationExtensions.cs +++ b/src/Umbraco.Core/ContentVariationExtensions.cs @@ -88,22 +88,22 @@ namespace Umbraco.Core /// /// Determines whether the property type is invariant. /// - public static bool VariesByNothing(this PublishedPropertyType propertyType) => propertyType.Variations.VariesByNothing(); + public static bool VariesByNothing(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByNothing(); /// /// Determines whether the property type varies by culture. /// - public static bool VariesByCulture(this PublishedPropertyType propertyType) => propertyType.Variations.VariesByCulture(); + public static bool VariesByCulture(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByCulture(); /// /// Determines whether the property type varies by segment. /// - public static bool VariesBySegment(this PublishedPropertyType propertyType) => propertyType.Variations.VariesBySegment(); + public static bool VariesBySegment(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesBySegment(); /// /// Determines whether the property type varies by culture and segment. /// - public static bool VariesByCultureAndSegment(this PublishedPropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment(); + public static bool VariesByCultureAndSegment(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment(); /// /// Determines whether a variation is invariant. diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs index 3c28ca1508..ab6920377c 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs @@ -42,7 +42,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Gets the content type properties. /// - IEnumerable PropertyTypes { get; } + IEnumerable PropertyTypes { get; } /// /// Gets a property type index. @@ -53,11 +53,11 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Gets a property type. /// - PublishedPropertyType GetPropertyType(string alias); + IPublishedPropertyType GetPropertyType(string alias); /// /// Gets a property type. /// - PublishedPropertyType GetPropertyType(int index); + IPublishedPropertyType GetPropertyType(int index); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs index a43f572a74..816bfdbb01 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs @@ -18,7 +18,7 @@ /// The published content type owning the property. /// A property type. /// Is used by constructor to create property types. - PublishedPropertyType CreatePropertyType(IPublishedContentType contentType, PropertyType propertyType); + IPublishedPropertyType CreatePropertyType(IPublishedContentType contentType, PropertyType propertyType); /// /// Creates a published property type. @@ -28,7 +28,7 @@ /// The datatype identifier. /// The variations. /// Is used by constructor to create special property types. - PublishedPropertyType CreatePropertyType(IPublishedContentType contentType, string propertyTypeAlias, int dataTypeId, ContentVariation variations); + IPublishedPropertyType CreatePropertyType(IPublishedContentType contentType, string propertyTypeAlias, int dataTypeId, ContentVariation variations); /// /// Gets a published datatype. diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs index 9a00e94d3e..2ee7dcb28f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs @@ -5,7 +5,7 @@ /// public interface IPublishedProperty { - PublishedPropertyType PropertyType { get; } + IPublishedPropertyType PropertyType { get; } /// /// Gets the alias of the property. diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedPropertyType.cs new file mode 100644 index 0000000000..40f2bf3df2 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedPropertyType.cs @@ -0,0 +1,108 @@ +using System; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Represents a published property type. + /// + /// Instances implementing the interface should be + /// immutable, ie if the property type changes, then a new instance needs to be created. + public interface IPublishedPropertyType + { + /// + /// Gets the published content type containing the property type. + /// + IPublishedContentType ContentType { get; } + + /// + /// Gets the data type. + /// + PublishedDataType DataType { get; } + + /// + /// Gets property type alias. + /// + string Alias { get; } + + /// + /// Gets the property editor alias. + /// + string EditorAlias { get; } + + /// + /// Gets a value indicating whether the property is a user content property. + /// + /// A non-user content property is a property that has been added to a + /// published content type by Umbraco but does not corresponds to a user-defined + /// published property. + bool IsUserProperty { get; } + + /// + /// Gets the content variations of the property type. + /// + ContentVariation Variations { get; } + + /// + /// Determines whether a value is an actual value, or not a value. + /// + /// Used by property.HasValue and, for instance, in fallback scenarios. + bool? IsValue(object value, PropertyValueLevel level); + + /// + /// Gets the property cache level. + /// + PropertyCacheLevel CacheLevel { get; } + + /// + /// Converts the source value into the intermediate value. + /// + /// The published element owning the property. + /// The source value. + /// A value indicating whether content should be considered draft. + /// The intermediate value. + object ConvertSourceToInter(IPublishedElement owner, object source, bool preview); + + /// + /// Converts the intermediate value into the object value. + /// + /// The published element owning the property. + /// The reference cache level. + /// The intermediate value. + /// A value indicating whether content should be considered draft. + /// The object value. + object ConvertInterToObject(IPublishedElement owner, PropertyCacheLevel referenceCacheLevel, object inter, bool preview); + + /// + /// Converts the intermediate value into the XPath value. + /// + /// The published element owning the property. + /// The reference cache level. + /// The intermediate value. + /// A value indicating whether content should be considered draft. + /// The XPath value. + /// + /// The XPath value can be either a string or an XPathNavigator. + /// + object ConvertInterToXPath(IPublishedElement owner, PropertyCacheLevel referenceCacheLevel, object inter, bool preview); + + /// + /// Gets the property model CLR type. + /// + /// + /// The model CLR type may be a type, or may contain types. + /// For the actual CLR type, see . + /// + Type ModelClrType { get; } + + /// + /// Gets the property CLR type. + /// + /// + /// Returns the actual CLR type which does not contain types. + /// Mapping from may throw if some instances + /// could not be mapped to actual CLR types. + /// + Type ClrType { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs index c6639b4109..fedd7445b7 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Models.PublishedContent /// if the content type changes, then a new class needs to be created. public class PublishedContentType : IPublishedContentType { - private readonly PublishedPropertyType[] _propertyTypes; + private readonly IPublishedPropertyType[] _propertyTypes; // fast alias-to-index xref containing both the raw alias and its lowercase version private readonly Dictionary _indexes = new Dictionary(); @@ -34,11 +34,11 @@ namespace Umbraco.Core.Models.PublishedContent InitializeIndexes(); } + // fixme should be internal? /// - /// Initializes a new instance of the with specific values. + /// This constructor is for tests and is not intended to be used directly from application code. /// /// - /// This constructor is for tests and is not intended to be used directly from application code. /// Values are assumed to be consisted and are not checked. /// public PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, IEnumerable propertyTypes, ContentVariation variations, bool isElement = false) @@ -52,6 +52,15 @@ namespace Umbraco.Core.Models.PublishedContent InitializeIndexes(); } + // fixme + public PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, Func> propertyTypes, ContentVariation variations, bool isElement = false) + : this(id, alias, itemType, compositionAliases, variations, isElement) + { + _propertyTypes = propertyTypes(this).ToArray(); + + InitializeIndexes(); + } + private PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, ContentVariation variations, bool isElement) { Id = id; @@ -75,7 +84,7 @@ namespace Umbraco.Core.Models.PublishedContent // Members have properties such as IMember LastLoginDate which are plain C# properties and not content // properties; they are exposed as pseudo content properties, as long as a content property with the // same alias does not exist already. - private void EnsureMemberProperties(List propertyTypes, IPublishedContentTypeFactory factory) + private void EnsureMemberProperties(List propertyTypes, IPublishedContentTypeFactory factory) { var aliases = new HashSet(propertyTypes.Select(x => x.Alias), StringComparer.OrdinalIgnoreCase); @@ -123,7 +132,7 @@ namespace Umbraco.Core.Models.PublishedContent #region Properties /// - public IEnumerable PropertyTypes => _propertyTypes; + public IEnumerable PropertyTypes => _propertyTypes; /// public int GetPropertyIndex(string alias) @@ -136,7 +145,7 @@ namespace Umbraco.Core.Models.PublishedContent // virtual for unit tests // TODO: explain why /// - public virtual PublishedPropertyType GetPropertyType(string alias) + public virtual IPublishedPropertyType GetPropertyType(string alias) { var index = GetPropertyIndex(alias); return GetPropertyType(index); @@ -145,7 +154,7 @@ namespace Umbraco.Core.Models.PublishedContent // virtual for unit tests // TODO: explain why /// - public virtual PublishedPropertyType GetPropertyType(int index) + public virtual IPublishedPropertyType GetPropertyType(int index) { return index >= 0 && index < _propertyTypes.Length ? _propertyTypes[index] : null; } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs index 01f01743de..78d05607b4 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs @@ -31,32 +31,43 @@ namespace Umbraco.Core.Models.PublishedContent return new PublishedContentType(contentType, this); } + // fixme kill // for tests internal IPublishedContentType CreateContentType(int id, string alias, IEnumerable propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) { return new PublishedContentType(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, variations, isElement); } + internal IPublishedContentType CreateContentType(int id, string alias, Func> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) + { + return new PublishedContentType(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, variations, isElement); + } + // fixme kill // for tests internal IPublishedContentType CreateContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) { return new PublishedContentType(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, variations, isElement); } + internal IPublishedContentType CreateContentType(int id, string alias, IEnumerable compositionAliases, Func> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) + { + return new PublishedContentType(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, variations, isElement); + } /// - public PublishedPropertyType CreatePropertyType(IPublishedContentType contentType, PropertyType propertyType) + public IPublishedPropertyType CreatePropertyType(IPublishedContentType contentType, PropertyType propertyType) { return new PublishedPropertyType(contentType, propertyType, _propertyValueConverters, _publishedModelFactory, this); } /// - public PublishedPropertyType CreatePropertyType(IPublishedContentType contentType, string propertyTypeAlias, int dataTypeId, ContentVariation variations = ContentVariation.Nothing) + public IPublishedPropertyType CreatePropertyType(IPublishedContentType contentType, string propertyTypeAlias, int dataTypeId, ContentVariation variations = ContentVariation.Nothing) { return new PublishedPropertyType(contentType, propertyTypeAlias, dataTypeId, true, variations, _propertyValueConverters, _publishedModelFactory, this); } + // fixme kill // for tests - internal PublishedPropertyType CreatePropertyType(string propertyTypeAlias, int dataTypeId, bool umbraco = false, ContentVariation variations = ContentVariation.Nothing) + internal IPublishedPropertyType CreatePropertyType(string propertyTypeAlias, int dataTypeId, bool umbraco = false, ContentVariation variations = ContentVariation.Nothing) { return new PublishedPropertyType(propertyTypeAlias, dataTypeId, umbraco, variations, _propertyValueConverters, _publishedModelFactory, this); } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs index 5f374f8bc8..e11d2391ec 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Initializes a new instance of the class. /// - protected PublishedPropertyBase(PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel) + protected PublishedPropertyBase(IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel) { PropertyType = propertyType ?? throw new ArgumentNullException(nameof(propertyType)); ReferenceCacheLevel = referenceCacheLevel; @@ -42,7 +42,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Gets the property type. /// - public PublishedPropertyType PropertyType { get; } + public IPublishedPropertyType PropertyType { get; } /// /// Gets the property reference cache level. diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs index 8dd5003582..1632a85f44 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs @@ -10,10 +10,8 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Instances of the class are immutable, ie /// if the property type changes, then a new class needs to be created. - public class PublishedPropertyType + public class PublishedPropertyType : IPublishedPropertyType { - // TODO: API design review, should this be an interface? - private readonly IPublishedModelFactory _publishedModelFactory; private readonly PropertyValueConverterCollection _propertyValueConverters; private readonly object _locker = new object(); @@ -38,6 +36,7 @@ namespace Umbraco.Core.Models.PublishedContent ContentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); } + // fixme should be internal? /// /// This constructor is for tests and is not intended to be used directly from application code. /// @@ -51,6 +50,7 @@ namespace Umbraco.Core.Models.PublishedContent ContentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); } + // fixme should be internal? /// /// This constructor is for tests and is not intended to be used directly from application code. /// @@ -75,37 +75,22 @@ namespace Umbraco.Core.Models.PublishedContent #region Property type - /// - /// Gets the published content type containing the property type. - /// + /// public IPublishedContentType ContentType { get; internal set; } // internally set by PublishedContentType constructor - /// - /// Gets the data type. - /// + /// public PublishedDataType DataType { get; } - /// - /// Gets property type alias. - /// + /// public string Alias { get; } - /// - /// Gets the property editor alias. - /// + /// public string EditorAlias => DataType.EditorAlias; - /// - /// Gets a value indicating whether the property is a user content property. - /// - /// A non-user content property is a property that has been added to a - /// published content type by Umbraco but does not corresponds to a user-defined - /// published property. + /// public bool IsUserProperty { get; } - /// - /// Gets the content variations of the property type. - /// + /// public ContentVariation Variations { get; } #endregion @@ -193,10 +178,7 @@ namespace Umbraco.Core.Models.PublishedContent _modelClrType = _converter == null ? typeof (object) : _converter.GetPropertyValueType(this); } - /// - /// Determines whether a value is an actual value, or not a value. - /// - /// Used by property.HasValue and, for instance, in fallback scenarios. + /// public bool? IsValue(object value, PropertyValueLevel level) { if (!_initialized) Initialize(); @@ -209,9 +191,7 @@ namespace Umbraco.Core.Models.PublishedContent return value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false); } - /// - /// Gets the property cache level. - /// + /// public PropertyCacheLevel CacheLevel { get @@ -221,13 +201,7 @@ namespace Umbraco.Core.Models.PublishedContent } } - /// - /// Converts the source value into the intermediate value. - /// - /// The published element owning the property. - /// The source value. - /// A value indicating whether content should be considered draft. - /// The intermediate value. + /// public object ConvertSourceToInter(IPublishedElement owner, object source, bool preview) { if (!_initialized) Initialize(); @@ -238,14 +212,7 @@ namespace Umbraco.Core.Models.PublishedContent : source; } - /// - /// Converts the intermediate value into the object value. - /// - /// The published element owning the property. - /// The reference cache level. - /// The intermediate value. - /// A value indicating whether content should be considered draft. - /// The object value. + /// public object ConvertInterToObject(IPublishedElement owner, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { if (!_initialized) Initialize(); @@ -256,17 +223,7 @@ namespace Umbraco.Core.Models.PublishedContent : inter; } - /// - /// Converts the intermediate value into the XPath value. - /// - /// The published element owning the property. - /// The reference cache level. - /// The intermediate value. - /// A value indicating whether content should be considered draft. - /// The XPath value. - /// - /// The XPath value can be either a string or an XPathNavigator. - /// + /// public object ConvertInterToXPath(IPublishedElement owner, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { if (!_initialized) Initialize(); @@ -282,13 +239,7 @@ namespace Umbraco.Core.Models.PublishedContent return inter.ToString().Trim(); } - /// - /// Gets the property model CLR type. - /// - /// - /// The model CLR type may be a type, or may contain types. - /// For the actual CLR type, see . - /// + /// public Type ModelClrType { get @@ -298,14 +249,7 @@ namespace Umbraco.Core.Models.PublishedContent } } - /// - /// Gets the property CLR type. - /// - /// - /// Returns the actual CLR type which does not contain types. - /// Mapping from may throw if some instances - /// could not be mapped to actual CLR types. - /// + /// public Type ClrType { get diff --git a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs index 7469222ab0..10f999532f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs +++ b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs @@ -38,7 +38,7 @@ namespace Umbraco.Core.Models.PublishedContent public override object GetXPathValue(string culture = null, string segment = null) => string.IsNullOrEmpty(culture) & string.IsNullOrEmpty(segment) ? _xpathValue.Value : null; - public RawValueProperty(PublishedPropertyType propertyType, IPublishedElement content, object sourceValue, bool isPreviewing = false) + public RawValueProperty(IPublishedPropertyType propertyType, IPublishedElement content, object sourceValue, bool isPreviewing = false) : base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored { if (propertyType.Variations != ContentVariation.Nothing) diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs b/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs index f6a7cbf32f..0a9cf632bc 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.PropertyEditors /// /// The property type. /// A value indicating whether the converter supports a property type. - bool IsConverter(PublishedPropertyType propertyType); + bool IsConverter(IPublishedPropertyType propertyType); /// /// Determines whether a value is an actual value, or not a value. @@ -36,14 +36,14 @@ namespace Umbraco.Core.PropertyEditors /// The CLR type of values returned by the converter. /// Some of the CLR types may be generated, therefore this method cannot directly return /// a Type object (which may not exist yet). In which case it needs to return a ModelType instance. - Type GetPropertyValueType(PublishedPropertyType propertyType); + Type GetPropertyValueType(IPublishedPropertyType propertyType); /// /// Gets the property cache level. /// /// The property type. /// The property cache level. - PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType); + PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType); /// /// Converts a property source value to an intermediate value. @@ -64,7 +64,7 @@ namespace Umbraco.Core.PropertyEditors /// strings, and xml-whitespace strings appropriately, ie it should know whether to preserve /// white spaces. /// - object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview); + object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview); /// /// Converts a property intermediate value to an Object value. @@ -83,7 +83,7 @@ namespace Umbraco.Core.PropertyEditors /// passed to eg a PublishedFragment constructor. It is used by the fragment and the properties to manage /// the cache levels of property values. It is not meant to be used by the converter. /// - object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview); + object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview); /// /// Converts a property intermediate value to an XPath value. @@ -107,6 +107,6 @@ namespace Umbraco.Core.PropertyEditors /// passed to eg a PublishedFragment constructor. It is used by the fragment and the properties to manage /// the cache levels of property values. It is not meant to be used by the converter. /// - object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview); + object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview); } } diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs index 48bfc49ed9..3b6ebc610c 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.PropertyEditors /// public abstract class PropertyValueConverterBase : IPropertyValueConverter { - public virtual bool IsConverter(PublishedPropertyType propertyType) + public virtual bool IsConverter(IPublishedPropertyType propertyType) => false; public virtual bool? IsValue(object value, PropertyValueLevel level) @@ -30,19 +30,19 @@ namespace Umbraco.Core.PropertyEditors return value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false); } - public virtual Type GetPropertyValueType(PublishedPropertyType propertyType) + public virtual Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (object); - public virtual PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public virtual PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; - public virtual object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public virtual object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) => source; - public virtual object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public virtual object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) => inter; - public virtual object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public virtual object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) => inter?.ToString() ?? string.Empty; } } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/CheckboxListValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/CheckboxListValueConverter.cs index 3d69c37b8b..dd2dfb49e7 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/CheckboxListValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/CheckboxListValueConverter.cs @@ -9,16 +9,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class CheckboxListValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.CheckBoxList); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (IEnumerable); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) { var sourceString = source?.ToString() ?? string.Empty; diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/ColorPickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/ColorPickerValueConverter.cs index 9f260fc973..46dae3e4f0 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/ColorPickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/ColorPickerValueConverter.cs @@ -8,16 +8,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class ColorPickerValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.ColorPicker); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => UseLabel(propertyType) ? typeof(PickedColor) : typeof(string); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { var useLabel = UseLabel(propertyType); @@ -39,7 +39,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters return ssource; } - private bool UseLabel(PublishedPropertyType propertyType) + private bool UseLabel(IPublishedPropertyType propertyType) { return ConfigurationEditor.ConfigurationAs(propertyType.DataType.Configuration).UseLabel; } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs index ffe9feb653..0206528bf7 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs @@ -8,16 +8,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class DatePickerValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.DateTime); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (DateTime); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if (source == null) return DateTime.MinValue; @@ -39,7 +39,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters // default ConvertSourceToObject just returns source ie a DateTime value - public override object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { // source should come from ConvertSource and be a DateTime already return XmlConvert.ToString((DateTime) inter, XmlDateTimeSerializationMode.Unspecified); diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DecimalValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DecimalValueConverter.cs index 6f7888aee3..7d6e7c0ce9 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/DecimalValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/DecimalValueConverter.cs @@ -7,16 +7,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class DecimalValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => Constants.PropertyEditors.Aliases.Decimal.Equals(propertyType.EditorAlias); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (decimal); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if (source == null) { diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs index e4ef3a50a3..88061a559e 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/EmailAddressValueConverter.cs @@ -6,16 +6,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class EmailAddressValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.EmailAddress); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (string); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) { return source?.ToString() ?? string.Empty; } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs index 29f6de0271..b3685457ec 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs @@ -28,16 +28,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters _config = config; } - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.Grid); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (JToken); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if (source == null) return null; var sourceString = source.ToString(); diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs index 79cb748960..6f5bd571b7 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs @@ -14,19 +14,19 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters public class ImageCropperValueConverter : PropertyValueConverterBase { /// - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.ImageCropper); /// - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (ImageCropperValue); /// - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; /// - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if (source == null) return null; var sourceString = source.ToString(); diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs index e0abf17a7e..ca8f23bca2 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs @@ -6,16 +6,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class IntegerValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => Constants.PropertyEditors.Aliases.Integer.Equals(propertyType.EditorAlias); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (int); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { return source.TryConvertTo().Result; } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs index e04893716a..12e6238705 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs @@ -31,19 +31,19 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters /// /// /// - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) { return _propertyEditors.TryGet(propertyType.EditorAlias, out var editor) && editor.GetValueEditor().ValueType.InvariantEquals(ValueTypes.Json); } - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (JToken); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if (source == null) return null; var sourceString = source.ToString(); diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/LabelValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/LabelValueConverter.cs index 05a5f15aaf..84baf226cf 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/LabelValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/LabelValueConverter.cs @@ -16,10 +16,10 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class LabelValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => Constants.PropertyEditors.Aliases.Label.Equals(propertyType.EditorAlias); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) { var valueType = ConfigurationEditor.ConfigurationAs(propertyType.DataType.Configuration); switch (valueType.ValueType) @@ -40,10 +40,10 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters } } - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { var valueType = ConfigurationEditor.ConfigurationAs(propertyType.DataType.Configuration); switch (valueType.ValueType) diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs index aeacf33eef..a062561ab1 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs @@ -7,17 +7,17 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class MarkdownEditorValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => Constants.PropertyEditors.Aliases.MarkdownEditor.Equals(propertyType.EditorAlias); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (IHtmlString); // PropertyCacheLevel.Content is ok here because that converter does not parse {locallink} nor executes macros - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { // in xml a string is: string // in the database a string is: string @@ -25,13 +25,13 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters return source; } - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { // source should come from ConvertSource and be a string (or null) already return new HtmlString(inter == null ? string.Empty : (string) inter); } - public override object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { // source should come from ConvertSource and be a string (or null) already return inter?.ToString() ?? string.Empty; diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MemberGroupPickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MemberGroupPickerValueConverter.cs index bdd09ea33b..cd7f48f510 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MemberGroupPickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MemberGroupPickerValueConverter.cs @@ -6,16 +6,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class MemberGroupPickerValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.MemberGroupPicker); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (string); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { return source?.ToString() ?? string.Empty; } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs index 1d5f0b1ca3..15e7ce4caf 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs @@ -9,18 +9,18 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class MultipleTextStringValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => Constants.PropertyEditors.Aliases.MultipleTextstring.Equals(propertyType.EditorAlias); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (IEnumerable); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; private static readonly string[] NewLineDelimiters = { "\r\n", "\r", "\n" }; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { // data is (both in database and xml): // @@ -58,7 +58,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters : values.ToArray(); } - public override object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { var d = new XmlDocument(); var e = d.CreateElement("values"); diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MustBeStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MustBeStringValueConverter.cs index c9528c3e8b..b9c61bb169 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MustBeStringValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MustBeStringValueConverter.cs @@ -22,16 +22,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters Constants.PropertyEditors.Aliases.MultiNodeTreePicker }; - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => Aliases.Contains(propertyType.EditorAlias); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (string); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { return source?.ToString(); } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/RadioButtonListValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/RadioButtonListValueConverter.cs index b99cc7e0e3..61adc9a93e 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/RadioButtonListValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/RadioButtonListValueConverter.cs @@ -6,16 +6,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class RadioButtonListValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.RadioButtonList); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (string); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { var attempt = source.TryConvertTo(); diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs index 31ab47223f..11502687b7 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs @@ -18,16 +18,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters _dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); } - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.Slider); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => IsRangeDataType(propertyType.DataType.Id) ? typeof (Range) : typeof (decimal); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) { if (source == null) return null; diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs index 9b857c2dff..b54c693c14 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs @@ -19,16 +19,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters _dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); } - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.Tags); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (IEnumerable); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if (source == null) return Array.Empty(); @@ -43,7 +43,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters return source.ToString().Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); } - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) { return (string[]) source; } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs index 2368a1d034..7caa9a90cc 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs @@ -13,16 +13,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters Constants.PropertyEditors.Aliases.TextArea }; - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => PropertyTypeAliases.Contains(propertyType.EditorAlias); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (string); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { // in xml a string is: string // in the database a string is: string @@ -30,13 +30,13 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters return source; } - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { // source should come from ConvertSource and be a string (or null) already return inter ?? string.Empty; } - public override object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { // source should come from ConvertSource and be a string (or null) already return inter; diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/TinyMceValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/TinyMceValueConverter.cs index 46f660d829..9938af671d 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/TinyMceValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/TinyMceValueConverter.cs @@ -10,17 +10,17 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class TinyMceValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias == Constants.PropertyEditors.Aliases.TinyMce; - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (IHtmlString); // PropertyCacheLevel.Content is ok here because that converter does not parse {locallink} nor executes macros - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { // in xml a string is: string // in the database a string is: string @@ -28,13 +28,13 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters return source; } - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { // source should come from ConvertSource and be a string (or null) already return new HtmlString(inter == null ? string.Empty : (string)inter); } - public override object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { // source should come from ConvertSource and be a string (or null) already return inter; diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/UploadPropertyConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/UploadPropertyConverter.cs index cfa247edaa..407ed13ddf 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/UploadPropertyConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/UploadPropertyConverter.cs @@ -9,16 +9,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class UploadPropertyConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.Equals(Constants.PropertyEditors.Aliases.UploadField); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (string); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) { return source?.ToString() ?? ""; } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs index 8ad09733f8..153462ccf5 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs @@ -6,16 +6,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class YesNoValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias == Constants.PropertyEditors.Aliases.Boolean; - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (bool); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { // in xml a boolean is: string // in the database a boolean is: string "1" or "0" or empty @@ -49,7 +49,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters // default ConvertSourceToObject just returns source ie a boolean value - public override object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { // source should come from ConvertSource and be a boolean already return (bool)inter ? "1" : "0"; diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index d6d0a1d9e5..20cc089d1d 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -222,6 +222,7 @@ + diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedProperty.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedProperty.cs index 0c90c8d1ff..7d2fa74aa6 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedProperty.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedProperty.cs @@ -50,7 +50,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache public override object GetXPathValue(string culture = null, string segment = null) { throw new NotImplementedException(); } - public XmlPublishedProperty(PublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing, XmlNode propertyXmlData) + public XmlPublishedProperty(IPublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing, XmlNode propertyXmlData) : this(propertyType, content, isPreviewing) { if (propertyXmlData == null) @@ -58,7 +58,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache _sourceValue = XmlHelper.GetNodeValue(propertyXmlData); } - public XmlPublishedProperty(PublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing, string propertyData) + public XmlPublishedProperty(IPublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing, string propertyData) : this(propertyType, content, isPreviewing) { if (propertyData == null) @@ -66,7 +66,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache _sourceValue = propertyData; } - public XmlPublishedProperty(PublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing) + public XmlPublishedProperty(IPublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing) : base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored { _sourceValue = string.Empty; diff --git a/src/Umbraco.Tests/Published/ConvertersTests.cs b/src/Umbraco.Tests/Published/ConvertersTests.cs index 0fce8ebfc3..f753acf82d 100644 --- a/src/Umbraco.Tests/Published/ConvertersTests.cs +++ b/src/Umbraco.Tests/Published/ConvertersTests.cs @@ -37,10 +37,12 @@ namespace Umbraco.Tests.Published var contentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, dataTypeService); - var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", new[] + IEnumerable CreatePropertyTypes(IPublishedContentType contentType) { - contentTypeFactory.CreatePropertyType("prop1", 1), - }); + yield return contentTypeFactory.CreatePropertyType(contentType, "prop1", 1); + } + + var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", CreatePropertyTypes); var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false); @@ -70,22 +72,22 @@ namespace Umbraco.Tests.Published } } - public bool IsConverter(PublishedPropertyType propertyType) + public bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals("Umbraco.Void"); - public Type GetPropertyValueType(PublishedPropertyType propertyType) + public Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (int); - public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; - public object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) => int.TryParse(source as string, out int i) ? i : 0; - public object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) => (int) inter; - public object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) => ((int) inter).ToString(); } @@ -115,10 +117,12 @@ namespace Umbraco.Tests.Published var contentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, dataTypeService); - var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", new[] + IEnumerable CreatePropertyTypes(IPublishedContentType contentType) { - contentTypeFactory.CreatePropertyType("prop1", 1), - }); + yield return contentTypeFactory.CreatePropertyType(contentType, "prop1", 1); + } + + var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", CreatePropertyTypes); var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false); @@ -143,26 +147,26 @@ namespace Umbraco.Tests.Published public bool? IsValue(object value, PropertyValueLevel level) => value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false); - public bool IsConverter(PublishedPropertyType propertyType) + public bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals("Umbraco.Void"); - public Type GetPropertyValueType(PublishedPropertyType propertyType) + public Type GetPropertyValueType(IPublishedPropertyType propertyType) // the first version would be the "generic" version, but say we want to be more precise // and return: whatever Clr type is generated for content type with alias "cnt1" -- which // we cannot really typeof() at the moment because it has not been generated, hence ModelType. // => typeof (IPublishedContent); => ModelType.For("cnt1"); - public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => _cacheLevel; - public object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) => int.TryParse(source as string, out int i) ? i : -1; - public object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) => _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById((int) inter); - public object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) => ((int) inter).ToString(); } @@ -208,25 +212,15 @@ namespace Umbraco.Tests.Published var contentTypeFactory = new PublishedContentTypeFactory(factory, converters, dataTypeService); - var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", new[] + IEnumerable CreatePropertyTypes(IPublishedContentType contentType, int i) { - contentTypeFactory.CreatePropertyType("prop1", 1), - }); + yield return contentTypeFactory.CreatePropertyType(contentType, "prop" + i, i); + } - var elementType2 = contentTypeFactory.CreateContentType(1001, "element2", new[] - { - contentTypeFactory.CreatePropertyType("prop2", 2), - }); - - var contentType1 = contentTypeFactory.CreateContentType(1002, "content1", new[] - { - contentTypeFactory.CreatePropertyType("prop1", 1), - }); - - var contentType2 = contentTypeFactory.CreateContentType(1003, "content2", new[] - { - contentTypeFactory.CreatePropertyType("prop2", 2), - }); + var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", t => CreatePropertyTypes(t, 1)); + var elementType2 = contentTypeFactory.CreateContentType(1001, "element2", t => CreatePropertyTypes(t, 2)); + var contentType1 = contentTypeFactory.CreateContentType(1002, "content1", t => CreatePropertyTypes(t, 1)); + var contentType2 = contentTypeFactory.CreateContentType(1003, "content2", t => CreatePropertyTypes(t, 2)); var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); var element2 = new PublishedElement(elementType2, Guid.NewGuid(), new Dictionary { { "prop2", "1003" } }, false); @@ -267,13 +261,13 @@ namespace Umbraco.Tests.Published public class SimpleConverter3A : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias == "Umbraco.Void"; - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (string); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; } @@ -286,22 +280,22 @@ namespace Umbraco.Tests.Published _publishedSnapshotAccessor = publishedSnapshotAccessor; } - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias == "Umbraco.Void.2"; - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (IEnumerable<>).MakeGenericType(ModelType.For("content1")); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Elements; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { var s = source as string; return s?.Split(',').Select(int.Parse).ToArray() ?? Array.Empty(); } - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { return ((int[]) inter).Select(x => (PublishedSnapshotTestObjects.TestContentModel1) _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(x)).ToArray(); } diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 35be4b0bf2..09c63dcf06 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -125,13 +125,24 @@ namespace Umbraco.Tests.Published var factory = new PublishedContentTypeFactory(publishedModelFactory.Object, converters, dataTypeService); - var propertyType1 = factory.CreatePropertyType("property1", 1); - var propertyType2 = factory.CreatePropertyType("property2", 2); - var propertyTypeN1 = factory.CreatePropertyType("propertyN1", 3); + IEnumerable CreatePropertyTypes1(IPublishedContentType contentType) + { + yield return factory.CreatePropertyType(contentType, "property1", 1); + } - var contentType1 = factory.CreateContentType(1, "content1", new[] { propertyType1 }); - var contentType2 = factory.CreateContentType(2, "content2", new[] { propertyType2 }); - var contentTypeN1 = factory.CreateContentType(2, "contentN1", new[] { propertyTypeN1 }, isElement: true); + IEnumerable CreatePropertyTypes2(IPublishedContentType contentType) + { + yield return factory.CreatePropertyType(contentType, "property2", 2); + } + + IEnumerable CreatePropertyTypesN1(IPublishedContentType contentType) + { + yield return factory.CreatePropertyType(contentType, "propertyN1", 3); + } + + var contentType1 = factory.CreateContentType(1, "content1", CreatePropertyTypes1); + var contentType2 = factory.CreateContentType(2, "content2", CreatePropertyTypes2); + var contentTypeN1 = factory.CreateContentType(2, "contentN1", CreatePropertyTypesN1, isElement: true); // mocked content cache returns content types contentCache @@ -219,14 +230,14 @@ namespace Umbraco.Tests.Published private readonly bool _hasValue; private IPublishedElement _owner; - public TestPublishedProperty(PublishedPropertyType propertyType, object source) + public TestPublishedProperty(IPublishedPropertyType propertyType, object source) : base(propertyType, PropertyCacheLevel.Element) // initial reference cache level always is .Content { _sourceValue = source; _hasValue = source != null && (!(source is string ssource) || !string.IsNullOrWhiteSpace(ssource)); } - public TestPublishedProperty(PublishedPropertyType propertyType, IPublishedElement element, bool preview, PropertyCacheLevel referenceCacheLevel, object source) + public TestPublishedProperty(IPublishedPropertyType propertyType, IPublishedElement element, bool preview, PropertyCacheLevel referenceCacheLevel, object source) : base(propertyType, referenceCacheLevel) { _sourceValue = source; diff --git a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs index 76fdd81ec2..9db539d142 100644 --- a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs +++ b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs @@ -35,10 +35,13 @@ namespace Umbraco.Tests.Published new DataType(new VoidEditor(Mock.Of())) { Id = 1 }); var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, dataTypeService); - var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", new[] + + IEnumerable CreatePropertyTypes(IPublishedContentType contentType) { - publishedContentTypeFactory.CreatePropertyType("prop1", 1), - }); + yield return publishedContentTypeFactory.CreatePropertyType(contentType, "prop1", 1); + } + + var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); // PublishedElementPropertyBase.GetCacheLevels: // @@ -113,10 +116,13 @@ namespace Umbraco.Tests.Published new DataType(new VoidEditor(Mock.Of())) { Id = 1 }); var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, dataTypeService); - var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", new[] + + IEnumerable CreatePropertyTypes(IPublishedContentType contentType) { - publishedContentTypeFactory.CreatePropertyType("prop1", 1), - }); + yield return publishedContentTypeFactory.CreatePropertyType(contentType, "prop1", 1); + } + + var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); var elementsCache = new FastDictionaryAppCache(); var snapshotCache = new FastDictionaryAppCache(); @@ -187,10 +193,13 @@ namespace Umbraco.Tests.Published new DataType(new VoidEditor(Mock.Of())) { Id = 1 }); var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, dataTypeService); - var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", new[] + + IEnumerable CreatePropertyTypes(IPublishedContentType contentType) { - publishedContentTypeFactory.CreatePropertyType("prop1", 1), - }); + yield return publishedContentTypeFactory.CreatePropertyType(contentType, "prop1", 1); + } + + var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); Assert.Throws(() => { @@ -213,28 +222,28 @@ namespace Umbraco.Tests.Published public bool? IsValue(object value, PropertyValueLevel level) => value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false); - public bool IsConverter(PublishedPropertyType propertyType) + public bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals("Umbraco.Void"); - public Type GetPropertyValueType(PublishedPropertyType propertyType) + public Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof(int); - public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => _cacheLevel; - public object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { SourceConverts++; return int.TryParse(source as string, out int i) ? i : 0; } - public object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { InterConverts++; return (int) inter; } - public object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) => ((int) inter).ToString(); } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index 108bfb9f18..5c28b98de7 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -65,17 +65,22 @@ namespace Umbraco.Tests.PublishedContent var welcome2Type = factory.CreatePropertyType("welcomeText2", 1, variations: ContentVariation.Culture); var nopropType = factory.CreatePropertyType("noprop", 1, variations: ContentVariation.Culture); - var props = new[] - { - prop1Type, - welcomeType, - welcome2Type, - nopropType - }; - var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), props); + IEnumerable CreatePropertyTypes1(IPublishedContentType contentType) + { + yield return factory.CreatePropertyType(contentType, "prop1", 1, variations: ContentVariation.Culture); + yield return factory.CreatePropertyType(contentType, "welcomeText", 1, variations: ContentVariation.Culture); + yield return factory.CreatePropertyType(contentType, "welcomeText2", 1, variations: ContentVariation.Culture); + yield return factory.CreatePropertyType(contentType, "noprop", 1, variations: ContentVariation.Culture); + } - var prop3Type = factory.CreatePropertyType("prop3", 1, variations: ContentVariation.Culture); - var contentType2 = factory.CreateContentType(2, "contentType2", Enumerable.Empty(), new[] { prop3Type }); + var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), CreatePropertyTypes1); + + IEnumerable CreatePropertyTypes2(IPublishedContentType contentType) + { + yield return factory.CreatePropertyType(contentType, "prop3", 1, variations: ContentVariation.Culture); + } + + var contentType2 = factory.CreateContentType(2, "contentType2", Enumerable.Empty(), CreatePropertyTypes2); var prop1 = new SolidPublishedPropertyWithLanguageVariants { @@ -150,7 +155,7 @@ namespace Umbraco.Tests.PublishedContent var prop4 = new SolidPublishedPropertyWithLanguageVariants { Alias = "prop3", - PropertyType = prop3Type + PropertyType = contentType2.GetPropertyType("prop3") }; prop4.SetSourceValue("en-US", "Oxxo", true); prop4.SetValue("en-US", "Oxxo", true); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index b2f1f311c3..48ad4aa464 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -1,4 +1,5 @@ -using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using NUnit.Framework; using Umbraco.Core.Models.PublishedContent; @@ -15,13 +16,14 @@ namespace Umbraco.Tests.PublishedContent { internal override void PopulateCache(PublishedContentTypeFactory factory, SolidPublishedContentCache cache) { - var props = new[] - { - factory.CreatePropertyType("prop1", 1), - }; - var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), props); - var contentType2 = factory.CreateContentType(2, "ContentType2", Enumerable.Empty(), props); - var contentType2Sub = factory.CreateContentType(3, "ContentType2Sub", Enumerable.Empty(), props); + IEnumerable CreatePropertyTypes(IPublishedContentType contentType) + { + yield return factory.CreatePropertyType(contentType, "prop1", 1); + } + + var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), CreatePropertyTypes); + var contentType2 = factory.CreateContentType(2, "ContentType2", Enumerable.Empty(), CreatePropertyTypes); + var contentType2Sub = factory.CreateContentType(3, "ContentType2Sub", Enumerable.Empty(), CreatePropertyTypes); cache.Add(new SolidPublishedContent(contentType1) { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index c5bcd29589..6907f6936c 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -1,4 +1,5 @@ -using Umbraco.Core; +using System.Collections.Generic; +using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; @@ -41,13 +42,12 @@ namespace Umbraco.Tests.PublishedContent var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, dataTypeService); - // need to specify a custom callback for unit tests - var propertyTypes = new[] + IEnumerable CreatePropertyTypes(IPublishedContentType contentType) { - // AutoPublishedContentType will auto-generate other properties - publishedContentTypeFactory.CreatePropertyType("content", 1), - }; - var type = new AutoPublishedContentType(0, "anything", propertyTypes); + yield return publishedContentTypeFactory.CreatePropertyType(contentType, "content", 1); + } + + var type = new AutoPublishedContentType(0, "anything", CreatePropertyTypes); ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; var umbracoContext = GetUmbracoContext("/test"); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 605fae7a1b..8be13345fb 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Web; @@ -62,18 +63,19 @@ namespace Umbraco.Tests.PublishedContent // when they are requested, but we must declare those that we // explicitely want to be here... - var propertyTypes = new[] + IEnumerable CreatePropertyTypes(IPublishedContentType contentType) { // AutoPublishedContentType will auto-generate other properties - factory.CreatePropertyType("umbracoNaviHide", 1001), - factory.CreatePropertyType("selectedNodes", 1), - factory.CreatePropertyType("umbracoUrlAlias", 1), - factory.CreatePropertyType("content", 1002), - factory.CreatePropertyType("testRecursive", 1), - }; + yield return factory.CreatePropertyType(contentType, "umbracoNaviHide", 1001); + yield return factory.CreatePropertyType(contentType, "selectedNodes", 1); + yield return factory.CreatePropertyType(contentType, "umbracoUrlAlias", 1); + yield return factory.CreatePropertyType(contentType, "content", 1002); + yield return factory.CreatePropertyType(contentType, "testRecursive", 1); + } + var compositionAliases = new[] { "MyCompositionAlias" }; - var anythingType = new AutoPublishedContentType(0, "anything", compositionAliases, propertyTypes); - var homeType = new AutoPublishedContentType(0, "home", compositionAliases, propertyTypes); + var anythingType = new AutoPublishedContentType(0, "anything", compositionAliases, CreatePropertyTypes); + var homeType = new AutoPublishedContentType(0, "home", compositionAliases, CreatePropertyTypes); ContentTypesCache.GetPublishedContentTypeByAlias = alias => alias.InvariantEquals("home") ? homeType : anythingType; } @@ -887,8 +889,13 @@ namespace Umbraco.Tests.PublishedContent { var factory = Factory.GetInstance() as PublishedContentTypeFactory; - var pt = factory.CreatePropertyType("detached", 1003); - var ct = factory.CreateContentType(0, "alias", new[] { pt }); + IEnumerable CreatePropertyTypes(IPublishedContentType contentType) + { + yield return factory.CreatePropertyType(contentType, "detached", 1003); + } + + var ct = factory.CreateContentType(0, "alias", CreatePropertyTypes); + var pt = ct.GetPropertyType("detached"); var prop = new PublishedElementPropertyBase(pt, null, false, PropertyCacheLevel.None, 5548); Assert.IsInstanceOf(prop.GetValue()); Assert.AreEqual(5548, prop.GetValue()); @@ -906,16 +913,20 @@ namespace Umbraco.Tests.PublishedContent { var factory = Factory.GetInstance() as PublishedContentTypeFactory; - var pt1 = factory.CreatePropertyType("legend", 1004); - var pt2 = factory.CreatePropertyType("image", 1005); - var pt3 = factory.CreatePropertyType("size", 1003); + IEnumerable CreatePropertyTypes(IPublishedContentType contentType) + { + yield return factory.CreatePropertyType(contentType, "legend", 1004); + yield return factory.CreatePropertyType(contentType, "image", 1005); + yield return factory.CreatePropertyType(contentType, "size", 1003); + } + const string val1 = "boom bam"; const int val2 = 0; const int val3 = 666; var guid = Guid.NewGuid(); - var ct = factory.CreateContentType(0, "alias", new[] { pt1, pt2, pt3 }); + var ct = factory.CreateContentType(0, "alias", CreatePropertyTypes); var c = new ImageWithLegendModel(ct, guid, new Dictionary { diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index c14a8c1740..f7d016f725 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -253,7 +253,7 @@ namespace Umbraco.Tests.PublishedContent internal class SolidPublishedProperty : IPublishedProperty { - public PublishedPropertyType PropertyType { get; set; } + public IPublishedPropertyType PropertyType { get; set; } public string Alias { get; set; } public object SolidSourceValue { get; set; } public object SolidValue { get; set; } @@ -397,7 +397,7 @@ namespace Umbraco.Tests.PublishedContent class AutoPublishedContentType : PublishedContentType { - private static readonly PublishedPropertyType Default; + private static readonly IPublishedPropertyType Default; static AutoPublishedContentType() { @@ -412,11 +412,19 @@ namespace Umbraco.Tests.PublishedContent : base(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) { } + public AutoPublishedContentType(int id, string alias, Func> propertyTypes) + : base(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) + { } + public AutoPublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) : base(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) { } - public override PublishedPropertyType GetPropertyType(string alias) + public AutoPublishedContentType(int id, string alias, IEnumerable compositionAliases, Func> propertyTypes) + : base(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) + { } + + public override IPublishedPropertyType GetPropertyType(string alias) { var propertyType = base.GetPropertyType(alias); return propertyType ?? Default; diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index 0ff2a41867..978bbbcecb 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -140,14 +140,14 @@ namespace Umbraco.Web.Macros private readonly object _sourceValue; private readonly IPublishedContent _content; - public PagePublishedProperty(PublishedPropertyType propertyType, IPublishedContent content) + public PagePublishedProperty(IPublishedPropertyType propertyType, IPublishedContent content) : base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored { _sourceValue = null; _content = content; } - public PagePublishedProperty(PublishedPropertyType propertyType, IPublishedContent content, Umbraco.Core.Models.Property property) + public PagePublishedProperty(IPublishedPropertyType propertyType, IPublishedContent content, Umbraco.Core.Models.Property property) : base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored { _sourceValue = property.GetValue(); diff --git a/src/Umbraco.Web/Models/PublishedProperty.cs b/src/Umbraco.Web/Models/PublishedProperty.cs index d32612c54c..f715df7450 100644 --- a/src/Umbraco.Web/Models/PublishedProperty.cs +++ b/src/Umbraco.Web/Models/PublishedProperty.cs @@ -19,8 +19,8 @@ namespace Umbraco.Web.Models /// and taking values from the collection of Property. /// Ensures that all conversions took place correctly. internal static IEnumerable MapProperties( - IEnumerable propertyTypes, IEnumerable properties, - Func map) + IEnumerable propertyTypes, IEnumerable properties, + Func map) { var propertyEditors = Current.PropertyEditors; var dataTypeService = Current.Services.DataTypeService; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs index d30762f13f..056334cfa5 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs @@ -23,16 +23,16 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters _publishedSnapshotAccessor = publishedSnapshotAccessor; } - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.Equals(Constants.PropertyEditors.Aliases.ContentPicker); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (IPublishedContent); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Elements; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if (source == null) return null; @@ -45,7 +45,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return null; } - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { if (inter == null) return null; @@ -73,7 +73,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return inter; } - public override object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { if (inter == null) return null; return inter.ToString(); diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs index 43add34327..0d6089f0f4 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs @@ -11,12 +11,12 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class FlexibleDropdownPropertyValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) { return propertyType.EditorAlias.Equals(Constants.PropertyEditors.Aliases.DropDownListFlexible); } - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if(source == null) return Array.Empty(); @@ -24,7 +24,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return JsonConvert.DeserializeObject(source.ToString()) ?? Array.Empty(); } - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { if (inter == null) return null; @@ -43,7 +43,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters : string.Empty; } - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) { return propertyType.DataType.ConfigurationAs().Multiple ? typeof(IEnumerable) diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs index e6e66b79ff..ef356e3604 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs @@ -28,13 +28,13 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters _macroRenderer = macroRenderer ?? throw new ArgumentNullException(nameof(macroRenderer)); } - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias == Constants.PropertyEditors.Aliases.MacroContainer; - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (IHtmlString); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; // NOT thread-safe over a request because it modifies the @@ -63,7 +63,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters } } - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if (source == null) return null; var sourceString = source.ToString(); diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs index cb9709157f..e11f3e0d3a 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs @@ -12,16 +12,16 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class MarkdownEditorValueConverter : PropertyValueConverterBase { - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => Constants.PropertyEditors.Aliases.MarkdownEditor == propertyType.EditorAlias; - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (IHtmlString); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if (source == null) return null; var sourceString = source.ToString(); @@ -33,7 +33,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return sourceString; } - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { // convert markup to HTML for frontend rendering. // source should come from ConvertSource and be a string (or null) already diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs index 0218867bb4..d2272a25b5 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs @@ -29,12 +29,12 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters _publishedModelFactory = publishedModelFactory; } - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) { return propertyType.EditorAlias.Equals(Constants.PropertyEditors.Aliases.MediaPicker); } - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) { var isMultiple = IsMultipleDataType(propertyType.DataType); var isOnlyImages = IsOnlyImagesDataType(propertyType.DataType); @@ -48,7 +48,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters : typeof(IPublishedContent); } - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; private bool IsMultipleDataType(PublishedDataType dataType) @@ -63,7 +63,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return config.OnlyImages; } - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if (source == null) return null; @@ -75,7 +75,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return nodeIds; } - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) { var isMultiple = IsMultipleDataType(propertyType.DataType); diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs index b4c7f99a75..cd69fd9de6 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs @@ -18,18 +18,18 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); } - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) { return propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.MemberPicker); } - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (IPublishedContent); - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { var attemptConvertInt = source.TryConvertTo(); if (attemptConvertInt.Success) @@ -40,7 +40,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return null; } - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) { if (source == null) return null; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs index a210aa62c6..f80f8f510f 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs @@ -34,18 +34,18 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); } - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) { return propertyType.EditorAlias.Equals(Constants.PropertyEditors.Aliases.MultiNodeTreePicker); } - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (IEnumerable); - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if (source == null) return null; @@ -60,7 +60,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return null; } - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) { if (source == null) { diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs index 48cec2a3d2..87ab5b8ff9 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs @@ -23,20 +23,20 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters _proflog = proflog ?? throw new ArgumentNullException(nameof(proflog)); } - public override bool IsConverter(PublishedPropertyType propertyType) => Constants.PropertyEditors.Aliases.MultiUrlPicker.Equals(propertyType.EditorAlias); + public override bool IsConverter(IPublishedPropertyType propertyType) => Constants.PropertyEditors.Aliases.MultiUrlPicker.Equals(propertyType.EditorAlias); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) => + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => propertyType.DataType.ConfigurationAs().MaxNumber == 1 ? typeof(Link) : typeof(IEnumerable); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; public override bool? IsValue(object value, PropertyValueLevel level) => value?.ToString() != "[]"; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) => source?.ToString(); + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) => source?.ToString(); - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { using (_proflog.DebugDuration($"ConvertPropertyToLinks ({propertyType.DataType.Id})")) { diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs index 93e4afc7e5..74b2af6498 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs @@ -28,11 +28,11 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters } /// - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => IsNestedMany(propertyType); /// - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) { var contentTypes = propertyType.DataType.ConfigurationAs().ContentTypes; return contentTypes.Length > 1 @@ -41,19 +41,19 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters } /// - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; /// - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { return source?.ToString(); } /// - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { - using (_proflog.DebugDuration($"ConvertPropertyToNestedContent ({propertyType.DataType.Id})")) + using (_proflog.DebugDuration($"ConvertPropertyToNestedContent ({propertyType.DataType.Id})")) { var configuration = propertyType.DataType.ConfigurationAs(); var contentTypes = configuration.ContentTypes; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs index e084b3a343..06aa0b42fb 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs @@ -27,11 +27,11 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters } /// - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => IsNestedSingle(propertyType); /// - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) { var contentTypes = propertyType.DataType.ConfigurationAs().ContentTypes; return contentTypes.Length > 1 @@ -40,19 +40,19 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters } /// - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; /// - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { return source?.ToString(); } /// - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { - using (_proflog.DebugDuration($"ConvertPropertyToNestedContent ({propertyType.DataType.Id})")) + using (_proflog.DebugDuration($"ConvertPropertyToNestedContent ({propertyType.DataType.Id})")) { var value = (string) inter; if (string.IsNullOrWhiteSpace(value)) return null; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs index e3723e2221..7c18d8ebca 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs @@ -20,12 +20,12 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters protected IPublishedModelFactory PublishedModelFactory { get; } - public static bool IsNested(PublishedPropertyType publishedProperty) + public static bool IsNested(IPublishedPropertyType publishedProperty) { return publishedProperty.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.NestedContent); } - public static bool IsNestedSingle(PublishedPropertyType publishedProperty) + public static bool IsNestedSingle(IPublishedPropertyType publishedProperty) { if (!IsNested(publishedProperty)) return false; @@ -34,7 +34,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return config.MinItems == 1 && config.MaxItems == 1; } - public static bool IsNestedMany(PublishedPropertyType publishedProperty) + public static bool IsNestedMany(IPublishedPropertyType publishedProperty) { return IsNested(publishedProperty) && !IsNestedSingle(publishedProperty); } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index fbbf058c49..990ef8daf1 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IMacroRenderer _macroRenderer; - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) { // because that version of RTE converter parses {locallink} and executes macros, its value has // to be cached at the published snapshot level, because we have no idea what the macros may depend on actually. @@ -63,7 +63,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters } } - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if (source == null) { diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs index 2d5c322f58..b8ad1477b4 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs @@ -17,16 +17,16 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters Constants.PropertyEditors.Aliases.TextArea }; - public override bool IsConverter(PublishedPropertyType propertyType) + public override bool IsConverter(IPublishedPropertyType propertyType) => PropertyTypeAliases.Contains(propertyType.EditorAlias); - public override Type GetPropertyValueType(PublishedPropertyType propertyType) + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof (string); - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { if (source == null) return null; var sourceString = source.ToString(); @@ -38,13 +38,13 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return sourceString; } - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { // source should come from ConvertSource and be a string (or null) already return inter ?? string.Empty; } - public override object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + public override object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { // source should come from ConvertSource and be a string (or null) already return inter; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs index 2c8bc94d90..0254b815c1 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs @@ -38,12 +38,12 @@ namespace Umbraco.Web.PublishedCache.NuCache private string _valuesCacheKey; // initializes a published content property with no value - public Property(PublishedPropertyType propertyType, PublishedContent content, IPublishedSnapshotAccessor publishedSnapshotAccessor, PropertyCacheLevel referenceCacheLevel = PropertyCacheLevel.Element) + public Property(IPublishedPropertyType propertyType, PublishedContent content, IPublishedSnapshotAccessor publishedSnapshotAccessor, PropertyCacheLevel referenceCacheLevel = PropertyCacheLevel.Element) : this(propertyType, content, null, publishedSnapshotAccessor, referenceCacheLevel) { } // initializes a published content property with a value - public Property(PublishedPropertyType propertyType, PublishedContent content, PropertyData[] sourceValues, IPublishedSnapshotAccessor publishedSnapshotAccessor, PropertyCacheLevel referenceCacheLevel = PropertyCacheLevel.Element) + public Property(IPublishedPropertyType propertyType, PublishedContent content, PropertyData[] sourceValues, IPublishedSnapshotAccessor publishedSnapshotAccessor, PropertyCacheLevel referenceCacheLevel = PropertyCacheLevel.Element) : base(propertyType, referenceCacheLevel) { if (sourceValues != null) diff --git a/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs b/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs index 62f72a27aa..ef62a3254d 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs @@ -26,7 +26,7 @@ namespace Umbraco.Web.PublishedCache // so making it configurable. private const bool FullCacheWhenPreviewing = true; - public PublishedElementPropertyBase(PublishedPropertyType propertyType, IPublishedElement element, bool previewing, PropertyCacheLevel referenceCacheLevel, object sourceValue = null, IPublishedSnapshotAccessor publishedSnapshotAccessor = null) + public PublishedElementPropertyBase(IPublishedPropertyType propertyType, IPublishedElement element, bool previewing, PropertyCacheLevel referenceCacheLevel, object sourceValue = null, IPublishedSnapshotAccessor publishedSnapshotAccessor = null) : base(propertyType, referenceCacheLevel) { _sourceValue = sourceValue; From 890d7d8ce32e1e527294b215d66a195c1d1fc8bf Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 16 Apr 2019 16:25:10 +0200 Subject: [PATCH 04/75] Refactor IPublishedContent.Name() --- .../PublishedContent/IPublishedContent.cs | 8 +-- .../PublishedContentWrapped.cs | 2 +- .../PublishedContent/PublishedCultureInfos.cs | 2 +- .../PublishedMediaCacheTests.cs | 2 +- .../DictionaryPublishedContent.cs | 2 +- .../XmlPublishedContent.cs | 9 +-- .../Published/NestedContentTests.cs | 2 +- .../PublishedContent/NuCacheTests.cs | 20 +++---- .../PublishedContentDataTableTests.cs | 7 ++- .../PublishedContentLanguageVariantTests.cs | 6 +- .../PublishedContentMoreTests.cs | 59 ++++++++++--------- .../PublishedContent/PublishedMediaTests.cs | 2 +- .../PublishedContent/PublishedRouterTests.cs | 2 +- .../SolidPublishedSnapshot.cs | 7 ++- src/Umbraco.Tests/Runtimes/StandaloneTests.cs | 6 +- .../Scoping/ScopedNuCacheTests.cs | 6 +- .../TestHelpers/Stubs/TestPublishedContent.cs | 5 +- .../Editors/TemplateQueryController.cs | 10 ++-- .../PublishedContentHashtableConverter.cs | 6 +- .../Models/PublishedContentBase.cs | 2 +- .../NuCache/Navigable/NavigableContent.cs | 2 +- .../NuCache/PublishedContent.cs | 19 +++--- .../PublishedCache/PublishedMember.cs | 6 +- src/Umbraco.Web/PublishedContentExtensions.cs | 2 +- .../Routing/UrlProviderExtensions.cs | 2 +- 25 files changed, 102 insertions(+), 94 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index d38c8eb721..95b943d751 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -29,11 +29,9 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Gets the name of the content item. /// - /// - /// The value of this property is contextual. When the content type is multi-lingual, - /// this is the name for the 'current' culture. Otherwise, it is the invariant name. - /// - string Name { get; } + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The name of the content. + string Name(string culture = null); /// /// Gets the url segment of the content item. diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 7f3f38f629..753f75c3cb 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -58,7 +58,7 @@ namespace Umbraco.Core.Models.PublishedContent public virtual int Id => _content.Id; /// - public virtual string Name => _content.Name; + public virtual string Name(string culture = null) => _content.Name(culture); /// public virtual string UrlSegment => _content.UrlSegment; diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs index 749b37a41a..9c5977e8a6 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs @@ -30,7 +30,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Gets the name of the item. /// - public string Name { get; } + internal string Name { get; } /// /// Gets the url segment of the item. diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index 08eeb8ef4d..8dd5cf3890 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -108,7 +108,7 @@ namespace Umbraco.Tests.Cache.PublishedCache Assert.AreEqual(mRoot.ContentType.Alias, publishedMedia.ContentType.Alias); Assert.AreEqual(mRoot.ContentType.Id, publishedMedia.ContentType.Id); Assert.AreEqual(mRoot.Level, publishedMedia.Level); - Assert.AreEqual(mRoot.Name, publishedMedia.Name); + Assert.AreEqual(mRoot.Name, publishedMedia.Name()); Assert.AreEqual(mRoot.Path, publishedMedia.Path); Assert.AreEqual(mRoot.SortOrder, publishedMedia.SortOrder); Assert.IsNull(publishedMedia.Parent); diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs index e472de85dd..c6c16a2466 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs @@ -156,7 +156,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache public override int SortOrder => _sortOrder; - public override string Name => _name; + public override string Name(string culture = null) => _name; public override PublishedCultureInfo GetCulture(string culture = null) => null; diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index 43c47ec569..c547512ba8 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -138,13 +138,10 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache } } - public override string Name + public override string Name(string culture = null) { - get - { - EnsureNodeInitialized(); - return _name; - } + EnsureNodeInitialized(); + return _name; } public override PublishedCultureInfo GetCulture(string culture = null) => null; diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 09c63dcf06..35288c1f75 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -284,7 +284,7 @@ namespace Umbraco.Tests.Published public override int Id { get; } public override int? TemplateId { get; } public override int SortOrder { get; } - public override string Name { get; } + public override string Name(string culture = null) => default; public override PublishedCultureInfo GetCulture(string culture = ".") => throw new NotSupportedException(); public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); public override string UrlSegment { get; } diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index ad2b0220bb..89c2458f18 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -202,35 +202,33 @@ namespace Umbraco.Tests.PublishedContent var publishedContent = snapshot.Content.GetById(1); 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")); - Assert.AreEqual("name-fr1", publishedContent.GetCulture("fr-FR").Name); - Assert.AreEqual("name-uk1", publishedContent.GetCulture("en-UK").Name); + Assert.IsNull(publishedContent.Name()); // no invariant name for varying content + Assert.AreEqual("name-fr1", publishedContent.Name("fr-FR")); + Assert.AreEqual("name-uk1", publishedContent.Name("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")); - Assert.AreEqual("name-fr2", draftContent.GetCulture("fr-FR").Name); - Assert.AreEqual("name-uk2", draftContent.GetCulture("en-UK").Name); + Assert.IsNull(draftContent.Name()); // no invariant name for varying content + Assert.AreEqual("name-fr2", draftContent.Name("fr-FR")); + Assert.AreEqual("name-uk2", draftContent.Name("en-UK")); // now french is default _variationAccesor.VariationContext = new VariationContext("fr-FR"); Assert.AreEqual("val-fr1", publishedContent.Value("prop")); - Assert.AreEqual("name-fr1", publishedContent.GetCulture().Name); - Assert.AreEqual("name-fr1", publishedContent.Name); + Assert.AreEqual("name-fr1", publishedContent.Name()); Assert.AreEqual(new DateTime(2018, 01, 01, 01, 00, 00), publishedContent.GetCulture().Date); // now uk is default _variationAccesor.VariationContext = new VariationContext("en-UK"); Assert.AreEqual("val-uk1", publishedContent.Value("prop")); - Assert.AreEqual("name-uk1", publishedContent.GetCulture().Name); - Assert.AreEqual("name-uk1", publishedContent.Name); + Assert.AreEqual("name-uk1", publishedContent.Name()); Assert.AreEqual(new DateTime(2018, 01, 02, 01, 00, 00), publishedContent.GetCulture().Date); // invariant needs to be retrieved explicitly, when it's not default @@ -251,7 +249,7 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(ContentVariation.Nothing, againContent.ContentType.GetPropertyType("prop").Variations); // now, "no culture" means "invariant" - Assert.AreEqual("It Works1!", againContent.Name); + Assert.IsNull(againContent.Name()); // no invariant name for varying content Assert.AreEqual("val1", againContent.Value("prop")); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 74b9619845..220909e237 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -140,7 +140,6 @@ namespace Umbraco.Tests.PublishedContent UpdateDate = DateTime.Now, Path = "-1,3", UrlSegment = "home-page", - Name = "Page" + Guid.NewGuid().ToString(), Version = Guid.NewGuid(), WriterId = 1, WriterName = "Shannon", @@ -148,6 +147,7 @@ namespace Umbraco.Tests.PublishedContent Level = 1, Children = new List() }; + d.SetName("Page" + Guid.NewGuid()); d.Properties = new Collection(new List { new RawValueProperty(factory.CreatePropertyType("property1", 1), d, "value" + indexVals), @@ -183,6 +183,8 @@ namespace Umbraco.Tests.PublishedContent // l8tr... private class TestPublishedContent : IPublishedContent { + private readonly Dictionary _names = new Dictionary(); + public string Url { get; set; } public string GetUrl(string culture = null) => throw new NotSupportedException(); @@ -203,7 +205,8 @@ namespace Umbraco.Tests.PublishedContent public Guid Key { get; set; } public int? TemplateId { get; set; } public int SortOrder { get; set; } - public string Name { get; set; } + public string Name(string culture = null) => _names.TryGetValue(culture ?? "", out var name) ? name : null; + public void SetName(string name, string culture = null) => _names[culture ?? ""] = name; public PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException(); public IReadOnlyDictionary Cultures => throw new NotSupportedException(); public string UrlSegment { get; set; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index 5c28b98de7..9cec962a38 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -122,7 +122,6 @@ namespace Umbraco.Tests.PublishedContent { Id = 1, SortOrder = 0, - Name = "Content 1", UrlSegment = "content-1", Path = "/1", Level = 1, @@ -134,12 +133,12 @@ namespace Umbraco.Tests.PublishedContent prop1, prop2, noprop } }; + item1.SetName("Content 1"); var item2 = new SolidPublishedContent(contentType1) { Id = 2, SortOrder = 0, - Name = "Content 2", UrlSegment = "content-2", Path = "/1/2", Level = 2, @@ -151,6 +150,7 @@ namespace Umbraco.Tests.PublishedContent prop3 } }; + item2.SetName("Content 2"); var prop4 = new SolidPublishedPropertyWithLanguageVariants { @@ -164,7 +164,6 @@ namespace Umbraco.Tests.PublishedContent { Id = 3, SortOrder = 0, - Name = "Content 3", UrlSegment = "content-3", Path = "/1/2/3", Level = 3, @@ -176,6 +175,7 @@ namespace Umbraco.Tests.PublishedContent prop4 } }; + item3.SetName("Content 3"); item1.Children = new List { item2 }; item2.Parent = item1; diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 48ad4aa464..ffe88f5d13 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -25,11 +25,10 @@ namespace Umbraco.Tests.PublishedContent var contentType2 = factory.CreateContentType(2, "ContentType2", Enumerable.Empty(), CreatePropertyTypes); var contentType2Sub = factory.CreateContentType(3, "ContentType2Sub", Enumerable.Empty(), CreatePropertyTypes); - cache.Add(new SolidPublishedContent(contentType1) + var content = new SolidPublishedContent(contentType1) { Id = 1, SortOrder = 0, - Name = "Content 1", UrlSegment = "content-1", Path = "/1", Level = 1, @@ -37,22 +36,23 @@ namespace Umbraco.Tests.PublishedContent ParentId = -1, ChildIds = new int[] { }, Properties = new Collection + { + new SolidPublishedProperty { - new SolidPublishedProperty - { - Alias = "prop1", - SolidHasValue = true, - SolidValue = 1234, - SolidSourceValue = "1234" - } + Alias = "prop1", + SolidHasValue = true, + SolidValue = 1234, + SolidSourceValue = "1234" } - }); + } + }; + content.SetName("Content 1"); + cache.Add(content); - cache.Add(new SolidPublishedContent(contentType2) + content = new SolidPublishedContent(contentType2) { Id = 2, SortOrder = 1, - Name = "Content 2", UrlSegment = "content-2", Path = "/2", Level = 1, @@ -60,22 +60,23 @@ namespace Umbraco.Tests.PublishedContent ParentId = -1, ChildIds = new int[] { }, Properties = new Collection + { + new SolidPublishedProperty { - new SolidPublishedProperty - { - Alias = "prop1", - SolidHasValue = true, - SolidValue = 1234, - SolidSourceValue = "1234" - } + Alias = "prop1", + SolidHasValue = true, + SolidValue = 1234, + SolidSourceValue = "1234" } - }); + } + }; + content.SetName("Content 2"); + cache.Add(content); - cache.Add(new SolidPublishedContent(contentType2Sub) + content = new SolidPublishedContent(contentType2Sub) { Id = 3, SortOrder = 2, - Name = "Content 2Sub", UrlSegment = "content-2sub", Path = "/3", Level = 1, @@ -92,14 +93,16 @@ namespace Umbraco.Tests.PublishedContent SolidSourceValue = "1234" } } - }); + }; + content.SetName("Content 2Sub"); + cache.Add(content); } [Test] public void First() { var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); - Assert.AreEqual("Content 1", content.Name); + Assert.AreEqual("Content 1", content.Name()); } [Test] @@ -111,17 +114,17 @@ namespace Umbraco.Tests.PublishedContent .ToIndexedArray(); var item = items[0]; - Assert.AreEqual("Content 1", item.Content.Name); + Assert.AreEqual("Content 1", item.Content.Name()); Assert.IsTrue(item.IsFirst()); Assert.IsFalse(item.IsLast()); item = items[1]; - Assert.AreEqual("Content 2", item.Content.Name); + Assert.AreEqual("Content 2", item.Content.Name()); Assert.IsFalse(item.IsFirst()); Assert.IsFalse(item.IsLast()); item = items[2]; - Assert.AreEqual("Content 2Sub", item.Content.Name); + Assert.AreEqual("Content 2Sub", item.Content.Name()); Assert.IsFalse(item.IsFirst()); Assert.IsTrue(item.IsLast()); } @@ -154,7 +157,7 @@ namespace Umbraco.Tests.PublishedContent var content = Current.UmbracoContext.ContentCache.GetAtRoot() .OfType() .First(x => x.Prop1 == 1234); - Assert.AreEqual("Content 2", content.Name); + Assert.AreEqual("Content 2", content.Name()); Assert.AreEqual(1234, content.Prop1); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index f801d02c5b..b789eb0ef8 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -495,7 +495,7 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(nodeId, converted.Id); Assert.AreEqual(3, converted.Level); Assert.AreEqual(1, converted.SortOrder); - Assert.AreEqual("Sam's Umbraco Image", converted.Name); + Assert.AreEqual("Sam's Umbraco Image", converted.Name()); Assert.AreEqual("-1,1111,2222,2112", converted.Path); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs index d8dbabb569..19944a2cd4 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs @@ -58,7 +58,7 @@ namespace Umbraco.Tests.PublishedContent { var pc = new Mock(); pc.Setup(content => content.Id).Returns(1); - pc.Setup(content => content.Name).Returns("test"); + pc.Setup(content => content.Name(It.IsAny())).Returns("test"); pc.Setup(content => content.WriterName).Returns("admin"); pc.Setup(content => content.CreatorName).Returns("admin"); pc.Setup(content => content.CreateDate).Returns(DateTime.Now); diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index f7d016f725..a25faea07a 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -153,8 +153,10 @@ namespace Umbraco.Tests.PublishedContent } } - class SolidPublishedContent : IPublishedContent + internal class SolidPublishedContent : IPublishedContent { + private readonly Dictionary _names = new Dictionary(); + #region Constructor public SolidPublishedContent(IPublishedContentType contentType) @@ -177,7 +179,8 @@ namespace Umbraco.Tests.PublishedContent public Guid Key { get; set; } public int? TemplateId { get; set; } public int SortOrder { get; set; } - public string Name { get; set; } + public string Name(string culture = null) => _names.TryGetValue(culture ?? "", out var name) ? name : null; + public void SetName(string name, string culture = null) => _names[culture ?? ""] = name; public PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException(); public IReadOnlyDictionary Cultures => throw new NotSupportedException(); public string UrlSegment { get; set; } diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 0f99b6b884..e07ae576e6 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -190,7 +190,7 @@ namespace Umbraco.Tests.Runtimes // but a draft document pcontent = umbracoContext.ContentCache.GetById(true, content.Id); Assert.IsNotNull(pcontent); - Assert.AreEqual("test", pcontent.Name); + Assert.AreEqual("test", pcontent.Name()); Assert.IsTrue(pcontent.IsDraft()); // no published url @@ -204,7 +204,7 @@ namespace Umbraco.Tests.Runtimes // assert that snapshot has been updated and there is now a published document pcontent = umbracoContext.ContentCache.GetById(content.Id); Assert.IsNotNull(pcontent); - Assert.AreEqual("test", pcontent.Name); + Assert.AreEqual("test", pcontent.Name()); Assert.IsFalse(pcontent.IsDraft()); // but the url is the published one - no draft url @@ -213,7 +213,7 @@ namespace Umbraco.Tests.Runtimes // and also an updated draft document pcontent = umbracoContext.ContentCache.GetById(true, content.Id); Assert.IsNotNull(pcontent); - Assert.AreEqual("testx", pcontent.Name); + Assert.AreEqual("testx", pcontent.Name()); Assert.IsTrue(pcontent.IsDraft()); // and the published document has a url diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 1dcc928141..f0efffc2cc 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -152,7 +152,7 @@ namespace Umbraco.Tests.Scoping // during events, due to LiveSnapshot, we see the changes Assert.IsNotNull(e); - Assert.AreEqual("changed", e.Name); + Assert.AreEqual("changed", e.Name()); }; using (var scope = ScopeProvider.CreateScope()) @@ -164,7 +164,7 @@ namespace Umbraco.Tests.Scoping // been created var x = umbracoContext.ContentCache.GetById(item.Id); Assert.IsNotNull(x); - Assert.AreEqual("name", x.Name); + Assert.AreEqual("name", x.Name()); ContentService.Published += OnPublishedAssert; @@ -186,7 +186,7 @@ namespace Umbraco.Tests.Scoping // else changes have been rolled back x = umbracoContext.ContentCache.GetById(item.Id); Assert.IsNotNull(x); - Assert.AreEqual(complete ? "changed" : "name", x.Name); + Assert.AreEqual(complete ? "changed" : "name", x.Name()); } } } diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 206660b904..81319a619c 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -7,6 +7,8 @@ namespace Umbraco.Tests.TestHelpers.Stubs { internal class TestPublishedContent : PublishedElement, IPublishedContent { + private readonly Dictionary _names = new Dictionary(); + public TestPublishedContent(IPublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultures = null) : base(contentType, key, values, previewing) { @@ -17,7 +19,8 @@ namespace Umbraco.Tests.TestHelpers.Stubs public int Id { get; } public int? TemplateId { get; set; } public int SortOrder { get; set; } - public string Name { get; set; } + public string Name(string culture = null) => _names.TryGetValue(culture ?? "", out var name) ? name : null; + public void SetName(string name, string culture = null) => _names[culture ?? ""] = name; public IVariationContextAccessor VariationContextAccessor { get; set; } public PublishedCultureInfo GetCulture(string culture = null) { diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs index ed737e7749..b252f51fae 100644 --- a/src/Umbraco.Web/Editors/TemplateQueryController.cs +++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs @@ -71,7 +71,7 @@ namespace Umbraco.Web.Editors QueryExpression = queryExpression.ToString(), ResultCount = results.Count, ExecutionTime = timer.ElapsedMilliseconds, - SampleResults = results.Take(20).Select(x => new TemplateQueryResult { Icon = "icon-file", Name = x.Name }) + SampleResults = results.Take(20).Select(x => new TemplateQueryResult { Icon = "icon-file", Name = x.Name() }) }; } @@ -186,12 +186,12 @@ namespace Umbraco.Web.Editors : contents.OrderByDescending(x => x.UpdateDate); case "name": return sortExpression.Direction == "ascending" - ? contents.OrderBy(x => x.Name) - : contents.OrderByDescending(x => x.Name); + ? contents.OrderBy(x => x.Name()) + : contents.OrderByDescending(x => x.Name()); default: return sortExpression.Direction == "ascending" - ? contents.OrderBy(x => x.Name) - : contents.OrderByDescending(x => x.Name); + ? contents.OrderBy(x => x.Name()) + : contents.OrderByDescending(x => x.Name()); } } diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index 978bbbcecb..f516aca080 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -35,7 +35,7 @@ namespace Umbraco.Web.Macros throw new ArgumentException("Document request has no node.", nameof(frequest)); PopulatePageData(frequest.PublishedContent.Id, - frequest.PublishedContent.Name, frequest.PublishedContent.ContentType.Id, frequest.PublishedContent.ContentType.Alias, + frequest.PublishedContent.Name(), frequest.PublishedContent.ContentType.Id, frequest.PublishedContent.ContentType.Alias, frequest.PublishedContent.WriterName, frequest.PublishedContent.CreatorName, frequest.PublishedContent.CreateDate, frequest.PublishedContent.UpdateDate, frequest.PublishedContent.Path, frequest.PublishedContent.Parent?.Id ?? -1); @@ -57,7 +57,7 @@ namespace Umbraco.Web.Macros if (doc == null) throw new ArgumentNullException(nameof(doc)); PopulatePageData(doc.Id, - doc.Name, doc.ContentType.Id, doc.ContentType.Alias, + doc.Name(), doc.ContentType.Id, doc.ContentType.Alias, doc.WriterName, doc.CreatorName, doc.CreateDate, doc.UpdateDate, doc.Path, doc.Parent?.Id ?? -1); @@ -228,7 +228,7 @@ namespace Umbraco.Web.Macros public int SortOrder => _inner.SortOrder; - public string Name => _inner.Name; + public string Name(string culture = null) => _inner.GetCultureName(culture); public PublishedCultureInfo GetCulture(string culture = null) { diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index d62b8c6665..04b5a64d07 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -41,7 +41,7 @@ namespace Umbraco.Web.Models public abstract int Id { get; } /// - public abstract string Name { get; } + public abstract string Name(string culture = null); /// public abstract string UrlSegment { get; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs index 51badc8b9a..bd6ad97d32 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.Navigable var i = 0; _builtInValues = new [] { - XmlString(i++, _content.Name), + XmlString(i++, _content.Name()), XmlString(i++, _content.ParentId), XmlString(i++, _content.CreateDate), XmlString(i++, _content.UpdateDate), diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 5712d55973..f208061c18 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -185,19 +185,18 @@ namespace Umbraco.Web.PublishedCache.NuCache public override int Id => _contentNode.Id; /// - public override string Name + public override string Name(string culture = null) { - get - { - if (!ContentType.VariesByCulture()) - return ContentData.Name; + // handle context culture + if (culture == null) + culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; - var culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; - if (culture == "") - return ContentData.Name; + // invariant culture + if (culture == "") + return ContentType.VariesByCulture() ? null : ContentData.Name; - return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos.Name : null; - } + // explicit culture + return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos.Name : null; } /// diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index d954411f5e..d87d75059e 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -136,7 +136,11 @@ namespace Umbraco.Web.PublishedCache public override int SortOrder => 0; - public override string Name => _member.Name; + public override string Name(string culture = null) + { + // member name does not vary, ignore culture + return _member.Name; + } public override PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException(); diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 54afb7abbd..29f32c1316 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -1077,7 +1077,7 @@ namespace Umbraco.Web var standardVals = new Dictionary { { "Id", n.Id }, - { "NodeName", n.Name }, + { "NodeName", n.Name() }, { "NodeTypeAlias", n.ContentType.Alias }, { "CreateDate", n.CreateDate }, { "UpdateDate", n.UpdateDate }, diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs index 2a840828b6..0a4f033bd2 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs @@ -187,7 +187,7 @@ namespace Umbraco.Web.Routing var l = new List(); while (o != null) { - l.Add(o.Name); + l.Add(o.Name()); o = o.Parent; } l.Reverse(); From f99302ac6322936bc11754771d958726aa4a5e0f Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 16 Apr 2019 17:10:25 +0200 Subject: [PATCH 05/75] Cleanup IPublishedContent/PropertyType --- .../PublishedContent/PublishedContentType.cs | 10 ++++--- .../PublishedContentTypeFactory.cs | 26 +++++++++---------- .../PublishedContent/PublishedPropertyType.cs | 2 -- .../Published/ConvertersTests.cs | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs index fedd7445b7..3b03cfc9ea 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs @@ -34,12 +34,11 @@ namespace Umbraco.Core.Models.PublishedContent InitializeIndexes(); } - // fixme should be internal? /// /// This constructor is for tests and is not intended to be used directly from application code. /// /// - /// Values are assumed to be consisted and are not checked. + /// Values are assumed to be consistent and are not checked. /// public PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, IEnumerable propertyTypes, ContentVariation variations, bool isElement = false) : this (id, alias, itemType, compositionAliases, variations, isElement) @@ -52,7 +51,12 @@ namespace Umbraco.Core.Models.PublishedContent InitializeIndexes(); } - // fixme + /// + /// This constructor is for tests and is not intended to be used directly from application code. + /// + /// + /// Values are assumed to be consistent and are not checked. + /// public PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, Func> propertyTypes, ContentVariation variations, bool isElement = false) : this(id, alias, itemType, compositionAliases, variations, isElement) { diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs index 78d05607b4..17a15a2536 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs @@ -31,23 +31,19 @@ namespace Umbraco.Core.Models.PublishedContent return new PublishedContentType(contentType, this); } - // fixme kill - // for tests - internal IPublishedContentType CreateContentType(int id, string alias, IEnumerable propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) - { - return new PublishedContentType(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, variations, isElement); - } + /// + /// This method is for tests and is not intended to be used directly from application code. + /// + /// Values are assumed to be consisted and are not checked. internal IPublishedContentType CreateContentType(int id, string alias, Func> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) { return new PublishedContentType(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, variations, isElement); } - // fixme kill - // for tests - internal IPublishedContentType CreateContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) - { - return new PublishedContentType(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, variations, isElement); - } + /// + /// This method is for tests and is not intended to be used directly from application code. + /// + /// Values are assumed to be consisted and are not checked. internal IPublishedContentType CreateContentType(int id, string alias, IEnumerable compositionAliases, Func> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) { return new PublishedContentType(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, variations, isElement); @@ -65,8 +61,10 @@ namespace Umbraco.Core.Models.PublishedContent return new PublishedPropertyType(contentType, propertyTypeAlias, dataTypeId, true, variations, _propertyValueConverters, _publishedModelFactory, this); } - // fixme kill - // for tests + /// + /// This method is for tests and is not intended to be used directly from application code. + /// + /// Values are assumed to be consisted and are not checked. internal IPublishedPropertyType CreatePropertyType(string propertyTypeAlias, int dataTypeId, bool umbraco = false, ContentVariation variations = ContentVariation.Nothing) { return new PublishedPropertyType(propertyTypeAlias, dataTypeId, umbraco, variations, _propertyValueConverters, _publishedModelFactory, this); diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs index 1632a85f44..0c2e62770e 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs @@ -36,7 +36,6 @@ namespace Umbraco.Core.Models.PublishedContent ContentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); } - // fixme should be internal? /// /// This constructor is for tests and is not intended to be used directly from application code. /// @@ -50,7 +49,6 @@ namespace Umbraco.Core.Models.PublishedContent ContentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); } - // fixme should be internal? /// /// This constructor is for tests and is not intended to be used directly from application code. /// diff --git a/src/Umbraco.Tests/Published/ConvertersTests.cs b/src/Umbraco.Tests/Published/ConvertersTests.cs index f753acf82d..7ad81922e2 100644 --- a/src/Umbraco.Tests/Published/ConvertersTests.cs +++ b/src/Umbraco.Tests/Published/ConvertersTests.cs @@ -126,7 +126,7 @@ namespace Umbraco.Tests.Published var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false); - var cntType1 = contentTypeFactory.CreateContentType(1001, "cnt1", Array.Empty()); + var cntType1 = contentTypeFactory.CreateContentType(1001, "cnt1", t => Enumerable.Empty()); var cnt1 = new TestPublishedContent(cntType1, 1234, Guid.NewGuid(), new Dictionary(), false); cacheContent[cnt1.Id] = cnt1; From 99319e0b493aa4e06a51f21321a251e8f8110445 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 22 Apr 2019 08:56:28 +0200 Subject: [PATCH 06/75] UmbracoContext: rename properties --- src/Umbraco.Web/UmbracoContext.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 8ab6f8c946..d6df5480cd 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -113,13 +113,30 @@ namespace Umbraco.Web /// /// Gets the published content cache. /// + [Obsolete("Use the Content property.")] public IPublishedContentCache ContentCache => PublishedSnapshot.Content; + /// + /// Gets the published content cache. + /// + public IPublishedContentCache Content => PublishedSnapshot.Content; + /// /// Gets the published media cache. /// + [Obsolete("Use the Media property.")] public IPublishedMediaCache MediaCache => PublishedSnapshot.Media; + /// + /// Gets the published media cache. + /// + public IPublishedMediaCache Media => PublishedSnapshot.Media; + + /// + /// Gets the domains cache. + /// + public IDomainCache Domains => PublishedSnapshot.Domains; + /// /// Boolean value indicating whether the current request is a front-end umbraco request /// @@ -191,6 +208,8 @@ namespace Umbraco.Web #region Urls + // fixme do something with these + /// /// Gets the url of a content identified by its identifier. /// From 2b54cc50ab4c80fa75f6b7f9dbd1ebb4f060970b Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 16 Apr 2019 18:32:33 +0200 Subject: [PATCH 07/75] Refactor IPublishedContent.UrlSegment() --- .../PublishedContent/IPublishedContent.cs | 10 +++----- .../PublishedContentWrapped.cs | 2 +- .../PublishedContent/PublishedCultureInfos.cs | 2 +- .../PublishedMediaCacheTests.cs | 2 +- .../DictionaryPublishedContent.cs | 2 +- .../PublishedContentCache.cs | 2 +- .../XmlPublishedContent.cs | 9 +++---- .../Published/NestedContentTests.cs | 2 +- .../PublishedContentDataTableTests.cs | 6 +++-- .../PublishedContentLanguageVariantTests.cs | 6 ++--- .../PublishedContentMoreTests.cs | 6 ++--- .../SolidPublishedSnapshot.cs | 4 +++- .../TestHelpers/Stubs/TestPublishedContent.cs | 4 +++- .../PublishedContentHashtableConverter.cs | 2 +- .../Models/PublishedContentBase.cs | 2 +- .../PublishedCache/NuCache/ContentCache.cs | 12 +++++----- .../NuCache/Navigable/NavigableContent.cs | 2 +- .../NuCache/PublishedContent.cs | 19 +++++++-------- .../PublishedCache/PublishedMember.cs | 2 +- src/Umbraco.Web/PublishedContentExtensions.cs | 24 ------------------- 20 files changed, 47 insertions(+), 73 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 95b943d751..304066196d 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -29,18 +29,14 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Gets the name of the content item. /// - /// The specific culture to filter for. If null is used the current culture is used. (Default is null) - /// The name of the content. + /// The specific culture to get the name for. If null is used the current culture is used (Default is null). string Name(string culture = null); /// /// Gets the url segment of the content item. /// - /// - /// The value of this property is contextual. When the content type is multi-lingual, - /// this is the name for the 'current' culture. Otherwise, it is the invariant url segment. - /// - string UrlSegment { get; } + /// The specific culture to get the url segment for. If null is used the current culture is used (Default is null). + string UrlSegment(string culture = null); /// /// Gets the sort order of the content item. diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 753f75c3cb..16732840ea 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -61,7 +61,7 @@ namespace Umbraco.Core.Models.PublishedContent public virtual string Name(string culture = null) => _content.Name(culture); /// - public virtual string UrlSegment => _content.UrlSegment; + public virtual string UrlSegment(string culture = null) => _content.UrlSegment(culture); /// public virtual int SortOrder => _content.SortOrder; diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs index 9c5977e8a6..eedc97dbfc 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs @@ -35,7 +35,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Gets the url segment of the item. /// - public string UrlSegment { get; } + internal string UrlSegment { get; } /// /// Gets the date associated with the culture. diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index 8dd5cf3890..2245f600dc 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -397,7 +397,7 @@ namespace Umbraco.Tests.Cache.PublishedCache Assert.AreEqual(keyVal, doc.Key); Assert.AreEqual(templateIdVal, doc.TemplateId); Assert.AreEqual(sortOrderVal, doc.SortOrder); - Assert.AreEqual(urlNameVal, doc.UrlSegment); + Assert.AreEqual(urlNameVal, doc.UrlSegment()); Assert.AreEqual(nodeTypeAliasVal, doc.ContentType.Alias); Assert.AreEqual(nodeTypeIdVal, doc.ContentType.Id); Assert.AreEqual(writerNameVal, doc.WriterName); diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs index c6c16a2466..9e615e6745 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs @@ -163,7 +163,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private static readonly Lazy> NoCultures = new Lazy>(() => new Dictionary()); public override IReadOnlyDictionary Cultures => NoCultures.Value; - public override string UrlSegment => _urlName; + public override string UrlSegment(string culture = null) => _urlName; public override string WriterName => _creatorName; diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs index d69799dfdf..1ad6e045c6 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs @@ -271,7 +271,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache while (hasDomains == false && n != null) // n is null at root { // get the url - var urlName = n.UrlSegment; + var urlName = n.UrlSegment(); pathParts.Add(urlName); // move to parent node diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index c547512ba8..8f30bdd789 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -212,13 +212,10 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache } } - public override string UrlSegment + public override string UrlSegment(string culture = null) { - get - { - EnsureNodeInitialized(); - return _urlName; - } + EnsureNodeInitialized(); + return _urlName; } public override int Level diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 35288c1f75..1cf291b263 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -287,7 +287,7 @@ namespace Umbraco.Tests.Published public override string Name(string culture = null) => default; public override PublishedCultureInfo GetCulture(string culture = ".") => throw new NotSupportedException(); public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public override string UrlSegment { get; } + public override string UrlSegment(string culture = null) => default; public override string WriterName { get; } public override string CreatorName { get; } public override int WriterId { get; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 220909e237..7d1ef25dcf 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -139,7 +139,6 @@ namespace Umbraco.Tests.PublishedContent TemplateId = 5, UpdateDate = DateTime.Now, Path = "-1,3", - UrlSegment = "home-page", Version = Guid.NewGuid(), WriterId = 1, WriterName = "Shannon", @@ -148,6 +147,7 @@ namespace Umbraco.Tests.PublishedContent Children = new List() }; d.SetName("Page" + Guid.NewGuid()); + d.SetUrlSegment("home-page"); d.Properties = new Collection(new List { new RawValueProperty(factory.CreatePropertyType("property1", 1), d, "value" + indexVals), @@ -184,6 +184,7 @@ namespace Umbraco.Tests.PublishedContent private class TestPublishedContent : IPublishedContent { private readonly Dictionary _names = new Dictionary(); + private readonly Dictionary _urlSegments = new Dictionary(); public string Url { get; set; } public string GetUrl(string culture = null) => throw new NotSupportedException(); @@ -209,7 +210,8 @@ namespace Umbraco.Tests.PublishedContent public void SetName(string name, string culture = null) => _names[culture ?? ""] = name; public PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException(); public IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public string UrlSegment { get; set; } + public string UrlSegment(string culture = null) => _urlSegments.TryGetValue(culture ?? "", out var urlSegment) ? urlSegment : null; + public void SetUrlSegment(string urlSegment, string culture = null) => _urlSegments[culture ?? ""] = urlSegment; public string WriterName { get; set; } public string CreatorName { get; set; } public int WriterId { get; set; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index 9cec962a38..bf84413fae 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -122,7 +122,6 @@ namespace Umbraco.Tests.PublishedContent { Id = 1, SortOrder = 0, - UrlSegment = "content-1", Path = "/1", Level = 1, Url = "/content-1", @@ -134,12 +133,12 @@ namespace Umbraco.Tests.PublishedContent } }; item1.SetName("Content 1"); + item1.SetUrlSegment("content-1"); var item2 = new SolidPublishedContent(contentType1) { Id = 2, SortOrder = 0, - UrlSegment = "content-2", Path = "/1/2", Level = 2, Url = "/content-1/content-2", @@ -151,6 +150,7 @@ namespace Umbraco.Tests.PublishedContent } }; item2.SetName("Content 2"); + item2.SetUrlSegment("content-2"); var prop4 = new SolidPublishedPropertyWithLanguageVariants { @@ -164,7 +164,6 @@ namespace Umbraco.Tests.PublishedContent { Id = 3, SortOrder = 0, - UrlSegment = "content-3", Path = "/1/2/3", Level = 3, Url = "/content-1/content-2/content-3", @@ -176,6 +175,7 @@ namespace Umbraco.Tests.PublishedContent } }; item3.SetName("Content 3"); + item3.SetUrlSegment("content-3"); item1.Children = new List { item2 }; item2.Parent = item1; diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index ffe88f5d13..9f9f3b8d41 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -29,7 +29,6 @@ namespace Umbraco.Tests.PublishedContent { Id = 1, SortOrder = 0, - UrlSegment = "content-1", Path = "/1", Level = 1, Url = "/content-1", @@ -47,13 +46,13 @@ namespace Umbraco.Tests.PublishedContent } }; content.SetName("Content 1"); + content.SetUrlSegment("content-1"); cache.Add(content); content = new SolidPublishedContent(contentType2) { Id = 2, SortOrder = 1, - UrlSegment = "content-2", Path = "/2", Level = 1, Url = "/content-2", @@ -71,13 +70,13 @@ namespace Umbraco.Tests.PublishedContent } }; content.SetName("Content 2"); + content.SetUrlSegment("content-2"); cache.Add(content); content = new SolidPublishedContent(contentType2Sub) { Id = 3, SortOrder = 2, - UrlSegment = "content-2sub", Path = "/3", Level = 1, Url = "/content-2sub", @@ -95,6 +94,7 @@ namespace Umbraco.Tests.PublishedContent } }; content.SetName("Content 2Sub"); + content.SetUrlSegment("content-2sub"); cache.Add(content); } diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index a25faea07a..e1c8cda3a9 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -156,6 +156,7 @@ namespace Umbraco.Tests.PublishedContent internal class SolidPublishedContent : IPublishedContent { private readonly Dictionary _names = new Dictionary(); + private readonly Dictionary _urlSegments = new Dictionary(); #region Constructor @@ -183,7 +184,8 @@ namespace Umbraco.Tests.PublishedContent public void SetName(string name, string culture = null) => _names[culture ?? ""] = name; public PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException(); public IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public string UrlSegment { get; set; } + public string UrlSegment(string culture = null) => _urlSegments.TryGetValue(culture ?? "", out var urlSegment) ? urlSegment : null; + public void SetUrlSegment(string urlSegment, string culture = null) => _urlSegments[culture ?? ""] = urlSegment; public string WriterName { get; set; } public string CreatorName { get; set; } public int WriterId { get; set; } diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 81319a619c..d621197bf8 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -8,6 +8,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs internal class TestPublishedContent : PublishedElement, IPublishedContent { private readonly Dictionary _names = new Dictionary(); + private readonly Dictionary _urlSegments = new Dictionary(); public TestPublishedContent(IPublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultures = null) : base(contentType, key, values, previewing) @@ -35,7 +36,8 @@ namespace Umbraco.Tests.TestHelpers.Stubs return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null; } public IReadOnlyDictionary Cultures { get; set; } - public string UrlSegment { get; set; } + public string UrlSegment(string culture = null) => _urlSegments.TryGetValue(culture ?? "", out var urlSegment) ? urlSegment : null; + public void SetUrlSegment(string urlSegment, string culture = null) => _urlSegments[culture ?? ""] = urlSegment; public string DocumentTypeAlias => ContentType.Alias; public int DocumentTypeId { get; set; } public string WriterName { get; set; } diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index f516aca080..977cd6fdc9 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -259,7 +259,7 @@ namespace Umbraco.Web.Macros } } - public string UrlSegment => throw new NotImplementedException(); + public string UrlSegment(string culture = null) => throw new NotImplementedException(); public string WriterName { get; } diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 04b5a64d07..f93a6d08c4 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -44,7 +44,7 @@ namespace Umbraco.Web.Models public abstract string Name(string culture = null); /// - public abstract string UrlSegment { get; } + public abstract string UrlSegment(string culture = null); /// public abstract int SortOrder { get; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index d070b959ed..08664f0a7a 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -109,8 +109,8 @@ namespace Umbraco.Web.PublishedCache.NuCache // hideTopLevelNode = support legacy stuff, look for /*/path/to/node // else normal, look for /path/to/node content = hideTopLevelNode.Value - ? GetAtRoot(preview).SelectMany(x => x.Children).FirstOrDefault(x => x.GetUrlSegment(culture) == parts[0]) - : GetAtRoot(preview).FirstOrDefault(x => x.GetUrlSegment(culture) == parts[0]); + ? GetAtRoot(preview).SelectMany(x => x.Children).FirstOrDefault(x => x.UrlSegment(culture) == parts[0]) + : GetAtRoot(preview).FirstOrDefault(x => x.UrlSegment(culture) == parts[0]); content = FollowRoute(content, parts, 1, culture); } @@ -119,7 +119,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // have to look for /foo (see note in ApplyHideTopLevelNodeFromPath). if (content == null && hideTopLevelNode.Value && parts.Length == 1) { - content = GetAtRoot(preview).FirstOrDefault(x => x.GetUrlSegment(culture) == parts[0]); + content = GetAtRoot(preview).FirstOrDefault(x => x.UrlSegment(culture) == parts[0]); } return content; @@ -149,7 +149,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // or we reach the content root, collecting urls in the way var pathParts = new List(); var n = node; - var urlSegment = n.GetUrlSegment(culture); + var urlSegment = n.UrlSegment(culture); var hasDomains = _domainHelper.NodeHasDomains(n.Id); while (hasDomains == false && n != null) // n is null at root { @@ -161,7 +161,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // move to parent node n = n.Parent; if (n != null) - urlSegment = n.GetUrlSegment(culture); + urlSegment = n.UrlSegment(culture); hasDomains = n != null && _domainHelper.NodeHasDomains(n.Id); } @@ -191,7 +191,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var part = parts[i++]; content = content.Children.FirstOrDefault(x => { - var urlSegment = x.GetUrlSegment(culture); + var urlSegment = x.UrlSegment(culture); return urlSegment == part; }); } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs index bd6ad97d32..c9dd493ee6 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs @@ -28,7 +28,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.Navigable XmlString(i++, _content.TemplateId), XmlString(i++, _content.WriterId), XmlString(i++, _content.CreatorId), - XmlString(i++, _content.UrlSegment), + XmlString(i++, _content.UrlSegment()), XmlString(i, _content.IsDraft()) }; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index f208061c18..e51220dfab 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -200,19 +200,18 @@ namespace Umbraco.Web.PublishedCache.NuCache } /// - public override string UrlSegment + public override string UrlSegment(string culture = null) { - get - { - if (!ContentType.VariesByCulture()) - return _urlSegment; + // handle context culture + if (culture == null) + culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; - var culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; - if (culture == "") - return _urlSegment; + // invariant culture + if (culture == "") + return ContentType.VariesByCulture() ? null : _urlSegment; - return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos.UrlSegment : null; - } + // explicit culture + return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos.UrlSegment : null; } /// diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index d87d75059e..733b4068e0 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -146,7 +146,7 @@ namespace Umbraco.Web.PublishedCache public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public override string UrlSegment => throw new NotSupportedException(); + public override string UrlSegment(string culture = null) => throw new NotSupportedException(); // TODO: ARGH! need to fix this - this is not good because it uses ApplicationContext.Current public override string WriterName => _member.GetCreatorProfile().Name; diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 29f32c1316..2495415834 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -60,30 +60,6 @@ namespace Umbraco.Web } } - /// - /// Gets the Url segment. - /// - /// - /// Gets the url segment for the document, taking its content type and a specified - /// culture in account. For invariant content types, the culture is ignored, else it is - /// used to try and find the segment corresponding to the culture. May return null. - /// - public static string GetUrlSegment(this IPublishedContent content, string culture = null) - { - // for invariant content, return the invariant url segment - if (!content.ContentType.VariesByCulture()) - return content.UrlSegment; - - // content.GetCulture(culture) will use the 'current' culture (via accessor) in case 'culture' - // is null (meaning, 'current') - and can return 'null' if that culture is not published - and - // will return 'null' if the content is variant and culture is invariant - - // else try and get the culture info - // return the corresponding url segment, or null if none - var cultureInfo = content.GetCulture(culture); - return cultureInfo?.UrlSegment; - } - public static bool IsAllowedTemplate(this IPublishedContent content, int templateId) { if (Current.Configs.Settings().WebRouting.DisableAlternativeTemplates) From 11ef00c63d9773fccf1d58897a74f7808c428760 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 16 Apr 2019 20:03:07 +0200 Subject: [PATCH 08/75] Refactor IPublishedContent.Url() --- .../PublishedContent/IPublishedContent.cs | 12 +-- .../PublishedContentWrapped.cs | 5 +- .../Models/PublishedContent/UrlMode.cs} | 10 +-- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../PublishedContentDataTableTests.cs | 3 +- .../PublishedContentLanguageVariantTests.cs | 6 +- .../PublishedContentMoreTests.cs | 6 +- .../SolidPublishedSnapshot.cs | 5 +- src/Umbraco.Tests/Routing/UrlProviderTests.cs | 4 +- .../Routing/UrlsProviderWithDomainsTests.cs | 3 +- src/Umbraco.Tests/Runtimes/StandaloneTests.cs | 6 +- .../TestControllerActivatorBase.cs | 2 +- .../TestHelpers/Stubs/TestPublishedContent.cs | 3 +- .../TestHelpers/TestObjects-Mocks.cs | 3 +- .../Testing/TestingTests/MockTests.cs | 2 +- .../Web/TemplateUtilitiesTests.cs | 4 +- .../Controllers/UmbLoginController.cs | 2 +- .../PublishedContentHashtableConverter.cs | 4 +- .../Models/PublishedContentBase.cs | 10 +-- .../MultiUrlPickerValueEditor.cs | 4 +- .../MultiUrlPickerValueConverter.cs | 2 +- src/Umbraco.Web/PublishedContentExtensions.cs | 82 +++++-------------- src/Umbraco.Web/Routing/AliasUrlProvider.cs | 2 +- .../Routing/ContentFinderByRedirectUrl.cs | 2 +- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 24 +++--- src/Umbraco.Web/Routing/IUrlProvider.cs | 2 +- src/Umbraco.Web/Routing/UrlProvider.cs | 16 ++-- .../Templates/TemplateUtilities.cs | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 - src/Umbraco.Web/UmbracoContext.cs | 8 +- src/Umbraco.Web/UrlHelperRenderExtensions.cs | 2 +- 31 files changed, 91 insertions(+), 147 deletions(-) rename src/{Umbraco.Web/Routing/UrlProviderMode.cs => Umbraco.Core/Models/PublishedContent/UrlMode.cs} (63%) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 304066196d..04a33fc0bc 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -93,16 +93,6 @@ namespace Umbraco.Core.Models.PublishedContent /// DateTime UpdateDate { get; } - /// - /// Gets the url of the content item. - /// - /// - /// The value of this property is contextual. It depends on the 'current' request uri, - /// if any. In addition, when the content type is multi-lingual, this is the url for the - /// 'current' culture. Otherwise, it is the invariant url. - /// - string Url { get; } - /// /// Gets the url of the content item. /// @@ -111,7 +101,7 @@ namespace Umbraco.Core.Models.PublishedContent /// if any. In addition, when the content type is multi-lingual, this is the url for the /// specified culture. Otherwise, it is the invariant url. /// - string GetUrl(string culture = null); + string Url(string culture = null, UrlMode mode = UrlMode.Auto); /// /// Gets culture infos for a culture. diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 16732840ea..ee7ed31d1d 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -94,10 +94,7 @@ namespace Umbraco.Core.Models.PublishedContent public virtual DateTime UpdateDate => _content.UpdateDate; /// - public virtual string Url => _content.Url; - - /// - public virtual string GetUrl(string culture = null) => _content.GetUrl(culture); + public virtual string Url(string culture = null, UrlMode mode = UrlMode.Auto) => _content.Url(culture, mode); /// public PublishedCultureInfo GetCulture(string culture = null) => _content.GetCulture(culture); diff --git a/src/Umbraco.Web/Routing/UrlProviderMode.cs b/src/Umbraco.Core/Models/PublishedContent/UrlMode.cs similarity index 63% rename from src/Umbraco.Web/Routing/UrlProviderMode.cs rename to src/Umbraco.Core/Models/PublishedContent/UrlMode.cs index ac29aed3fb..f19f93bec1 100644 --- a/src/Umbraco.Web/Routing/UrlProviderMode.cs +++ b/src/Umbraco.Core/Models/PublishedContent/UrlMode.cs @@ -1,13 +1,9 @@ -namespace Umbraco.Web.Routing +namespace Umbraco.Core.Models.PublishedContent { /// - /// Specifies the type of urls that the url provider should produce, Auto is the default + /// Specifies the type of urls that the url provider should produce, Auto is the default. /// - /// - /// The Relative option can lead to invalid results when combined with hostnames, but it is the only way to reproduce - /// the true, pre-4.10, always-relative behavior of Umbraco. - /// - public enum UrlProviderMode + public enum UrlMode { /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 20cc089d1d..d0936fd579 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -223,6 +223,7 @@ + diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 7d1ef25dcf..69eca7ef88 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -186,8 +186,7 @@ namespace Umbraco.Tests.PublishedContent private readonly Dictionary _names = new Dictionary(); private readonly Dictionary _urlSegments = new Dictionary(); - public string Url { get; set; } - public string GetUrl(string culture = null) => throw new NotSupportedException(); + public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => default; public PublishedItemType ItemType { get; set; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index bf84413fae..f978c8501b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -124,7 +124,6 @@ namespace Umbraco.Tests.PublishedContent SortOrder = 0, Path = "/1", Level = 1, - Url = "/content-1", ParentId = -1, ChildIds = new[] { 2 }, Properties = new Collection @@ -134,6 +133,7 @@ namespace Umbraco.Tests.PublishedContent }; item1.SetName("Content 1"); item1.SetUrlSegment("content-1"); + item1.SetUrl("/content-1"); var item2 = new SolidPublishedContent(contentType1) { @@ -141,7 +141,6 @@ namespace Umbraco.Tests.PublishedContent SortOrder = 0, Path = "/1/2", Level = 2, - Url = "/content-1/content-2", ParentId = 1, ChildIds = new int[] { 3 }, Properties = new Collection @@ -151,6 +150,7 @@ namespace Umbraco.Tests.PublishedContent }; item2.SetName("Content 2"); item2.SetUrlSegment("content-2"); + item2.SetUrl("/content-1/content-2"); var prop4 = new SolidPublishedPropertyWithLanguageVariants { @@ -166,7 +166,6 @@ namespace Umbraco.Tests.PublishedContent SortOrder = 0, Path = "/1/2/3", Level = 3, - Url = "/content-1/content-2/content-3", ParentId = 2, ChildIds = new int[] { }, Properties = new Collection @@ -176,6 +175,7 @@ namespace Umbraco.Tests.PublishedContent }; item3.SetName("Content 3"); item3.SetUrlSegment("content-3"); + item3.SetUrl("/content-1/content-2/content-3"); item1.Children = new List { item2 }; item2.Parent = item1; diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 9f9f3b8d41..378671b81b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -31,7 +31,6 @@ namespace Umbraco.Tests.PublishedContent SortOrder = 0, Path = "/1", Level = 1, - Url = "/content-1", ParentId = -1, ChildIds = new int[] { }, Properties = new Collection @@ -47,6 +46,7 @@ namespace Umbraco.Tests.PublishedContent }; content.SetName("Content 1"); content.SetUrlSegment("content-1"); + content.SetUrl("/content-1"); cache.Add(content); content = new SolidPublishedContent(contentType2) @@ -55,7 +55,6 @@ namespace Umbraco.Tests.PublishedContent SortOrder = 1, Path = "/2", Level = 1, - Url = "/content-2", ParentId = -1, ChildIds = new int[] { }, Properties = new Collection @@ -71,6 +70,7 @@ namespace Umbraco.Tests.PublishedContent }; content.SetName("Content 2"); content.SetUrlSegment("content-2"); + content.SetUrl("/content-2"); cache.Add(content); content = new SolidPublishedContent(contentType2Sub) @@ -79,7 +79,6 @@ namespace Umbraco.Tests.PublishedContent SortOrder = 2, Path = "/3", Level = 1, - Url = "/content-2sub", ParentId = -1, ChildIds = new int[] { }, Properties = new Collection @@ -95,6 +94,7 @@ namespace Umbraco.Tests.PublishedContent }; content.SetName("Content 2Sub"); content.SetUrlSegment("content-2sub"); + content.SetUrl("/content-2sub"); cache.Add(content); } diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index e1c8cda3a9..91fd1efb92 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -157,6 +157,7 @@ namespace Umbraco.Tests.PublishedContent { private readonly Dictionary _names = new Dictionary(); private readonly Dictionary _urlSegments = new Dictionary(); + private readonly Dictionary _urls = new Dictionary(); #region Constructor @@ -195,8 +196,8 @@ namespace Umbraco.Tests.PublishedContent public DateTime UpdateDate { get; set; } public Guid Version { get; set; } public int Level { get; set; } - public string Url { get; set; } - public string GetUrl(string culture = null) => throw new NotSupportedException(); + public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => _urls.TryGetValue(culture ?? "", out var url) ? url : null; + public void SetUrl(string url, string culture = null) => _urls[culture ?? ""] = url; public PublishedItemType ItemType { get { return PublishedItemType.Content; } } public bool IsDraft(string culture = null) => false; diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 236c198b3a..38ef983120 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -314,7 +314,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual("/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); - umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; + umbracoContext.UrlProvider.Mode = UrlMode.Absolute; Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); } @@ -335,7 +335,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); - umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; + umbracoContext.UrlProvider.Mode = UrlMode.Absolute; Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); } diff --git a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs index c01ee83d6a..5a20d4da31 100644 --- a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs @@ -7,6 +7,7 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Tests.LegacyXmlPublishedCache; using Umbraco.Tests.TestHelpers; @@ -359,7 +360,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); - umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; + umbracoContext.UrlProvider.Mode = UrlMode.Absolute; Assert.AreEqual("http://domain1.com/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index e07ae576e6..9f57109329 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -194,7 +194,7 @@ namespace Umbraco.Tests.Runtimes Assert.IsTrue(pcontent.IsDraft()); // no published url - Assert.AreEqual("#", pcontent.GetUrl()); + Assert.AreEqual("#", pcontent.Url()); // now publish the document + make some unpublished changes contentService.SaveAndPublish(content); @@ -208,7 +208,7 @@ namespace Umbraco.Tests.Runtimes Assert.IsFalse(pcontent.IsDraft()); // but the url is the published one - no draft url - Assert.AreEqual("/test/", pcontent.GetUrl()); + Assert.AreEqual("/test/", pcontent.Url()); // and also an updated draft document pcontent = umbracoContext.ContentCache.GetById(true, content.Id); @@ -217,7 +217,7 @@ namespace Umbraco.Tests.Runtimes Assert.IsTrue(pcontent.IsDraft()); // and the published document has a url - Assert.AreEqual("/test/", pcontent.GetUrl()); + Assert.AreEqual("/test/", pcontent.Url()); umbracoContextReference.Dispose(); mainDom.Stop(); diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index c6bbebf550..cf2b3b843f 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -146,7 +146,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting umbracoContextAccessor.UmbracoContext = umbCtx; var urlHelper = new Mock(); - urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/hello/world/1234")); var membershipHelper = new MembershipHelper(umbCtx.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of()); diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index d621197bf8..4262851fc9 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -49,8 +49,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs public DateTime UpdateDate { get; set; } public Guid Version { get; set; } public int Level { get; set; } - public string Url { get; set; } - public string GetUrl(string culture = null) => throw new NotSupportedException(); + public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => throw new NotSupportedException(); public PublishedItemType ItemType => ContentType.ItemType; public bool IsDraft(string culture = null) => false; public bool IsPublished(string culture = null) => true; diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 75e9cd60cb..ee938cd027 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Services; @@ -145,7 +146,7 @@ namespace Umbraco.Tests.TestHelpers var umbracoSettingsMock = new Mock(); var webRoutingSectionMock = new Mock(); - webRoutingSectionMock.Setup(x => x.UrlProviderMode).Returns(UrlProviderMode.Auto.ToString()); + webRoutingSectionMock.Setup(x => x.UrlProviderMode).Returns(UrlMode.Auto.ToString()); umbracoSettingsMock.Setup(x => x.WebRouting).Returns(webRoutingSectionMock.Object); return umbracoSettingsMock.Object; } diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 57381eb287..f0e31226e2 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -76,7 +76,7 @@ namespace Umbraco.Tests.Testing.TestingTests var umbracoContext = TestObjects.GetUmbracoContextMock(); var urlProviderMock = new Mock(); - urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/hello/world/1234")); var urlProvider = urlProviderMock.Object; diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index 5e9208faf9..c868914347 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -80,8 +80,8 @@ namespace Umbraco.Tests.Web //setup a mock url provider which we'll use fo rtesting var testUrlProvider = new Mock(); testUrlProvider - .Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((UmbracoContext umbCtx, IPublishedContent content, UrlProviderMode mode, string culture, Uri url) => UrlInfo.Url("/my-test-url")); + .Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((UmbracoContext umbCtx, IPublishedContent content, UrlMode mode, string culture, Uri url) => UrlInfo.Url("/my-test-url")); var globalSettings = SettingsForTests.GenerateMockGlobalSettings(); diff --git a/src/Umbraco.Web/Controllers/UmbLoginController.cs b/src/Umbraco.Web/Controllers/UmbLoginController.cs index 2980b4a4c0..02130da8b5 100644 --- a/src/Umbraco.Web/Controllers/UmbLoginController.cs +++ b/src/Umbraco.Web/Controllers/UmbLoginController.cs @@ -45,7 +45,7 @@ namespace Umbraco.Web.Controllers // if it's not a local url we'll redirect to the root of the current site return Redirect(Url.IsLocalUrl(model.RedirectUrl) ? model.RedirectUrl - : CurrentPage.AncestorOrSelf(1).Url); + : CurrentPage.AncestorOrSelf(1).Url()); } //redirect to current page by default diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index 977cd6fdc9..1fafefdcc5 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -277,9 +277,7 @@ namespace Umbraco.Web.Macros public int Level => _inner.Level; - public string Url => throw new NotImplementedException(); - - public string GetUrl(string culture = null) => throw new NotSupportedException(); + public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => throw new NotSupportedException(); public PublishedItemType ItemType => PublishedItemType.Content; diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index f93a6d08c4..e46bb8fddd 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -76,15 +76,12 @@ namespace Umbraco.Web.Models /// public abstract DateTime UpdateDate { get; } - /// - public virtual string Url => GetUrl(); - /// /// /// The url of documents are computed by the document url providers. The url of medias are, at the moment, /// computed here from the 'umbracoFile' property -- but we should move to media url providers at some point. /// - public virtual string GetUrl(string culture = null) // TODO: consider .GetCulture("fr-FR").Url + public virtual string Url(string culture = null, UrlMode mode = UrlMode.Auto) { switch (ItemType) { @@ -96,9 +93,12 @@ namespace Umbraco.Web.Models if (umbracoContext.UrlProvider == null) throw new InvalidOperationException("Cannot compute Url for a content item when UmbracoContext.UrlProvider is null."); - return umbracoContext.UrlProvider.GetUrl(this, culture); + return umbracoContext.UrlProvider.GetUrl(this, mode, culture); case PublishedItemType.Media: + if (mode == UrlMode.Absolute) + throw new NotSupportedException("Absolute urls are not supported for media items."); + var prop = GetProperty(Constants.Conventions.Media.File); if (prop?.GetValue() == null) { diff --git a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerValueEditor.cs b/src/Umbraco.Web/PropertyEditors/MultiUrlPickerValueEditor.cs index 1cb590d43c..ac0fb375c7 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultiUrlPickerValueEditor.cs @@ -82,7 +82,7 @@ namespace Umbraco.Web.PropertyEditors icon = documentEntity.ContentTypeIcon; published = culture == null ? documentEntity.Published : documentEntity.PublishedCultures.Contains(culture); udi = new GuidUdi(Constants.UdiEntityType.Document, documentEntity.Key); - url = _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(entity.Key)?.Url ?? "#"; + url = _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(entity.Key)?.Url() ?? "#"; trashed = documentEntity.Trashed; } else if(entity is IContentEntitySlim contentEntity) @@ -91,7 +91,7 @@ namespace Umbraco.Web.PropertyEditors isMedia = true; published = !contentEntity.Trashed; udi = new GuidUdi(Constants.UdiEntityType.Media, contentEntity.Key); - url = _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(entity.Key)?.Url ?? "#"; + url = _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(entity.Key)?.Url() ?? "#"; trashed = contentEntity.Trashed; } else diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs index 87ab5b8ff9..8e9ee9d6f8 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs @@ -69,7 +69,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { continue; } - url = content.Url; + url = content.Url(); } links.Add( diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 2495415834..7776a7c8ac 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -24,65 +24,6 @@ namespace Umbraco.Web private static IPublishedValueFallback PublishedValueFallback => Current.PublishedValueFallback; private static IPublishedSnapshot PublishedSnapshot => Current.PublishedSnapshot; - #region Urls - - /// - /// Gets the url for the content. - /// - /// The content. - /// The url for the content. - /// Better use the Url property but that method is here to complement UrlAbsolute(). - public static string Url(this IPublishedContent content) - { - return content.Url; - } - - /// - /// Gets the absolute url for the content. - /// - /// The content. - /// The absolute url for the content. - public static string UrlAbsolute(this IPublishedContent content) - { - // adapted from PublishedContentBase.Url - switch (content.ItemType) - { - case PublishedItemType.Content: - if (Current.UmbracoContext == null) - throw new InvalidOperationException("Cannot resolve a Url for a content item when Current.UmbracoContext is null."); - if (Current.UmbracoContext.UrlProvider == null) - throw new InvalidOperationException("Cannot resolve a Url for a content item when Current.UmbracoContext.UrlProvider is null."); - return Current.UmbracoContext.UrlProvider.GetUrl(content.Id, true); - case PublishedItemType.Media: - throw new NotSupportedException("AbsoluteUrl is not supported for media types."); - default: - throw new ArgumentOutOfRangeException(); - } - } - - public static bool IsAllowedTemplate(this IPublishedContent content, int templateId) - { - if (Current.Configs.Settings().WebRouting.DisableAlternativeTemplates) - return content.TemplateId == templateId; - - if (content.TemplateId == templateId || !Current.Configs.Settings().WebRouting.ValidateAlternativeTemplates) - return true; - - var publishedContentContentType = Current.Services.ContentTypeService.Get(content.ContentType.Id); - if (publishedContentContentType == null) - throw new NullReferenceException("No content type returned for published content (contentType='" + content.ContentType.Id + "')"); - - return publishedContentContentType.IsAllowedTemplate(templateId); - - } - public static bool IsAllowedTemplate(this IPublishedContent content, string templateAlias) - { - var template = Current.Services.FileService.GetTemplate(templateAlias); - return template != null && content.IsAllowedTemplate(template.Id); - } - - #endregion - #region IsComposedOf /// @@ -116,6 +57,27 @@ namespace Umbraco.Web return template == null ? string.Empty : template.Alias; } + public static bool IsAllowedTemplate(this IPublishedContent content, int templateId) + { + if (Current.Configs.Settings().WebRouting.DisableAlternativeTemplates) + return content.TemplateId == templateId; + + if (content.TemplateId == templateId || !Current.Configs.Settings().WebRouting.ValidateAlternativeTemplates) + return true; + + var publishedContentContentType = Current.Services.ContentTypeService.Get(content.ContentType.Id); + if (publishedContentContentType == null) + throw new NullReferenceException("No content type returned for published content (contentType='" + content.ContentType.Id + "')"); + + return publishedContentContentType.IsAllowedTemplate(templateId); + + } + public static bool IsAllowedTemplate(this IPublishedContent content, string templateAlias) + { + var template = Current.Services.FileService.GetTemplate(templateAlias); + return template != null && content.IsAllowedTemplate(template.Id); + } + #endregion #region HasValue, Value, Value @@ -1059,7 +1021,7 @@ namespace Umbraco.Web { "UpdateDate", n.UpdateDate }, { "CreatorName", n.CreatorName }, { "WriterName", n.WriterName }, - { "Url", n.Url } + { "Url", n.Url() } }; var userVals = new Dictionary(); diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index 4c879e931f..bbe3a9db8c 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -31,7 +31,7 @@ namespace Umbraco.Web.Routing #region GetUrl /// - public UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current) + public UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlMode mode, string culture, Uri current) { return null; // we have nothing to say } diff --git a/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs b/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs index f20aa95c0d..eae2b57378 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs @@ -45,7 +45,7 @@ namespace Umbraco.Web.Routing } var content = frequest.UmbracoContext.ContentCache.GetById(redirectUrl.ContentId); - var url = content == null ? "#" : content.GetUrl(redirectUrl.Culture); + var url = content == null ? "#" : content.Url(redirectUrl.Culture); if (url.StartsWith("#")) { _logger.Debug("Route {Route} matches content {ContentId} which has no url.", route, redirectUrl.ContentId); diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 9e13d3b9c1..98ba743fe5 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -29,7 +29,7 @@ namespace Umbraco.Web.Routing #region GetUrl /// - public virtual UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current) + public virtual UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlMode mode, string culture, Uri current) { if (!current.IsAbsoluteUri) throw new ArgumentException("Current url must be absolute.", nameof(current)); @@ -39,7 +39,7 @@ namespace Umbraco.Web.Routing return GetUrlFromRoute(route, umbracoContext, content.Id, current, mode, culture); } - internal UrlInfo GetUrlFromRoute(string route, UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture) + internal UrlInfo GetUrlFromRoute(string route, UmbracoContext umbracoContext, int id, Uri current, UrlMode mode, string culture) { if (string.IsNullOrWhiteSpace(route)) { @@ -121,7 +121,7 @@ namespace Umbraco.Web.Routing #region Utilities - Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, UrlProviderMode mode) + Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, UrlMode mode) { Uri uri; @@ -130,15 +130,15 @@ namespace Umbraco.Web.Routing if (domainUri == null) // no domain was found { if (current == null) - mode = UrlProviderMode.Relative; // best we can do + mode = UrlMode.Relative; // best we can do switch (mode) { - case UrlProviderMode.Absolute: + case UrlMode.Absolute: uri = new Uri(current.GetLeftPart(UriPartial.Authority) + path); break; - case UrlProviderMode.Relative: - case UrlProviderMode.Auto: + case UrlMode.Relative: + case UrlMode.Auto: uri = new Uri(path, UriKind.Relative); break; default: @@ -147,21 +147,21 @@ namespace Umbraco.Web.Routing } else // a domain was found { - if (mode == UrlProviderMode.Auto) + if (mode == UrlMode.Auto) { //this check is a little tricky, we can't just compare domains if (current != null && domainUri.Uri.GetLeftPart(UriPartial.Authority) == current.GetLeftPart(UriPartial.Authority)) - mode = UrlProviderMode.Relative; + mode = UrlMode.Relative; else - mode = UrlProviderMode.Absolute; + mode = UrlMode.Absolute; } switch (mode) { - case UrlProviderMode.Absolute: + case UrlMode.Absolute: uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path)); break; - case UrlProviderMode.Relative: + case UrlMode.Relative: uri = new Uri(CombinePaths(domainUri.Uri.AbsolutePath, path), UriKind.Relative); break; default: diff --git a/src/Umbraco.Web/Routing/IUrlProvider.cs b/src/Umbraco.Web/Routing/IUrlProvider.cs index 55d39880d6..854363f110 100644 --- a/src/Umbraco.Web/Routing/IUrlProvider.cs +++ b/src/Umbraco.Web/Routing/IUrlProvider.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.Routing /// when no culture is specified, the current culture. /// If the provider is unable to provide a url, it should return null. /// - UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current); + UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlMode mode, string culture, Uri current); /// /// Gets the other urls of a published content. diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index 0662d46f49..3e07c8020a 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -31,10 +31,10 @@ namespace Umbraco.Web.Routing _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); _urlProviders = urlProviders; _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); - var provider = UrlProviderMode.Auto; + var provider = UrlMode.Auto; Mode = provider; - if (Enum.TryParse(routingSettings.UrlProviderMode, out provider)) + if (Enum.TryParse(routingSettings.UrlProviderMode, out provider)) { Mode = provider; } @@ -47,7 +47,7 @@ namespace Umbraco.Web.Routing /// The list of url providers. /// The current variation accessor. /// An optional provider mode. - public UrlProvider(UmbracoContext umbracoContext, IEnumerable urlProviders, IVariationContextAccessor variationContextAccessor, UrlProviderMode mode = UrlProviderMode.Auto) + public UrlProvider(UmbracoContext umbracoContext, IEnumerable urlProviders, IVariationContextAccessor variationContextAccessor, UrlMode mode = UrlMode.Auto) { _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); _urlProviders = urlProviders; @@ -63,13 +63,13 @@ namespace Umbraco.Web.Routing /// /// Gets or sets the provider url mode. /// - public UrlProviderMode Mode { get; set; } + public UrlMode Mode { get; set; } #endregion #region GetUrl - private UrlProviderMode GetMode(bool absolute) => absolute ? UrlProviderMode.Absolute : Mode; + private UrlMode GetMode(bool absolute) => absolute ? UrlMode.Absolute : Mode; private IPublishedContent GetDocument(int id) => _umbracoContext.ContentCache.GetById(id); private IPublishedContent GetDocument(Guid id) => _umbracoContext.ContentCache.GetById(id); @@ -131,7 +131,7 @@ namespace Umbraco.Web.Routing /// A culture. /// The current absolute url. /// The url for the published content. - public string GetUrl(Guid id, UrlProviderMode mode, string culture = null, Uri current = null) + public string GetUrl(Guid id, UrlMode mode, string culture = null, Uri current = null) => GetUrl(GetDocument(id), mode, culture, current); /// @@ -167,7 +167,7 @@ namespace Umbraco.Web.Routing /// A culture. /// The current absolute url. /// The url for the published content. - public string GetUrl(int id, UrlProviderMode mode, string culture = null, Uri current = null) + public string GetUrl(int id, UrlMode mode, string culture = null, Uri current = null) => GetUrl(GetDocument(id), mode, culture, current); /// @@ -184,7 +184,7 @@ namespace Umbraco.Web.Routing /// when no culture is specified, the current culture. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(IPublishedContent content, UrlProviderMode mode, string culture = null, Uri current = null) + public string GetUrl(IPublishedContent content, UrlMode mode, string culture = null, Uri current = null) { if (content == null || content.ItemType == PublishedItemType.Element) return "#"; diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index 6c979e9a95..0be392c6dd 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -158,7 +158,7 @@ namespace Umbraco.Web.Templates return match.Value; } - var url = media.Url; + var url = media.Url(); return $"{match.Groups[1].Value}{url}{match.Groups[3].Value}{udi}{match.Groups[5].Value}"; }); } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index b3cab3b2cb..42a1a766e5 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1017,7 +1017,6 @@ - diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index d6df5480cd..a39535ba6e 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -208,8 +208,6 @@ namespace Umbraco.Web #region Urls - // fixme do something with these - /// /// Gets the url of a content identified by its identifier. /// @@ -239,7 +237,7 @@ namespace Umbraco.Web /// The mode. /// /// The url for the content. - public string Url(int contentId, UrlProviderMode mode, string culture = null) + public string Url(int contentId, UrlMode mode, string culture = null) { return UrlProvider.GetUrl(contentId, mode, culture); } @@ -251,7 +249,7 @@ namespace Umbraco.Web /// The mode. /// /// The url for the content. - public string Url(Guid contentId, UrlProviderMode mode, string culture = null) + public string Url(Guid contentId, UrlMode mode, string culture = null) { return UrlProvider.GetUrl(contentId, mode, culture); } @@ -262,6 +260,7 @@ namespace Umbraco.Web /// The content identifier. /// /// The absolute url for the content. + [Obsolete("Use the Url() method with UrlMode.Absolute.")] public string UrlAbsolute(int contentId, string culture = null) { return UrlProvider.GetUrl(contentId, true, culture); @@ -273,6 +272,7 @@ namespace Umbraco.Web /// The content identifier. /// /// The absolute url for the content. + [Obsolete("Use the Url() method with UrlMode.Absolute.")] public string UrlAbsolute(Guid contentId, string culture = null) { return UrlProvider.GetUrl(contentId, true, culture); diff --git a/src/Umbraco.Web/UrlHelperRenderExtensions.cs b/src/Umbraco.Web/UrlHelperRenderExtensions.cs index 6f7fbacf7a..8fe0524209 100644 --- a/src/Umbraco.Web/UrlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/UrlHelperRenderExtensions.cs @@ -385,7 +385,7 @@ namespace Umbraco.Web if (urlHelper.RequestContext.HttpContext.Request.Url != null) { var requestUrl = urlHelper.RequestContext.HttpContext.Request.Url.GetLeftPart(UriPartial.Authority); - return string.Format("{0}{1}", requestUrl, mediaItem.Url); + return string.Format("{0}{1}", requestUrl, mediaItem.Url()); } return null; } From afda7a566145eb45741fe33c4cee97ac126dd7d8 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 16 Apr 2019 21:15:13 +0200 Subject: [PATCH 09/75] Refactor IPublishedContent.ItemType --- .../Models/PublishedContent/IPublishedContent.cs | 5 ----- .../Models/PublishedContent/PublishedContentWrapped.cs | 3 --- .../LegacyXmlPublishedCache/DictionaryPublishedContent.cs | 5 ----- .../LegacyXmlPublishedCache/XmlPublishedContent.cs | 2 -- src/Umbraco.Tests/Published/NestedContentTests.cs | 1 - .../PublishedContent/PublishedContentDataTableTests.cs | 2 -- src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs | 1 - src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs | 1 - src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs | 2 -- src/Umbraco.Web/Models/PublishedContentBase.cs | 5 +---- .../ValueConverters/ContentPickerValueConverter.cs | 2 +- .../ValueConverters/MultiNodeTreePickerValueConverter.cs | 2 +- .../ValueConverters/MultiUrlPickerValueConverter.cs | 2 +- src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs | 5 +---- src/Umbraco.Web/PublishedCache/PublishedMember.cs | 2 -- src/Umbraco.Web/Routing/UrlProvider.cs | 2 +- 16 files changed, 6 insertions(+), 36 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 04a33fc0bc..978958413a 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -118,11 +118,6 @@ namespace Umbraco.Core.Models.PublishedContent /// IReadOnlyDictionary Cultures { get; } - /// - /// Gets the type of the content item (document, media...). - /// - PublishedItemType ItemType { get; } - /// /// Gets a value indicating whether the content is draft. /// diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index ee7ed31d1d..cd92888f7e 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -102,9 +102,6 @@ namespace Umbraco.Core.Models.PublishedContent /// public IReadOnlyDictionary Cultures => _content.Cultures; - /// - public virtual PublishedItemType ItemType => _content.ItemType; - /// public virtual bool IsDraft(string culture = null) => _content.IsDraft(culture); diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs index 9e615e6745..40d7146248 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs @@ -139,11 +139,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private readonly Func _getProperty; private readonly IAppCache _appCache; - /// - /// Returns 'Media' as the item type - /// - public override PublishedItemType ItemType => PublishedItemType.Media; - public override IPublishedContent Parent => _getParent.Value; public int ParentId { get; private set; } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index 8f30bdd789..b738b22e2c 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -91,8 +91,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache return _properties.TryGetValue(alias, out property) ? property : null; } - public override PublishedItemType ItemType => PublishedItemType.Content; - public override IPublishedContent Parent { get diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 1cf291b263..e91f456922 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -272,7 +272,6 @@ namespace Umbraco.Tests.Published } // ReSharper disable UnassignedGetOnlyAutoProperty - public override PublishedItemType ItemType { get; } public override bool IsDraft(string culture = null) => false; public override bool IsPublished(string culture = null) => true; public override IPublishedContent Parent { get; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 69eca7ef88..41b5588234 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -188,8 +188,6 @@ namespace Umbraco.Tests.PublishedContent public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => default; - public PublishedItemType ItemType { get; set; } - IPublishedContent IPublishedContent.Parent { get { return Parent; } diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 91fd1efb92..77f25c0b0b 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -199,7 +199,6 @@ namespace Umbraco.Tests.PublishedContent public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => _urls.TryGetValue(culture ?? "", out var url) ? url : null; public void SetUrl(string url, string culture = null) => _urls[culture ?? ""] = url; - public PublishedItemType ItemType { get { return PublishedItemType.Content; } } public bool IsDraft(string culture = null) => false; public bool IsPublished(string culture = null) => true; diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 4262851fc9..8a092c3453 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -50,7 +50,6 @@ namespace Umbraco.Tests.TestHelpers.Stubs public Guid Version { get; set; } public int Level { get; set; } public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => throw new NotSupportedException(); - public PublishedItemType ItemType => ContentType.ItemType; public bool IsDraft(string culture = null) => false; public bool IsPublished(string culture = null) => true; public IPublishedContent Parent { get; set; } diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index 1fafefdcc5..108f58e929 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -279,8 +279,6 @@ namespace Umbraco.Web.Macros public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => throw new NotSupportedException(); - public PublishedItemType ItemType => PublishedItemType.Content; - public bool IsDraft(string culture = null) { throw new NotImplementedException(); diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index e46bb8fddd..7017e84882 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -83,7 +83,7 @@ namespace Umbraco.Web.Models /// public virtual string Url(string culture = null, UrlMode mode = UrlMode.Auto) { - switch (ItemType) + switch (ContentType.ItemType) { case PublishedItemType.Content: var umbracoContext = UmbracoContextAccessor.UmbracoContext; @@ -140,9 +140,6 @@ namespace Umbraco.Web.Models /// public abstract IReadOnlyDictionary Cultures { get; } - /// - public abstract PublishedItemType ItemType { get; } - /// public abstract bool IsDraft(string culture = null); diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs index 056334cfa5..8b3655f0cc 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs @@ -65,7 +65,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters if (udi == null) return null; content = _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(udi.Guid); - if (content != null && content.ItemType == PublishedItemType.Content) + if (content != null && content.ContentType.ItemType == PublishedItemType.Content) return content; } } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs index f80f8f510f..0ee6ff6f6c 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs @@ -99,7 +99,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters break; } - if (multiNodeTreePickerItem != null && multiNodeTreePickerItem.ItemType != PublishedItemType.Element) + if (multiNodeTreePickerItem != null && multiNodeTreePickerItem.ContentType.ItemType != PublishedItemType.Element) { multiNodeTreePicker.Add(multiNodeTreePickerItem); } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs index 8e9ee9d6f8..2ceac6cfa8 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs @@ -65,7 +65,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(preview, dto.Udi.Guid) : _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(preview, dto.Udi.Guid); - if (content == null || content.ItemType == PublishedItemType.Element) + if (content == null || content.ContentType.ItemType == PublishedItemType.Element) { continue; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index e51220dfab..7f67aff870 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -280,9 +280,6 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - /// - public override PublishedItemType ItemType => _contentNode.ContentType.ItemType; - /// public override bool IsDraft(string culture = null) { @@ -421,7 +418,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var publishedSnapshot = (PublishedSnapshot)_publishedSnapshotAccessor.PublishedSnapshot; var cache = publishedSnapshot == null ? null - : ((IsPreviewing == false || PublishedSnapshotService.FullCacheWhenPreviewing) && (ItemType != PublishedItemType.Member) + : ((IsPreviewing == false || PublishedSnapshotService.FullCacheWhenPreviewing) && (ContentType.ItemType != PublishedItemType.Member) ? publishedSnapshot.ElementsCache : publishedSnapshot.SnapshotCache); return cache; diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index 733b4068e0..68b742caa3 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -78,8 +78,6 @@ namespace Umbraco.Web.PublishedCache #region IPublishedContent - public override PublishedItemType ItemType => PublishedItemType.Member; - public override bool IsDraft(string culture = null) => false; public override bool IsPublished(string culture = null) => true; diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index 3e07c8020a..f40aa00b01 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -186,7 +186,7 @@ namespace Umbraco.Web.Routing /// public string GetUrl(IPublishedContent content, UrlMode mode, string culture = null, Uri current = null) { - if (content == null || content.ItemType == PublishedItemType.Element) + if (content == null || content.ContentType.ItemType == PublishedItemType.Element) return "#"; // this the ONLY place where we deal with default culture - IUrlProvider always receive a culture From 7cf13cbf94b3888e7a19080ed1ab9644e9d87f92 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 17 Apr 2019 11:29:00 +0200 Subject: [PATCH 10/75] Fix IPublishedContent.Name(), .Segment() for invariant --- .../PublishedContent/NuCacheTests.cs | 2 +- .../NuCache/PublishedContent.cs | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 89c2458f18..073ebf58ca 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -249,7 +249,7 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(ContentVariation.Nothing, againContent.ContentType.GetPropertyType("prop").Variations); // now, "no culture" means "invariant" - Assert.IsNull(againContent.Name()); // no invariant name for varying content + Assert.AreEqual("It Works1!", againContent.Name()); Assert.AreEqual("val1", againContent.Value("prop")); } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 7f67aff870..2e745aacb0 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -187,31 +187,31 @@ namespace Umbraco.Web.PublishedCache.NuCache /// public override string Name(string culture = null) { - // handle context culture + // invariant has invariant value (whatever the requested culture) + if (!ContentType.VariesByCulture()) + return ContentData.Name; + + // handle context culture for variant if (culture == null) culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; - // invariant culture - if (culture == "") - return ContentType.VariesByCulture() ? null : ContentData.Name; - - // explicit culture - return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos.Name : null; + // get + return culture != "" && Cultures.TryGetValue(culture, out var infos) ? infos.Name : null; } /// public override string UrlSegment(string culture = null) { - // handle context culture + // invariant has invariant value (whatever the requested culture) + if (!ContentType.VariesByCulture()) + return _urlSegment; + + // handle context culture fpr variant if (culture == null) culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; - // invariant culture - if (culture == "") - return ContentType.VariesByCulture() ? null : _urlSegment; - - // explicit culture - return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos.UrlSegment : null; + // get + return culture != "" && Cultures.TryGetValue(culture, out var infos) ? infos.UrlSegment : null; } /// From e062ea8d31238fc53abf86259ddfab3874d1595a Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 17 Apr 2019 10:03:49 +0200 Subject: [PATCH 11/75] Refactor IPublishedContent.CultureDate(), .Cultures --- .../PublishedContent/IPublishedContent.cs | 11 +++--- .../PublishedContentWrapped.cs | 4 +- .../DictionaryPublishedContent.cs | 7 ++-- .../XmlPublishedContent.cs | 7 ++-- .../Published/NestedContentTests.cs | 4 +- .../PublishedContent/NuCacheTests.cs | 4 +- .../PublishedContentDataTableTests.cs | 4 +- .../SolidPublishedSnapshot.cs | 4 +- .../TestHelpers/Stubs/TestPublishedContent.cs | 13 ++++--- .../Editors/MacroRenderingController.cs | 18 +++++++-- .../PublishedContentHashtableConverter.cs | 29 +++++++------- .../Models/PublishedContentBase.cs | 4 +- .../NuCache/PublishedContent.cs | 39 ++++++++----------- .../PublishedCache/PublishedMember.cs | 4 +- src/Umbraco.Web/PublishedContentExtensions.cs | 2 +- src/Umbraco.Web/Routing/PublishedRouter.cs | 2 +- .../Routing/RedirectTrackingComponent.cs | 4 +- 17 files changed, 84 insertions(+), 76 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 978958413a..2d1c48b854 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -88,7 +88,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// /// For published content items, this is also the date the item was published. - /// This date is always global to the content item, see GetCulture().Date for the + /// This date is always global to the content item, see CultureDate() for the /// date each culture was published. /// DateTime UpdateDate { get; } @@ -104,19 +104,20 @@ namespace Umbraco.Core.Models.PublishedContent string Url(string culture = null, UrlMode mode = UrlMode.Auto); /// - /// Gets culture infos for a culture. + /// Gets the culture date of the content item. /// - PublishedCultureInfo GetCulture(string culture = null); + /// The specific culture to get the name for. If null is used the current culture is used (Default is null). + DateTime CultureDate(string culture = null); /// - /// Gets culture infos. + /// Gets all available cultures. /// /// /// Contains only those culture that are available. For a published content, these are /// the cultures that are published. For a draft content, those that are 'available' ie /// have a non-empty content name. /// - IReadOnlyDictionary Cultures { get; } + IReadOnlyList Cultures { get; } /// /// Gets a value indicating whether the content is draft. diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index cd92888f7e..9132fb1f85 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -97,10 +97,10 @@ namespace Umbraco.Core.Models.PublishedContent public virtual string Url(string culture = null, UrlMode mode = UrlMode.Auto) => _content.Url(culture, mode); /// - public PublishedCultureInfo GetCulture(string culture = null) => _content.GetCulture(culture); + public DateTime CultureDate(string culture = null) => _content.CultureDate(culture); /// - public IReadOnlyDictionary Cultures => _content.Cultures; + public IReadOnlyList Cultures => _content.Cultures; /// public virtual bool IsDraft(string culture = null) => _content.IsDraft(culture); diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs index 40d7146248..7c6ecf5934 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs @@ -153,10 +153,11 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache public override string Name(string culture = null) => _name; - public override PublishedCultureInfo GetCulture(string culture = null) => null; + public override DateTime CultureDate(string culture = null) => UpdateDate; - private static readonly Lazy> NoCultures = new Lazy>(() => new Dictionary()); - public override IReadOnlyDictionary Cultures => NoCultures.Value; + // ReSharper disable once CollectionNeverUpdated.Local + private static readonly List EmptyListOfString = new List(); + public override IReadOnlyList Cultures => EmptyListOfString; public override string UrlSegment(string culture = null) => _urlName; diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index b738b22e2c..a1395b46bc 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -142,10 +142,11 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache return _name; } - public override PublishedCultureInfo GetCulture(string culture = null) => null; + public override DateTime CultureDate(string culture = null) => UpdateDate; - private static readonly Lazy> NoCultures = new Lazy>(() => new Dictionary()); - public override IReadOnlyDictionary Cultures => NoCultures.Value; + // ReSharper disable once CollectionNeverUpdated.Local + private static readonly List EmptyListOfString = new List(); + public override IReadOnlyList Cultures => EmptyListOfString; public override string WriterName { diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index e91f456922..b92724b033 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -284,8 +284,8 @@ namespace Umbraco.Tests.Published public override int? TemplateId { get; } public override int SortOrder { get; } public override string Name(string culture = null) => default; - public override PublishedCultureInfo GetCulture(string culture = ".") => throw new NotSupportedException(); - public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); + public override DateTime CultureDate(string culture = null) => throw new NotSupportedException(); + public override IReadOnlyList Cultures => throw new NotSupportedException(); public override string UrlSegment(string culture = null) => default; public override string WriterName { get; } public override string CreatorName { get; } diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 073ebf58ca..7d5fb8e736 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -223,13 +223,13 @@ namespace Umbraco.Tests.PublishedContent _variationAccesor.VariationContext = new VariationContext("fr-FR"); Assert.AreEqual("val-fr1", publishedContent.Value("prop")); Assert.AreEqual("name-fr1", publishedContent.Name()); - Assert.AreEqual(new DateTime(2018, 01, 01, 01, 00, 00), publishedContent.GetCulture().Date); + Assert.AreEqual(new DateTime(2018, 01, 01, 01, 00, 00), publishedContent.CultureDate()); // now uk is default _variationAccesor.VariationContext = new VariationContext("en-UK"); Assert.AreEqual("val-uk1", publishedContent.Value("prop")); Assert.AreEqual("name-uk1", publishedContent.Name()); - Assert.AreEqual(new DateTime(2018, 01, 02, 01, 00, 00), publishedContent.GetCulture().Date); + Assert.AreEqual(new DateTime(2018, 01, 02, 01, 00, 00), publishedContent.CultureDate()); // invariant needs to be retrieved explicitly, when it's not default Assert.AreEqual("val1", publishedContent.Value("prop", culture: "")); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 41b5588234..7f3c77cc72 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -205,8 +205,8 @@ namespace Umbraco.Tests.PublishedContent public int SortOrder { get; set; } public string Name(string culture = null) => _names.TryGetValue(culture ?? "", out var name) ? name : null; public void SetName(string name, string culture = null) => _names[culture ?? ""] = name; - public PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException(); - public IReadOnlyDictionary Cultures => throw new NotSupportedException(); + public DateTime CultureDate(string culture = null) => throw new NotSupportedException(); + public IReadOnlyList Cultures => throw new NotSupportedException(); public string UrlSegment(string culture = null) => _urlSegments.TryGetValue(culture ?? "", out var urlSegment) ? urlSegment : null; public void SetUrlSegment(string urlSegment, string culture = null) => _urlSegments[culture ?? ""] = urlSegment; public string WriterName { get; set; } diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 77f25c0b0b..22d6fe8ef1 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -183,8 +183,8 @@ namespace Umbraco.Tests.PublishedContent public int SortOrder { get; set; } public string Name(string culture = null) => _names.TryGetValue(culture ?? "", out var name) ? name : null; public void SetName(string name, string culture = null) => _names[culture ?? ""] = name; - public PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException(); - public IReadOnlyDictionary Cultures => throw new NotSupportedException(); + public DateTime CultureDate(string culture = null) => throw new NotSupportedException(); + public IReadOnlyList Cultures => throw new NotSupportedException(); public string UrlSegment(string culture = null) => _urlSegments.TryGetValue(culture ?? "", out var urlSegment) ? urlSegment : null; public void SetUrlSegment(string urlSegment, string culture = null) => _urlSegments[culture ?? ""] = urlSegment; public string WriterName { get; set; } diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 8a092c3453..6dd25b966e 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -9,12 +9,13 @@ namespace Umbraco.Tests.TestHelpers.Stubs { private readonly Dictionary _names = new Dictionary(); private readonly Dictionary _urlSegments = new Dictionary(); + private readonly Dictionary _cultures; - public TestPublishedContent(IPublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultures = null) + public TestPublishedContent(IPublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultures = null) : base(contentType, key, values, previewing) { Id = id; - Cultures = cultures; + _cultures = cultures ?? new Dictionary(); } public int Id { get; } @@ -23,19 +24,19 @@ namespace Umbraco.Tests.TestHelpers.Stubs public string Name(string culture = null) => _names.TryGetValue(culture ?? "", out var name) ? name : null; public void SetName(string name, string culture = null) => _names[culture ?? ""] = name; public IVariationContextAccessor VariationContextAccessor { get; set; } - public PublishedCultureInfo GetCulture(string culture = null) + public DateTime CultureDate(string culture = null) { // handle context culture if (culture == null) culture = VariationContextAccessor?.VariationContext?.Culture; // no invariant culture infos - if (culture == "" || Cultures == null) return null; + if (culture == "" || Cultures == null) return UpdateDate; // get - return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null; + return _cultures.TryGetValue(culture, out var date) ? date : DateTime.MinValue; } - public IReadOnlyDictionary Cultures { get; set; } + public IReadOnlyList Cultures { get; set; } public string UrlSegment(string culture = null) => _urlSegments.TryGetValue(culture ?? "", out var urlSegment) ? urlSegment : null; public void SetUrlSegment(string urlSegment, string culture = null) => _urlSegments[culture ?? ""] = urlSegment; public string DocumentTypeAlias => ContentType.Alias; diff --git a/src/Umbraco.Web/Editors/MacroRenderingController.cs b/src/Umbraco.Web/Editors/MacroRenderingController.cs index a2bbfe1dfd..64706a7f04 100644 --- a/src/Umbraco.Web/Editors/MacroRenderingController.cs +++ b/src/Umbraco.Web/Editors/MacroRenderingController.cs @@ -123,12 +123,22 @@ namespace Umbraco.Web.Editors // Since a Macro might contain thing thats related to the culture of the "IPublishedContent" (ie Dictionary keys) we want // to set the current culture to the culture related to the content item. This is hacky but it works. - var culture = publishedContent.GetCulture(); - _variationContextAccessor.VariationContext = new VariationContext(); //must have an active variation context! + // fixme I don't even know how this ever worked?! + + // assume this was some sort of "the culture of the item" + // but... with multilingual it does not make any sense?! + //var culture = publishedContent.GetCulture(); + + string culture = ""; // needs to be eg fr-FR + if (culture != null) { - Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture.Culture); - _variationContextAccessor.VariationContext = new VariationContext(Thread.CurrentThread.CurrentCulture.Name); + Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture); + _variationContextAccessor.VariationContext = new VariationContext(culture); + } + else + { + _variationContextAccessor.VariationContext = new VariationContext(); //must have an active variation context! } var result = Request.CreateResponse(); diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index 108f58e929..be7bbc37a8 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -230,32 +230,31 @@ namespace Umbraco.Web.Macros public string Name(string culture = null) => _inner.GetCultureName(culture); - public PublishedCultureInfo GetCulture(string culture = null) + public DateTime CultureDate(string culture = null) { - // handle context culture + // invariant has invariant value (whatever the requested culture) + if (!ContentType.VariesByCulture()) + return UpdateDate; + + // handle context culture for variant if (culture == null) culture = _variationContextAccessor.VariationContext.Culture; - // no invariant culture infos - if (culture == "") return null; - // get - return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null; + return culture != "" && _inner.PublishCultureInfos.TryGetValue(culture, out var infos) ? infos.Date : DateTime.MinValue; } - public IReadOnlyDictionary Cultures + // ReSharper disable once CollectionNeverUpdated.Local + private static readonly List EmptyListOfString = new List(); + private IReadOnlyList _cultures; + + public IReadOnlyList Cultures { get { if (!_inner.ContentType.VariesByCulture()) - return NoCultureInfos; - - if (_cultureInfos != null) - return _cultureInfos; - - var urlSegmentProviders = Current.UrlSegmentProviders; // TODO inject - return _cultureInfos = _inner.PublishCultureInfos.Values - .ToDictionary(x => x.Culture, x => new PublishedCultureInfo(x.Culture, x.Name, _inner.GetUrlSegment(urlSegmentProviders, x.Culture), x.Date)); + return EmptyListOfString; + return _cultures ?? (_cultures = _inner.PublishCultureInfos.Values.Select(x => x.Culture).ToList()); } } diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 7017e84882..22a76fb907 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -135,10 +135,10 @@ namespace Umbraco.Web.Models } /// - public abstract PublishedCultureInfo GetCulture(string culture = null); + public abstract DateTime CultureDate(string culture = null); /// - public abstract IReadOnlyDictionary Cultures { get; } + public abstract IReadOnlyList Cultures { get; } /// public abstract bool IsDraft(string culture = null); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 2e745aacb0..86ed272b13 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -196,7 +196,7 @@ namespace Umbraco.Web.PublishedCache.NuCache culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; // get - return culture != "" && Cultures.TryGetValue(culture, out var infos) ? infos.Name : null; + return culture != "" && ContentData.CultureInfos.TryGetValue(culture, out var infos) ? infos.Name : null; } /// @@ -206,12 +206,12 @@ namespace Umbraco.Web.PublishedCache.NuCache if (!ContentType.VariesByCulture()) return _urlSegment; - // handle context culture fpr variant + // handle context culture for variant if (culture == null) culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; // get - return culture != "" && Cultures.TryGetValue(culture, out var infos) ? infos.UrlSegment : null; + return ContentData.CultureInfos.TryGetValue(culture, out var infos) ? infos.UrlSegment : null; } /// @@ -244,39 +244,34 @@ namespace Umbraco.Web.PublishedCache.NuCache /// public override DateTime UpdateDate => ContentData.VersionDate; - private IReadOnlyDictionary _cultureInfos; - - private static readonly IReadOnlyDictionary NoCultureInfos = new Dictionary(); - /// - public override PublishedCultureInfo GetCulture(string culture = null) + public override DateTime CultureDate(string culture = null) { - // handle context culture + // invariant has invariant value (whatever the requested culture) + if (!ContentType.VariesByCulture()) + return UpdateDate; + + // handle context culture for variant if (culture == null) culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; - // no invariant culture infos - if (culture == "") return null; - // get - return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null; + return culture != "" && ContentData.CultureInfos.TryGetValue(culture, out var infos) ? infos.Date : DateTime.MinValue; } + // ReSharper disable once CollectionNeverUpdated.Local + private static readonly List EmptyListOfString = new List(); + private IReadOnlyList _cultures; + /// - public override IReadOnlyDictionary Cultures + public override IReadOnlyList Cultures { get { if (!ContentType.VariesByCulture()) - return NoCultureInfos; + return EmptyListOfString; - if (_cultureInfos != null) return _cultureInfos; - - if (ContentData.CultureInfos == null) - throw new Exception("oops: _contentDate.CultureInfos is null."); - - return _cultureInfos = ContentData.CultureInfos - .ToDictionary(x => x.Key, x => new PublishedCultureInfo(x.Key, x.Value.Name, x.Value.UrlSegment, x.Value.Date), StringComparer.OrdinalIgnoreCase); + return _cultures ?? (_cultures = ContentData.CultureInfos.Keys.ToList()); } } diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index 68b742caa3..46b12b5ca3 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -140,9 +140,9 @@ namespace Umbraco.Web.PublishedCache return _member.Name; } - public override PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException(); + public override DateTime CultureDate(string culture = null) => throw new NotSupportedException(); - public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); + public override IReadOnlyList Cultures => throw new NotSupportedException(); public override string UrlSegment(string culture = null) => throw new NotSupportedException(); diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 7776a7c8ac..d7996034a8 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -168,7 +168,7 @@ namespace Umbraco.Web /// /// Culture is case-insensitive. public static bool HasCulture(this IPublishedContent content, string culture) - => content.Cultures.ContainsKey(culture ?? string.Empty); + => content.Cultures.Contains(culture ?? string.Empty); /// /// Filters a sequence of to return invariant items, and items that are published for the specified culture. diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index c93b7c06b2..aeb329a9e9 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -276,7 +276,7 @@ namespace Umbraco.Web.Routing return true; // variant, ensure that the culture corresponding to the domain's language is published - return domainDocument.Cultures.ContainsKey(domain.Culture.Name); + return domainDocument.Cultures.Contains(domain.Culture.Name); } domains = domains.Where(IsPublishedContentDomain).ToList(); diff --git a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs b/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs index 0d82467179..4f9086e50b 100644 --- a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs +++ b/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs @@ -171,12 +171,12 @@ namespace Umbraco.Web.Routing if (entityContent == null) continue; // get the default affected cultures by going up the tree until we find the first culture variant entity (default to no cultures) - var defaultCultures = entityContent.AncestorsOrSelf()?.FirstOrDefault(a => a.Cultures.Any())?.Cultures.Select(c => c.Key).ToArray() + var defaultCultures = entityContent.AncestorsOrSelf()?.FirstOrDefault(a => a.Cultures.Any())?.Cultures.ToArray() ?? new[] {(string) null}; foreach (var x in entityContent.DescendantsOrSelf()) { // if this entity defines specific cultures, use those instead of the default ones - var cultures = x.Cultures.Any() ? x.Cultures.Select(c => c.Key) : defaultCultures; + var cultures = x.Cultures.Any() ? x.Cultures : defaultCultures; foreach (var culture in cultures) { From a996d46b6ff85bd927b0a331f37de6d36f123529 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 17 Apr 2019 14:41:54 +0200 Subject: [PATCH 12/75] Refactor IPublishedContent.GetCultureFromDomains() --- .../LegacyXmlPublishedCache/DomainCache.cs | 9 +- .../PublishedContentCache.cs | 8 +- src/Umbraco.Web/Editors/ContentController.cs | 2 +- .../Editors/MacroRenderingController.cs | 20 ++--- .../PublishedCache/IDomainCache.cs | 25 ++++-- .../PublishedCache/NuCache/ContentCache.cs | 12 ++- .../PublishedCache/NuCache/DomainCache.cs | 19 ++++- .../NuCache/PublishedSnapshotService.cs | 5 +- src/Umbraco.Web/PublishedContentExtensions.cs | 25 ++++++ src/Umbraco.Web/Routing/AliasUrlProvider.cs | 7 +- .../Routing/ContentFinderByConfigured404.cs | 2 +- .../Routing/ContentFinderByRedirectUrl.cs | 2 +- src/Umbraco.Web/Routing/ContentFinderByUrl.cs | 2 +- .../Routing/ContentFinderByUrlAndTemplate.cs | 2 +- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 10 +-- src/Umbraco.Web/Routing/DomainAndUri.cs | 2 +- .../{DomainHelper.cs => DomainUtilities.cs} | 83 +++++++++++++------ src/Umbraco.Web/Routing/PublishedRouter.cs | 4 +- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- src/Umbraco.Web/UmbracoContext.cs | 20 ----- src/Umbraco.Web/UmbracoHelper.cs | 6 -- 21 files changed, 153 insertions(+), 114 deletions(-) rename src/Umbraco.Web/Routing/{DomainHelper.cs => DomainUtilities.cs} (81%) diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/DomainCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/DomainCache.cs index cde2077551..abaa239598 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/DomainCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/DomainCache.cs @@ -27,13 +27,18 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache } /// - public IEnumerable GetAssigned(int contentId, bool includeWildcards) + public IEnumerable GetAssigned(int documentId, bool includeWildcards = false) { - return _domainService.GetAssignedDomains(contentId, includeWildcards) + return _domainService.GetAssignedDomains(documentId, includeWildcards) .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard)); } + /// + public bool HasAssigned(int documentId, bool includeWildcards = false) + => documentId > 0 && GetAssigned(documentId, includeWildcards).Any(); + + /// public string DefaultCulture { get; } } } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs index 1ad6e045c6..1ccbbf950b 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs @@ -22,7 +22,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly RoutesCache _routesCache; private readonly IDomainCache _domainCache; - private readonly DomainHelper _domainHelper; private readonly PublishedContentTypeCache _contentTypeCache; // initialize a PublishedContentCache instance with @@ -48,7 +47,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache _routesCache = routesCache; // may be null for unit-testing _contentTypeCache = contentTypeCache; _domainCache = domainCache; - _domainHelper = new DomainHelper(_domainCache, siteDomainHelper); _xmlStore = xmlStore; _xml = _xmlStore.Xml; // capture - because the cache has to remain consistent @@ -107,7 +105,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache // that would be returned - the "deepest" route - and that is the route we want to cache, *not* the // longer one - so make sure we don't cache the wrong route - var deepest = DomainHelper.ExistsDomainInPath(_domainCache.GetAll(false), content.Path, domainRootNodeId) == false; + var deepest = DomainUtilities.ExistsDomainInPath(_domainCache.GetAll(false), content.Path, domainRootNodeId) == false; if (deepest) _routesCache.Store(content.Id, route, true); // trusted route @@ -267,7 +265,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache // or we reach the content root, collecting urls in the way var pathParts = new List(); var n = node; - var hasDomains = _domainHelper.NodeHasDomains(n.Id); + var hasDomains = _domainCache.HasAssigned(n.Id); while (hasDomains == false && n != null) // n is null at root { // get the url @@ -276,7 +274,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache // move to parent node n = n.Parent; - hasDomains = n != null && _domainHelper.NodeHasDomains(n.Id); + hasDomains = n != null && _domainCache.HasAssigned(n.Id); } // no domain, respect HideTopLevelNodeFromPath for legacy purposes diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 4b23e46a6c..a8824d416f 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -1656,7 +1656,7 @@ namespace Umbraco.Web.Editors { try { - var uri = DomainHelper.ParseUriFromDomainName(domain.Name, Request.RequestUri); + var uri = DomainUtilities.ParseUriFromDomainName(domain.Name, Request.RequestUri); } catch (UriFormatException) { diff --git a/src/Umbraco.Web/Editors/MacroRenderingController.cs b/src/Umbraco.Web/Editors/MacroRenderingController.cs index 64706a7f04..0c3b1626d0 100644 --- a/src/Umbraco.Web/Editors/MacroRenderingController.cs +++ b/src/Umbraco.Web/Editors/MacroRenderingController.cs @@ -123,23 +123,17 @@ namespace Umbraco.Web.Editors // Since a Macro might contain thing thats related to the culture of the "IPublishedContent" (ie Dictionary keys) we want // to set the current culture to the culture related to the content item. This is hacky but it works. - // fixme I don't even know how this ever worked?! + // fixme + // in a 1:1 situation we do not handle the language being edited + // so the macro renders in the wrong language - // assume this was some sort of "the culture of the item" - // but... with multilingual it does not make any sense?! - //var culture = publishedContent.GetCulture(); - - string culture = ""; // needs to be eg fr-FR + var culture = publishedContent.GetCultureFromDomains(); if (culture != null) - { Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture); - _variationContextAccessor.VariationContext = new VariationContext(culture); - } - else - { - _variationContextAccessor.VariationContext = new VariationContext(); //must have an active variation context! - } + + // must have an active variation context! + _variationContextAccessor.VariationContext = new VariationContext(culture); var result = Request.CreateResponse(); //need to create a specific content result formatted as HTML since this controller has been configured diff --git a/src/Umbraco.Web/PublishedCache/IDomainCache.cs b/src/Umbraco.Web/PublishedCache/IDomainCache.cs index dbee8908a0..3ec84c9d48 100644 --- a/src/Umbraco.Web/PublishedCache/IDomainCache.cs +++ b/src/Umbraco.Web/PublishedCache/IDomainCache.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.Routing; namespace Umbraco.Web.PublishedCache @@ -6,20 +8,29 @@ namespace Umbraco.Web.PublishedCache public interface IDomainCache { /// - /// Returns all in the current domain cache including any domains that may be referenced by content items that are no longer published + /// Gets all in the current domain cache, including any domains that may be referenced by documents that are no longer published. /// /// /// IEnumerable GetAll(bool includeWildcards); /// - /// Returns all assigned for the content id specified even if the content item is not published + /// Gets all assigned for specified document, even if it is not published. /// - /// - /// - /// - IEnumerable GetAssigned(int contentId, bool includeWildcards); + /// The document identifier. + /// A value indicating whether to consider wildcard domains. + IEnumerable GetAssigned(int documentId, bool includeWildcards = false); + /// + /// Determines whether a document has domains. + /// + /// The document identifier. + /// A value indicating whether to consider wildcard domains. + bool HasAssigned(int documentId, bool includeWildcards = false); + + /// + /// Gets the system default culture. + /// string DefaultCulture { get; } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index 08664f0a7a..705ce17595 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -23,9 +23,8 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly ContentStore.Snapshot _snapshot; private readonly IAppCache _snapshotCache; private readonly IAppCache _elementsCache; - private readonly DomainHelper _domainHelper; + private readonly IDomainCache _domainCache; private readonly IGlobalSettings _globalSettings; - private readonly ILocalizationService _localizationService; #region Constructor @@ -34,15 +33,14 @@ namespace Umbraco.Web.PublishedCache.NuCache // it's too late for UmbracoContext which has captured previewDefault and stuff into these ctor vars // but, no, UmbracoContext returns snapshot.Content which comes from elements SO a resync should create a new cache - public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache, IAppCache elementsCache, DomainHelper domainHelper, IGlobalSettings globalSettings, ILocalizationService localizationService) + public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache, IAppCache elementsCache, IDomainCache domainCache, IGlobalSettings globalSettings) : base(previewDefault) { _snapshot = snapshot; _snapshotCache = snapshotCache; _elementsCache = elementsCache; - _domainHelper = domainHelper; + _domainCache = domainCache; _globalSettings = globalSettings; - _localizationService = localizationService; } private bool HideTopLevelNodeFromPath => _globalSettings.HideTopLevelNodeFromPath; @@ -150,7 +148,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var pathParts = new List(); var n = node; var urlSegment = n.UrlSegment(culture); - var hasDomains = _domainHelper.NodeHasDomains(n.Id); + var hasDomains = _domainCache.HasAssigned(n.Id); while (hasDomains == false && n != null) // n is null at root { // no segment indicates this is not published when this is a variant @@ -163,7 +161,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (n != null) urlSegment = n.UrlSegment(culture); - hasDomains = n != null && _domainHelper.NodeHasDomains(n.Id); + hasDomains = n != null && _domainCache.HasAssigned(n.Id); } // at this point this will be the urlSegment of the root, no segment indicates this is not published when this is a variant diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DomainCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/DomainCache.cs index 896a04a0b3..6bc0de7268 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DomainCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DomainCache.cs @@ -4,16 +4,23 @@ using Umbraco.Web.Routing; namespace Umbraco.Web.PublishedCache.NuCache { + /// + /// Implements for NuCache. + /// internal class DomainCache : IDomainCache { private readonly SnapDictionary.Snapshot _snapshot; + /// + /// Initializes a new instance of the class. + /// public DomainCache(SnapDictionary.Snapshot snapshot, string defaultCulture) { _snapshot = snapshot; - DefaultCulture = defaultCulture; // capture - fast + DefaultCulture = defaultCulture; } + /// public IEnumerable GetAll(bool includeWildcards) { var list = _snapshot.GetAll(); @@ -21,17 +28,23 @@ namespace Umbraco.Web.PublishedCache.NuCache return list; } - public IEnumerable GetAssigned(int contentId, bool includeWildcards) + /// + public IEnumerable GetAssigned(int documentId, bool includeWildcards = false) { // probably this could be optimized with an index // but then we'd need a custom DomainStore of some sort var list = _snapshot.GetAll(); - list = list.Where(x => x.ContentId == contentId); + list = list.Where(x => x.ContentId == documentId); if (includeWildcards == false) list = list.Where(x => x.IsWildcard == false); return list; } + /// + public bool HasAssigned(int documentId, bool includeWildcards = false) + => documentId > 0 && GetAssigned(documentId, includeWildcards).Any(); + + /// public string DefaultCulture { get; } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 3b683cdd4e..ce19764fb6 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -4,13 +4,11 @@ using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; -using System.Web; using CSharpTest.Net.Collections; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; @@ -1074,11 +1072,10 @@ namespace Umbraco.Web.PublishedCache.NuCache var defaultCulture = _defaultCultureAccessor.DefaultCulture; var domainCache = new DomainCache(domainSnap, defaultCulture); - var domainHelper = new DomainHelper(domainCache, _siteDomainHelper); return new PublishedSnapshot.PublishedSnapshotElements { - ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainHelper, _globalSettings, _serviceContext.LocalizationService), + ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainCache, _globalSettings), MediaCache = new MediaCache(previewDefault, mediaSnap, snapshotCache, elementsCache), MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, memberTypeCache, PublishedSnapshotAccessor, VariationContextAccessor, _umbracoContextAccessor, _entitySerializer), DomainCache = domainCache, diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index d7996034a8..a243d6e77a 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Services; using Umbraco.Examine; using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; namespace Umbraco.Web { @@ -20,9 +21,12 @@ namespace Umbraco.Web public static class PublishedContentExtensions { // see notes in PublishedElementExtensions + // (yes, this is not pretty, but works for now) // private static IPublishedValueFallback PublishedValueFallback => Current.PublishedValueFallback; private static IPublishedSnapshot PublishedSnapshot => Current.PublishedSnapshot; + private static UmbracoContext UmbracoContext => Current.UmbracoContext; + private static ISiteDomainHelper SiteDomainHelper => Current.Factory.GetInstance(); #region IsComposedOf @@ -186,6 +190,27 @@ namespace Umbraco.Web return contents.Where(x => !x.ContentType.VariesByCulture() || x.HasCulture(culture)); } + /// + /// Gets the culture assigned to a document by domains, in the context of a current Uri. + /// + /// The document. + /// An optional current Uri. + /// The culture assigned to the document by domains. + /// + /// In 1:1 multilingual setup, a document contains several cultures (there is not + /// one document per culture), and domains, withing the context of a current Uri, assign + /// a culture to that document. + /// + public static string GetCultureFromDomains(this IPublishedContent content, Uri current = null) + { + var umbracoContext = UmbracoContext; + + if (umbracoContext == null) + throw new InvalidOperationException("A current UmbracoContext is required."); + + return DomainUtilities.GetCultureFromDomains(content.Id, content.Path, current, umbracoContext, SiteDomainHelper); + } + #endregion #region Search diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index bbe3a9db8c..411fabbf35 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; @@ -60,16 +59,14 @@ namespace Umbraco.Web.Routing if (!node.HasProperty(Constants.Conventions.Content.UrlAlias)) yield break; - var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper); - // look for domains, walking up the tree var n = node; - var domainUris = domainHelper.DomainsForNode(n.Id, current, false); + var domainUris = DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, n.Id, current, false); while (domainUris == null && n != null) // n is null at root { // move to parent node n = n.Parent; - domainUris = n == null ? null : domainHelper.DomainsForNode(n.Id, current, excludeDefault: false); + domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, n.Id, current, excludeDefault: false); } // determine whether the alias property varies diff --git a/src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs b/src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs index e3cad25c6f..e5bb23bfce 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs @@ -53,7 +53,7 @@ namespace Umbraco.Web.Routing } if (node != null) { - var d = DomainHelper.FindWildcardDomainInPath(frequest.UmbracoContext.PublishedSnapshot.Domains.GetAll(true), node.Path, null); + var d = DomainUtilities.FindWildcardDomainInPath(frequest.UmbracoContext.PublishedSnapshot.Domains.GetAll(true), node.Path, null); if (d != null) errorCulture = d.Culture; } diff --git a/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs b/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs index eae2b57378..d7b4c9925c 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs @@ -33,7 +33,7 @@ namespace Umbraco.Web.Routing public bool TryFindContent(PublishedRequest frequest) { var route = frequest.HasDomain - ? frequest.Domain.ContentId + DomainHelper.PathRelativeToDomain(frequest.Domain.Uri, frequest.Uri.GetAbsolutePathDecoded()) + ? frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.Uri.GetAbsolutePathDecoded()) : frequest.Uri.GetAbsolutePathDecoded(); var redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl(route); diff --git a/src/Umbraco.Web/Routing/ContentFinderByUrl.cs b/src/Umbraco.Web/Routing/ContentFinderByUrl.cs index 94b2b9dbf2..9aa52782d1 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByUrl.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByUrl.cs @@ -28,7 +28,7 @@ namespace Umbraco.Web.Routing { string route; if (frequest.HasDomain) - route = frequest.Domain.ContentId + DomainHelper.PathRelativeToDomain(frequest.Domain.Uri, frequest.Uri.GetAbsolutePathDecoded()); + route = frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.Uri.GetAbsolutePathDecoded()); else route = frequest.Uri.GetAbsolutePathDecoded(); diff --git a/src/Umbraco.Web/Routing/ContentFinderByUrlAndTemplate.cs b/src/Umbraco.Web/Routing/ContentFinderByUrlAndTemplate.cs index 1e86b40a79..39c80afd56 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByUrlAndTemplate.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByUrlAndTemplate.cs @@ -38,7 +38,7 @@ namespace Umbraco.Web.Routing var path = frequest.Uri.GetAbsolutePathDecoded(); if (frequest.HasDomain) - path = DomainHelper.PathRelativeToDomain(frequest.Domain.Uri, path); + path = DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, path); // no template if "/" if (path == "/") diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 98ba743fe5..06f275a685 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -47,15 +47,13 @@ namespace Umbraco.Web.Routing return null; } - var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper); - // extract domainUri and path // route is / or / var pos = route.IndexOf('/'); var path = pos == 0 ? route : route.Substring(pos); var domainUri = pos == 0 ? null - : domainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current, culture); + : DomainUtilities.DomainForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, int.Parse(route.Substring(0, pos)), current, culture); // assemble the url from domainUri (maybe null) and path var url = AssembleUrl(domainUri, path, current, mode).ToString(); @@ -84,15 +82,13 @@ namespace Umbraco.Web.Routing if (node == null) yield break; - var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper); - // look for domains, walking up the tree var n = node; - var domainUris = domainHelper.DomainsForNode(n.Id, current, false); + var domainUris = DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, n.Id, current, false); while (domainUris == null && n != null) // n is null at root { n = n.Parent; // move to parent node - domainUris = n == null ? null : domainHelper.DomainsForNode(n.Id, current, excludeDefault: true); + domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, n.Id, current, excludeDefault: true); } // no domains = exit diff --git a/src/Umbraco.Web/Routing/DomainAndUri.cs b/src/Umbraco.Web/Routing/DomainAndUri.cs index 1151055621..46dc085998 100644 --- a/src/Umbraco.Web/Routing/DomainAndUri.cs +++ b/src/Umbraco.Web/Routing/DomainAndUri.cs @@ -22,7 +22,7 @@ namespace Umbraco.Web.Routing { try { - Uri = DomainHelper.ParseUriFromDomainName(Name, currentUri); + Uri = DomainUtilities.ParseUriFromDomainName(Name, currentUri); } catch (UriFormatException) { diff --git a/src/Umbraco.Web/Routing/DomainHelper.cs b/src/Umbraco.Web/Routing/DomainUtilities.cs similarity index 81% rename from src/Umbraco.Web/Routing/DomainHelper.cs rename to src/Umbraco.Web/Routing/DomainUtilities.cs index 95d97653a0..fb0c56b28d 100644 --- a/src/Umbraco.Web/Routing/DomainHelper.cs +++ b/src/Umbraco.Web/Routing/DomainUtilities.cs @@ -2,29 +2,69 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core; -using Umbraco.Web.PublishedCache; // published snapshot +using Umbraco.Web.PublishedCache; namespace Umbraco.Web.Routing { /// /// Provides utilities to handle domains. /// - public class DomainHelper + public static class DomainUtilities { - private readonly IDomainCache _domainCache; - private readonly ISiteDomainHelper _siteDomainHelper; + #region Document Culture - public DomainHelper(IDomainCache domainCache, ISiteDomainHelper siteDomainHelper) + /// + /// Gets the culture assigned to a document by domains, in the context of a current Uri. + /// + /// The document identifier. + /// The document path. + /// An optional current Uri. + /// An Umbraco context. + /// The site domain helper. + /// The culture assigned to the document by domains. + /// + /// In 1:1 multilingual setup, a document contains several cultures (there is not + /// one document per culture), and domains, withing the context of a current Uri, assign + /// a culture to that document. + /// + internal static string GetCultureFromDomains(int contentId, string contentPath, Uri current, UmbracoContext umbracoContext, ISiteDomainHelper siteDomainHelper) { - _domainCache = domainCache; - _siteDomainHelper = siteDomainHelper; + if (umbracoContext == null) + throw new InvalidOperationException("A current UmbracoContext is required."); + + if (current == null) + current = umbracoContext.CleanedUmbracoUrl; + + // get the published route, else the preview route + // if both are null then the content does not exist + var route = umbracoContext.Content.GetRouteById(contentId) ?? + umbracoContext.Content.GetRouteById(true, contentId); + + if (route == null) + return null; + + var pos = route.IndexOf('/'); + var domain = pos == 0 + ? null + : DomainForNode(umbracoContext.Domains, siteDomainHelper, int.Parse(route.Substring(0, pos)), current); + + var rootContentId = domain?.ContentId ?? -1; + var wcDomain = FindWildcardDomainInPath(umbracoContext.Domains.GetAll(true), contentPath, rootContentId); + + if (wcDomain != null) return wcDomain.Culture.Name; + if (domain != null) return domain.Culture.Name; + return umbracoContext.Domains.DefaultCulture; } - #region Domain for Node + #endregion + + #region Domain for Document /// /// Finds the domain for the specified node, if any, that best matches a specified uri. /// + /// A domain cache. + /// The site domain helper. /// The node identifier. /// The uri, or null. /// The culture, or null. @@ -35,14 +75,14 @@ namespace Umbraco.Web.Routing /// If culture is null, uses the default culture for the installation instead. Otherwise, /// will try with the specified culture, else return null. /// - internal DomainAndUri DomainForNode(int nodeId, Uri current, string culture = null) + internal static DomainAndUri DomainForNode(IDomainCache domainCache, ISiteDomainHelper siteDomainHelper, int nodeId, Uri current, string culture = null) { // be safe if (nodeId <= 0) return null; // get the domains on that node - var domains = _domainCache.GetAssigned(nodeId, false).ToArray(); + var domains = domainCache.GetAssigned(nodeId).ToArray(); // none? if (domains.Length == 0) @@ -50,37 +90,28 @@ namespace Umbraco.Web.Routing // else filter // it could be that none apply (due to culture) - return SelectDomain(domains, current, culture, _domainCache.DefaultCulture, - (cdomainAndUris, ccurrent, cculture, cdefaultCulture) => _siteDomainHelper.MapDomain(cdomainAndUris, ccurrent, cculture, cdefaultCulture)); - } - - /// - /// Gets a value indicating whether a specified node has domains. - /// - /// The node identifier. - /// True if the node has domains, else false. - internal bool NodeHasDomains(int nodeId) - { - return nodeId > 0 && _domainCache.GetAssigned(nodeId, false).Any(); + return SelectDomain(domains, current, culture, domainCache.DefaultCulture, siteDomainHelper.MapDomain); } /// /// Find the domains for the specified node, if any, that match a specified uri. /// + /// A domain cache. + /// The site domain helper. /// The node identifier. /// The uri, or null. /// A value indicating whether to exclude the current/default domain. True by default. /// The domains and their uris, that match the specified uri, else null. /// If at least a domain is set on the node then the method returns the domains that /// best match the specified uri, else it returns null. - internal IEnumerable DomainsForNode(int nodeId, Uri current, bool excludeDefault = true) + internal static IEnumerable DomainsForNode(IDomainCache domainCache, ISiteDomainHelper siteDomainHelper, int nodeId, Uri current, bool excludeDefault = true) { // be safe if (nodeId <= 0) return null; // get the domains on that node - var domains = _domainCache.GetAssigned(nodeId, false).ToArray(); + var domains = domainCache.GetAssigned(nodeId).ToArray(); // none? if (domains.Length == 0) @@ -90,7 +121,7 @@ namespace Umbraco.Web.Routing var domainAndUris = SelectDomains(domains, current).ToArray(); // filter - return _siteDomainHelper.MapDomains(domainAndUris, current, excludeDefault, null, _domainCache.DefaultCulture).ToArray(); + return siteDomainHelper.MapDomains(domainAndUris, current, excludeDefault, null, domainCache.DefaultCulture).ToArray(); } #endregion @@ -252,7 +283,7 @@ namespace Umbraco.Web.Routing } /// - /// Parses a domain name into a URI. + /// Parses a domain name into a URI. /// /// The domain name to parse /// The currently requested URI. If the domain name is relative, the authority of URI will be used. diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index aeb329a9e9..2e772cb175 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -284,7 +284,7 @@ namespace Umbraco.Web.Routing var defaultCulture = domainsCache.DefaultCulture; // try to find a domain matching the current request - var domainAndUri = DomainHelper.SelectDomain(domains, request.Uri, defaultCulture: defaultCulture); + var domainAndUri = DomainUtilities.SelectDomain(domains, request.Uri, defaultCulture: defaultCulture); // handle domain - always has a contentId and a culture if (domainAndUri != null) @@ -328,7 +328,7 @@ namespace Umbraco.Web.Routing var nodePath = request.PublishedContent.Path; _logger.Debug("{TracePrefix}Path={NodePath}", tracePrefix, nodePath); var rootNodeId = request.HasDomain ? request.Domain.ContentId : (int?)null; - var domain = DomainHelper.FindWildcardDomainInPath(request.UmbracoContext.PublishedSnapshot.Domains.GetAll(true), nodePath, rootNodeId); + var domain = DomainUtilities.FindWildcardDomainInPath(request.UmbracoContext.PublishedSnapshot.Domains.GetAll(true), nodePath, rootNodeId); // always has a contentId and a culture if (domain != null) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 42a1a766e5..28337e6d2c 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1137,7 +1137,7 @@ - + diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index a39535ba6e..04052e8be3 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -3,12 +3,10 @@ using System.Collections.Generic; using System.Web; using Umbraco.Core; using Umbraco.Core.Configuration; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; -using Umbraco.Web.Runtime; using Umbraco.Web.Security; namespace Umbraco.Web @@ -20,7 +18,6 @@ namespace Umbraco.Web { private readonly IGlobalSettings _globalSettings; private readonly Lazy _publishedSnapshot; - private DomainHelper _domainHelper; private string _previewToken; private bool? _previewing; @@ -107,9 +104,6 @@ namespace Umbraco.Web /// public IPublishedSnapshot PublishedSnapshot => _publishedSnapshot.Value; - // for unit tests - internal bool HasPublishedSnapshot => _publishedSnapshot.IsValueCreated; - /// /// Gets the published content cache. /// @@ -162,20 +156,6 @@ namespace Umbraco.Web /// public IVariationContextAccessor VariationContextAccessor { get; } - /// - /// Creates and caches an instance of a DomainHelper - /// - /// - /// We keep creating new instances of DomainHelper, it would be better if we didn't have to do that so instead we can - /// have one attached to the UmbracoContext. This method accepts an external ISiteDomainHelper otherwise the UmbracoContext - /// ctor will have to have another parameter added only for this one method which is annoying and doesn't make a ton of sense - /// since the UmbracoContext itself doesn't use this. - /// - /// TODO: The alternative is to have a IDomainHelperAccessor singleton which is cached per UmbracoContext - /// - internal DomainHelper GetDomainHelper(ISiteDomainHelper siteDomainHelper) - => _domainHelper ?? (_domainHelper = new DomainHelper(PublishedSnapshot.Domains, siteDomainHelper)); - /// /// Gets a value indicating whether the request has debugging enabled /// diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 1c6eb28b92..6e7b05691e 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -809,11 +809,5 @@ namespace Umbraco.Web } #endregion - - - - - - } } From 41e14a62c05c21a8864ade7423939712dc1679da Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 22 Apr 2019 11:59:06 +0200 Subject: [PATCH 13/75] Refactor IPublishedContent.Parent() --- .../PublishedContent/IPublishedContent.cs | 2 +- .../PublishedContentWrapped.cs | 2 +- .../PublishedMediaCacheTests.cs | 6 ++--- .../DictionaryPublishedContent.cs | 2 +- .../PublishedContentCache.cs | 4 ++-- .../XmlPublishedContent.cs | 9 +++----- .../Published/NestedContentTests.cs | 2 +- .../PublishedContentDataTableTests.cs | 7 ++---- .../PublishedContentLanguageVariantTests.cs | 4 ++-- .../PublishedContent/PublishedMediaTests.cs | 6 ++--- .../PublishedContent/PublishedRouterTests.cs | 2 +- .../PublishedContent/RootNodeTests.cs | 2 +- .../SolidPublishedSnapshot.cs | 8 ++++--- .../TestHelpers/Stubs/TestPublishedContent.cs | 6 +++-- .../PublishedContentHashtableConverter.cs | 10 ++++---- .../PublishedValueFallback.cs | 2 +- .../Models/PublishedContentBase.cs | 2 +- .../PublishedCache/NuCache/ContentCache.cs | 4 ++-- .../NuCache/PublishedContent.cs | 23 ++++++++----------- .../PublishedCache/PublishedMember.cs | 2 +- src/Umbraco.Web/PublishedContentExtensions.cs | 18 +++++++-------- src/Umbraco.Web/Routing/AliasUrlProvider.cs | 2 +- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 2 +- .../Routing/UrlProviderExtensions.cs | 2 +- 24 files changed, 62 insertions(+), 67 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 2d1c48b854..7f15e367c8 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -155,7 +155,7 @@ namespace Umbraco.Core.Models.PublishedContent /// Gets the parent of the content item. /// /// The parent of root content is null. - IPublishedContent Parent { get; } + IPublishedContent Parent(); /// /// Gets the children of the content item. diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 9132fb1f85..d0db4d6e09 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -113,7 +113,7 @@ namespace Umbraco.Core.Models.PublishedContent #region Tree /// - public virtual IPublishedContent Parent => _content.Parent; + public virtual IPublishedContent Parent() => _content.Parent(); /// public virtual IEnumerable Children => _content.Children; diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index 2245f600dc..ce89310f35 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -111,7 +111,7 @@ namespace Umbraco.Tests.Cache.PublishedCache Assert.AreEqual(mRoot.Name, publishedMedia.Name()); Assert.AreEqual(mRoot.Path, publishedMedia.Path); Assert.AreEqual(mRoot.SortOrder, publishedMedia.SortOrder); - Assert.IsNull(publishedMedia.Parent); + Assert.IsNull(publishedMedia.Parent()); } [TestCase("id")] @@ -212,7 +212,7 @@ namespace Umbraco.Tests.Cache.PublishedCache var doc = store.CreateFromCacheValues(store.ConvertFromSearchResult(result)); DoAssert(doc, 1234, key, null, 0, "/media/test.jpg", "Image", 23, "Shannon", "Shannon", 0, 0, "-1,1234", DateTime.Parse("2012-07-17T10:34:09"), DateTime.Parse("2012-07-16T10:34:09"), 2); - Assert.AreEqual(null, doc.Parent); + Assert.AreEqual(null, doc.Parent()); } [Test] @@ -228,7 +228,7 @@ namespace Umbraco.Tests.Cache.PublishedCache var doc = cache.CreateFromCacheValues(cache.ConvertFromXPathNavigator(navigator, true)); DoAssert(doc, 2000, key, null, 2, "image1", "Image", 23, "Shannon", "Shannon", 33, 33, "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1); - Assert.AreEqual(null, doc.Parent); + Assert.AreEqual(null, doc.Parent()); Assert.AreEqual(2, doc.Children.Count()); Assert.AreEqual(2001, doc.Children.ElementAt(0).Id); Assert.AreEqual(2002, doc.Children.ElementAt(1).Id); diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs index 7c6ecf5934..3175d0aca0 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs @@ -139,7 +139,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private readonly Func _getProperty; private readonly IAppCache _appCache; - public override IPublishedContent Parent => _getParent.Value; + public override IPublishedContent Parent() => _getParent.Value; public int ParentId { get; private set; } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs index 1ccbbf950b..a4e08178ed 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs @@ -273,14 +273,14 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache pathParts.Add(urlName); // move to parent node - n = n.Parent; + n = n.Parent(); hasDomains = n != null && _domainCache.HasAssigned(n.Id); } // no domain, respect HideTopLevelNodeFromPath for legacy purposes if (hasDomains == false && _globalSettings.HideTopLevelNodeFromPath) { - if (node.Parent == null) + if (node.Parent() == null) { var rootNode = GetByRoute(preview, "/", true); if (rootNode == null) diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index a1395b46bc..8981b6c691 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -91,13 +91,10 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache return _properties.TryGetValue(alias, out property) ? property : null; } - public override IPublishedContent Parent + public override IPublishedContent Parent() { - get - { - EnsureNodeInitialized(andParent: true); - return _parent; - } + EnsureNodeInitialized(andParent: true); + return _parent; } public override int Id diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index b92724b033..31fac026f9 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -274,7 +274,7 @@ namespace Umbraco.Tests.Published // ReSharper disable UnassignedGetOnlyAutoProperty public override bool IsDraft(string culture = null) => false; public override bool IsPublished(string culture = null) => true; - public override IPublishedContent Parent { get; } + public override IPublishedContent Parent() => null; public override IEnumerable Children { get; } public override IPublishedContentType ContentType { get; } // ReSharper restore UnassignedGetOnlyAutoProperty diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 7f3c77cc72..7dddd76b8e 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -188,10 +188,7 @@ namespace Umbraco.Tests.PublishedContent public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => default; - IPublishedContent IPublishedContent.Parent - { - get { return Parent; } - } + IPublishedContent IPublishedContent.Parent() => Parent; IEnumerable IPublishedContent.Children { @@ -238,7 +235,7 @@ namespace Umbraco.Tests.PublishedContent IPublishedContent content = this; while (content != null && (property == null || property.HasValue() == false)) { - content = content.Parent; + content = content.Parent(); property = content == null ? null : content.GetProperty(alias); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index f978c8501b..0b549d51ef 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -178,10 +178,10 @@ namespace Umbraco.Tests.PublishedContent item3.SetUrl("/content-1/content-2/content-3"); item1.Children = new List { item2 }; - item2.Parent = item1; + item2.SetParent(item1); item2.Children = new List { item3 }; - item3.Parent = item2; + item3.SetParent(item2); cache.Add(item1); cache.Add(item2); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index b789eb0ef8..c2cf22f85f 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -416,13 +416,13 @@ namespace Umbraco.Tests.PublishedContent var mSubChild3 = MakeNewMedia("SubChild3", mType, user, mChild1.Id); var publishedRoot = GetNode(mRoot.Id); - Assert.AreEqual(null, publishedRoot.Parent); + Assert.AreEqual(null, publishedRoot.Parent()); var publishedChild1 = GetNode(mChild1.Id); - Assert.AreEqual(mRoot.Id, publishedChild1.Parent.Id); + Assert.AreEqual(mRoot.Id, publishedChild1.Parent().Id); var publishedSubChild1 = GetNode(mSubChild1.Id); - Assert.AreEqual(mChild1.Id, publishedSubChild1.Parent.Id); + Assert.AreEqual(mChild1.Id, publishedSubChild1.Parent().Id); } [Test] diff --git a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs index 19944a2cd4..ea3a00371a 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs @@ -64,7 +64,7 @@ namespace Umbraco.Tests.PublishedContent pc.Setup(content => content.CreateDate).Returns(DateTime.Now); pc.Setup(content => content.UpdateDate).Returns(DateTime.Now); pc.Setup(content => content.Path).Returns("-1,1"); - pc.Setup(content => content.Parent).Returns(() => null); + pc.Setup(content => content.Parent()).Returns(() => null); pc.Setup(content => content.Properties).Returns(new Collection()); pc.Setup(content => content.ContentType).Returns(new PublishedContentType(22, "anything", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing)); return pc; diff --git a/src/Umbraco.Tests/PublishedContent/RootNodeTests.cs b/src/Umbraco.Tests/PublishedContent/RootNodeTests.cs index 77eab1dbb7..0af30b4642 100644 --- a/src/Umbraco.Tests/PublishedContent/RootNodeTests.cs +++ b/src/Umbraco.Tests/PublishedContent/RootNodeTests.cs @@ -20,7 +20,7 @@ namespace Umbraco.Tests.PublishedContent content = ctx.ContentCache.GetById(1046); Assert.IsNotNull(content); Assert.AreEqual(1, content.Level); - Assert.IsNull(content.Parent); + Assert.IsNull(content.Parent()); // non-existing content is null content = ctx.ContentCache.GetById(666); diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 22d6fe8ef1..de9f82c6f7 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -99,7 +99,7 @@ namespace Umbraco.Tests.PublishedContent public override IEnumerable GetAtRoot(bool preview) { - return _content.Values.Where(x => x.Parent == null); + return _content.Values.Where(x => x.Parent() == null); } public override IPublishedContent GetSingleByXPath(bool preview, string xpath, Core.Xml.XPathVariable[] vars) @@ -209,7 +209,9 @@ namespace Umbraco.Tests.PublishedContent public int ParentId { get; set; } public IEnumerable ChildIds { get; set; } - public IPublishedContent Parent { get; set; } + private IPublishedContent _parent; + public IPublishedContent Parent() => _parent; + public void SetParent(IPublishedContent parent) => _parent = parent; public IEnumerable Children { get; set; } #endregion @@ -237,7 +239,7 @@ namespace Umbraco.Tests.PublishedContent IPublishedContent content = this; while (content != null && (property == null || property.HasValue() == false)) { - content = content.Parent; + content = content.Parent(); property = content == null ? null : content.GetProperty(alias); } diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 6dd25b966e..4334e24f6d 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -53,7 +53,9 @@ namespace Umbraco.Tests.TestHelpers.Stubs public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => throw new NotSupportedException(); public bool IsDraft(string culture = null) => false; public bool IsPublished(string culture = null) => true; - public IPublishedContent Parent { get; set; } + private IPublishedContent _parent; + public IPublishedContent Parent() => _parent; + public void SetParent(IPublishedContent parent) => _parent = parent; public IEnumerable Children { get; set; } // copied from PublishedContentBase @@ -66,7 +68,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs var firstNonNullProperty = property; while (content != null && (property == null || property.HasValue() == false)) { - content = content.Parent; + content = content.Parent(); property = content?.GetProperty(alias); if (firstNonNullProperty == null && property != null) firstNonNullProperty = property; } diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index be7bbc37a8..209808e282 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -37,7 +37,7 @@ namespace Umbraco.Web.Macros PopulatePageData(frequest.PublishedContent.Id, frequest.PublishedContent.Name(), frequest.PublishedContent.ContentType.Id, frequest.PublishedContent.ContentType.Alias, frequest.PublishedContent.WriterName, frequest.PublishedContent.CreatorName, frequest.PublishedContent.CreateDate, frequest.PublishedContent.UpdateDate, - frequest.PublishedContent.Path, frequest.PublishedContent.Parent?.Id ?? -1); + frequest.PublishedContent.Path, frequest.PublishedContent.Parent()?.Id ?? -1); if (frequest.HasTemplate) { @@ -59,7 +59,7 @@ namespace Umbraco.Web.Macros PopulatePageData(doc.Id, doc.Name(), doc.ContentType.Id, doc.ContentType.Alias, doc.WriterName, doc.CreatorName, doc.CreateDate, doc.UpdateDate, - doc.Path, doc.Parent?.Id ?? -1); + doc.Path, doc.Parent()?.Id ?? -1); if (doc.TemplateId.HasValue) { @@ -182,8 +182,8 @@ namespace Umbraco.Web.Macros { private readonly IContent _inner; private readonly IPublishedProperty[] _properties; - private IReadOnlyDictionary _cultureInfos; private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IPublishedContent _parent; private static readonly IReadOnlyDictionary NoCultureInfos = new Dictionary(); @@ -215,7 +215,7 @@ namespace Umbraco.Web.Macros .Cast() .ToArray(); - Parent = new PagePublishedContent(_inner.ParentId); + _parent = new PagePublishedContent(_inner.ParentId); } public IPublishedContentType ContentType { get; } @@ -288,7 +288,7 @@ namespace Umbraco.Web.Macros throw new NotImplementedException(); } - public IPublishedContent Parent { get; } + public IPublishedContent Parent() => _parent; public IEnumerable Children => throw new NotImplementedException(); diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs index 7b467b6d15..6e0246f416 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -160,7 +160,7 @@ namespace Umbraco.Web.Models.PublishedContent IPublishedProperty property; // if we are here, content's property has no value do { - content = content.Parent; + content = content.Parent(); var propertyType = content?.ContentType.GetPropertyType(alias); diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 22a76fb907..d2b3a3749b 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -151,7 +151,7 @@ namespace Umbraco.Web.Models #region Tree /// - public abstract IPublishedContent Parent { get; } + public abstract IPublishedContent Parent(); /// public abstract IEnumerable Children { get; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index 705ce17595..c364f5788a 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -157,7 +157,7 @@ namespace Umbraco.Web.PublishedCache.NuCache pathParts.Add(urlSegment); // move to parent node - n = n.Parent; + n = n.Parent(); if (n != null) urlSegment = n.UrlSegment(culture); @@ -206,7 +206,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // "/foo" fails (looking for "/*/foo") we try also "/foo". // this does not make much sense anyway esp. if both "/foo/" and "/bar/foo" exist, but // that's the way it works pre-4.10 and we try to be backward compat for the time being - if (content.Parent == null) + if (content.Parent() == null) { var rootNode = GetByRoute(preview, "/", true); if (rootNode == null) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 86ed272b13..a854a8a2ad 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -325,21 +325,18 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Tree /// - public override IPublishedContent Parent + public override IPublishedContent Parent() { - get + // have to use the "current" cache because a PublishedContent can be shared + // amongst many snapshots and other content depend on the snapshots + switch (_contentNode.ContentType.ItemType) { - // have to use the "current" cache because a PublishedContent can be shared - // amongst many snapshots and other content depend on the snapshots - switch (_contentNode.ContentType.ItemType) - { - case PublishedItemType.Content: - return GetContentById(IsPreviewing, _contentNode.ParentContentId); - case PublishedItemType.Media: - return GetMediaById(IsPreviewing, _contentNode.ParentContentId); - default: - throw new Exception($"Panic: unsupported item type \"{_contentNode.ContentType.ItemType}\"."); - } + case PublishedItemType.Content: + return GetContentById(IsPreviewing, _contentNode.ParentContentId); + case PublishedItemType.Media: + return GetMediaById(IsPreviewing, _contentNode.ParentContentId); + default: + throw new Exception($"Panic: unsupported item type \"{_contentNode.ContentType.ItemType}\"."); } } diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index 46b12b5ca3..09d01229f3 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -82,7 +82,7 @@ namespace Umbraco.Web.PublishedCache public override bool IsPublished(string culture = null) => true; - public override IPublishedContent Parent => null; + public override IPublishedContent Parent() => null; public override IEnumerable Children => Enumerable.Empty(); diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index a243d6e77a..708c9a8e09 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -548,7 +548,7 @@ namespace Umbraco.Web /// This method is here for consistency purposes but does not make much sense. public static IPublishedContent Ancestor(this IPublishedContent content) { - return content.Parent; + return content.Parent(); } /// @@ -680,7 +680,7 @@ namespace Umbraco.Web { if (content == null) throw new ArgumentNullException(nameof(content)); if (orSelf) yield return content; - while ((content = content.Parent) != null) + while ((content = content.Parent()) != null) yield return content; } @@ -884,7 +884,7 @@ namespace Umbraco.Web where T : class, IPublishedContent { if (content == null) throw new ArgumentNullException(nameof(content)); - return content.Parent as T; + return content.Parent() as T; } #endregion @@ -1121,8 +1121,8 @@ namespace Umbraco.Web /// The siblings of the content including the node itself. public static IEnumerable SiblingsAndSelf(this IPublishedContent content, string culture = null) { - return content.Parent != null - ? content.Parent.Children(culture) + return content.Parent() != null + ? content.Parent().Children(culture) : PublishedSnapshot.Content.GetAtRoot().WhereIsInvariantOrHasCulture(culture); } @@ -1135,8 +1135,8 @@ namespace Umbraco.Web /// The siblings of the content including the node itself, of the given content type. public static IEnumerable SiblingsAndSelfOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) { - return content.Parent != null - ? content.Parent.ChildrenOfType(contentTypeAlias, culture) + return content.Parent() != null + ? content.Parent().ChildrenOfType(contentTypeAlias, culture) : PublishedSnapshot.Content.GetAtRoot().OfTypes(contentTypeAlias).WhereIsInvariantOrHasCulture(culture); } @@ -1150,8 +1150,8 @@ namespace Umbraco.Web public static IEnumerable SiblingsAndSelf(this IPublishedContent content, string culture = null) where T : class, IPublishedContent { - return content.Parent != null - ? content.Parent.Children(culture) + return content.Parent() != null + ? content.Parent().Children(culture) : PublishedSnapshot.Content.GetAtRoot().OfType().WhereIsInvariantOrHasCulture(culture); } diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index 411fabbf35..f1c755191e 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -65,7 +65,7 @@ namespace Umbraco.Web.Routing while (domainUris == null && n != null) // n is null at root { // move to parent node - n = n.Parent; + n = n.Parent(); domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, n.Id, current, excludeDefault: false); } diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 06f275a685..842f855a0a 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -87,7 +87,7 @@ namespace Umbraco.Web.Routing var domainUris = DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, n.Id, current, false); while (domainUris == null && n != null) // n is null at root { - n = n.Parent; // move to parent node + n = n.Parent(); // move to parent node domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, n.Id, current, excludeDefault: true); } diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs index 0a4f033bd2..4b66d56830 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs @@ -188,7 +188,7 @@ namespace Umbraco.Web.Routing while (o != null) { l.Add(o.Name()); - o = o.Parent; + o = o.Parent(); } l.Reverse(); var s = "/" + string.Join("/", l) + " (id=" + pcr.PublishedContent.Id + ")"; From 2270188668a729da770f4e8a5eedb44747b5f1c5 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 22 Apr 2019 15:39:24 +0200 Subject: [PATCH 14/75] Refactor IPublishedContent.Children() --- .../PublishedContent/IPublishedContent.cs | 9 +++++-- .../PublishedContentWrapped.cs | 2 +- .../PublishedMediaCacheTests.cs | 12 +++++----- .../DictionaryPublishedContent.cs | 2 +- .../XmlPublishedContent.cs | 9 +++---- .../Published/NestedContentTests.cs | 2 +- .../PublishedContentDataTableTests.cs | 7 ++---- .../PublishedContentLanguageVariantTests.cs | 24 +++++++++---------- .../PublishedContent/PublishedContentTests.cs | 20 ++++++++-------- .../SolidPublishedSnapshot.cs | 4 +++- .../TestHelpers/Stubs/TestPublishedContent.cs | 4 +++- .../ListChildPagesFromChangeableSource.cshtml | 2 +- .../ListChildPagesFromCurrentPage.cshtml | 2 +- .../ListChildPagesOrderedByDate.cshtml | 2 +- .../ListChildPagesOrderedByName.cshtml | 2 +- .../ListChildPagesOrderedByProperty.cshtml | 2 +- .../ListDescendantsFromCurrentPage.cshtml | 6 ++--- .../ListImagesFromMediaFolder.cshtml | 2 +- .../Templates/Navigation.cshtml | 2 +- .../Templates/SiteMap.cshtml | 2 +- .../PublishedContentHashtableConverter.cs | 2 +- .../Models/PublishedContentBase.cs | 2 +- .../PublishedCache/NuCache/ContentCache.cs | 4 ++-- .../NuCache/PublishedContent.cs | 19 ++++++++------- .../PublishedCache/PublishedMember.cs | 2 +- src/Umbraco.Web/PublishedContentExtensions.cs | 5 ++-- 26 files changed, 78 insertions(+), 73 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 7f15e367c8..a6482b48ab 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -160,8 +160,13 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Gets the children of the content item. /// - /// Children are sorted by their sortOrder. - IEnumerable Children { get; } + /// The specific culture to get the url children for. If null is used the current culture is used (Default is null). + /// + /// Gets children that are available for the specified culture. + /// Children are sorted by their sortOrder. + /// + // FIXME: can culture be '*'? + IEnumerable Children(string culture = null); #endregion } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index d0db4d6e09..5bac22ad24 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -116,7 +116,7 @@ namespace Umbraco.Core.Models.PublishedContent public virtual IPublishedContent Parent() => _content.Parent(); /// - public virtual IEnumerable Children => _content.Children; + public virtual IEnumerable Children(string culture = null) => _content.Children(culture); #endregion diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index ce89310f35..bcd34ef2e3 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -172,9 +172,9 @@ namespace Umbraco.Tests.Cache.PublishedCache child1, child2 }); - Assert.AreEqual(2, dicDoc.Children.Count()); - Assert.AreEqual(222333, dicDoc.Children.ElementAt(0).Id); - Assert.AreEqual(444555, dicDoc.Children.ElementAt(1).Id); + Assert.AreEqual(2, dicDoc.Children().Count()); + Assert.AreEqual(222333, dicDoc.Children().ElementAt(0).Id); + Assert.AreEqual(444555, dicDoc.Children().ElementAt(1).Id); } [Test] @@ -229,9 +229,9 @@ namespace Umbraco.Tests.Cache.PublishedCache DoAssert(doc, 2000, key, null, 2, "image1", "Image", 23, "Shannon", "Shannon", 33, 33, "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1); Assert.AreEqual(null, doc.Parent()); - Assert.AreEqual(2, doc.Children.Count()); - Assert.AreEqual(2001, doc.Children.ElementAt(0).Id); - Assert.AreEqual(2002, doc.Children.ElementAt(1).Id); + Assert.AreEqual(2, doc.Children().Count()); + Assert.AreEqual(2001, doc.Children().ElementAt(0).Id); + Assert.AreEqual(2002, doc.Children().ElementAt(1).Id); } private XmlDocument GetMediaXml() diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs index 3175d0aca0..cd362cadc0 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs @@ -183,7 +183,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache public override IEnumerable Properties => _properties; - public override IEnumerable Children => _getChildren.Value; + public override IEnumerable Children(string culture = null) => _getChildren.Value; public override IPublishedProperty GetProperty(string alias) { diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index 8981b6c691..bc9ab8010d 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -75,13 +75,10 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private bool _isDraft; - public override IEnumerable Children + public override IEnumerable Children(string culture = null) { - get - { - EnsureNodeInitialized(andChildren: true); - return _children; - } + EnsureNodeInitialized(andChildren: true); + return _children; } public override IPublishedProperty GetProperty(string alias) diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 31fac026f9..8b954d6ab0 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -275,7 +275,7 @@ namespace Umbraco.Tests.Published public override bool IsDraft(string culture = null) => false; public override bool IsPublished(string culture = null) => true; public override IPublishedContent Parent() => null; - public override IEnumerable Children { get; } + public override IEnumerable Children(string culture = null) => Enumerable.Empty(); public override IPublishedContentType ContentType { get; } // ReSharper restore UnassignedGetOnlyAutoProperty diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 7dddd76b8e..077dfd3c94 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -97,7 +97,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetContent(true, 1); //change a doc type alias - var c = (TestPublishedContent)doc.Children.ElementAt(0); + var c = (TestPublishedContent)doc.Children().ElementAt(0); c.ContentType = new PublishedContentType(22, "DontMatch", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var dt = doc.ChildrenAsTable(Current.Services, "Child"); @@ -190,10 +190,7 @@ namespace Umbraco.Tests.PublishedContent IPublishedContent IPublishedContent.Parent() => Parent; - IEnumerable IPublishedContent.Children - { - get { return Children; } - } + IEnumerable IPublishedContent.Children(string culture = null) => Children; public IPublishedContent Parent { get; set; } public int Id { get; set; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index 0b549d51ef..4fe028b1db 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -177,10 +177,10 @@ namespace Umbraco.Tests.PublishedContent item3.SetUrlSegment("content-3"); item3.SetUrl("/content-1/content-2/content-3"); - item1.Children = new List { item2 }; + item1.SetChildren(new List { item2 }); item2.SetParent(item1); - item2.Children = new List { item3 }; + item2.SetChildren(new List { item3 }); item3.SetParent(item2); cache.Add(item1); @@ -247,7 +247,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Do_Not_Get_Content_Recursively_Unless_Requested() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); var value = content.Value("welcomeText2"); Assert.IsNull(value); } @@ -255,7 +255,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_Recursively() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); var value = content.Value("welcomeText2", fallback: Fallback.ToAncestors); Assert.AreEqual("Welcome", value); } @@ -263,7 +263,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Do_Not_Get_Content_Recursively_Unless_Requested2() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First().Children().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First().Children().First(); Assert.IsNull(content.GetProperty("welcomeText2")); var value = content.Value("welcomeText2"); Assert.IsNull(value); @@ -272,7 +272,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_Recursively2() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First().Children().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First().Children().First(); Assert.IsNull(content.GetProperty("welcomeText2")); var value = content.Value("welcomeText2", fallback: Fallback.ToAncestors); Assert.AreEqual("Welcome", value); @@ -281,7 +281,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_Recursively3() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First().Children().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First().Children().First(); Assert.IsNull(content.GetProperty("noprop")); var value = content.Value("noprop", fallback: Fallback.ToAncestors); // property has no value but we still get the value (ie, the converter would do something) @@ -292,7 +292,7 @@ namespace Umbraco.Tests.PublishedContent public void Can_Get_Content_With_Recursive_Priority() { Current.VariationContextAccessor.VariationContext = new VariationContext("nl"); - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); var value = content.Value("welcomeText", "nl", fallback: Fallback.To(Fallback.Ancestors, Fallback.Language)); @@ -303,7 +303,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_With_Fallback_Language_Priority() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); var value = content.Value("welcomeText", "nl", fallback: Fallback.ToLanguage); // No Dutch value is directly assigned. Check has fallen back to English value from language variant. @@ -313,14 +313,14 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Throws_For_Non_Supported_Fallback() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); Assert.Throws(() => content.Value("welcomeText", "nl", fallback: Fallback.To(999))); } [Test] public void Can_Fallback_To_Default_Value() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); // no Dutch value is assigned, so getting null var value = content.Value("welcomeText", "nl"); @@ -338,7 +338,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Have_Custom_Default_Value() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); // HACK: the value, pretend the converter would return something var prop = content.GetProperty("welcomeText") as SolidPublishedPropertyWithLanguageVariants; diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 8be13345fb..4472842251 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -169,7 +169,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetNode(1173); - var items = doc.Children.Where(x => x.IsVisible()).ToIndexedArray(); + var items = doc.Children().Where(x => x.IsVisible()).ToIndexedArray(); foreach (var item in items) { @@ -190,7 +190,7 @@ namespace Umbraco.Tests.PublishedContent var doc = GetNode(1173); var items = doc - .Children + .Children() .Where(x => x.IsVisible()) .ToIndexedArray(); @@ -245,7 +245,7 @@ namespace Umbraco.Tests.PublishedContent var doc = GetNode(1173); var ct = doc.ContentType; - var items = doc.Children + var items = doc.Children() .Select(x => x.CreateModel()) // linq, returns IEnumerable // only way around this is to make sure every IEnumerable extension @@ -277,7 +277,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetNode(1173); - var items = doc.Children.Take(4).ToIndexedArray(); + var items = doc.Children().Take(4).ToIndexedArray(); foreach (var item in items) { @@ -297,7 +297,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetNode(1173); - foreach (var d in doc.Children.Skip(1).ToIndexedArray()) + foreach (var d in doc.Children().Skip(1).ToIndexedArray()) { if (d.Content.Id != 1176) { @@ -315,7 +315,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetNode(1173); - var items = doc.Children + var items = doc.Children() .Concat(new[] { GetNode(1175), GetNode(4444) }) .ToIndexedArray(); @@ -400,7 +400,7 @@ namespace Umbraco.Tests.PublishedContent var doc = GetNode(1046); - var found1 = doc.Children.GroupBy(x => x.ContentType.Alias).ToArray(); + var found1 = doc.Children().GroupBy(x => x.ContentType.Alias).ToArray(); Assert.AreEqual(2, found1.Length); Assert.AreEqual(2, found1.Single(x => x.Key.ToString() == "Home").Count()); @@ -421,8 +421,8 @@ namespace Umbraco.Tests.PublishedContent var doc = GetNode(1046); - var found1 = doc.Children.Where(x => x.ContentType.Alias == "CustomDocument"); - var found2 = doc.Children.Where(x => x.ContentType.Alias == "Home"); + var found1 = doc.Children().Where(x => x.ContentType.Alias == "CustomDocument"); + var found2 = doc.Children().Where(x => x.ContentType.Alias == "Home"); Assert.AreEqual(1, found1.Count()); Assert.AreEqual(2, found2.Count()); @@ -433,7 +433,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetNode(1173); - var ordered = doc.Children.OrderBy(x => x.UpdateDate); + var ordered = doc.Children().OrderBy(x => x.UpdateDate); var correctOrder = new[] { 1178, 1177, 1174, 1176 }; for (var i = 0; i < correctOrder.Length; i++) diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index de9f82c6f7..d61dead9c2 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -212,7 +212,9 @@ namespace Umbraco.Tests.PublishedContent private IPublishedContent _parent; public IPublishedContent Parent() => _parent; public void SetParent(IPublishedContent parent) => _parent = parent; - public IEnumerable Children { get; set; } + private IEnumerable _children; + public IEnumerable Children(string culture = null) => _children; + public void SetChildren(IEnumerable children) => _children = children; #endregion diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 4334e24f6d..8c631bb067 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -56,7 +56,9 @@ namespace Umbraco.Tests.TestHelpers.Stubs private IPublishedContent _parent; public IPublishedContent Parent() => _parent; public void SetParent(IPublishedContent parent) => _parent = parent; - public IEnumerable Children { get; set; } + private IEnumerable _children; + public IEnumerable Children(string culture = null) => _children; + public void SetChildren(IEnumerable children) => _children = children; // copied from PublishedContentBase public IPublishedProperty GetProperty(string alias, bool recurse) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml index 46c8de695c..495141f821 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml @@ -19,7 +19,7 @@ { @* Get the starting page *@ var startNode = Umbraco.Content(startNodeId); - var selection = startNode.Children.Where(x => x.IsVisible()).ToArray(); + var selection = startNode.Children().Where(x => x.IsVisible()).ToArray(); if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromCurrentPage.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromCurrentPage.cshtml index e6606d6204..6ba7471899 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromCurrentPage.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromCurrentPage.cshtml @@ -9,7 +9,7 @@ - It then generates links so the visitor can go to each page *@ -@{ var selection = Model.Content.Children.Where(x => x.IsVisible()).ToArray(); } +@{ var selection = Model.Content.Children().Where(x => x.IsVisible()).ToArray(); } @if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByDate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByDate.cshtml index 2c2cc4422b..4ace3da7d1 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByDate.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByDate.cshtml @@ -10,7 +10,7 @@ - It then generates links so the visitor can go to each page *@ -@{ var selection = Model.Content.Children.Where(x => x.IsVisible()).OrderByDescending(x => x.CreateDate).ToArray(); } +@{ var selection = Model.Content.Children().Where(x => x.IsVisible()).OrderByDescending(x => x.CreateDate).ToArray(); } @if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByName.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByName.cshtml index d0398e7272..ecfd8ebe9d 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByName.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByName.cshtml @@ -10,7 +10,7 @@ - It then generates links so the visitor can go to each page *@ -@{ var selection = Model.Content.Children.Where(x => x.IsVisible()).OrderBy(x => x.Name).ToArray(); } +@{ var selection = Model.Content.Children().Where(x => x.IsVisible()).OrderBy(x => x.Name).ToArray(); } @if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByProperty.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByProperty.cshtml index 1bffae04c4..667a384dfe 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByProperty.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByProperty.cshtml @@ -17,7 +17,7 @@ @if (propertyAlias != null) { - var selection = Model.Content.Children.Where(x => x.IsVisible()).OrderBy(x => x.Value(propertyAlias.ToString())).ToArray(); + var selection = Model.Content.Children().Where(x => x.IsVisible()).OrderBy(x => x.Value(propertyAlias.ToString())).ToArray(); if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListDescendantsFromCurrentPage.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListDescendantsFromCurrentPage.cshtml index 7ae917b41d..196db52d92 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListDescendantsFromCurrentPage.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListDescendantsFromCurrentPage.cshtml @@ -7,7 +7,7 @@ the page currently being viewed by the website visitor, displayed as nested unordered HTML lists. *@ -@{ var selection = Model.Content.Children.Where(x => x.IsVisible()).ToArray(); } +@{ var selection = Model.Content.Children().Where(x => x.IsVisible()).ToArray(); } @* Ensure that the Current Page has children *@ @if (selection.Length > 0) @@ -25,7 +25,7 @@ @* if this child page has any children, where the property umbracoNaviHide is not True *@ @{ - var children = item.Children.Where(x => x.IsVisible()).ToArray(); + var children = item.Children().Where(x => x.IsVisible()).ToArray(); if (children.Length > 0) { @* Call our helper to display the children *@ @@ -54,7 +54,7 @@ @* if the page has any children, where the property umbracoNaviHide is not True *@ @{ - var children = item.Children.Where(x => x.IsVisible()).ToArray(); + var children = item.Children().Where(x => x.IsVisible()).ToArray(); if (children.Length > 0) { @* Recurse and call our helper to display the children *@ diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml index 51fdbadb00..b6067ff93a 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml @@ -18,7 +18,7 @@ { @* Get the media item associated with the id passed in *@ var media = Umbraco.Media(mediaId); - var selection = media.Children.ToArray(); + var selection = media.Children().ToArray(); if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml index 1c01eeb855..733c6be801 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml @@ -7,7 +7,7 @@ It also highlights the current active page/section in the navigation with the CSS class "current". *@ -@{ var selection = Model.Content.Root().Children.Where(x => x.IsVisible()).ToArray(); } +@{ var selection = Model.Content.Root().Children().Where(x => x.IsVisible()).ToArray(); } @if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml index 567ed5d07d..a14d6fdc09 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml @@ -23,7 +23,7 @@ const int maxLevelForSitemap = 4; @* Select visible children *@ - var selection = node.Children.Where(x => x.IsVisible() && x.Level <= maxLevelForSitemap).ToArray(); + var selection = node.Children().Where(x => x.IsVisible() && x.Level <= maxLevelForSitemap).ToArray(); @* If any items are returned, render a list *@ if (selection.Length > 0) diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index 209808e282..ebeafd2c06 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -290,7 +290,7 @@ namespace Umbraco.Web.Macros public IPublishedContent Parent() => _parent; - public IEnumerable Children => throw new NotImplementedException(); + public IEnumerable Children(string culture = null) => throw new NotImplementedException(); public IEnumerable Properties => _properties; diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index d2b3a3749b..f29dc48d2b 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -154,7 +154,7 @@ namespace Umbraco.Web.Models public abstract IPublishedContent Parent(); /// - public abstract IEnumerable Children { get; } + public abstract IEnumerable Children(string culture = null); #endregion diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index c364f5788a..e422c04f72 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -107,7 +107,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // hideTopLevelNode = support legacy stuff, look for /*/path/to/node // else normal, look for /path/to/node content = hideTopLevelNode.Value - ? GetAtRoot(preview).SelectMany(x => x.Children).FirstOrDefault(x => x.UrlSegment(culture) == parts[0]) + ? GetAtRoot(preview).SelectMany(x => x.Children(culture)).FirstOrDefault(x => x.UrlSegment(culture) == parts[0]) : GetAtRoot(preview).FirstOrDefault(x => x.UrlSegment(culture) == parts[0]); content = FollowRoute(content, parts, 1, culture); } @@ -187,7 +187,7 @@ namespace Umbraco.Web.PublishedCache.NuCache while (content != null && i < parts.Count) { var part = parts[i++]; - content = content.Children.FirstOrDefault(x => + content = content.Children(culture).FirstOrDefault(x => { var urlSegment = x.UrlSegment(culture); return urlSegment == part; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index a854a8a2ad..c1215c881b 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -341,17 +341,18 @@ namespace Umbraco.Web.PublishedCache.NuCache } /// - public override IEnumerable Children + public override IEnumerable Children(string culture = null) { - get - { - var cache = GetAppropriateCache(); - if (cache == null || PublishedSnapshotService.CachePublishedContentChildren == false) - return GetChildren(); + // FIXME THIS CANNOT WORK + // we cannot cache children this way, they should be a linked list! + throw new NotImplementedException(); - // note: ToArray is important here, we want to cache the result, not the function! - return (IEnumerable)cache.Get(ChildrenCacheKey, () => GetChildren().ToArray()); - } + var cache = GetAppropriateCache(); + if (cache == null || PublishedSnapshotService.CachePublishedContentChildren == false) + return GetChildren(); + + // note: ToArray is important here, we want to cache the result, not the function! + return (IEnumerable)cache.Get(ChildrenCacheKey, () => GetChildren().ToArray()); } private string _childrenCacheKey; diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index 09d01229f3..f0c3ac4f5b 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -84,7 +84,7 @@ namespace Umbraco.Web.PublishedCache public override IPublishedContent Parent() => null; - public override IEnumerable Children => Enumerable.Empty(); + public override IEnumerable Children(string culture = null) => Enumerable.Empty(); public override IEnumerable Properties => _properties; diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 708c9a8e09..15d4432a90 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -891,6 +891,7 @@ namespace Umbraco.Web #region Axes: children + // FIXME: kill that one /// /// Gets the children of the content. /// @@ -905,7 +906,7 @@ namespace Umbraco.Web { if (content == null) throw new ArgumentNullException(nameof(content)); - return content.Children.WhereIsInvariantOrHasCulture(culture); + return content.Children(culture); //.WhereIsInvariantOrHasCulture(culture); } /// @@ -1029,7 +1030,7 @@ namespace Umbraco.Web //create all row data var tableData = Core.DataTableExtensions.CreateTableData(); //loop through each child and create row data for it - foreach (var n in content.Children.OrderBy(x => x.SortOrder)) + foreach (var n in content.Children().OrderBy(x => x.SortOrder)) { if (contentTypeAliasFilter.IsNullOrWhiteSpace() == false) { From 05c85bbce56292d42f94919870a8ffe7a32ef2f6 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 22 Apr 2019 17:51:07 +0200 Subject: [PATCH 15/75] NuCache: children as linked lists --- .../PublishedCache/NuCache/ContentNode.cs | 80 ++---- .../PublishedCache/NuCache/ContentStore.cs | 237 +++++++++++++----- .../NuCache/PublishedContent.cs | 89 ++----- .../PublishedCache/NuCache/Snap/LinkedNode.cs | 2 +- src/Umbraco.Web/PublishedContentExtensions.cs | 6 +- 5 files changed, 227 insertions(+), 187 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs index f7eb2ca19e..58e60cd8ad 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.PublishedCache.NuCache.DataSource; @@ -9,6 +8,10 @@ namespace Umbraco.Web.PublishedCache.NuCache // internal, never exposed, to be accessed from ContentStore (only!) internal class ContentNode { + // special ctor for root pseudo node + public ContentNode() + { } + // special ctor with no content data - for members public ContentNode(int id, Guid uid, IPublishedContentType contentType, int level, string path, int sortOrder, @@ -22,10 +25,10 @@ namespace Umbraco.Web.PublishedCache.NuCache Path = path; SortOrder = sortOrder; ParentContentId = parentContentId; + FirstChildContentId = -1; + NextSiblingContentId = -1; CreateDate = createDate; CreatorId = creatorId; - - ChildContentIds = new List(); } public ContentNode(int id, Guid uid, IPublishedContentType contentType, @@ -53,10 +56,10 @@ namespace Umbraco.Web.PublishedCache.NuCache Path = path; SortOrder = sortOrder; ParentContentId = parentContentId; + FirstChildContentId = -1; + NextSiblingContentId = -1; CreateDate = createDate; CreatorId = creatorId; - - ChildContentIds = new List(); } // two-phase ctor, phase 2 @@ -80,56 +83,29 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - // clone parent - private ContentNode(ContentNode origin, IUmbracoContextAccessor umbracoContextAccessor) + // clone + public ContentNode(ContentNode origin, IPublishedContentType contentType = null) { - // everything is the same, except for the child items - // list which is a clone of the original list - Id = origin.Id; Uid = origin.Uid; - ContentType = origin.ContentType; + ContentType = contentType ?? origin.ContentType; Level = origin.Level; Path = origin.Path; SortOrder = origin.SortOrder; ParentContentId = origin.ParentContentId; + FirstChildContentId = origin.FirstChildContentId; + NextSiblingContentId = origin.NextSiblingContentId; CreateDate = origin.CreateDate; CreatorId = origin.CreatorId; var originDraft = origin.DraftContent; var originPublished = origin.PublishedContent; + DraftContent = originDraft == null ? null : new PublishedContent(this, originDraft); + PublishedContent = originPublished == null ? null : new PublishedContent(this, originPublished); - DraftContent = originDraft == null ? null : new PublishedContent(this, originDraft, umbracoContextAccessor); - PublishedContent = originPublished == null ? null : new PublishedContent(this, originPublished, umbracoContextAccessor); DraftModel = DraftContent?.CreateModel(); PublishedModel = PublishedContent?.CreateModel(); - - ChildContentIds = new List(origin.ChildContentIds); // needs to be *another* list - } - - // clone with new content type - public ContentNode(ContentNode origin, IPublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IUmbracoContextAccessor umbracoContextAccessor) - { - Id = origin.Id; - Uid = origin.Uid; - ContentType = contentType; // change! - Level = origin.Level; - Path = origin.Path; - SortOrder = origin.SortOrder; - ParentContentId = origin.ParentContentId; - CreateDate = origin.CreateDate; - CreatorId = origin.CreatorId; - - var originDraft = origin.DraftContent; - var originPublished = origin.PublishedContent; - - DraftContent = originDraft == null ? null : new PublishedContent(this, originDraft.ContentData, publishedSnapshotAccessor, variationContextAccessor, umbracoContextAccessor); - DraftModel = DraftContent?.CreateModel(); - PublishedContent = originPublished == null ? null : new PublishedContent(this, originPublished.ContentData, publishedSnapshotAccessor, variationContextAccessor, umbracoContextAccessor); - PublishedModel = PublishedContent?.CreateModel(); - - ChildContentIds = origin.ChildContentIds; // can be the *same* list } // everything that is common to both draft and published versions @@ -141,7 +117,8 @@ namespace Umbraco.Web.PublishedCache.NuCache public readonly string Path; public readonly int SortOrder; public readonly int ParentContentId; - public List ChildContentIds; + public int FirstChildContentId; + public int NextSiblingContentId; public readonly DateTime CreateDate; public readonly int CreatorId; @@ -155,23 +132,14 @@ namespace Umbraco.Web.PublishedCache.NuCache public IPublishedContent DraftModel; public IPublishedContent PublishedModel; - public ContentNode CloneParent( - IPublishedSnapshotAccessor publishedSnapshotAccessor, - IUmbracoContextAccessor umbracoContextAccessor) - { - return new ContentNode(this, umbracoContextAccessor); - } - public ContentNodeKit ToKit() - { - return new ContentNodeKit - { - Node = this, - ContentTypeId = ContentType.Id, + => new ContentNodeKit + { + Node = this, + ContentTypeId = ContentType.Id, - DraftData = DraftContent?.ContentData, - PublishedData = PublishedContent?.ContentData - }; - } + DraftData = DraftContent?.ContentData, + PublishedData = PublishedContent?.ContentData + }; } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 5693bd3204..5b54bb7345 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -23,7 +23,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IVariationContextAccessor _variationContextAccessor; private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly ConcurrentDictionary> _contentNodes; - private readonly ConcurrentDictionary> _contentRootNodes; + private LinkedNode _root; private readonly ConcurrentDictionary> _contentTypesById; private readonly ConcurrentDictionary> _contentTypesByAlias; private readonly ConcurrentDictionary _xmap; @@ -60,7 +60,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _localDb = localDb; _contentNodes = new ConcurrentDictionary>(); - _contentRootNodes = new ConcurrentDictionary>(); + _root = new LinkedNode(new ContentNode(), 0); _contentTypesById = new ConcurrentDictionary>(); _contentTypesByAlias = new ConcurrentDictionary>(StringComparer.InvariantCultureIgnoreCase); _xmap = new ConcurrentDictionary(); @@ -176,7 +176,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } Rollback(_contentNodes); - Rollback(_contentRootNodes); + RollbackRoot(); Rollback(_contentTypesById); Rollback(_contentTypesByAlias); } @@ -202,6 +202,14 @@ namespace Umbraco.Web.PublishedCache.NuCache if (lockInfo.Taken) Monitor.Exit(_rlocko); } + private void RollbackRoot() + { + if (_root.Gen <= _liveGen) return; + + if (_root.Next != null) + _root = _root.Next; + } + private void Rollback(ConcurrentDictionary> dictionary) where TValue : class { @@ -289,7 +297,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (node == null) continue; var contentTypeId = node.ContentType.Id; if (index.TryGetValue(contentTypeId, out var contentType) == false) continue; - SetValueLocked(_contentNodes, node.Id, new ContentNode(node, contentType, _publishedSnapshotAccessor, _variationContextAccessor, _umbracoContextAccessor)); + SetValueLocked(_contentNodes, node.Id, new ContentNode(node, contentType)); } } finally @@ -347,7 +355,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } // perform update of content with refreshed content type - from the kits - // skip missing type, skip missing parents & unbuildable kits - what else could we do? + // skip missing type, skip missing parents & un-buildable kits - what else could we do? // kits are ordered by level, so ParentExits is ok here var visited = new List(); foreach (var kit in kits.Where(x => @@ -358,7 +366,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // replacing the node: must preserve the parents var node = GetHead(_contentNodes, kit.Node.Id)?.Value; if (node != null) - kit.Node.ChildContentIds = node.ChildContentIds; + kit.Node.FirstChildContentId = node.FirstChildContentId; SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); @@ -412,10 +420,10 @@ namespace Umbraco.Web.PublishedCache.NuCache foreach (var id in contentTypeNodes[contentType.Id]) { - _contentNodes.TryGetValue(id, out LinkedNode link); + _contentNodes.TryGetValue(id, out var link); if (link?.Value == null) continue; - var node = new ContentNode(link.Value, contentType, _publishedSnapshotAccessor, _variationContextAccessor, _umbracoContextAccessor); + var node = new ContentNode(link.Value, contentType); SetValueLocked(_contentNodes, id, node); if (_localDb != null) RegisterChange(id, node.ToKit()); } @@ -455,7 +463,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private static LinkedNode GetHead(ConcurrentDictionary> dict, TKey key) where TValue : class { - dict.TryGetValue(key, out LinkedNode link); // else null + dict.TryGetValue(key, out var link); // else null return link; } @@ -464,7 +472,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // ReSharper disable LocalizableElement if (kit.IsEmpty) throw new ArgumentException("Kit is empty.", nameof(kit)); - if (kit.Node.ChildContentIds.Count > 0) + if (kit.Node.FirstChildContentId > 0) throw new ArgumentException("Kit content cannot have children.", nameof(kit)); // ReSharper restore LocalizableElement @@ -476,7 +484,7 @@ namespace Umbraco.Web.PublishedCache.NuCache Lock(lockInfo); // get existing - _contentNodes.TryGetValue(kit.Node.Id, out LinkedNode link); + _contentNodes.TryGetValue(kit.Node.Id, out var link); var existing = link?.Value; // else ignore, what else could we do? @@ -488,7 +496,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // manage children if (existing != null) - kit.Node.ChildContentIds = existing.ChildContentIds; + kit.Node.FirstChildContentId = existing.FirstChildContentId; // set SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); @@ -498,13 +506,13 @@ namespace Umbraco.Web.PublishedCache.NuCache if (existing == null) { // new, add to parent - AddToParentLocked(kit.Node); + AddNodeLocked(kit.Node); } else if (moving) { // moved, remove existing from its parent, add content to its parent - RemoveFromParentLocked(existing); - AddToParentLocked(kit.Node); + RemoveNodeLocked(existing); + AddNodeLocked(kit.Node); } _xmap[kit.Node.Uid] = kit.Node.Id; @@ -515,6 +523,14 @@ namespace Umbraco.Web.PublishedCache.NuCache } } + private void ClearRootLocked() + { + if (_root.Gen < _liveGen) + _root = new LinkedNode(new ContentNode(), _liveGen, _root); + else + _root.Value.FirstChildContentId = -1; + } + // IMPORTANT kits must be sorted out by LEVEL public void SetAll(IEnumerable kits) { @@ -524,18 +540,18 @@ namespace Umbraco.Web.PublishedCache.NuCache Lock(lockInfo); ClearLocked(_contentNodes); - ClearLocked(_contentRootNodes); + ClearRootLocked(); // do NOT clear types else they are gone! //ClearLocked(_contentTypesById); //ClearLocked(_contentTypesByAlias); - // skip missing parents & unbuildable kits - what else could we do? + // skip missing parents & un-buildable kits - what else could we do? foreach (var kit in kits.Where(x => ParentExistsLocked(x) && BuildKit(x))) { SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); if (_localDb != null) RegisterChange(kit.Node.Id, kit); - AddToParentLocked(kit.Node); + AddNodeLocked(kit.Node); _xmap[kit.Node.Uid] = kit.Node.Id; } @@ -555,23 +571,23 @@ namespace Umbraco.Web.PublishedCache.NuCache Lock(lockInfo); // get existing - _contentNodes.TryGetValue(rootContentId, out LinkedNode link); + _contentNodes.TryGetValue(rootContentId, out var link); var existing = link?.Value; // clear if (existing != null) { ClearBranchLocked(existing); - RemoveFromParentLocked(existing); + RemoveNodeLocked(existing); } // now add them all back - // skip missing parents & unbuildable kits - what else could we do? + // skip missing parents & un-buildable kits - what else could we do? foreach (var kit in kits.Where(x => ParentExistsLocked(x) && BuildKit(x))) { SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); if (_localDb != null) RegisterChange(kit.Node.Id, kit); - AddToParentLocked(kit.Node); + AddNodeLocked(kit.Node); _xmap[kit.Node.Uid] = kit.Node.Id; } @@ -601,7 +617,7 @@ namespace Umbraco.Web.PublishedCache.NuCache ClearBranchLocked(content); // manage the tree - RemoveFromParentLocked(content); + RemoveNodeLocked(content); return true; } @@ -626,10 +642,13 @@ namespace Umbraco.Web.PublishedCache.NuCache _xmap.TryRemove(content.Uid, out _); - foreach (var childId in content.ChildContentIds) + var id = content.FirstChildContentId; + while (id > 0) { - if (_contentNodes.TryGetValue(childId, out LinkedNode link) == false || link.Value == null) continue; + if (!_contentNodes.TryGetValue(id, out var link) || link.Value == null) + throw new Exception("panic: failed to get child"); ClearBranchLocked(link.Value); + id = link.Value.NextSiblingContentId; } } @@ -641,24 +660,37 @@ namespace Umbraco.Web.PublishedCache.NuCache return link; } - private void RemoveFromParentLocked(ContentNode content) + private void RemoveNodeLocked(ContentNode content) { - // remove from root content index, - // or parent's children index + LinkedNode parentLink; if (content.ParentContentId < 0) { - SetValueLocked(_contentRootNodes, content.Id, null); + parentLink = _root; } else { - // obviously parent has to exist - var link = GetParentLink(content); - var parent = link.Value; - if (link.Gen < _liveGen) - parent = parent.CloneParent(_publishedSnapshotAccessor, _umbracoContextAccessor); - parent.ChildContentIds.Remove(content.Id); - if (link.Gen < _liveGen) - SetValueLocked(_contentNodes, parent.Id, parent); + if (!_contentNodes.TryGetValue(content.ParentContentId, out parentLink) || parentLink.Value == null) + throw new Exception("panic: failed to get parent"); + } + + var parent = parentLink.Value; + + if (parent.FirstChildContentId == content.Id) + { + parent = GenCloneLocked(parentLink); + parent.FirstChildContentId = content.NextSiblingContentId; + } + else + { + if (!_contentNodes.TryGetValue(parent.FirstChildContentId, out var link) || link.Value == null) + throw new Exception("panic: failed to get first child"); + + while (link.Value.NextSiblingContentId != content.Id) + if (!_contentNodes.TryGetValue(parent.NextSiblingContentId, out link) || link.Value == null) + throw new Exception("panic: failed to get next sibling"); + + var prevChild = GenCloneLocked(link); + prevChild.NextSiblingContentId = content.NextSiblingContentId; } } @@ -679,25 +711,90 @@ namespace Umbraco.Web.PublishedCache.NuCache return node?.PublishedModel != null; } - private void AddToParentLocked(ContentNode content) + private ContentNode GenCloneLocked(LinkedNode link) { - // add to root content index, - // or parent's children index + var node = link.Value; + + if (node != null && link.Gen < _liveGen) + { + node = new ContentNode(link.Value); + if (link == _root) + SetRootLocked(node); + else + SetValueLocked(_contentNodes, node.Id, node); + } + + return node; + } + + private void AddNodeLocked(ContentNode content) + { + LinkedNode parentLink; + if (content.ParentContentId < 0) { - // need an object reference... just use this... - SetValueLocked(_contentRootNodes, content.Id, this); + parentLink = _root; } else { - // assume parent has been validated and exists - var link = GetParentLink(content); - var parent = link.Value; - if (link.Gen < _liveGen) - parent = parent.CloneParent(_publishedSnapshotAccessor, _umbracoContextAccessor); - parent.ChildContentIds.Add(content.Id); - if (link.Gen < _liveGen) - SetValueLocked(_contentNodes, parent.Id, parent); + if (!_contentNodes.TryGetValue(content.ParentContentId, out parentLink) || parentLink.Value == null) + throw new Exception("panic: failed to get parent"); + } + + var parent = parentLink.Value; + + if (parent.FirstChildContentId < 0) + { + parent = GenCloneLocked(parentLink); + parent.FirstChildContentId = content.Id; + return; + } + + if (!_contentNodes.TryGetValue(parent.FirstChildContentId, out var prevChildLink) || prevChildLink.Value == null) + throw new Exception("panic: failed to get first child"); + + var prevChild = prevChildLink.Value; + + if (prevChild.SortOrder > content.SortOrder) + { + content.NextSiblingContentId = parent.FirstChildContentId; + + parent = GenCloneLocked(parentLink); + parent.FirstChildContentId = content.Id; + return; + } + + while (prevChild.NextSiblingContentId > 0) + { + if (!_contentNodes.TryGetValue(prevChild.NextSiblingContentId, out var link) || link.Value == null) + throw new Exception("panic: failed to get next child"); + var nextChild = link.Value; + + if (nextChild.SortOrder > content.SortOrder) + { + content.NextSiblingContentId = nextChild.Id; + prevChild = GenCloneLocked(prevChildLink); + prevChild.NextSiblingContentId = content.Id; + return; + } + + prevChild = nextChild; + prevChildLink = link; + } + + prevChild = GenCloneLocked(prevChildLink); + prevChild.NextSiblingContentId = content.Id; + } + + private void SetRootLocked(ContentNode node) + { + if (_root.Gen != _liveGen) + { + _root = new LinkedNode(node, _liveGen, _root); + } + else + { + _root.Value = node; } } @@ -764,18 +861,24 @@ namespace Umbraco.Web.PublishedCache.NuCache public IEnumerable GetAtRoot(long gen) { - // look ma, no lock! - foreach (var kvp in _contentRootNodes) + var z = _root; + while (z != null) { - var link = kvp.Value; - while (link != null) - { - if (link.Gen <= gen) - break; - link = link.Next; - } - if (link?.Value != null) - yield return Get(kvp.Key, gen); + if (z.Gen <= gen) + break; + z = z.Next; + } + if (z == null) + yield break; + + var id = z.Value.FirstChildContentId; + + while (id > 0) + { + if (!_contentNodes.TryGetValue(id, out var link) || link == null) + throw new Exception("panic: failed to get sibling"); + yield return link.Value; + id = link.Value.NextSiblingContentId; } } @@ -928,7 +1031,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return _collectTask; // ReSharper disable InconsistentlySynchronizedField - var task = _collectTask = Task.Run(() => Collect()); + var task = _collectTask = Task.Run(Collect); _collectTask.ContinueWith(_ => { lock (_rlocko) @@ -957,11 +1060,19 @@ namespace Umbraco.Web.PublishedCache.NuCache } Collect(_contentNodes); - Collect(_contentRootNodes); + CollectRoot(); Collect(_contentTypesById); Collect(_contentTypesByAlias); } + private void CollectRoot() + { + var link = _root; + while (link.Next != null && link.Next.Gen > _floorGen) + link = link.Next; + link.Next = null; + } + private void Collect(ConcurrentDictionary> dict) where TValue : class { diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index c1215c881b..3ededddba3 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -67,12 +67,9 @@ namespace Umbraco.Web.PublishedCache.NuCache return user?.Name; } - // (see ContentNode.CloneParent) - public PublishedContent( - ContentNode contentNode, - PublishedContent origin, - IUmbracoContextAccessor umbracoContextAccessor) - : base(umbracoContextAccessor) + // used when cloning in ContentNode + public PublishedContent(ContentNode contentNode, PublishedContent origin) + : base(origin.UmbracoContextAccessor) { _contentNode = contentNode; _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; @@ -124,43 +121,11 @@ namespace Umbraco.Web.PublishedCache.NuCache return GetContentByIdFunc(_publishedSnapshotAccessor.PublishedSnapshot, previewing, id); } - private IEnumerable GetContentByIds(bool previewing, IEnumerable ids) - { - var publishedSnapshot = _publishedSnapshotAccessor.PublishedSnapshot; - - // beware! the loop below CANNOT be converted to query such as: - //return ids.Select(x => _getContentByIdFunc(publishedSnapshot, previewing, x)).Where(x => x != null); - // because it would capture the published snapshot and cause all sorts of issues - // - // we WANT to get the actual current published snapshot each time we run - - // ReSharper disable once LoopCanBeConvertedToQuery - foreach (var id in ids) - { - var content = GetContentByIdFunc(publishedSnapshot, previewing, id); - if (content != null) yield return content; - } - } - private IPublishedContent GetMediaById(bool previewing, int id) { return GetMediaByIdFunc(_publishedSnapshotAccessor.PublishedSnapshot, previewing, id); } - private IEnumerable GetMediaByIds(bool previewing, IEnumerable ids) - { - var publishedShapshot = _publishedSnapshotAccessor.PublishedSnapshot; - - // see note above for content - - // ReSharper disable once LoopCanBeConvertedToQuery - foreach (var id in ids) - { - var content = GetMediaByIdFunc(publishedShapshot, previewing, id); - if (content != null) yield return content; - } - } - #endregion #region Content Type @@ -343,43 +308,34 @@ namespace Umbraco.Web.PublishedCache.NuCache /// public override IEnumerable Children(string culture = null) { - // FIXME THIS CANNOT WORK - // we cannot cache children this way, they should be a linked list! - throw new NotImplementedException(); + Func getById; - var cache = GetAppropriateCache(); - if (cache == null || PublishedSnapshotService.CachePublishedContentChildren == false) - return GetChildren(); - - // note: ToArray is important here, we want to cache the result, not the function! - return (IEnumerable)cache.Get(ChildrenCacheKey, () => GetChildren().ToArray()); - } - - private string _childrenCacheKey; - - private string ChildrenCacheKey => _childrenCacheKey ?? (_childrenCacheKey = CacheKeys.PublishedContentChildren(Key, IsPreviewing)); - - private IEnumerable GetChildren() - { - IEnumerable c; - switch (_contentNode.ContentType.ItemType) + switch (ContentType.ItemType) { case PublishedItemType.Content: - c = GetContentByIds(IsPreviewing, _contentNode.ChildContentIds); + getById = GetContentByIdFunc; break; case PublishedItemType.Media: - c = GetMediaByIds(IsPreviewing, _contentNode.ChildContentIds); + getById = GetMediaByIdFunc; break; default: - throw new Exception("oops"); + throw new Exception("panic: invalid item type"); } - return c.OrderBy(x => x.SortOrder); + var publishedSnapshot = _publishedSnapshotAccessor.PublishedSnapshot; + var id = _contentNode.FirstChildContentId; - // notes: - // _contentNode.ChildContentIds is an unordered int[] - // needs to fetch & sort - do it only once, lazily, though - // Q: perfs-wise, is it better than having the store managed an ordered list + while (id > 0) + { + var content = (PublishedContent) getById(publishedSnapshot, IsPreviewing, id); + if (content == null) + throw new Exception("panic: failed to get content"); + + if (content.IsInvariantOrHasCulture(culture)) + yield return content; + + id = content._contentNode.NextSiblingContentId; + } } #endregion @@ -439,7 +395,8 @@ namespace Umbraco.Web.PublishedCache.NuCache // used by navigable content // includes all children, published or unpublished // NavigableNavigator takes care of selecting those it wants - internal IList ChildIds => _contentNode.ChildContentIds; + // note: this is not efficient - we do not try to be (would require a double-linked list) + internal IList ChildIds => Children().Select(x => x.Id).ToList(); // used by Property // gets a value indicating whether the content or media exists in diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Snap/LinkedNode.cs b/src/Umbraco.Web/PublishedCache/NuCache/Snap/LinkedNode.cs index 20d7e7ddcd..78e0ec8d33 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Snap/LinkedNode.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Snap/LinkedNode.cs @@ -17,4 +17,4 @@ public volatile TValue Value; public volatile LinkedNode Next; } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 15d4432a90..2fc449731d 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -172,7 +172,11 @@ namespace Umbraco.Web /// /// Culture is case-insensitive. public static bool HasCulture(this IPublishedContent content, string culture) - => content.Cultures.Contains(culture ?? string.Empty); + => content.Cultures.Contains(culture ?? string.Empty); // fixme oops?! + + // fixme + public static bool IsInvariantOrHasCulture(this IPublishedContent content, string culture) + => !content.ContentType.VariesByCulture() || content.Cultures.Contains(culture ?? ""); /// /// Filters a sequence of to return invariant items, and items that are published for the specified culture. From 55d4457c70c1cf1f7ba24d6224d8b1192ccfa6bd Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 22 Apr 2019 18:14:03 +0200 Subject: [PATCH 16/75] UmbracoContext: don't use obsolete properties --- .../PublishedContentCacheTests.cs | 2 +- .../PublishedContentExtensionTests.cs | 10 +++--- .../PublishedContentLanguageVariantTests.cs | 34 +++++++++---------- .../PublishedContentMoreTests.cs | 18 +++++----- .../PublishedContent/PublishedContentTests.cs | 10 +++--- .../PublishedContent/RootNodeTests.cs | 6 ++-- .../Routing/RenderRouteHandlerTests.cs | 4 +-- src/Umbraco.Tests/Routing/UrlProviderTests.cs | 2 +- src/Umbraco.Tests/Routing/UrlRoutesTests.cs | 12 +++---- .../Routing/UrlsProviderWithDomainsTests.cs | 2 +- .../Routing/UrlsWithNestedDomains.cs | 2 +- src/Umbraco.Tests/Runtimes/StandaloneTests.cs | 8 ++--- .../Scoping/ScopedNuCacheTests.cs | 6 ++-- src/Umbraco.Tests/Scoping/ScopedXmlTests.cs | 6 ++-- .../config/splashes/NoNodes.aspx.cs | 2 +- .../Editors/MacroRenderingController.cs | 2 +- src/Umbraco.Web/Macros/MacroRenderer.cs | 2 +- .../Mvc/RedirectToUmbracoPageResult.cs | 2 +- .../Mvc/UmbracoVirtualNodeByIdRouteHandler.cs | 2 +- src/Umbraco.Web/PublishedContentExtensions.cs | 4 +-- src/Umbraco.Web/Routing/AliasUrlProvider.cs | 2 +- .../Routing/ContentFinderByConfigured404.cs | 4 +-- .../Routing/ContentFinderByIdPath.cs | 2 +- .../Routing/ContentFinderByPageIdQuery.cs | 2 +- .../Routing/ContentFinderByRedirectUrl.cs | 2 +- src/Umbraco.Web/Routing/ContentFinderByUrl.cs | 2 +- .../Routing/ContentFinderByUrlAlias.cs | 2 +- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 6 ++-- src/Umbraco.Web/Routing/PublishedRouter.cs | 4 +-- .../Routing/RedirectTrackingComponent.cs | 4 +-- src/Umbraco.Web/Routing/UrlProvider.cs | 4 +-- src/Umbraco.Web/Templates/TemplateRenderer.cs | 2 +- .../Templates/TemplateUtilities.cs | 4 +-- src/Umbraco.Web/UmbracoComponentRenderer.cs | 2 +- src/Umbraco.Web/UmbracoInjectedModule.cs | 2 +- 35 files changed, 90 insertions(+), 90 deletions(-) diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs index 2a6739df38..11fb55f9b3 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs @@ -84,7 +84,7 @@ namespace Umbraco.Tests.Cache.PublishedCache globalSettings, new TestVariationContextAccessor()); - _cache = _umbracoContext.ContentCache; + _cache = _umbracoContext.Content; } [Test] diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs index acbad002ee..ced4c012f8 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs @@ -27,7 +27,7 @@ namespace Umbraco.Tests.PublishedContent { InitializeInheritedContentTypes(); - var publishedContent = _ctx.ContentCache.GetById(1100); + var publishedContent = _ctx.Content.GetById(1100); Assert.That(publishedContent.IsDocumentType("inherited", false)); } @@ -36,7 +36,7 @@ namespace Umbraco.Tests.PublishedContent { InitializeInheritedContentTypes(); - var publishedContent = _ctx.ContentCache.GetById(1100); + var publishedContent = _ctx.Content.GetById(1100); Assert.That(publishedContent.IsDocumentType("base", false), Is.False); } @@ -45,7 +45,7 @@ namespace Umbraco.Tests.PublishedContent { InitializeInheritedContentTypes(); - var publishedContent = _ctx.ContentCache.GetById(1100); + var publishedContent = _ctx.Content.GetById(1100); Assert.That(publishedContent.IsDocumentType("inherited", true)); } @@ -55,7 +55,7 @@ namespace Umbraco.Tests.PublishedContent InitializeInheritedContentTypes(); ContentTypesCache.GetPublishedContentTypeByAlias = null; - var publishedContent = _ctx.ContentCache.GetById(1100); + var publishedContent = _ctx.Content.GetById(1100); Assert.That(publishedContent.IsDocumentType("base", true)); } @@ -64,7 +64,7 @@ namespace Umbraco.Tests.PublishedContent { InitializeInheritedContentTypes(); - var publishedContent = _ctx.ContentCache.GetById(1100); + var publishedContent = _ctx.Content.GetById(1100); Assert.That(publishedContent.IsDocumentType("invalidbase", true), Is.False); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index 4fe028b1db..dc726c5748 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -191,7 +191,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_For_Populated_Requested_Language() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First(); var value = content.Value("welcomeText", "en-US"); Assert.AreEqual("Welcome", value); } @@ -199,7 +199,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_For_Populated_Requested_Non_Default_Language() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First(); var value = content.Value("welcomeText", "de"); Assert.AreEqual("Willkommen", value); } @@ -207,7 +207,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_Without_Fallback() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First(); var value = content.Value("welcomeText", "fr"); Assert.IsNull(value); } @@ -215,7 +215,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Unless_Requested() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First(); var value = content.Value("welcomeText", "es"); Assert.IsNull(value); } @@ -223,7 +223,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First(); var value = content.Value("welcomeText", "es", fallback: Fallback.ToLanguage); Assert.AreEqual("Welcome", value); } @@ -231,7 +231,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Over_Two_Levels() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First(); var value = content.Value("welcomeText", "it", fallback: Fallback.To(Fallback.Language, Fallback.Ancestors)); Assert.AreEqual("Welcome", value); } @@ -239,7 +239,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Do_Not_GetContent_For_Unpopulated_Requested_Language_With_Fallback_Over_That_Loops() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First(); var value = content.Value("welcomeText", "no", fallback: Fallback.ToLanguage); Assert.IsNull(value); } @@ -247,7 +247,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Do_Not_Get_Content_Recursively_Unless_Requested() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); var value = content.Value("welcomeText2"); Assert.IsNull(value); } @@ -255,7 +255,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_Recursively() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); var value = content.Value("welcomeText2", fallback: Fallback.ToAncestors); Assert.AreEqual("Welcome", value); } @@ -263,7 +263,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Do_Not_Get_Content_Recursively_Unless_Requested2() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First().Children().First(); Assert.IsNull(content.GetProperty("welcomeText2")); var value = content.Value("welcomeText2"); Assert.IsNull(value); @@ -272,7 +272,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_Recursively2() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First().Children().First(); Assert.IsNull(content.GetProperty("welcomeText2")); var value = content.Value("welcomeText2", fallback: Fallback.ToAncestors); Assert.AreEqual("Welcome", value); @@ -281,7 +281,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_Recursively3() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First().Children().First(); Assert.IsNull(content.GetProperty("noprop")); var value = content.Value("noprop", fallback: Fallback.ToAncestors); // property has no value but we still get the value (ie, the converter would do something) @@ -292,7 +292,7 @@ namespace Umbraco.Tests.PublishedContent public void Can_Get_Content_With_Recursive_Priority() { Current.VariationContextAccessor.VariationContext = new VariationContext("nl"); - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); var value = content.Value("welcomeText", "nl", fallback: Fallback.To(Fallback.Ancestors, Fallback.Language)); @@ -303,7 +303,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_With_Fallback_Language_Priority() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); var value = content.Value("welcomeText", "nl", fallback: Fallback.ToLanguage); // No Dutch value is directly assigned. Check has fallen back to English value from language variant. @@ -313,14 +313,14 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Throws_For_Non_Supported_Fallback() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); Assert.Throws(() => content.Value("welcomeText", "nl", fallback: Fallback.To(999))); } [Test] public void Can_Fallback_To_Default_Value() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); // no Dutch value is assigned, so getting null var value = content.Value("welcomeText", "nl"); @@ -338,7 +338,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Have_Custom_Default_Value() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); // HACK: the value, pretend the converter would return something var prop = content.GetProperty("welcomeText") as SolidPublishedPropertyWithLanguageVariants; diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 378671b81b..abd4771547 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -101,14 +101,14 @@ namespace Umbraco.Tests.PublishedContent [Test] public void First() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First(); Assert.AreEqual("Content 1", content.Name()); } [Test] public void Distinct() { - var items = Current.UmbracoContext.ContentCache.GetAtRoot() + var items = Current.UmbracoContext.Content.GetAtRoot() .Distinct() .Distinct() .ToIndexedArray(); @@ -132,7 +132,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void OfType1() { - var items = Current.UmbracoContext.ContentCache.GetAtRoot() + var items = Current.UmbracoContext.Content.GetAtRoot() .OfType() .Distinct() .ToIndexedArray(); @@ -143,7 +143,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void OfType2() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot() + var content = Current.UmbracoContext.Content.GetAtRoot() .OfType() .Distinct() .ToIndexedArray(); @@ -154,7 +154,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void OfType() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot() + var content = Current.UmbracoContext.Content.GetAtRoot() .OfType() .First(x => x.Prop1 == 1234); Assert.AreEqual("Content 2", content.Name()); @@ -164,7 +164,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Position() { - var items = Current.UmbracoContext.ContentCache.GetAtRoot() + var items = Current.UmbracoContext.Content.GetAtRoot() .Where(x => x.Value("prop1") == 1234) .ToIndexedArray(); @@ -179,7 +179,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Issue() { - var content = Current.UmbracoContext.ContentCache.GetAtRoot() + var content = Current.UmbracoContext.Content.GetAtRoot() .Distinct() .OfType(); @@ -187,12 +187,12 @@ namespace Umbraco.Tests.PublishedContent var first = where.First(); Assert.AreEqual(1234, first.Prop1); - var content2 = Current.UmbracoContext.ContentCache.GetAtRoot() + var content2 = Current.UmbracoContext.Content.GetAtRoot() .OfType() .First(x => x.Prop1 == 1234); Assert.AreEqual(1234, content2.Prop1); - var content3 = Current.UmbracoContext.ContentCache.GetAtRoot() + var content3 = Current.UmbracoContext.Content.GetAtRoot() .OfType() .First(); Assert.AreEqual(1234, content3.Prop1); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 4472842251..fd0837b0ab 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -142,7 +142,7 @@ namespace Umbraco.Tests.PublishedContent internal IPublishedContent GetNode(int id) { var ctx = GetUmbracoContext("/test"); - var doc = ctx.ContentCache.GetById(id); + var doc = ctx.Content.GetById(id); Assert.IsNotNull(doc); return doc; } @@ -151,16 +151,16 @@ namespace Umbraco.Tests.PublishedContent public void GetNodeByIds() { var ctx = GetUmbracoContext("/test"); - var contentById = ctx.ContentCache.GetById(1173); + var contentById = ctx.Content.GetById(1173); Assert.IsNotNull(contentById); - var contentByGuid = ctx.ContentCache.GetById(_node1173Guid); + var contentByGuid = ctx.Content.GetById(_node1173Guid); Assert.IsNotNull(contentByGuid); Assert.AreEqual(contentById.Id, contentByGuid.Id); Assert.AreEqual(contentById.Key, contentByGuid.Key); - contentById = ctx.ContentCache.GetById(666); + contentById = ctx.Content.GetById(666); Assert.IsNull(contentById); - contentByGuid = ctx.ContentCache.GetById(Guid.NewGuid()); + contentByGuid = ctx.Content.GetById(Guid.NewGuid()); Assert.IsNull(contentByGuid); } diff --git a/src/Umbraco.Tests/PublishedContent/RootNodeTests.cs b/src/Umbraco.Tests/PublishedContent/RootNodeTests.cs index 0af30b4642..be94923ad0 100644 --- a/src/Umbraco.Tests/PublishedContent/RootNodeTests.cs +++ b/src/Umbraco.Tests/PublishedContent/RootNodeTests.cs @@ -13,17 +13,17 @@ namespace Umbraco.Tests.PublishedContent var ctx = GetUmbracoContext("/test"); // there is no content node with ID -1 - var content = ctx.ContentCache.GetById(-1); + var content = ctx.Content.GetById(-1); Assert.IsNull(content); // content at root has null parent - content = ctx.ContentCache.GetById(1046); + content = ctx.Content.GetById(1046); Assert.IsNotNull(content); Assert.AreEqual(1, content.Level); Assert.IsNull(content.Parent()); // non-existing content is null - content = ctx.ContentCache.GetById(666); + content = ctx.Content.GetById(666); Assert.IsNull(content); } diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index f1f38504f1..935417c510 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -100,7 +100,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("~/dummy-page", template.Id, routeData); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - frequest.PublishedContent = umbracoContext.ContentCache.GetById(1174); + frequest.PublishedContent = umbracoContext.Content.GetById(1174); frequest.TemplateModel = template; var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); @@ -136,7 +136,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("~/dummy-page", template.Id, routeData, true); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - frequest.PublishedContent = umbracoContext.ContentCache.GetById(1172); + frequest.PublishedContent = umbracoContext.Content.GetById(1172); frequest.TemplateModel = template; var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 38ef983120..3f39e4d4e8 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -78,7 +78,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(randomSample.Value, result); } - var cache = umbracoContext.ContentCache as PublishedContentCache; + var cache = umbracoContext.Content as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); Assert.AreEqual(8, cachedRoutes.Count); diff --git a/src/Umbraco.Tests/Routing/UrlRoutesTests.cs b/src/Umbraco.Tests/Routing/UrlRoutesTests.cs index 2e944211ca..4b8d708df6 100644 --- a/src/Umbraco.Tests/Routing/UrlRoutesTests.cs +++ b/src/Umbraco.Tests/Routing/UrlRoutesTests.cs @@ -200,7 +200,7 @@ DetermineRouteById(id): globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(hide); var umbracoContext = GetUmbracoContext("/test", 0, globalSettings: globalSettings.Object); - var cache = umbracoContext.ContentCache as PublishedContentCache; + var cache = umbracoContext.Content as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); var route = cache.GetRouteById(false, id); @@ -224,7 +224,7 @@ DetermineRouteById(id): globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(hide); var umbracoContext = GetUmbracoContext("/test", 0, globalSettings: globalSettings.Object); - var cache = umbracoContext.ContentCache as PublishedContentCache; + var cache = umbracoContext.Content as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); var route = cache.GetRouteById(false, id); @@ -238,7 +238,7 @@ DetermineRouteById(id): globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); var umbracoContext = GetUmbracoContext("/test", 0, globalSettings:globalSettings.Object); - var cache = umbracoContext.ContentCache as PublishedContentCache; + var cache = umbracoContext.Content as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); var route = cache.GetRouteById(false, 1000); @@ -269,7 +269,7 @@ DetermineRouteById(id): globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(hide); var umbracoContext = GetUmbracoContext("/test", 0, globalSettings:globalSettings.Object); - var cache = umbracoContext.ContentCache as PublishedContentCache; + var cache = umbracoContext.Content as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); const bool preview = false; // make sure we don't cache - but HOW? should be some sort of switch?! @@ -300,7 +300,7 @@ DetermineRouteById(id): globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(hide); var umbracoContext = GetUmbracoContext("/test", 0, globalSettings:globalSettings.Object); - var cache = umbracoContext.ContentCache as PublishedContentCache; + var cache = umbracoContext.Content as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); const bool preview = false; // make sure we don't cache - but HOW? should be some sort of switch?! @@ -323,7 +323,7 @@ DetermineRouteById(id): globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); var umbracoContext = GetUmbracoContext("/test", 0, globalSettings:globalSettings.Object); - var cache = umbracoContext.ContentCache as PublishedContentCache; + var cache = umbracoContext.Content as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); var content = cache.GetByRoute(false, "/a/b/c"); diff --git a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs index 5a20d4da31..583d264f57 100644 --- a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs @@ -307,7 +307,7 @@ namespace Umbraco.Tests.Routing ignore = umbracoContext.UrlProvider.GetUrl(100111, false, current: new Uri("http://domain2.com")); ignore = umbracoContext.UrlProvider.GetUrl(1002, false, current: new Uri("http://domain2.com")); - var cache = umbracoContext.ContentCache as PublishedContentCache; + var cache = umbracoContext.Content as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); Assert.AreEqual(7, cachedRoutes.Count); diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index 0eb621bd93..d50f2ee687 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -47,7 +47,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual("http://domain2.com/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, true)); // check that the proper route has been cached - var cache = umbracoContext.ContentCache as PublishedContentCache; + var cache = umbracoContext.Content as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 9f57109329..46e78eb26b 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -184,11 +184,11 @@ namespace Umbraco.Tests.Runtimes var umbracoContext = umbracoContextReference.UmbracoContext; // assert that there is no published document - var pcontent = umbracoContext.ContentCache.GetById(content.Id); + var pcontent = umbracoContext.Content.GetById(content.Id); Assert.IsNull(pcontent); // but a draft document - pcontent = umbracoContext.ContentCache.GetById(true, content.Id); + pcontent = umbracoContext.Content.GetById(true, content.Id); Assert.IsNotNull(pcontent); Assert.AreEqual("test", pcontent.Name()); Assert.IsTrue(pcontent.IsDraft()); @@ -202,7 +202,7 @@ namespace Umbraco.Tests.Runtimes contentService.Save(content); // assert that snapshot has been updated and there is now a published document - pcontent = umbracoContext.ContentCache.GetById(content.Id); + pcontent = umbracoContext.Content.GetById(content.Id); Assert.IsNotNull(pcontent); Assert.AreEqual("test", pcontent.Name()); Assert.IsFalse(pcontent.IsDraft()); @@ -211,7 +211,7 @@ namespace Umbraco.Tests.Runtimes Assert.AreEqual("/test/", pcontent.Url()); // and also an updated draft document - pcontent = umbracoContext.ContentCache.GetById(true, content.Id); + pcontent = umbracoContext.Content.GetById(true, content.Id); Assert.IsNotNull(pcontent); Assert.AreEqual("testx", pcontent.Name()); Assert.IsTrue(pcontent.IsDraft()); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index f0efffc2cc..4f297b894b 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -148,7 +148,7 @@ namespace Umbraco.Tests.Scoping { evented++; - var e = umbracoContext.ContentCache.GetById(item.Id); + var e = umbracoContext.Content.GetById(item.Id); // during events, due to LiveSnapshot, we see the changes Assert.IsNotNull(e); @@ -162,7 +162,7 @@ namespace Umbraco.Tests.Scoping } // been created - var x = umbracoContext.ContentCache.GetById(item.Id); + var x = umbracoContext.Content.GetById(item.Id); Assert.IsNotNull(x); Assert.AreEqual("name", x.Name()); @@ -184,7 +184,7 @@ namespace Umbraco.Tests.Scoping // after the scope, // if completed, we see the changes // else changes have been rolled back - x = umbracoContext.ContentCache.GetById(item.Id); + x = umbracoContext.Content.GetById(item.Id); Assert.IsNotNull(x); Assert.AreEqual(complete ? "changed" : "name", x.Name()); } diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index 044965bc79..34482a79fa 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -75,7 +75,7 @@ namespace Umbraco.Tests.Scoping private static XmlStore XmlStore => (Current.Factory.GetInstance() as PublishedSnapshotService).XmlStore; private static XmlDocument XmlMaster => XmlStore.Xml; - private static XmlDocument XmlInContext => ((PublishedContentCache) Umbraco.Web.Composing.Current.UmbracoContext.ContentCache).GetXml(false); + private static XmlDocument XmlInContext => ((PublishedContentCache) Umbraco.Web.Composing.Current.UmbracoContext.Content).GetXml(false); [TestCase(true)] [TestCase(false)] @@ -85,7 +85,7 @@ namespace Umbraco.Tests.Scoping // sanity checks Assert.AreSame(umbracoContext, Umbraco.Web.Composing.Current.UmbracoContext); - Assert.AreSame(XmlStore, ((PublishedContentCache) umbracoContext.ContentCache).XmlStore); + Assert.AreSame(XmlStore, ((PublishedContentCache) umbracoContext.Content).XmlStore); // create document type, document var contentType = new ContentType(-1) { Alias = "CustomDocument", Name = "Custom Document" }; @@ -199,7 +199,7 @@ namespace Umbraco.Tests.Scoping // sanity checks Assert.AreSame(umbracoContext, Umbraco.Web.Composing.Current.UmbracoContext); - Assert.AreSame(XmlStore, ((PublishedContentCache)umbracoContext.ContentCache).XmlStore); + Assert.AreSame(XmlStore, ((PublishedContentCache)umbracoContext.Content).XmlStore); // create document type var contentType = new ContentType(-1) { Alias = "CustomDocument", Name = "Custom Document" }; diff --git a/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs b/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs index 38f6793fbe..51653d54ca 100644 --- a/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs +++ b/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web.UI.Config.Splashes { base.OnInit(e); - var store = Current.UmbracoContext.ContentCache; + var store = Current.UmbracoContext.Content; if (store.HasContent()) { //if there is actually content, go to the root diff --git a/src/Umbraco.Web/Editors/MacroRenderingController.cs b/src/Umbraco.Web/Editors/MacroRenderingController.cs index 0c3b1626d0..d182a32ade 100644 --- a/src/Umbraco.Web/Editors/MacroRenderingController.cs +++ b/src/Umbraco.Web/Editors/MacroRenderingController.cs @@ -104,7 +104,7 @@ namespace Umbraco.Web.Editors if (m == null) throw new HttpResponseException(HttpStatusCode.NotFound); - var publishedContent = UmbracoContext.ContentCache.GetById(true, pageId); + var publishedContent = UmbracoContext.Content.GetById(true, pageId); //if it isn't supposed to be rendered in the editor then return an empty string //currently we cannot render a macro if the page doesn't yet exist diff --git a/src/Umbraco.Web/Macros/MacroRenderer.cs b/src/Umbraco.Web/Macros/MacroRenderer.cs index 9736aa283b..61b2f0bda3 100755 --- a/src/Umbraco.Web/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web/Macros/MacroRenderer.cs @@ -439,7 +439,7 @@ namespace Umbraco.Web.Macros // this was, and still is, an ugly piece of nonsense var value = string.Empty; - var cache = _umbracoContextAccessor.UmbracoContext.ContentCache; + var cache = _umbracoContextAccessor.UmbracoContext.Content; var splitpath = (string[])pageElements["splitpath"]; for (var i = splitpath.Length - 1; i > 0; i--) // at 0 we have root (-1) diff --git a/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs b/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs index 52d168620b..437eec4e5c 100644 --- a/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs +++ b/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs @@ -56,7 +56,7 @@ namespace Umbraco.Web.Mvc if (_publishedContent != null) return _publishedContent; //need to get the URL for the page - _publishedContent = Current.UmbracoContext.ContentCache.GetById(_pageId); + _publishedContent = Current.UmbracoContext.Content.GetById(_pageId); return _publishedContent; } diff --git a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeByIdRouteHandler.cs b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeByIdRouteHandler.cs index 304f59b64b..0ada310b12 100644 --- a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeByIdRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeByIdRouteHandler.cs @@ -14,7 +14,7 @@ namespace Umbraco.Web.Mvc protected sealed override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext) { - var byId = umbracoContext.ContentCache.GetById(_realNodeId); + var byId = umbracoContext.Content.GetById(_realNodeId); return byId == null ? null : FindContent(requestContext, umbracoContext, byId); } diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 2fc449731d..2e8e532a58 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -237,7 +237,7 @@ namespace Umbraco.Web .And() .ManagedQuery(term); - return query.Execute().ToPublishedSearchResults(Current.UmbracoContext.ContentCache); + return query.Execute().ToPublishedSearchResults(Current.UmbracoContext.Content); } public static IEnumerable SearchChildren(this IPublishedContent content, string term, string indexName = null) @@ -258,7 +258,7 @@ namespace Umbraco.Web .And() .ManagedQuery(term); - return query.Execute().ToPublishedSearchResults(Current.UmbracoContext.ContentCache); + return query.Execute().ToPublishedSearchResults(Current.UmbracoContext.Content); } #endregion diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index f1c755191e..354c315202 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -52,7 +52,7 @@ namespace Umbraco.Web.Routing /// public IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current) { - var node = umbracoContext.ContentCache.GetById(id); + var node = umbracoContext.Content.GetById(id); if (node == null) yield break; diff --git a/src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs b/src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs index e5bb23bfce..4019b0e917 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs @@ -47,7 +47,7 @@ namespace Umbraco.Web.Routing while (pos > 1) { route = route.Substring(0, pos); - node = frequest.UmbracoContext.ContentCache.GetByRoute(route, culture: frequest?.Culture?.Name); + node = frequest.UmbracoContext.Content.GetByRoute(route, culture: frequest?.Culture?.Name); if (node != null) break; pos = route.LastIndexOf('/'); } @@ -71,7 +71,7 @@ namespace Umbraco.Web.Routing { _logger.Debug("Got id={ErrorNodeId}.", error404.Value); - content = frequest.UmbracoContext.ContentCache.GetById(error404.Value); + content = frequest.UmbracoContext.Content.GetById(error404.Value); _logger.Debug(content == null ? "Could not find content with that id." diff --git a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs b/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs index 8360ad7e38..8d2492f25c 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs @@ -50,7 +50,7 @@ namespace Umbraco.Web.Routing if (nodeId > 0) { _logger.Debug("Id={NodeId}", nodeId); - node = frequest.UmbracoContext.ContentCache.GetById(nodeId); + node = frequest.UmbracoContext.Content.GetById(nodeId); if (node != null) { diff --git a/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs b/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs index 54745b2529..70a920f6f0 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs @@ -14,7 +14,7 @@ int pageId; if (int.TryParse(frequest.UmbracoContext.HttpContext.Request["umbPageID"], out pageId)) { - var doc = frequest.UmbracoContext.ContentCache.GetById(pageId); + var doc = frequest.UmbracoContext.Content.GetById(pageId); if (doc != null) { diff --git a/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs b/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs index d7b4c9925c..b8d7c0edd8 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs @@ -44,7 +44,7 @@ namespace Umbraco.Web.Routing return false; } - var content = frequest.UmbracoContext.ContentCache.GetById(redirectUrl.ContentId); + var content = frequest.UmbracoContext.Content.GetById(redirectUrl.ContentId); var url = content == null ? "#" : content.Url(redirectUrl.Culture); if (url.StartsWith("#")) { diff --git a/src/Umbraco.Web/Routing/ContentFinderByUrl.cs b/src/Umbraco.Web/Routing/ContentFinderByUrl.cs index 9aa52782d1..2dc665a00f 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByUrl.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByUrl.cs @@ -48,7 +48,7 @@ namespace Umbraco.Web.Routing Logger.Debug("Test route {Route}", route); - var node = docreq.UmbracoContext.ContentCache.GetByRoute(docreq.UmbracoContext.InPreviewMode, route, culture: docreq.Culture?.Name); + var node = docreq.UmbracoContext.Content.GetByRoute(docreq.UmbracoContext.InPreviewMode, route, culture: docreq.Culture?.Name); if (node != null) { docreq.PublishedContent = node; diff --git a/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs b/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs index 60124a96a2..ce75cf64f6 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web.Routing if (frequest.Uri.AbsolutePath != "/") // no alias if "/" { - node = FindContentByAlias(frequest.UmbracoContext.ContentCache, + node = FindContentByAlias(frequest.UmbracoContext.Content, frequest.HasDomain ? frequest.Domain.ContentId : 0, frequest.Culture.Name, frequest.Uri.GetAbsolutePathDecoded()); diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 842f855a0a..4e9944a808 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -34,7 +34,7 @@ namespace Umbraco.Web.Routing if (!current.IsAbsoluteUri) throw new ArgumentException("Current url must be absolute.", nameof(current)); // will not use cache if previewing - var route = umbracoContext.ContentCache.GetRouteById(content.Id, culture); + var route = umbracoContext.Content.GetRouteById(content.Id, culture); return GetUrlFromRoute(route, umbracoContext, content.Id, current, mode, culture); } @@ -78,7 +78,7 @@ namespace Umbraco.Web.Routing /// public virtual IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current) { - var node = umbracoContext.ContentCache.GetById(id); + var node = umbracoContext.Content.GetById(id); if (node == null) yield break; @@ -100,7 +100,7 @@ namespace Umbraco.Web.Routing var culture = d?.Culture?.Name; //although we are passing in culture here, if any node in this path is invariant, it ignores the culture anyways so this is ok - var route = umbracoContext.ContentCache.GetRouteById(id, culture); + var route = umbracoContext.Content.GetRouteById(id, culture); if (route == null) continue; //need to strip off the leading ID for the route if it exists (occurs if the route is for a node with a domain assigned) diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index 2e772cb175..086fd589ab 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -517,7 +517,7 @@ namespace Umbraco.Web.Routing { // try and get the redirect node from a legacy integer ID valid = true; - internalRedirectNode = request.UmbracoContext.ContentCache.GetById(internalRedirectId); + internalRedirectNode = request.UmbracoContext.Content.GetById(internalRedirectId); } else { @@ -526,7 +526,7 @@ namespace Umbraco.Web.Routing { // try and get the redirect node from a UDI Guid valid = true; - internalRedirectNode = request.UmbracoContext.ContentCache.GetById(udiInternalRedirectId.Guid); + internalRedirectNode = request.UmbracoContext.Content.GetById(udiInternalRedirectId.Guid); } } diff --git a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs b/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs index 4f9086e50b..9c6459da62 100644 --- a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs +++ b/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs @@ -164,7 +164,7 @@ namespace Umbraco.Web.Routing { if (LockedEvents) return; - var contentCache = Current.UmbracoContext.ContentCache; + var contentCache = Current.UmbracoContext.Content; foreach (var entity in args.PublishedEntities) { var entityContent = contentCache.GetById(entity.Id); @@ -210,7 +210,7 @@ namespace Umbraco.Web.Routing private static void CreateRedirect(int contentId, string culture, Guid contentKey, string oldRoute) { - var contentCache = Current.UmbracoContext.ContentCache; + var contentCache = Current.UmbracoContext.Content; var newRoute = contentCache.GetRouteById(contentId, culture); if (IsNotRoute(newRoute) || oldRoute == newRoute) return; var redirectUrlService = Current.Services.RedirectUrlService; diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index f40aa00b01..d34123c96c 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -70,8 +70,8 @@ namespace Umbraco.Web.Routing #region GetUrl private UrlMode GetMode(bool absolute) => absolute ? UrlMode.Absolute : Mode; - private IPublishedContent GetDocument(int id) => _umbracoContext.ContentCache.GetById(id); - private IPublishedContent GetDocument(Guid id) => _umbracoContext.ContentCache.GetById(id); + private IPublishedContent GetDocument(int id) => _umbracoContext.Content.GetById(id); + private IPublishedContent GetDocument(Guid id) => _umbracoContext.Content.GetById(id); /// /// Gets the url of a published content. diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index df711a084e..a7dcc6e422 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -49,7 +49,7 @@ namespace Umbraco.Web.Templates // terribly much for this implementation since we are just creating a doc content request to modify it's properties manually. var contentRequest = _publishedRouter.CreateRequest(_umbracoContextAccessor.UmbracoContext); - var doc = contentRequest.UmbracoContext.ContentCache.GetById(pageId); + var doc = contentRequest.UmbracoContext.Content.GetById(pageId); if (doc == null) { diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index 0be392c6dd..7c66bf6505 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -131,7 +131,7 @@ namespace Umbraco.Web.Templates public static string ResolveMediaFromTextString(string text) { // don't attempt to proceed without a context - if (Current.UmbracoContext == null || Current.UmbracoContext.MediaCache == null) + if (Current.UmbracoContext == null || Current.UmbracoContext.Media == null) { return text; } @@ -150,7 +150,7 @@ namespace Umbraco.Web.Templates { return match.Value; } - var media = Current.UmbracoContext.MediaCache.GetById(guidUdi.Guid); + var media = Current.UmbracoContext.Media.GetById(guidUdi.Guid); if(media == null) { // image does not exist - we could choose to remove the image entirely here (return empty string), diff --git a/src/Umbraco.Web/UmbracoComponentRenderer.cs b/src/Umbraco.Web/UmbracoComponentRenderer.cs index 25c2d78731..f6c3d30da3 100644 --- a/src/Umbraco.Web/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/UmbracoComponentRenderer.cs @@ -92,7 +92,7 @@ namespace Umbraco.Web if (contentId == default) throw new ArgumentException("Invalid content id " + contentId); - var content = _umbracoContextAccessor.UmbracoContext.ContentCache?.GetById(true, contentId); + var content = _umbracoContextAccessor.UmbracoContext.Content?.GetById(true, contentId); if (content == null) throw new InvalidOperationException("Cannot render a macro, no content found by id " + contentId); diff --git a/src/Umbraco.Web/UmbracoInjectedModule.cs b/src/Umbraco.Web/UmbracoInjectedModule.cs index 6a786ed6bf..c973fafa81 100644 --- a/src/Umbraco.Web/UmbracoInjectedModule.cs +++ b/src/Umbraco.Web/UmbracoInjectedModule.cs @@ -300,7 +300,7 @@ namespace Umbraco.Web // if yes, return true private bool EnsureHasContent(UmbracoContext context, HttpContextBase httpContext) { - if (context.ContentCache.HasContent()) + if (context.Content.HasContent()) return true; _logger.Warn("Umbraco has no content"); From 4feb766cc6ba27c008a84fa4a9a1c0c4b1f64358 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 22 Apr 2019 19:49:49 +0200 Subject: [PATCH 17/75] Fix IPublishedContent.Children() and NuCache --- .../PublishedContent/IPublishedContent.cs | 4 +- .../PublishedContentWrapped.cs | 2 +- .../DictionaryPublishedContent.cs | 2 +- .../XmlPublishedContent.cs | 2 +- .../Published/NestedContentTests.cs | 2 +- .../PublishedContentDataTableTests.cs | 2 +- .../SolidPublishedSnapshot.cs | 2 +- .../TestHelpers/Stubs/TestPublishedContent.cs | 2 +- .../PublishedContentHashtableConverter.cs | 2 +- .../Models/PublishedContentBase.cs | 4 +- .../PublishedCache/NuCache/ContentCache.cs | 23 +------ .../PublishedCache/NuCache/ContentNode.cs | 9 ++- .../PublishedCache/NuCache/ContentStore.cs | 4 +- ....DictionaryOfCultureVariationSerializer.cs | 5 +- ...Tree.DictionaryOfPropertyDataSerializer.cs | 5 +- .../NuCache/PublishedContent.cs | 67 ++++++++----------- .../NuCache/PublishedSnapshotService.cs | 4 +- .../PublishedCache/PublishedMember.cs | 2 +- src/Umbraco.Web/PublishedContentExtensions.cs | 25 ++----- 19 files changed, 64 insertions(+), 104 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index a6482b48ab..8ff648553c 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -117,7 +117,7 @@ namespace Umbraco.Core.Models.PublishedContent /// the cultures that are published. For a draft content, those that are 'available' ie /// have a non-empty content name. /// - IReadOnlyList Cultures { get; } + IReadOnlyCollection Cultures { get; } /// /// Gets a value indicating whether the content is draft. @@ -164,8 +164,8 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Gets children that are available for the specified culture. /// Children are sorted by their sortOrder. + /// The '*' culture and supported and returns everything. /// - // FIXME: can culture be '*'? IEnumerable Children(string culture = null); #endregion diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 5bac22ad24..b2feec38c6 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -100,7 +100,7 @@ namespace Umbraco.Core.Models.PublishedContent public DateTime CultureDate(string culture = null) => _content.CultureDate(culture); /// - public IReadOnlyList Cultures => _content.Cultures; + public IReadOnlyCollection Cultures => _content.Cultures; /// public virtual bool IsDraft(string culture = null) => _content.IsDraft(culture); diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs index cd362cadc0..811851224c 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs @@ -157,7 +157,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache // ReSharper disable once CollectionNeverUpdated.Local private static readonly List EmptyListOfString = new List(); - public override IReadOnlyList Cultures => EmptyListOfString; + public override IReadOnlyCollection Cultures => EmptyListOfString; public override string UrlSegment(string culture = null) => _urlName; diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index bc9ab8010d..7d24eec1e9 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -140,7 +140,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache // ReSharper disable once CollectionNeverUpdated.Local private static readonly List EmptyListOfString = new List(); - public override IReadOnlyList Cultures => EmptyListOfString; + public override IReadOnlyCollection Cultures => EmptyListOfString; public override string WriterName { diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 8b954d6ab0..5e024c2a72 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -285,7 +285,7 @@ namespace Umbraco.Tests.Published public override int SortOrder { get; } public override string Name(string culture = null) => default; public override DateTime CultureDate(string culture = null) => throw new NotSupportedException(); - public override IReadOnlyList Cultures => throw new NotSupportedException(); + public override IReadOnlyCollection Cultures => throw new NotSupportedException(); public override string UrlSegment(string culture = null) => default; public override string WriterName { get; } public override string CreatorName { get; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 077dfd3c94..e4bf02fec0 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -200,7 +200,7 @@ namespace Umbraco.Tests.PublishedContent public string Name(string culture = null) => _names.TryGetValue(culture ?? "", out var name) ? name : null; public void SetName(string name, string culture = null) => _names[culture ?? ""] = name; public DateTime CultureDate(string culture = null) => throw new NotSupportedException(); - public IReadOnlyList Cultures => throw new NotSupportedException(); + public IReadOnlyCollection Cultures => throw new NotSupportedException(); public string UrlSegment(string culture = null) => _urlSegments.TryGetValue(culture ?? "", out var urlSegment) ? urlSegment : null; public void SetUrlSegment(string urlSegment, string culture = null) => _urlSegments[culture ?? ""] = urlSegment; public string WriterName { get; set; } diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index d61dead9c2..23aa93bb8d 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -184,7 +184,7 @@ namespace Umbraco.Tests.PublishedContent public string Name(string culture = null) => _names.TryGetValue(culture ?? "", out var name) ? name : null; public void SetName(string name, string culture = null) => _names[culture ?? ""] = name; public DateTime CultureDate(string culture = null) => throw new NotSupportedException(); - public IReadOnlyList Cultures => throw new NotSupportedException(); + public IReadOnlyCollection Cultures => throw new NotSupportedException(); public string UrlSegment(string culture = null) => _urlSegments.TryGetValue(culture ?? "", out var urlSegment) ? urlSegment : null; public void SetUrlSegment(string urlSegment, string culture = null) => _urlSegments[culture ?? ""] = urlSegment; public string WriterName { get; set; } diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 8c631bb067..1fb090e220 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -36,7 +36,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs // get return _cultures.TryGetValue(culture, out var date) ? date : DateTime.MinValue; } - public IReadOnlyList Cultures { get; set; } + public IReadOnlyCollection Cultures { get; set; } public string UrlSegment(string culture = null) => _urlSegments.TryGetValue(culture ?? "", out var urlSegment) ? urlSegment : null; public void SetUrlSegment(string urlSegment, string culture = null) => _urlSegments[culture ?? ""] = urlSegment; public string DocumentTypeAlias => ContentType.Alias; diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index ebeafd2c06..cf2dca0b64 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -248,7 +248,7 @@ namespace Umbraco.Web.Macros private static readonly List EmptyListOfString = new List(); private IReadOnlyList _cultures; - public IReadOnlyList Cultures + public IReadOnlyCollection Cultures { get { diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index f29dc48d2b..086fae8b16 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -12,7 +12,7 @@ namespace Umbraco.Web.Models /// /// This base class does which (a) consistently resolves and caches the Url, (b) provides an implementation /// for this[alias], and (c) provides basic content set management. - [DebuggerDisplay("Content Id: {Id}, Name: {Name}")] + [DebuggerDisplay("Content Id: {Id}}")] public abstract class PublishedContentBase : IPublishedContent { protected PublishedContentBase(IUmbracoContextAccessor umbracoContextAccessor) @@ -138,7 +138,7 @@ namespace Umbraco.Web.Models public abstract DateTime CultureDate(string culture = null); /// - public abstract IReadOnlyList Cultures { get; } + public abstract IReadOnlyCollection Cultures { get; } /// public abstract bool IsDraft(string culture = null); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index e422c04f72..89de144403 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -246,28 +246,9 @@ namespace Umbraco.Web.PublishedCache.NuCache public override IEnumerable GetAtRoot(bool preview) { - if (PublishedSnapshotService.CacheContentCacheRoots == false) - return GetAtRootNoCache(preview); - - var cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing - ? _elementsCache - : _snapshotCache; - - if (cache == null) - return GetAtRootNoCache(preview); - - // note: ToArray is important here, we want to cache the result, not the function! - return (IEnumerable)cache.Get( - CacheKeys.ContentCacheRoots(preview), - () => GetAtRootNoCache(preview).ToArray()); - } - - private IEnumerable GetAtRootNoCache(bool preview) - { - var c = _snapshot.GetAtRoot(); - // both .Draft and .Published cannot be null at the same time - return c.Select(n => GetNodePublishedContent(n, preview)).WhereNotNull().OrderBy(x => x.SortOrder); + // root is already sorted by sortOrder, and does not contain nulls + return _snapshot.GetAtRoot().Select(n => GetNodePublishedContent(n, preview)); } private static IPublishedContent GetNodePublishedContent(ContentNode node, bool preview) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs index 58e60cd8ad..7c9a739448 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.PublishedCache.NuCache.DataSource; @@ -10,13 +11,17 @@ namespace Umbraco.Web.PublishedCache.NuCache { // special ctor for root pseudo node public ContentNode() - { } + { + FirstChildContentId = -1; + NextSiblingContentId = -1; + } // special ctor with no content data - for members public ContentNode(int id, Guid uid, IPublishedContentType contentType, int level, string path, int sortOrder, int parentContentId, DateTime createDate, int creatorId) + : this() { Id = id; Uid = uid; @@ -25,8 +30,6 @@ namespace Umbraco.Web.PublishedCache.NuCache Path = path; SortOrder = sortOrder; ParentContentId = parentContentId; - FirstChildContentId = -1; - NextSiblingContentId = -1; CreateDate = createDate; CreatorId = creatorId; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 5b54bb7345..9b1955fe43 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -508,7 +508,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // new, add to parent AddNodeLocked(kit.Node); } - else if (moving) + else if (moving || existing.SortOrder != kit.Node.SortOrder) { // moved, remove existing from its parent, add content to its parent RemoveNodeLocked(existing); @@ -562,7 +562,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - // IMPORTANT kits must be sorted out by LEVEL + // IMPORTANT kits must be sorted out by LEVEL and by SORT ORDER public void SetBranch(int rootContentId, IEnumerable kits) { var lockInfo = new WriteLockInfo(); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs index 958f6302fa..4521311302 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using CSharpTest.Net.Serialization; using Umbraco.Core; @@ -14,7 +15,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource if (pcount == 0) return Empty; // read each variation - var dict = new Dictionary(); + var dict = new Dictionary(StringComparer.InvariantCultureIgnoreCase); for (var i = 0; i < pcount; i++) { var languageId = PrimitiveSerializer.String.ReadFrom(stream); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs index 19c8beedb5..aa5dc9eb30 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using CSharpTest.Net.Serialization; using Umbraco.Core; @@ -9,7 +10,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource { public IDictionary ReadFrom(Stream stream) { - var dict = new Dictionary(); + var dict = new Dictionary(StringComparer.InvariantCultureIgnoreCase); // read properties count var pcount = PrimitiveSerializer.Int32.ReadFrom(stream); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 3ededddba3..0a55049f9f 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -116,14 +116,18 @@ namespace Umbraco.Web.PublishedCache.NuCache internal static Func GetMediaByIdFunc { get; set; } = (publishedShapshot, previewing, id) => publishedShapshot.Media.GetById(previewing, id); - private IPublishedContent GetContentById(bool previewing, int id) + private Func GetGetterById(PublishedItemType itemType) { - return GetContentByIdFunc(_publishedSnapshotAccessor.PublishedSnapshot, previewing, id); - } + switch (ContentType.ItemType) + { + case PublishedItemType.Content: + return GetContentByIdFunc; + case PublishedItemType.Media: + return GetMediaByIdFunc; + default: + throw new Exception("panic: invalid item type"); + } - private IPublishedContent GetMediaById(bool previewing, int id) - { - return GetMediaByIdFunc(_publishedSnapshotAccessor.PublishedSnapshot, previewing, id); } #endregion @@ -226,17 +230,17 @@ namespace Umbraco.Web.PublishedCache.NuCache // ReSharper disable once CollectionNeverUpdated.Local private static readonly List EmptyListOfString = new List(); - private IReadOnlyList _cultures; + private IReadOnlyCollection _cultures; /// - public override IReadOnlyList Cultures + public override IReadOnlyCollection Cultures { get { if (!ContentType.VariesByCulture()) return EmptyListOfString; - return _cultures ?? (_cultures = ContentData.CultureInfos.Keys.ToList()); + return _cultures ?? (_cultures = new HashSet(ContentData.CultureInfos.Keys, StringComparer.OrdinalIgnoreCase)); } } @@ -292,49 +296,36 @@ namespace Umbraco.Web.PublishedCache.NuCache /// public override IPublishedContent Parent() { - // have to use the "current" cache because a PublishedContent can be shared - // amongst many snapshots and other content depend on the snapshots - switch (_contentNode.ContentType.ItemType) - { - case PublishedItemType.Content: - return GetContentById(IsPreviewing, _contentNode.ParentContentId); - case PublishedItemType.Media: - return GetMediaById(IsPreviewing, _contentNode.ParentContentId); - default: - throw new Exception($"Panic: unsupported item type \"{_contentNode.ContentType.ItemType}\"."); - } + var getById = GetGetterById(ContentType.ItemType); + var publishedSnapshot = _publishedSnapshotAccessor.PublishedSnapshot; + return getById(publishedSnapshot, IsPreviewing, ParentId); } /// public override IEnumerable Children(string culture = null) { - Func getById; + // invariant has invariant value (whatever the requested culture) + if (!ContentType.VariesByCulture() && culture != "*") + culture = ""; - switch (ContentType.ItemType) - { - case PublishedItemType.Content: - getById = GetContentByIdFunc; - break; - case PublishedItemType.Media: - getById = GetMediaByIdFunc; - break; - default: - throw new Exception("panic: invalid item type"); - } + // handle context culture for variant + if (culture == null) + culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; + var getById = GetGetterById(ContentType.ItemType); var publishedSnapshot = _publishedSnapshotAccessor.PublishedSnapshot; var id = _contentNode.FirstChildContentId; while (id > 0) { - var content = (PublishedContent) getById(publishedSnapshot, IsPreviewing, id); + var content = getById(publishedSnapshot, IsPreviewing, id); if (content == null) throw new Exception("panic: failed to get content"); - if (content.IsInvariantOrHasCulture(culture)) + if (culture == "*" || content.IsInvariantOrHasCulture(culture)) yield return content; - id = content._contentNode.NextSiblingContentId; + id = UnwrapIPublishedContent(content)._contentNode.NextSiblingContentId; } } @@ -422,11 +413,9 @@ namespace Umbraco.Web.PublishedCache.NuCache // used by Navigable.Source,... internal static PublishedContent UnwrapIPublishedContent(IPublishedContent content) { - PublishedContentWrapped wrapped; - while ((wrapped = content as PublishedContentWrapped) != null) + while (content is PublishedContentWrapped wrapped) content = wrapped.Unwrap(); - var inner = content as PublishedContent; - if (inner == null) + if (!(content is PublishedContent inner)) throw new InvalidOperationException("Innermost content is not PublishedContent."); return inner; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index ce19764fb6..7c8b69f9b2 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -649,7 +649,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (capture.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) { // ?? should we do some RV check here? - // IMPORTANT GetbranchContentSources sorts kits by level + // IMPORTANT GetbranchContentSources sorts kits by level and by sort order var kits = _dataSource.GetBranchContentSources(scope, capture.Id); _contentStore.SetBranch(capture.Id, kits); } @@ -741,7 +741,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (capture.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) { // ?? should we do some RV check here? - // IMPORTANT GetbranchContentSources sorts kits by level + // IMPORTANT GetbranchContentSources sorts kits by level and by sort order var kits = _dataSource.GetBranchMediaSources(scope, capture.Id); _mediaStore.SetBranch(capture.Id, kits); } diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index f0c3ac4f5b..eec973c182 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -142,7 +142,7 @@ namespace Umbraco.Web.PublishedCache public override DateTime CultureDate(string culture = null) => throw new NotSupportedException(); - public override IReadOnlyList Cultures => throw new NotSupportedException(); + public override IReadOnlyCollection Cultures => throw new NotSupportedException(); public override string UrlSegment(string culture = null) => throw new NotSupportedException(); diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 2e8e532a58..b31527eb57 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -172,9 +172,12 @@ namespace Umbraco.Web /// /// Culture is case-insensitive. public static bool HasCulture(this IPublishedContent content, string culture) - => content.Cultures.Contains(culture ?? string.Empty); // fixme oops?! + => content.Cultures.Contains(culture ?? string.Empty); - // fixme + /// + /// Determines whether the content is invariant, or has a culture. + /// + /// Culture is case-insensitive. public static bool IsInvariantOrHasCulture(this IPublishedContent content, string culture) => !content.ContentType.VariesByCulture() || content.Cultures.Contains(culture ?? ""); @@ -895,24 +898,6 @@ namespace Umbraco.Web #region Axes: children - // FIXME: kill that one - /// - /// Gets the children of the content. - /// - /// The content. - /// The specific culture to filter for. If null is used the current culture is used. (Default is null) - /// The children of the content. - /// - /// Children are sorted by their sortOrder. - /// This method exists for consistency, it is the same as calling content.Children as a property. - /// - public static IEnumerable Children(this IPublishedContent content, string culture = null) - { - if (content == null) throw new ArgumentNullException(nameof(content)); - - return content.Children(culture); //.WhereIsInvariantOrHasCulture(culture); - } - /// /// Gets the children of the content, filtered by a predicate. /// From a56f3b23d13de1370eb201e04028757831f083cd Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 22 Apr 2019 11:58:51 +0200 Subject: [PATCH 18/75] Cleanup PublishedMember --- src/Umbraco.Web/Models/PublishedProperty.cs | 58 ------------------- .../PublishedCache/PublishedMember.cs | 5 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 - 3 files changed, 1 insertion(+), 63 deletions(-) delete mode 100644 src/Umbraco.Web/Models/PublishedProperty.cs diff --git a/src/Umbraco.Web/Models/PublishedProperty.cs b/src/Umbraco.Web/Models/PublishedProperty.cs deleted file mode 100644 index f715df7450..0000000000 --- a/src/Umbraco.Web/Models/PublishedProperty.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web.Composing; - -namespace Umbraco.Web.Models -{ - public static class PublishedProperty - { - /// - /// Maps a collection of Property to a collection of IPublishedProperty for a specified collection of PublishedPropertyType. - /// - /// The published property types. - /// The properties. - /// A mapping function. - /// A collection of IPublishedProperty corresponding to the collection of PublishedPropertyType - /// and taking values from the collection of Property. - /// Ensures that all conversions took place correctly. - internal static IEnumerable MapProperties( - IEnumerable propertyTypes, IEnumerable properties, - Func map) - { - var propertyEditors = Current.PropertyEditors; - var dataTypeService = Current.Services.DataTypeService; - - // TODO: not dealing with variants - // but the entire thing should die anyways - - return propertyTypes.Select(x => - { - var p = properties.SingleOrDefault(xx => xx.Alias == x.Alias); - var v = p == null || p.GetValue() == null ? null : p.GetValue(); - if (v != null) - { - var e = propertyEditors[x.EditorAlias]; - - // We are converting to string, even for database values which are integer or - // DateTime, which is not optimum. Doing differently would require that we have a way to tell - // whether the conversion to XML string changes something or not... which we don't, and we - // don't want to implement it as PropertyValueEditor.ConvertDbToXml/String should die anyway. - - // Don't think about improving the situation here: this is a corner case and the real - // thing to do is to get rig of PropertyValueEditor.ConvertDbToXml/String. - - // Use ConvertDbToString to keep it simple, although everywhere we use ConvertDbToXml and - // nothing ensures that the two methods are consistent. - - if (e != null) - v = e.GetValueEditor().ConvertDbToString(p.PropertyType, v, dataTypeService); - } - - return map(x, v); - }); - } - } -} diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index eec973c182..08e00c1f17 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -35,16 +35,13 @@ namespace Umbraco.Web.PublishedCache // if they are not part of the member type properties - in which case they are created as // simple raw properties - which are completely invariant - var _properties = PublishedProperty.MapProperties(_publishedMemberType.PropertyTypes, _member.Properties, - (t, v) => new RawValueProperty(t, this, v ?? string.Empty)); - var properties = new List(); foreach (var propertyType in _publishedMemberType.PropertyTypes) { var property = _member.Properties[propertyType.Alias]; if (property == null) continue; - //properties.Add(new FooProperty(propertyType, this, property.Values)); + properties.Add(new RawValueProperty(propertyType, this, property.GetValue())); } EnsureMemberProperties(properties); _properties = properties.ToArray(); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 28337e6d2c..a190d6711c 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -775,7 +775,6 @@ - From eb390f8d80eb17ee905157ecf5e7380b3e7876a0 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 24 Apr 2019 09:32:49 +0200 Subject: [PATCH 19/75] Fix merge --- .../Routing/MediaUrlProviderTests.cs | 10 +-- .../Routing/UrlsProviderWithDomainsTests.cs | 52 ++++++----- .../Routing/UrlsWithNestedDomains.cs | 3 +- src/Umbraco.Web/PublishedElementExtensions.cs | 44 ++++++++++ .../Routing/DefaultMediaUrlProvider.cs | 12 +-- src/Umbraco.Web/Routing/IMediaUrlProvider.cs | 2 +- src/Umbraco.Web/Routing/UrlProvider.cs | 88 +++---------------- src/Umbraco.Web/UmbracoContext.cs | 4 +- 8 files changed, 100 insertions(+), 115 deletions(-) diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs index 5de99fdd38..268e4e9d85 100644 --- a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -45,7 +45,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.UploadField, expected, null); - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlProviderMode.Auto, null, null); + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlMode.Auto, null, null); Assert.AreEqual(expected, resolvedUrl); } @@ -64,7 +64,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.ImageCropper, imageCropperValue, configuration); - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlProviderMode.Auto, null, null); + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlMode.Auto, null, null); Assert.AreEqual(expected, resolvedUrl); } @@ -78,7 +78,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("http://localhost", mediaUrlProviders: new[] { _mediaUrlProvider }); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.UploadField, mediaUrl, null); - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlProviderMode.Absolute, null, null); + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlMode.Absolute, null, null); Assert.AreEqual(expected, resolvedUrl); } @@ -89,7 +89,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.Boolean, "0", null); - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "test", UrlProviderMode.Absolute, null, null); + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "test", UrlMode.Absolute, null, null); Assert.AreEqual(string.Empty, resolvedUrl); } @@ -116,7 +116,7 @@ namespace Umbraco.Tests.Routing var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), new [] { umbracoFilePropertyType }, ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) {Properties = new[] {property}}; - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlProviderMode.Auto, "da", null); + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlMode.Auto, "da", null); Assert.AreEqual(daMediaUrl, resolvedUrl); } diff --git a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs index 583d264f57..0a34fb8041 100644 --- a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs @@ -189,7 +189,8 @@ namespace Umbraco.Tests.Routing SetDomains1(); var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, absolute, current: currentUri); + var mode = absolute ? UrlMode.Absolute : UrlMode.Auto; + var result = umbracoContext.UrlProvider.GetUrl(nodeId, mode, current: currentUri); Assert.AreEqual(expected, result); } @@ -221,7 +222,8 @@ namespace Umbraco.Tests.Routing SetDomains2(); var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, absolute, current : currentUri); + var mode = absolute ? UrlMode.Absolute : UrlMode.Auto; + var result = umbracoContext.UrlProvider.GetUrl(nodeId, mode, current : currentUri); Assert.AreEqual(expected, result); } @@ -245,7 +247,8 @@ namespace Umbraco.Tests.Routing SetDomains3(); var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, absolute, current : currentUri); + var mode = absolute ? UrlMode.Absolute : UrlMode.Auto; + var result = umbracoContext.UrlProvider.GetUrl(nodeId, mode, current : currentUri); Assert.AreEqual(expected, result); } @@ -275,7 +278,8 @@ namespace Umbraco.Tests.Routing SetDomains4(); var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, absolute, current : currentUri); + var mode = absolute ? UrlMode.Absolute : UrlMode.Auto; + var result = umbracoContext.UrlProvider.GetUrl(nodeId, mode, current : currentUri); Assert.AreEqual(expected, result); } @@ -295,17 +299,17 @@ namespace Umbraco.Tests.Routing SetDomains4(); string ignore; - ignore = umbracoContext.UrlProvider.GetUrl(1001, false, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(10011, false, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(100111, false, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(10012, false, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(100121, false, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(10013, false, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(1002, false, current: new Uri("http://domain1.com")); - ignore = umbracoContext.UrlProvider.GetUrl(1001, false, current: new Uri("http://domain2.com")); - ignore = umbracoContext.UrlProvider.GetUrl(10011, false, current: new Uri("http://domain2.com")); - ignore = umbracoContext.UrlProvider.GetUrl(100111, false, current: new Uri("http://domain2.com")); - ignore = umbracoContext.UrlProvider.GetUrl(1002, false, current: new Uri("http://domain2.com")); + ignore = umbracoContext.UrlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(10012, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(10013, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain2.com")); + ignore = umbracoContext.UrlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain2.com")); + ignore = umbracoContext.UrlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain2.com")); + ignore = umbracoContext.UrlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain2.com")); var cache = umbracoContext.Content as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); @@ -324,15 +328,15 @@ namespace Umbraco.Tests.Routing CheckRoute(cachedRoutes, cachedIds, 1002, "/1002"); // use the cache - Assert.AreEqual("/", umbracoContext.UrlProvider.GetUrl(1001, false, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/en/", umbracoContext.UrlProvider.GetUrl(10011, false, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, false, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/fr/", umbracoContext.UrlProvider.GetUrl(10012, false, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, false, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/1001-3/", umbracoContext.UrlProvider.GetUrl(10013, false, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/1002/", umbracoContext.UrlProvider.GetUrl(1002, false, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/", umbracoContext.UrlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/en/", umbracoContext.UrlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/fr/", umbracoContext.UrlProvider.GetUrl(10012, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/1001-3/", umbracoContext.UrlProvider.GetUrl(10013, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/1002/", umbracoContext.UrlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("http://domain1.com/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, false, current: new Uri("http://domain2.com"))); + Assert.AreEqual("http://domain1.com/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain2.com"))); } private static void CheckRoute(IDictionary routes, IDictionary ids, int id, string route) @@ -381,7 +385,7 @@ namespace Umbraco.Tests.Routing SetDomains5(); - var url = umbracoContext.UrlProvider.GetUrl(100111, true); + var url = umbracoContext.UrlProvider.GetUrl(100111, UrlMode.Absolute); Assert.AreEqual("http://domain1.com/en/1001-1-1/", url); var result = umbracoContext.UrlProvider.GetOtherUrls(100111).ToArray(); diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index d50f2ee687..6587b2e4f6 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -5,6 +5,7 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; using Umbraco.Core.Services; @@ -44,7 +45,7 @@ namespace Umbraco.Tests.Routing { new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); - Assert.AreEqual("http://domain2.com/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, true)); + Assert.AreEqual("http://domain2.com/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, UrlMode.Absolute)); // check that the proper route has been cached var cache = umbracoContext.Content as PublishedContentCache; diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index de7c72d21a..49df6b2685 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -188,5 +188,49 @@ namespace Umbraco.Web } #endregion + + #region MediaUrl + + /// + /// Gets the url for the media. + /// + /// The content. + /// The property alias to resolve the url from. + /// The variation language. + /// The url for the content. + /// Better use the GetMediaUrl method but that method is here to complement MediaUrlAbsolute(). + public static string MediaUrl(this IPublishedContent content, string propertyAlias, string culture = null) + { + var umbracoContext = Composing.Current.UmbracoContext; + + if (umbracoContext == null) + throw new InvalidOperationException("Cannot resolve a Url for a content item when Current.UmbracoContext is null."); + if (umbracoContext.UrlProvider == null) + throw new InvalidOperationException("Cannot resolve a Url for a content item when Current.UmbracoContext.UrlProvider is null."); + + return umbracoContext.UrlProvider.GetMediaUrl(content, propertyAlias, culture); + } + + /// + /// Gets the absolute url for the media. + /// + /// The content. + /// The property alias to resolve the url from. + /// The url mode. + /// The variation language. + /// The absolute url for the media. + public static string MediaUrl(this IPublishedContent content, string propertyAlias, UrlMode mode, string culture = null) + { + var umbracoContext = Composing.Current.UmbracoContext; + + if (umbracoContext == null) + throw new InvalidOperationException("Cannot resolve a Url for a content item when Current.UmbracoContext is null."); + if (umbracoContext.UrlProvider == null) + throw new InvalidOperationException("Cannot resolve a Url for a content item when Current.UmbracoContext.UrlProvider is null."); + + return umbracoContext.UrlProvider.GetMediaUrl(content, propertyAlias, mode, culture); + } + + #endregion } } diff --git a/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs index a9c961c280..18d10e577d 100644 --- a/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.Routing /// public virtual UrlInfo GetMediaUrl(UmbracoContext umbracoContext, IPublishedContent content, string propertyAlias, - UrlProviderMode mode, string culture, Uri current) + UrlMode mode, string culture, Uri current) { var prop = content.GetProperty(propertyAlias); var value = prop?.GetValue(culture); @@ -40,7 +40,7 @@ namespace Umbraco.Web.Routing return url == null ? null : UrlInfo.Url(url.ToString(), culture); } - private Uri AssembleUrl(string path, Uri current, UrlProviderMode mode) + private Uri AssembleUrl(string path, Uri current, UrlMode mode) { if (string.IsNullOrEmpty(path)) return null; @@ -48,15 +48,15 @@ namespace Umbraco.Web.Routing Uri uri; if (current == null) - mode = UrlProviderMode.Relative; // best we can do + mode = UrlMode.Relative; // best we can do switch (mode) { - case UrlProviderMode.Absolute: + case UrlMode.Absolute: uri = new Uri(current?.GetLeftPart(UriPartial.Authority) + path); break; - case UrlProviderMode.Relative: - case UrlProviderMode.Auto: + case UrlMode.Relative: + case UrlMode.Auto: uri = new Uri(path, UriKind.Relative); break; default: diff --git a/src/Umbraco.Web/Routing/IMediaUrlProvider.cs b/src/Umbraco.Web/Routing/IMediaUrlProvider.cs index 419e4d78df..8a81b27415 100644 --- a/src/Umbraco.Web/Routing/IMediaUrlProvider.cs +++ b/src/Umbraco.Web/Routing/IMediaUrlProvider.cs @@ -26,6 +26,6 @@ namespace Umbraco.Web.Routing /// e.g. a cdn url provider will most likely always return an absolute url. /// If the provider is unable to provide a url, it returns null. /// - UrlInfo GetMediaUrl(UmbracoContext umbracoContext, IPublishedContent content, string propertyAlias, UrlProviderMode mode, string culture, Uri current); + UrlInfo GetMediaUrl(UmbracoContext umbracoContext, IPublishedContent content, string propertyAlias, UrlMode mode, string culture, Uri current); } } diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index 10dde3bbae..0f79d11364 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -79,31 +79,6 @@ namespace Umbraco.Web.Routing private IPublishedContent GetDocument(int id) => _umbracoContext.Content.GetById(id); private IPublishedContent GetDocument(Guid id) => _umbracoContext.Content.GetById(id); - /// - /// Gets the url of a published content. - /// - /// The published content. - /// A culture. - /// The current absolute url. - /// The url for the published content. - public string GetUrl(IPublishedContent content, string culture = null, Uri current = null) - => GetUrl(content, Mode, culture, current); - - /// - /// Gets the url of a published content. - /// - /// The published content. - /// A value indicating whether the url should be absolute in any case. - /// A culture. - /// The current absolute url. - /// The url for the published content. - /// - /// The url is absolute or relative depending on Mode and on current, unless - /// absolute is true, in which case the url is always absolute. - /// - public string GetUrl(IPublishedContent content, bool absolute, string culture = null, Uri current = null) - => GetUrl(content, GetMode(absolute), culture, current); - /// /// Gets the url of a published content. /// @@ -114,21 +89,6 @@ namespace Umbraco.Web.Routing public string GetUrl(Guid id, string culture = null, Uri current = null) => GetUrl(GetDocument(id), Mode, culture, current); - /// - /// Gets the url of a published content. - /// - /// The published content identifier. - /// A value indicating whether the url should be absolute in any case. - /// A culture. - /// The current absolute url. - /// The url for the published content. - /// - /// The url is absolute or relative depending on Mode and on current, unless - /// absolute is true, in which case the url is always absolute. - /// - public string GetUrl(Guid id, bool absolute, string culture = null, Uri current = null) - => GetUrl(GetDocument(id), GetMode(absolute), culture, current); - /// /// Gets the url of a published content. /// @@ -150,21 +110,6 @@ namespace Umbraco.Web.Routing public string GetUrl(int id, string culture = null, Uri current = null) => GetUrl(GetDocument(id), Mode, culture, current); - /// - /// Gets the url of a published content. - /// - /// The published content identifier. - /// A value indicating whether the url should be absolute in any case. - /// A culture. - /// The current absolute url. - /// The url for the published content. - /// - /// The url is absolute or relative depending on Mode and on current, unless - /// absolute is true, in which case the url is always absolute. - /// - public string GetUrl(int id, bool absolute, string culture = null, Uri current = null) - => GetUrl(GetDocument(id), GetMode(absolute), culture, current); - /// /// Gets the url of a published content. /// @@ -176,6 +121,16 @@ namespace Umbraco.Web.Routing public string GetUrl(int id, UrlMode mode, string culture = null, Uri current = null) => GetUrl(GetDocument(id), mode, culture, current); + /// + /// Gets the url of a published content. + /// + /// The published content. + /// A culture. + /// The current absolute url. + /// The url for the published content. + public string GetUrl(IPublishedContent content, string culture = null, Uri current = null) + => GetUrl(content, Mode, culture, current); + /// /// Gets the url of a published content. /// @@ -273,7 +228,7 @@ namespace Umbraco.Web.Routing /// when no culture is specified, the current culture. /// If the provider is unable to provide a url, it returns . /// - public string GetMediaUrl(IPublishedContent content, string propertyAlias, UrlMode mode = UrlMode.Wtf, string culture = null, Uri current = null) + public string GetMediaUrl(IPublishedContent content, string propertyAlias, string culture = null, Uri current = null) => GetMediaUrl(content, propertyAlias, Mode, culture, current); /// @@ -281,23 +236,6 @@ namespace Umbraco.Web.Routing /// /// The published content. /// The property alias to resolve the url from. - /// A value indicating whether the url should be absolute in any case. - /// The variation language. - /// The current absolute url. - /// The url for the media. - /// - /// The url is absolute or relative depending on mode and on current. - /// If the media is multi-lingual, gets the url for the specified culture or, - /// when no culture is specified, the current culture. - /// If the provider is unable to provide a url, it returns . - /// - public string GetMediaUrl(IPublishedContent content, string propertyAlias, boolx absolute, string culture = null, Uri current = null) - => GetMediaUrl(content, propertyAlias, GetMode(absolute), culture, current); - /// - /// Gets the url of a media item. - /// - /// The published content. - /// The property alias to resolve the url from. /// The url mode. /// The variation language. /// The current absolute url. @@ -308,9 +246,7 @@ namespace Umbraco.Web.Routing /// when no culture is specified, the current culture. /// If the provider is unable to provide a url, it returns . /// - public string GetMediaUrl(IPublishedContent content, - string propertyAlias, UrlMode mode = UrlMode.Wtf, - string culture = null, Uri current = null) + public string GetMediaUrl(IPublishedContent content, string propertyAlias, UrlMode mode, string culture = null, Uri current = null) { if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias)); diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 69b9ec1db9..3947adc0e5 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -245,7 +245,7 @@ namespace Umbraco.Web [Obsolete("Use the Url() method with UrlMode.Absolute.")] public string UrlAbsolute(int contentId, string culture = null) { - return UrlProvider.GetUrl(contentId, true, culture); + return UrlProvider.GetUrl(contentId, UrlMode.Absolute, culture); } /// @@ -257,7 +257,7 @@ namespace Umbraco.Web [Obsolete("Use the Url() method with UrlMode.Absolute.")] public string UrlAbsolute(Guid contentId, string culture = null) { - return UrlProvider.GetUrl(contentId, true, culture); + return UrlProvider.GetUrl(contentId, UrlMode.Absolute, culture); } #endregion From 25b8c8a5652d396f98e525a17fbe11d7926ba4d6 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 24 Apr 2019 11:53:35 +0200 Subject: [PATCH 20/75] Refactor getting Urls --- .../PublishedContent/IPublishedContent.cs | 4 ++ .../Models/PublishedContent/UrlMode.cs | 4 ++ .../Models/PublishedContentBase.cs | 2 +- src/Umbraco.Web/PublishedElementExtensions.cs | 43 ++++-------- src/Umbraco.Web/Routing/UrlProvider.cs | 68 ++++--------------- src/Umbraco.Web/UmbracoContext.cs | 4 +- 6 files changed, 37 insertions(+), 88 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 8ff648553c..6917538331 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -97,6 +97,10 @@ namespace Umbraco.Core.Models.PublishedContent /// Gets the url of the content item. /// /// + /// If the content item is a document, then this method returns the url of the + /// document. If it is a media, then this methods return the media url for the + /// 'umbracoFile' property. Use the MediaUrl() method to get the media url for other + /// properties. /// The value of this property is contextual. It depends on the 'current' request uri, /// if any. In addition, when the content type is multi-lingual, this is the url for the /// specified culture. Otherwise, it is the invariant url. diff --git a/src/Umbraco.Core/Models/PublishedContent/UrlMode.cs b/src/Umbraco.Core/Models/PublishedContent/UrlMode.cs index f19f93bec1..4cd6a680f4 100644 --- a/src/Umbraco.Core/Models/PublishedContent/UrlMode.cs +++ b/src/Umbraco.Core/Models/PublishedContent/UrlMode.cs @@ -5,6 +5,10 @@ /// public enum UrlMode { + /// + /// Indicates that the url provider should do what it has been configured to do. + /// + Default = 0, /// /// Indicates that the url provider should produce relative urls exclusively. diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 4fc08f6ff3..25c84c97e5 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -95,7 +95,7 @@ namespace Umbraco.Web.Models return umbracoContext.UrlProvider.GetUrl(this, mode, culture); case PublishedItemType.Media: - return umbracoContext.UrlProvider.GetMediaUrl(this, Constants.Conventions.Media.File, mode, culture); + return umbracoContext.UrlProvider.GetMediaUrl(this, mode, culture, Constants.Conventions.Media.File); default: throw new NotSupportedException(); diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index 49df6b2685..2de8259d3c 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -145,7 +145,7 @@ namespace Umbraco.Web } #endregion - + #region ToIndexedArray public static IndexedArrayItem[] ToIndexedArray(this IEnumerable source) @@ -192,14 +192,19 @@ namespace Umbraco.Web #region MediaUrl /// - /// Gets the url for the media. + /// Gets the url for a media. /// - /// The content. - /// The property alias to resolve the url from. - /// The variation language. - /// The url for the content. - /// Better use the GetMediaUrl method but that method is here to complement MediaUrlAbsolute(). - public static string MediaUrl(this IPublishedContent content, string propertyAlias, string culture = null) + /// The content item. + /// The culture (use current culture by default). + /// The url mode (use site configuration by default). + /// The alias of the property (use 'umbracoFile' by default). + /// The url for the media. + /// + /// The value of this property is contextual. It depends on the 'current' request uri, + /// if any. In addition, when the content type is multi-lingual, this is the url for the + /// specified culture. Otherwise, it is the invariant url. + /// + public static string MediaUrl(this IPublishedContent content, string culture = null, UrlMode mode = UrlMode.Default, string propertyAlias = Constants.Conventions.Media.File) { var umbracoContext = Composing.Current.UmbracoContext; @@ -208,27 +213,7 @@ namespace Umbraco.Web if (umbracoContext.UrlProvider == null) throw new InvalidOperationException("Cannot resolve a Url for a content item when Current.UmbracoContext.UrlProvider is null."); - return umbracoContext.UrlProvider.GetMediaUrl(content, propertyAlias, culture); - } - - /// - /// Gets the absolute url for the media. - /// - /// The content. - /// The property alias to resolve the url from. - /// The url mode. - /// The variation language. - /// The absolute url for the media. - public static string MediaUrl(this IPublishedContent content, string propertyAlias, UrlMode mode, string culture = null) - { - var umbracoContext = Composing.Current.UmbracoContext; - - if (umbracoContext == null) - throw new InvalidOperationException("Cannot resolve a Url for a content item when Current.UmbracoContext is null."); - if (umbracoContext.UrlProvider == null) - throw new InvalidOperationException("Cannot resolve a Url for a content item when Current.UmbracoContext.UrlProvider is null."); - - return umbracoContext.UrlProvider.GetMediaUrl(content, propertyAlias, mode, culture); + return umbracoContext.UrlProvider.GetMediaUrl(content, mode, culture, propertyAlias); } #endregion diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index 0f79d11364..59e39fa80a 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -3,11 +3,8 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core; -using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Services; using Umbraco.Web.Composing; -using Umbraco.Web.Models; namespace Umbraco.Web.Routing { @@ -83,11 +80,12 @@ namespace Umbraco.Web.Routing /// Gets the url of a published content. /// /// The published content identifier. + /// The url mode. /// A culture. /// The current absolute url. /// The url for the published content. - public string GetUrl(Guid id, string culture = null, Uri current = null) - => GetUrl(GetDocument(id), Mode, culture, current); + public string GetUrl(Guid id, UrlMode mode = UrlMode.Default, string culture = null, Uri current = null) + => GetUrl(GetDocument(id), mode, culture, current); /// /// Gets the url of a published content. @@ -97,40 +95,9 @@ namespace Umbraco.Web.Routing /// A culture. /// The current absolute url. /// The url for the published content. - public string GetUrl(Guid id, UrlMode mode, string culture = null, Uri current = null) + public string GetUrl(int id, UrlMode mode = UrlMode.Default, string culture = null, Uri current = null) => GetUrl(GetDocument(id), mode, culture, current); - /// - /// Gets the url of a published content. - /// - /// The published content identifier. - /// A culture. - /// The current absolute url. - /// The url for the published content. - public string GetUrl(int id, string culture = null, Uri current = null) - => GetUrl(GetDocument(id), Mode, culture, current); - - /// - /// Gets the url of a published content. - /// - /// The published content identifier. - /// The url mode. - /// A culture. - /// The current absolute url. - /// The url for the published content. - public string GetUrl(int id, UrlMode mode, string culture = null, Uri current = null) - => GetUrl(GetDocument(id), mode, culture, current); - - /// - /// Gets the url of a published content. - /// - /// The published content. - /// A culture. - /// The current absolute url. - /// The url for the published content. - public string GetUrl(IPublishedContent content, string culture = null, Uri current = null) - => GetUrl(content, Mode, culture, current); - /// /// Gets the url of a published content. /// @@ -145,11 +112,14 @@ namespace Umbraco.Web.Routing /// when no culture is specified, the current culture. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(IPublishedContent content, UrlMode mode, string culture = null, Uri current = null) + public string GetUrl(IPublishedContent content, UrlMode mode = UrlMode.Default, string culture = null, Uri current = null) { if (content == null || content.ContentType.ItemType == PublishedItemType.Element) return "#"; + if (mode == UrlMode.Default) + mode = Mode; + // this the ONLY place where we deal with default culture - IUrlProvider always receive a culture // be nice with tests, assume things can be null, ultimately fall back to invariant // (but only for variant content of course) @@ -214,23 +184,6 @@ namespace Umbraco.Web.Routing #region GetMediaUrl - /// - /// Gets the url of a media item. - /// - /// The published content. - /// The property alias to resolve the url from. - /// The variation language. - /// The current absolute url. - /// The url for the media. - /// - /// The url is absolute or relative depending on mode and on current. - /// If the media is multi-lingual, gets the url for the specified culture or, - /// when no culture is specified, the current culture. - /// If the provider is unable to provide a url, it returns . - /// - public string GetMediaUrl(IPublishedContent content, string propertyAlias, string culture = null, Uri current = null) - => GetMediaUrl(content, propertyAlias, Mode, culture, current); - /// /// Gets the url of a media item. /// @@ -246,13 +199,16 @@ namespace Umbraco.Web.Routing /// when no culture is specified, the current culture. /// If the provider is unable to provide a url, it returns . /// - public string GetMediaUrl(IPublishedContent content, string propertyAlias, UrlMode mode, string culture = null, Uri current = null) + public string GetMediaUrl(IPublishedContent content, UrlMode mode = UrlMode.Default, string culture = null, string propertyAlias = Constants.Conventions.Media.File, Uri current = null) { if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias)); if (content == null) return ""; + if (mode == UrlMode.Default) + mode = Mode; + // this the ONLY place where we deal with default culture - IMediaUrlProvider always receive a culture // be nice with tests, assume things can be null, ultimately fall back to invariant // (but only for variant content of course) diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 3947adc0e5..664818bda6 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -198,7 +198,7 @@ namespace Umbraco.Web /// The url for the content. public string Url(int contentId, string culture = null) { - return UrlProvider.GetUrl(contentId, culture); + return UrlProvider.GetUrl(contentId, culture: culture); } /// @@ -209,7 +209,7 @@ namespace Umbraco.Web /// The url for the content. public string Url(Guid contentId, string culture = null) { - return UrlProvider.GetUrl(contentId, culture); + return UrlProvider.GetUrl(contentId, culture: culture); } /// From bb0331e9ccac7b7f6b6667d85f04d3576db2603c Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 24 Apr 2019 14:25:41 +0200 Subject: [PATCH 21/75] Move IPublishedContent.Url() to ext method --- .../PublishedContent/IPublishedContent.cs | 14 ------- .../PublishedContentWrapped.cs | 3 -- .../PublishedContentCacheTests.cs | 4 +- .../PublishedMediaCacheTests.cs | 6 +-- .../DictionaryPublishedContent.cs | 5 +-- .../PublishedContentCache.cs | 9 +---- .../PublishedMediaCache.cs | 3 +- .../PublishedMemberCache.cs | 16 +++----- .../PublishedSnapshotService.cs | 4 +- .../XmlPublishedContent.cs | 14 +++---- .../Published/NestedContentTests.cs | 7 ++-- .../PublishedContent/NuCacheTests.cs | 3 -- .../Routing/MediaUrlProviderTests.cs | 10 ++--- src/Umbraco.Tests/Routing/UrlProviderTests.cs | 6 +-- .../Scoping/ScopedNuCacheTests.cs | 3 +- .../ContentTypeServiceVariantsTests.cs | 5 +-- .../Mapping/RedirectUrlMapDefinition.cs | 2 +- .../Models/PublishedContentBase.cs | 33 ---------------- .../PublishedCache/NuCache/ContentNode.cs | 12 +++--- .../PublishedCache/NuCache/ContentNodeKit.cs | 5 +-- .../PublishedCache/NuCache/ContentStore.cs | 5 +-- .../PublishedCache/NuCache/MemberCache.cs | 13 +++---- .../NuCache/PublishedContent.cs | 14 ++----- .../PublishedCache/NuCache/PublishedMember.cs | 11 ++---- .../NuCache/PublishedSnapshotService.cs | 19 ++++------ .../PublishedCache/PublishedMember.cs | 4 +- src/Umbraco.Web/PublishedContentExtensions.cs | 38 +++++++++++++++++++ src/Umbraco.Web/PublishedElementExtensions.cs | 4 +- .../Routing/UrlProviderExtensions.cs | 2 +- 29 files changed, 105 insertions(+), 169 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 6917538331..93487b6492 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -93,20 +93,6 @@ namespace Umbraco.Core.Models.PublishedContent /// DateTime UpdateDate { get; } - /// - /// Gets the url of the content item. - /// - /// - /// If the content item is a document, then this method returns the url of the - /// document. If it is a media, then this methods return the media url for the - /// 'umbracoFile' property. Use the MediaUrl() method to get the media url for other - /// properties. - /// The value of this property is contextual. It depends on the 'current' request uri, - /// if any. In addition, when the content type is multi-lingual, this is the url for the - /// specified culture. Otherwise, it is the invariant url. - /// - string Url(string culture = null, UrlMode mode = UrlMode.Auto); - /// /// Gets the culture date of the content item. /// diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index b2feec38c6..3da690a7e2 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -93,9 +93,6 @@ namespace Umbraco.Core.Models.PublishedContent /// public virtual DateTime UpdateDate => _content.UpdateDate; - /// - public virtual string Url(string culture = null, UrlMode mode = UrlMode.Auto) => _content.Url(culture, mode); - /// public DateTime CultureDate(string culture = null) => _content.CultureDate(culture); diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs index bb46b369c5..be160a483c 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs @@ -68,9 +68,9 @@ namespace Umbraco.Tests.Cache.PublishedCache var appCache = new DictionaryAppCache(); var domainCache = new DomainCache(ServiceContext.DomainService, DefaultCultureAccessor); var publishedShapshot = new PublishedSnapshot( - new PublishedContentCache(xmlStore, domainCache, appCache, globalSettings, new SiteDomainHelper(), umbracoContextAccessor, ContentTypesCache, null, null), + new PublishedContentCache(xmlStore, domainCache, appCache, globalSettings, ContentTypesCache, null, null), new PublishedMediaCache(xmlStore, ServiceContext.MediaService, ServiceContext.UserService, appCache, ContentTypesCache, Factory.GetInstance(), umbracoContextAccessor), - new PublishedMemberCache(null, appCache, Current.Services.MemberService, ContentTypesCache, umbracoContextAccessor), + new PublishedMemberCache(null, appCache, Current.Services.MemberService, ContentTypesCache), domainCache); var publishedSnapshotService = new Mock(); publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedShapshot); diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index bcd34ef2e3..d31401c725 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -323,8 +323,7 @@ namespace Umbraco.Tests.Cache.PublishedCache // no xpath null, // not from examine - false, - _umbracoContextAccessor), + false), //callback to get the children (dd, n) => children, // callback to get a property @@ -334,8 +333,7 @@ namespace Umbraco.Tests.Cache.PublishedCache // no xpath null, // not from examine - false, - _umbracoContextAccessor); + false); return dicDoc; } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs index 811851224c..3a39d23ccb 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs @@ -8,7 +8,6 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web; using Umbraco.Web.Composing; using Umbraco.Web.Models; using Umbraco.Web.PublishedCache; @@ -40,9 +39,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache IAppCache appCache, PublishedContentTypeCache contentTypeCache, XPathNavigator nav, - bool fromExamine, - IUmbracoContextAccessor umbracoContextAccessor) - :base(umbracoContextAccessor) + bool fromExamine) { if (valueDictionary == null) throw new ArgumentNullException(nameof(valueDictionary)); if (getParent == null) throw new ArgumentNullException(nameof(getParent)); diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs index 8b6ac372ec..33348d071a 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs @@ -9,7 +9,6 @@ using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Xml; -using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -19,7 +18,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache { private readonly IAppCache _appCache; private readonly IGlobalSettings _globalSettings; - private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly RoutesCache _routesCache; private readonly IDomainCache _domainCache; private readonly PublishedContentTypeCache _contentTypeCache; @@ -34,8 +32,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache IDomainCache domainCache, // an IDomainCache implementation IAppCache appCache, // an IAppCache that should be at request-level IGlobalSettings globalSettings, - ISiteDomainHelper siteDomainHelper, - IUmbracoContextAccessor umbracoContextAccessor, PublishedContentTypeCache contentTypeCache, // a PublishedContentType cache RoutesCache routesCache, // a RoutesCache string previewToken) // a preview token string (or null if not previewing) @@ -43,7 +39,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache { _appCache = appCache; _globalSettings = globalSettings; - _umbracoContextAccessor = umbracoContextAccessor; _routesCache = routesCache; // may be null for unit-testing _contentTypeCache = contentTypeCache; _domainCache = domainCache; @@ -318,13 +313,13 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private IPublishedContent ConvertToDocument(XmlNode xmlNode, bool isPreviewing) { - return xmlNode == null ? null : XmlPublishedContent.Get(xmlNode, isPreviewing, _appCache, _contentTypeCache,_umbracoContextAccessor); + return xmlNode == null ? null : XmlPublishedContent.Get(xmlNode, isPreviewing, _appCache, _contentTypeCache); } private IEnumerable ConvertToDocuments(XmlNodeList xmlNodes, bool isPreviewing) { return xmlNodes.Cast() - .Select(xmlNode => XmlPublishedContent.Get(xmlNode, isPreviewing, _appCache, _contentTypeCache, _umbracoContextAccessor)); + .Select(xmlNode => XmlPublishedContent.Get(xmlNode, isPreviewing, _appCache, _contentTypeCache)); } #endregion diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs index 7ebb026a21..ba43921f1c 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs @@ -674,8 +674,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache _appCache, _contentTypeCache, cacheValues.XPath, // though, outside of tests, that should be null - cacheValues.FromExamine, - _umbracoContextAccessor + cacheValues.FromExamine ); return content.CreateModel(); } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs index c28575f83d..19328c241e 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs @@ -6,7 +6,6 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Security; using Umbraco.Core.Services; -using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Security; @@ -18,16 +17,13 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private readonly IAppCache _requestCache; private readonly XmlStore _xmlStore; private readonly PublishedContentTypeCache _contentTypeCache; - private readonly IUmbracoContextAccessor _umbracoContextAccessor; - public PublishedMemberCache(XmlStore xmlStore, IAppCache requestCache, IMemberService memberService, - PublishedContentTypeCache contentTypeCache, IUmbracoContextAccessor umbracoContextAccessor) + public PublishedMemberCache(XmlStore xmlStore, IAppCache requestCache, IMemberService memberService, PublishedContentTypeCache contentTypeCache) { _requestCache = requestCache; _memberService = memberService; _xmlStore = xmlStore; _contentTypeCache = contentTypeCache; - _umbracoContextAccessor = umbracoContextAccessor; } public IPublishedContent GetByProviderKey(object key) @@ -44,7 +40,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var result = _memberService.GetByProviderKey(key); if (result == null) return null; var type = _contentTypeCache.Get(PublishedItemType.Member, result.ContentTypeId); - return new PublishedMember(result, type, _umbracoContextAccessor).CreateModel(); + return new PublishedMember(result, type).CreateModel(); }); } @@ -62,7 +58,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var result = _memberService.GetById(memberId); if (result == null) return null; var type = _contentTypeCache.Get(PublishedItemType.Member, result.ContentTypeId); - return new PublishedMember(result, type, _umbracoContextAccessor).CreateModel(); + return new PublishedMember(result, type).CreateModel(); }); } @@ -80,7 +76,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var result = _memberService.GetByUsername(username); if (result == null) return null; var type = _contentTypeCache.Get(PublishedItemType.Member, result.ContentTypeId); - return new PublishedMember(result, type, _umbracoContextAccessor).CreateModel(); + return new PublishedMember(result, type).CreateModel(); }); } @@ -98,14 +94,14 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var result = _memberService.GetByEmail(email); if (result == null) return null; var type = _contentTypeCache.Get(PublishedItemType.Member, result.ContentTypeId); - return new PublishedMember(result, type, _umbracoContextAccessor).CreateModel(); + return new PublishedMember(result, type).CreateModel(); }); } public IPublishedContent GetByMember(IMember member) { var type = _contentTypeCache.Get(PublishedItemType.Member, member.ContentTypeId); - return new PublishedMember(member, type, _umbracoContextAccessor).CreateModel(); + return new PublishedMember(member, type).CreateModel(); } public XPathNavigator CreateNavigator() diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshotService.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshotService.cs index 4a201ae44c..394a33d777 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshotService.cs @@ -145,9 +145,9 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var domainCache = new DomainCache(_domainService, _defaultCultureAccessor); return new PublishedSnapshot( - new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _siteDomainHelper,_umbracoContextAccessor, _contentTypeCache, _routesCache, previewToken), + new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _contentTypeCache, _routesCache, previewToken), new PublishedMediaCache(_xmlStore, _mediaService, _userService, _requestCache, _contentTypeCache, _entitySerializer, _umbracoContextAccessor), - new PublishedMemberCache(_xmlStore, _requestCache, _memberService, _contentTypeCache, _umbracoContextAccessor), + new PublishedMemberCache(_xmlStore, _requestCache, _memberService, _contentTypeCache), domainCache); } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index 7d24eec1e9..2edb2bae32 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -26,23 +26,19 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache XmlNode xmlNode, bool isPreviewing, IAppCache appCache, - PublishedContentTypeCache contentTypeCache, - IUmbracoContextAccessor umbracoContextAccessor) - :base(umbracoContextAccessor) + PublishedContentTypeCache contentTypeCache) { _xmlNode = xmlNode; _isPreviewing = isPreviewing; _appCache = appCache; _contentTypeCache = contentTypeCache; - _umbracoContextAccessor = umbracoContextAccessor; } private readonly XmlNode _xmlNode; private readonly bool _isPreviewing; private readonly IAppCache _appCache; // at snapshot/request level (see PublishedContentCache) private readonly PublishedContentTypeCache _contentTypeCache; - private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly object _initializeLock = new object(); @@ -256,7 +252,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache if (parent == null) return; if (parent.Attributes?.GetNamedItem("isDoc") != null) - _parent = Get(parent, _isPreviewing, _appCache, _contentTypeCache, _umbracoContextAccessor); + _parent = Get(parent, _isPreviewing, _appCache, _contentTypeCache); _parentInitialized = true; } @@ -413,7 +409,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var iterator = nav.Select(expr); _children = iterator.Cast() - .Select(n => Get(((IHasXmlNode) n).GetNode(), _isPreviewing, _appCache, _contentTypeCache, _umbracoContextAccessor)) + .Select(n => Get(((IHasXmlNode) n).GetNode(), _isPreviewing, _appCache, _contentTypeCache)) .OrderBy(x => x.SortOrder) .ToList(); @@ -433,7 +429,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache /// sure that we create only one instance of each for the duration of a request. The /// returned IPublishedContent is a model, if models are enabled. public static IPublishedContent Get(XmlNode node, bool isPreviewing, IAppCache appCache, - PublishedContentTypeCache contentTypeCache, IUmbracoContextAccessor umbracoContextAccessor) + PublishedContentTypeCache contentTypeCache) { // only 1 per request @@ -441,7 +437,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache var id = attrs?.GetNamedItem("id").Value; if (id.IsNullOrWhiteSpace()) throw new InvalidOperationException("Node has no ID attribute."); var key = CacheKeyPrefix + id; // dont bother with preview, wont change during request in Xml cache - return (IPublishedContent) appCache.Get(key, () => (new XmlPublishedContent(node, isPreviewing, appCache, contentTypeCache, umbracoContextAccessor)).CreateModel()); + return (IPublishedContent) appCache.Get(key, () => (new XmlPublishedContent(node, isPreviewing, appCache, contentTypeCache)).CreateModel()); } public static void ClearRequest() diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 5e024c2a72..a1b736ae90 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -172,7 +172,7 @@ namespace Umbraco.Tests.Published new TestPublishedProperty(contentType1.GetPropertyType("property1"), $@"[ {{ ""key"": ""{keyA}"", ""propertyN1"": ""foo"", ""ncContentTypeAlias"": ""contentN1"" }} ]") - }, Mock.Of()); + }); var value = content.Value("property1"); // nested single converter returns proper TestModel value @@ -200,8 +200,7 @@ namespace Umbraco.Tests.Published {{ ""key"": ""{keyA}"", ""propertyN1"": ""foo"", ""ncContentTypeAlias"": ""contentN1"" }}, {{ ""key"": ""{keyB}"", ""propertyN1"": ""bar"", ""ncContentTypeAlias"": ""contentN1"" }} ]") - }, - Mock.Of()); + }); var value = content.Value("property2"); // nested many converter returns proper IEnumerable value @@ -261,7 +260,7 @@ namespace Umbraco.Tests.Published class TestPublishedContent : PublishedContentBase { - public TestPublishedContent(IPublishedContentType contentType, Guid key, IEnumerable properties, IUmbracoContextAccessor umbracoContextAccessor): base(umbracoContextAccessor) + public TestPublishedContent(IPublishedContentType contentType, Guid key, IEnumerable properties) { ContentType = contentType; Key = key; diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 7d5fb8e736..dc035c1645 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -24,7 +24,6 @@ using Umbraco.Web.Cache; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.NuCache; using Umbraco.Web.PublishedCache.NuCache.DataSource; -using Umbraco.Web.Routing; namespace Umbraco.Tests.PublishedContent { @@ -171,7 +170,6 @@ namespace Umbraco.Tests.PublishedContent null, new TestPublishedSnapshotAccessor(), _variationAccesor, - Mock.Of(), Mock.Of(), scopeProvider, Mock.Of(), @@ -180,7 +178,6 @@ namespace Umbraco.Tests.PublishedContent new TestDefaultCultureAccessor(), dataSource, globalSettings, - new SiteDomainHelper(), Mock.Of(), Mock.Of(), new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs index 268e4e9d85..9eee6eb32d 100644 --- a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -45,7 +45,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.UploadField, expected, null); - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlMode.Auto, null, null); + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Auto); Assert.AreEqual(expected, resolvedUrl); } @@ -64,7 +64,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.ImageCropper, imageCropperValue, configuration); - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlMode.Auto, null, null); + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Auto); Assert.AreEqual(expected, resolvedUrl); } @@ -78,7 +78,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("http://localhost", mediaUrlProviders: new[] { _mediaUrlProvider }); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.UploadField, mediaUrl, null); - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlMode.Absolute, null, null); + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Absolute); Assert.AreEqual(expected, resolvedUrl); } @@ -89,7 +89,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.Boolean, "0", null); - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "test", UrlMode.Absolute, null, null); + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Absolute, propertyAlias: "test"); Assert.AreEqual(string.Empty, resolvedUrl); } @@ -116,7 +116,7 @@ namespace Umbraco.Tests.Routing var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), new [] { umbracoFilePropertyType }, ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) {Properties = new[] {property}}; - var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlMode.Auto, "da", null); + var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Auto, "da"); Assert.AreEqual(daMediaUrl, resolvedUrl); } diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 3f39e4d4e8..ca13e06f0a 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -185,7 +185,7 @@ namespace Umbraco.Tests.Routing snapshotService: snapshotService.Object); //even though we are asking for a specific culture URL, there are no domains assigned so all that can be returned is a normal relative url. - var url = umbracoContext.UrlProvider.GetUrl(1234, "fr-FR"); + var url = umbracoContext.UrlProvider.GetUrl(1234, culture: "fr-FR"); Assert.AreEqual("/home/test-fr/", url); } @@ -239,7 +239,7 @@ namespace Umbraco.Tests.Routing snapshotService: snapshotService.Object); - var url = umbracoContext.UrlProvider.GetUrl(1234, "fr-FR"); + var url = umbracoContext.UrlProvider.GetUrl(1234, culture: "fr-FR"); Assert.AreEqual("/home/test-fr/", url); } @@ -293,7 +293,7 @@ namespace Umbraco.Tests.Routing snapshotService: snapshotService.Object); - var url = umbracoContext.UrlProvider.GetUrl(1234, "fr-FR"); + var url = umbracoContext.UrlProvider.GetUrl(1234, culture: "fr-FR"); //the current uri is not the culture specific domain we want, so the result is an absolute path to the culture specific domain Assert.AreEqual("http://example.fr/home/test-fr/", url); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 3709490697..397a22fc62 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -92,13 +92,12 @@ namespace Umbraco.Tests.Scoping null, publishedSnapshotAccessor, Mock.Of(), - Mock.Of(), Logger, ScopeProvider, documentRepository, mediaRepository, memberRepository, DefaultCultureAccessor, new DatabaseDataSource(), - Factory.GetInstance(), new SiteDomainHelper(), + Factory.GetInstance(), Factory.GetInstance(), Mock.Of(), new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index c70b96a175..3121988bfe 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -18,11 +18,9 @@ using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Core.Sync; using Umbraco.Tests.Testing; -using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.NuCache; using Umbraco.Web.PublishedCache.NuCache.DataSource; -using Umbraco.Web.Routing; namespace Umbraco.Tests.Services { @@ -65,13 +63,12 @@ namespace Umbraco.Tests.Services null, publishedSnapshotAccessor, Mock.Of(), - Mock.Of(), Logger, ScopeProvider, documentRepository, mediaRepository, memberRepository, DefaultCultureAccessor, new DatabaseDataSource(), - Factory.GetInstance(), new SiteDomainHelper(), + Factory.GetInstance(), Factory.GetInstance(), Mock.Of(), new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); diff --git a/src/Umbraco.Web/Models/Mapping/RedirectUrlMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/RedirectUrlMapDefinition.cs index 73123a0407..e773fcfee5 100644 --- a/src/Umbraco.Web/Models/Mapping/RedirectUrlMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/RedirectUrlMapDefinition.cs @@ -26,7 +26,7 @@ namespace Umbraco.Web.Models.Mapping target.ContentId = source.ContentId; target.CreateDateUtc = source.CreateDateUtc; target.Culture = source.Culture; - target.DestinationUrl = source.ContentId > 0 ? UmbracoContext?.UrlProvider?.GetUrl(source.ContentId, source.Culture) : "#"; + target.DestinationUrl = source.ContentId > 0 ? UmbracoContext?.UrlProvider?.GetUrl(source.ContentId, culture: source.Culture) : "#"; target.OriginalUrl = UmbracoContext?.UrlProvider?.GetUrlFromRoute(source.ContentId, source.Url, source.Culture); target.RedirectId = source.Key; } diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 25c84c97e5..935c594285 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -15,13 +15,6 @@ namespace Umbraco.Web.Models [DebuggerDisplay("Content Id: {Id}}")] public abstract class PublishedContentBase : IPublishedContent { - protected PublishedContentBase(IUmbracoContextAccessor umbracoContextAccessor) - { - UmbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); - } - - protected IUmbracoContextAccessor UmbracoContextAccessor { get; } - #region ContentType public abstract IPublishedContentType ContentType { get; } @@ -76,32 +69,6 @@ namespace Umbraco.Web.Models /// public abstract DateTime UpdateDate { get; } - /// - /// - /// The url of documents are computed by the document url providers. The url of medias are computed by the media url providers. - /// - public virtual string Url(string culture = null, UrlMode mode = UrlMode.Auto) - { - var umbracoContext = UmbracoContextAccessor.UmbracoContext; - - if (umbracoContext == null) - throw new InvalidOperationException("Cannot compute Url for a content item when UmbracoContext is null."); - if (umbracoContext.UrlProvider == null) - throw new InvalidOperationException("Cannot compute Url for a content item when UmbracoContext.UrlProvider is null."); - - switch (ContentType.ItemType) - { - case PublishedItemType.Content: - return umbracoContext.UrlProvider.GetUrl(this, mode, culture); - - case PublishedItemType.Media: - return umbracoContext.UrlProvider.GetMediaUrl(this, mode, culture, Constants.Conventions.Media.File); - - default: - throw new NotSupportedException(); - } - } - /// public abstract DateTime CultureDate(string culture = null); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs index 7c9a739448..1e8d5ddfc9 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.PublishedCache.NuCache.DataSource; @@ -40,11 +39,10 @@ namespace Umbraco.Web.PublishedCache.NuCache DateTime createDate, int creatorId, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, - IVariationContextAccessor variationContextAccessor, - IUmbracoContextAccessor umbracoContextAccessor) + IVariationContextAccessor variationContextAccessor) : this(id, uid, level, path, sortOrder, parentContentId, createDate, creatorId) { - SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor, variationContextAccessor, umbracoContextAccessor); + SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor, variationContextAccessor); } // 2-phases ctor, phase 1 @@ -66,7 +64,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } // two-phase ctor, phase 2 - public void SetContentTypeAndData(IPublishedContentType contentType, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IUmbracoContextAccessor umbracoContextAccessor) + public void SetContentTypeAndData(IPublishedContentType contentType, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor) { ContentType = contentType; @@ -75,13 +73,13 @@ namespace Umbraco.Web.PublishedCache.NuCache if (draftData != null) { - DraftContent = new PublishedContent(this, draftData, publishedSnapshotAccessor, variationContextAccessor, umbracoContextAccessor); + DraftContent = new PublishedContent(this, draftData, publishedSnapshotAccessor, variationContextAccessor); DraftModel = DraftContent.CreateModel(); } if (publishedData != null) { - PublishedContent = new PublishedContent(this, publishedData, publishedSnapshotAccessor, variationContextAccessor, umbracoContextAccessor); + PublishedContent = new PublishedContent(this, publishedData, publishedSnapshotAccessor, variationContextAccessor); PublishedModel = PublishedContent.CreateModel(); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs index 08557fe3db..5d757fff7b 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs @@ -21,8 +21,7 @@ namespace Umbraco.Web.PublishedCache.NuCache IPublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, - bool canBePublished, - IUmbracoContextAccessor umbracoContextAccessor) + bool canBePublished) { var draftData = DraftData; @@ -35,7 +34,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (draftData == null && !canBePublished) draftData = PublishedData; - Node.SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor, variationContextAccessor,umbracoContextAccessor); + 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 9b1955fe43..298b98ca05 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -21,7 +21,6 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly IVariationContextAccessor _variationContextAccessor; - private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly ConcurrentDictionary> _contentNodes; private LinkedNode _root; private readonly ConcurrentDictionary> _contentTypesById; @@ -49,13 +48,11 @@ namespace Umbraco.Web.PublishedCache.NuCache public ContentStore( IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, - IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, BPlusTree localDb = null) { _publishedSnapshotAccessor = publishedSnapshotAccessor; _variationContextAccessor = variationContextAccessor; - _umbracoContextAccessor = umbracoContextAccessor; _logger = logger; _localDb = localDb; @@ -449,7 +446,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var canBePublished = ParentPublishedLocked(kit); // and use - kit.Build(link.Value, _publishedSnapshotAccessor, _variationContextAccessor, canBePublished, _umbracoContextAccessor); + kit.Build(link.Value, _publishedSnapshotAccessor, _variationContextAccessor, canBePublished); return true; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs index 5164b2b3bf..2e196f629e 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs @@ -8,7 +8,6 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Security; using Umbraco.Core.Services; -using Umbraco.Core.Services.Implement; using Umbraco.Core.Xml.XPath; using Umbraco.Web.PublishedCache.NuCache.Navigable; @@ -19,7 +18,6 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; public readonly IVariationContextAccessor VariationContextAccessor; - private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IEntityXmlSerializer _entitySerializer; private readonly IAppCache _snapshotCache; private readonly IMemberService _memberService; @@ -27,12 +25,11 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly bool _previewDefault; public MemberCache(bool previewDefault, IAppCache snapshotCache, IMemberService memberService, PublishedContentTypeCache contentTypeCache, - IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IUmbracoContextAccessor umbracoContextAccessor, IEntityXmlSerializer entitySerializer) + IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IEntityXmlSerializer entitySerializer) { _snapshotCache = snapshotCache; _publishedSnapshotAccessor = publishedSnapshotAccessor; VariationContextAccessor = variationContextAccessor; - _umbracoContextAccessor = umbracoContextAccessor; _entitySerializer = entitySerializer; _memberService = memberService; _previewDefault = previewDefault; @@ -68,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, VariationContextAccessor, _umbracoContextAccessor); + : 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, VariationContextAccessor, _umbracoContextAccessor)); + PublishedMember.Create(member, GetContentType(member.ContentTypeId), previewing, _publishedSnapshotAccessor, VariationContextAccessor)); } public IPublishedContent /*IPublishedMember*/ GetByProviderKey(object key) @@ -110,7 +107,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public IPublishedContent /*IPublishedMember*/ GetByMember(IMember member) { - return PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, VariationContextAccessor, _umbracoContextAccessor); + return PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, VariationContextAccessor); } public IEnumerable GetAtRoot(bool preview) @@ -118,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, VariationContextAccessor, _umbracoContextAccessor)); + return members.Select(m => PublishedMember.Create(m, GetContentType(m.ContentTypeId), preview, _publishedSnapshotAccessor, VariationContextAccessor)); } public XPathNavigator CreateNavigator() diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 0a55049f9f..c227c75952 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -22,9 +22,7 @@ namespace Umbraco.Web.PublishedCache.NuCache ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, - IVariationContextAccessor variationContextAccessor, - IUmbracoContextAccessor umbracoContextAccessor) - : base(umbracoContextAccessor) + IVariationContextAccessor variationContextAccessor) { _contentNode = contentNode ?? throw new ArgumentNullException(nameof(contentNode)); ContentData = contentData ?? throw new ArgumentNullException(nameof(contentData)); @@ -69,7 +67,6 @@ namespace Umbraco.Web.PublishedCache.NuCache // used when cloning in ContentNode public PublishedContent(ContentNode contentNode, PublishedContent origin) - : base(origin.UmbracoContextAccessor) { _contentNode = contentNode; _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; @@ -86,10 +83,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } // clone for previewing as draft a published content that is published and has no draft - private PublishedContent( - PublishedContent origin, - IUmbracoContextAccessor umbracoContextAccessor) - : base(umbracoContextAccessor) + private PublishedContent(PublishedContent origin) { _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; VariationContextAccessor = origin.VariationContextAccessor; @@ -406,8 +400,8 @@ namespace Umbraco.Web.PublishedCache.NuCache return this; var cache = GetAppropriateCache(); - if (cache == null) return new PublishedContent(this, UmbracoContextAccessor).CreateModel(); - return (IPublishedContent)cache.Get(AsPreviewingCacheKey, () => new PublishedContent(this, UmbracoContextAccessor).CreateModel()); + if (cache == null) return new PublishedContent(this).CreateModel(); + return (IPublishedContent)cache.Get(AsPreviewingCacheKey, () => new PublishedContent(this).CreateModel()); } // used by Navigable.Source,... diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs index 4bfcbb2a3d..dc608fe391 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs @@ -20,10 +20,8 @@ namespace Umbraco.Web.PublishedCache.NuCache ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, - IVariationContextAccessor variationContextAccessor, - IUmbracoContextAccessor umbracoContextAccessor - ) - : base(contentNode, contentData, publishedSnapshotAccessor, variationContextAccessor, umbracoContextAccessor) + IVariationContextAccessor variationContextAccessor) + : base(contentNode, contentData, publishedSnapshotAccessor, variationContextAccessor) { _member = member; } @@ -33,8 +31,7 @@ namespace Umbraco.Web.PublishedCache.NuCache IPublishedContentType contentType, bool previewing, IPublishedSnapshotAccessor publishedSnapshotAccessor, - IVariationContextAccessor variationContextAccessor, - IUmbracoContextAccessor umbracoContextAccessor) + IVariationContextAccessor variationContextAccessor) { var d = new ContentData { @@ -50,7 +47,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, variationContextAccessor, umbracoContextAccessor).CreateModel(); + return new PublishedMember(member, n, d, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); } private static Dictionary GetPropertyValues(IPublishedContentType contentType, IMember member) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index f847a9f2ce..5a3672ed57 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -34,7 +34,6 @@ namespace Umbraco.Web.PublishedCache.NuCache { private readonly ServiceContext _serviceContext; private readonly IPublishedContentTypeFactory _publishedContentTypeFactory; - private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IScopeProvider _scopeProvider; private readonly IDataSource _dataSource; private readonly ILogger _logger; @@ -42,7 +41,6 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IMediaRepository _mediaRepository; private readonly IMemberRepository _memberRepository; private readonly IGlobalSettings _globalSettings; - private readonly ISiteDomainHelper _siteDomainHelper; private readonly IEntityXmlSerializer _entitySerializer; private readonly IDefaultCultureAccessor _defaultCultureAccessor; private readonly UrlSegmentProviderCollection _urlSegmentProviders; @@ -83,11 +81,10 @@ namespace Umbraco.Web.PublishedCache.NuCache public PublishedSnapshotService(Options options, IMainDom mainDom, IRuntimeState runtime, ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, IdkMap idkMap, - IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, - IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IScopeProvider scopeProvider, + IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, ILogger logger, IScopeProvider scopeProvider, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IDefaultCultureAccessor defaultCultureAccessor, - IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, + IDataSource dataSource, IGlobalSettings globalSettings, IEntityXmlSerializer entitySerializer, IPublishedModelFactory publishedModelFactory, UrlSegmentProviderCollection urlSegmentProviders) : base(publishedSnapshotAccessor, variationContextAccessor) @@ -97,7 +94,6 @@ namespace Umbraco.Web.PublishedCache.NuCache _serviceContext = serviceContext; _publishedContentTypeFactory = publishedContentTypeFactory; - _umbracoContextAccessor = umbracoContextAccessor; _dataSource = dataSource; _logger = logger; _scopeProvider = scopeProvider; @@ -106,7 +102,6 @@ namespace Umbraco.Web.PublishedCache.NuCache _memberRepository = memberRepository; _defaultCultureAccessor = defaultCultureAccessor; _globalSettings = globalSettings; - _siteDomainHelper = siteDomainHelper; _urlSegmentProviders = urlSegmentProviders; // we need an Xml serializer here so that the member cache can support XPath, @@ -156,13 +151,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 databases or it should populate them from sql - _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, _umbracoContextAccessor, logger, _localContentDb); - _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, _umbracoContextAccessor, logger, _localMediaDb); + _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localContentDb); + _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localMediaDb); } else { - _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, _umbracoContextAccessor, logger); - _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, _umbracoContextAccessor, logger); + _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger); + _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger); } _domainStore = new SnapDictionary(); @@ -1077,7 +1072,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainCache, _globalSettings), MediaCache = new MediaCache(previewDefault, mediaSnap, snapshotCache, elementsCache), - MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, memberTypeCache, PublishedSnapshotAccessor, VariationContextAccessor, _umbracoContextAccessor, _entitySerializer), + MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, memberTypeCache, PublishedSnapshotAccessor, VariationContextAccessor, _entitySerializer), DomainCache = domainCache, SnapshotCache = snapshotCache, ElementsCache = elementsCache diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index 08e00c1f17..9c6f60da97 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -21,9 +21,7 @@ namespace Umbraco.Web.PublishedCache public PublishedMember( IMember member, - IPublishedContentType publishedMemberType, - IUmbracoContextAccessor umbracoContextAccessor) - :base(umbracoContextAccessor) + IPublishedContentType publishedMemberType) { _member = member ?? throw new ArgumentNullException(nameof(member)); _membershipUser = member; diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index b31527eb57..eb38826193 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -1208,5 +1208,43 @@ namespace Umbraco.Web } #endregion + + #region Url + + /// + /// Gets the url of the content item. + /// + /// + /// If the content item is a document, then this method returns the url of the + /// document. If it is a media, then this methods return the media url for the + /// 'umbracoFile' property. Use the MediaUrl() method to get the media url for other + /// properties. + /// The value of this property is contextual. It depends on the 'current' request uri, + /// if any. In addition, when the content type is multi-lingual, this is the url for the + /// specified culture. Otherwise, it is the invariant url. + /// + public static string Url(this IPublishedContent content, string culture = null, UrlMode mode = UrlMode.Auto) + { + var umbracoContext = Composing.Current.UmbracoContext; + + if (umbracoContext == null) + throw new InvalidOperationException("Cannot resolve a Url when Current.UmbracoContext is null."); + if (umbracoContext.UrlProvider == null) + throw new InvalidOperationException("Cannot resolve a Url when Current.UmbracoContext.UrlProvider is null."); + + switch (content.ContentType.ItemType) + { + case PublishedItemType.Content: + return umbracoContext.UrlProvider.GetUrl(content, mode, culture); + + case PublishedItemType.Media: + return umbracoContext.UrlProvider.GetMediaUrl(content, mode, culture, Constants.Conventions.Media.File); + + default: + throw new NotSupportedException(); + } + } + + #endregion } } diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index 2de8259d3c..c35c85c606 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -209,9 +209,9 @@ namespace Umbraco.Web var umbracoContext = Composing.Current.UmbracoContext; if (umbracoContext == null) - throw new InvalidOperationException("Cannot resolve a Url for a content item when Current.UmbracoContext is null."); + throw new InvalidOperationException("Cannot resolve a Url when Current.UmbracoContext is null."); if (umbracoContext.UrlProvider == null) - throw new InvalidOperationException("Cannot resolve a Url for a content item when Current.UmbracoContext.UrlProvider is null."); + throw new InvalidOperationException("Cannot resolve a Url when Current.UmbracoContext.UrlProvider is null."); return umbracoContext.UrlProvider.GetMediaUrl(content, mode, culture, propertyAlias); } diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs index 4b66d56830..27a27399bf 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs @@ -109,7 +109,7 @@ namespace Umbraco.Web.Routing string url; try { - url = umbracoContext.UrlProvider.GetUrl(content.Id, culture); + url = umbracoContext.UrlProvider.GetUrl(content.Id, culture: culture); } catch (Exception ex) { From c4514989753ae2e401833c8fb7725ee59decca26 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 29 May 2019 15:13:28 +0200 Subject: [PATCH 22/75] Fix v8/dev merge --- src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs | 2 +- src/Umbraco.Web/Templates/TemplateUtilities.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index 3a5405548b..c0a6e97dda 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -99,7 +99,7 @@ namespace Umbraco.Tests.Web var snapshotService = Mock.Of(); Mock.Get(snapshotService).Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(snapshot); var media = Mock.Of(); - Mock.Get(media).Setup(x => x.Url).Returns("/media/1001/my-image.jpg"); + Mock.Get(media).Setup(x => x.Url(It.IsAny(), It.IsAny())).Returns("/media/1001/my-image.jpg"); var mediaCache = Mock.Of(); Mock.Get(mediaCache).Setup(x => x.GetById(It.IsAny())).Returns(media); diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index 040c4e128a..06706e475b 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -60,7 +60,7 @@ namespace Umbraco.Web.Templates if (guidUdi.EntityType == Constants.UdiEntityType.Document) newLink = urlProvider.GetUrl(guidUdi.Guid); else if (guidUdi.EntityType == Constants.UdiEntityType.Media) - newLink = mediaCache.GetById(guidUdi.Guid)?.Url; + newLink = mediaCache.GetById(guidUdi.Guid)?.Url(); if (newLink == null) newLink = "#"; From 983144c3f98d54b46723e66ce711dc94193c6e6c Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 29 May 2019 16:48:20 +0200 Subject: [PATCH 23/75] Unit-test published content linked-list children --- .../PublishedContent/NuCacheChildrenTests.cs | 522 ++++++++++++++++++ .../Testing/Objects/TestDataSource.cs | 26 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../PublishedCache/NuCache/ContentNodeKit.cs | 9 + .../NuCache/DataSource/IDataSource.cs | 10 +- 5 files changed, 554 insertions(+), 14 deletions(-) create mode 100644 src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs new file mode 100644 index 0000000000..d9cb8dbbc2 --- /dev/null +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -0,0 +1,522 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using Moq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Composing; +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.Core.Services.Changes; +using Umbraco.Core.Strings; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing.Objects; +using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Web.Cache; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.PublishedCache.NuCache; +using Umbraco.Web.PublishedCache.NuCache.DataSource; + +namespace Umbraco.Tests.PublishedContent +{ + [TestFixture] + public class NuCacheChildrenTests + { + private IPublishedSnapshotService _snapshotService; + private IVariationContextAccessor _variationAccesor; + private IPublishedSnapshotAccessor _snapshotAccessor; + private ContentType _contentType; + private PropertyType _propertyType; + private TestDataSource _source; + + private void Init(IEnumerable kits) + { + Current.Reset(); + Current.UnlockConfigs(); + Current.Configs.Add(SettingsForTests.GenerateMockUmbracoSettings); + Current.Configs.Add(() => new GlobalSettings()); + var globalSettings = Current.Configs.Global(); + + // create a data source for NuCache + _source = new TestDataSource(kits); + + var runtime = Mock.Of(); + Mock.Get(runtime).Setup(x => x.Level).Returns(RuntimeLevel.Run); + + // create data types, property types and content types + var dataType = new DataType(new VoidEditor("Editor", Mock.Of())) { Id = 3 }; + + var dataTypes = new[] + { + dataType + }; + + _propertyType = new PropertyType("Umbraco.Void.Editor", ValueStorageType.Nvarchar) { Alias = "prop", DataTypeId = 3, Variations = ContentVariation.Nothing }; + _contentType = new ContentType(-1) { Id = 2, Alias = "ctype", Variations = ContentVariation.Nothing }; + _contentType.AddPropertyType(_propertyType); + + var contentTypes = new[] + { + _contentType + }; + + var contentTypeService = Mock.Of(); + Mock.Get(contentTypeService).Setup(x => x.GetAll()).Returns(contentTypes); + Mock.Get(contentTypeService).Setup(x => x.GetAll(It.IsAny())).Returns(contentTypes); + + var contentTypeServiceBaseFactory = Mock.Of(); + Mock.Get(contentTypeServiceBaseFactory).Setup(x => x.For(It.IsAny())).Returns(contentTypeService); + + var dataTypeService = Mock.Of(); + Mock.Get(dataTypeService).Setup(x => x.GetAll()).Returns(dataTypes); + + // create a service context + var serviceContext = ServiceContext.CreatePartial( + dataTypeService: dataTypeService, + memberTypeService: Mock.Of(), + memberService: Mock.Of(), + contentTypeService: contentTypeService, + localizationService: Mock.Of() + ); + + // create a scope provider + 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); + + // create a published content type factory + var contentTypeFactory = new PublishedContentTypeFactory( + Mock.Of(), + new PropertyValueConverterCollection(Array.Empty()), + dataTypeService); + + // create accessors + _variationAccesor = new TestVariationContextAccessor(); + _snapshotAccessor = new TestPublishedSnapshotAccessor(); + + // at last, create the complete NuCache snapshot service! + var options = new PublishedSnapshotService.Options { IgnoreLocalDb = true }; + _snapshotService = new PublishedSnapshotService(options, + null, + runtime, + serviceContext, + contentTypeFactory, + null, + _snapshotAccessor, + _variationAccesor, + Mock.Of(), + scopeProvider, + Mock.Of(), + Mock.Of(), + Mock.Of(), + new TestDefaultCultureAccessor(), + _source, + globalSettings, + Mock.Of(), + Mock.Of(), + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); + + // invariant is the current default + _variationAccesor.VariationContext = new VariationContext(); + } + + private IEnumerable GetKits() + { + var paths = new Dictionary { { -1, "-1" } }; + + ContentNodeKit CreateKit(int id, int parentId, int sortOrder) + { + if (!paths.TryGetValue(parentId, out var parentPath)) + throw new Exception("Unknown parent."); + + var path = paths[id] = parentPath + "," + id; + var level = path.Count(x => x == ','); + + return new ContentNodeKit + { + ContentTypeId = 2, + Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0), + DraftData = null, + PublishedData = new ContentData + { + Name = "N" + id, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = DateTime.Now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = new Dictionary() + } + }; + } + + yield return CreateKit(1, -1, 1); + yield return CreateKit(2, -1, 2); + yield return CreateKit(3, -1, 3); + + yield return CreateKit(4, 1, 1); + yield return CreateKit(5, 1, 2); + yield return CreateKit(6, 1, 3); + + yield return CreateKit(7, 2, 3); + yield return CreateKit(8, 2, 2); + yield return CreateKit(9, 2, 1); + + yield return CreateKit(10, 3, 1); + + yield return CreateKit(11, 4, 1); + yield return CreateKit(12, 4, 2); + } + + [Test] + public void EmptyTest() + { + Init(Enumerable.Empty()); + + var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); + _snapshotAccessor.PublishedSnapshot = snapshot; + + var documents = snapshot.Content.GetAtRoot().ToArray(); + Assert.AreEqual(0, documents.Length); + } + + [Test] + public void ChildrenTest() + { + Init(GetKits()); + + var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); + _snapshotAccessor.PublishedSnapshot = snapshot; + + var documents = snapshot.Content.GetAtRoot().ToArray(); + AssertDocuments(documents, "N1", "N2", "N3"); + + documents = snapshot.Content.GetById(1).Children().ToArray(); + AssertDocuments(documents, "N4", "N5", "N6"); + + documents = snapshot.Content.GetById(2).Children().ToArray(); + AssertDocuments(documents, "N9", "N8", "N7"); + + documents = snapshot.Content.GetById(3).Children().ToArray(); + AssertDocuments(documents, "N10"); + + documents = snapshot.Content.GetById(4).Children().ToArray(); + AssertDocuments(documents, "N11", "N12"); + + documents = snapshot.Content.GetById(10).Children().ToArray(); + AssertDocuments(documents); + } + + [Test] + public void ParentTest() + { + Init(GetKits()); + + var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); + _snapshotAccessor.PublishedSnapshot = snapshot; + + Assert.IsNull(snapshot.Content.GetById(1).Parent()); + Assert.IsNull(snapshot.Content.GetById(2).Parent()); + Assert.IsNull(snapshot.Content.GetById(3).Parent()); + + Assert.AreEqual(1, snapshot.Content.GetById(4).Parent()?.Id); + Assert.AreEqual(1, snapshot.Content.GetById(5).Parent()?.Id); + Assert.AreEqual(1, snapshot.Content.GetById(6).Parent()?.Id); + + Assert.AreEqual(2, snapshot.Content.GetById(7).Parent()?.Id); + Assert.AreEqual(2, snapshot.Content.GetById(8).Parent()?.Id); + Assert.AreEqual(2, snapshot.Content.GetById(9).Parent()?.Id); + + Assert.AreEqual(3, snapshot.Content.GetById(10).Parent()?.Id); + + Assert.AreEqual(4, snapshot.Content.GetById(11).Parent()?.Id); + Assert.AreEqual(4, snapshot.Content.GetById(12).Parent()?.Id); + } + + [Test] + public void MoveToRootTest() + { + Init(GetKits()); + + // get snapshot + var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); + _snapshotAccessor.PublishedSnapshot = snapshot; + + // do some changes + var kit = _source.Kits[10]; + _source.Kits[10] = new ContentNodeKit + { + ContentTypeId = 2, + Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), 1, "-1,10", 4, -1, DateTime.Now, 0), + DraftData = null, + PublishedData = new ContentData + { + Name = kit.PublishedData.Name, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = DateTime.Now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = new Dictionary() + } + }; + + // notify + _snapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(10, TreeChangeTypes.RefreshBranch) }, out _, out _); + + // changes that *I* make are immediately visible on the current snapshot + var documents = snapshot.Content.GetAtRoot().ToArray(); + AssertDocuments(documents, "N1", "N2", "N3", "N10"); + + documents = snapshot.Content.GetById(3).Children().ToArray(); + AssertDocuments(documents); + + Assert.IsNull(snapshot.Content.GetById(10).Parent()); + } + + [Test] + public void MoveFromRootTest() + { + Init(GetKits()); + + // get snapshot + var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); + _snapshotAccessor.PublishedSnapshot = snapshot; + + // do some changes + var kit = _source.Kits[1]; + _source.Kits[1] = new ContentNodeKit + { + ContentTypeId = 2, + Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), 1, "-1,3,10,1", 1, 10, DateTime.Now, 0), + DraftData = null, + PublishedData = new ContentData + { + Name = kit.PublishedData.Name, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = DateTime.Now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = new Dictionary() + } + }; + + // notify + _snapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(1, TreeChangeTypes.RefreshBranch) }, out _, out _); + + // changes that *I* make are immediately visible on the current snapshot + var documents = snapshot.Content.GetAtRoot().ToArray(); + AssertDocuments(documents, "N2", "N3"); + + documents = snapshot.Content.GetById(10).Children().ToArray(); + AssertDocuments(documents, "N1"); + + Assert.AreEqual(10, snapshot.Content.GetById(1).Parent()?.Id); + } + + [Test] + public void ReOrderTest() + { + Init(GetKits()); + + // get snapshot + var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); + _snapshotAccessor.PublishedSnapshot = snapshot; + + // do some changes + var kit = _source.Kits[7]; + _source.Kits[7] = new ContentNodeKit + { + ContentTypeId = 2, + Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, kit.Node.Path, 1, kit.Node.ParentContentId, DateTime.Now, 0), + DraftData = null, + PublishedData = new ContentData + { + Name = kit.PublishedData.Name, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = DateTime.Now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = new Dictionary() + } + }; + + kit = _source.Kits[8]; + _source.Kits[8] = new ContentNodeKit + { + ContentTypeId = 2, + Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, kit.Node.Path, 3, kit.Node.ParentContentId, DateTime.Now, 0), + DraftData = null, + PublishedData = new ContentData + { + Name = kit.PublishedData.Name, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = DateTime.Now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = new Dictionary() + } + }; + + kit = _source.Kits[9]; + _source.Kits[9] = new ContentNodeKit + { + ContentTypeId = 2, + Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, kit.Node.Path, 2, kit.Node.ParentContentId, DateTime.Now, 0), + DraftData = null, + PublishedData = new ContentData + { + Name = kit.PublishedData.Name, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = DateTime.Now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = new Dictionary() + } + }; + + // notify + _snapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(kit.Node.ParentContentId, TreeChangeTypes.RefreshBranch) }, out _, out _); + + // changes that *I* make are immediately visible on the current snapshot + var documents = snapshot.Content.GetById(kit.Node.ParentContentId).Children().ToArray(); + AssertDocuments(documents, "N7", "N9", "N8"); + } + + [Test] + public void MoveTest() + { + Init(GetKits()); + + // get snapshot + var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); + _snapshotAccessor.PublishedSnapshot = snapshot; + + // do some changes + var kit = _source.Kits[4]; + _source.Kits[4] = new ContentNodeKit + { + ContentTypeId = 2, + Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, kit.Node.Path, 2, kit.Node.ParentContentId, DateTime.Now, 0), + DraftData = null, + PublishedData = new ContentData + { + Name = kit.PublishedData.Name, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = DateTime.Now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = new Dictionary() + } + }; + + kit = _source.Kits[5]; + _source.Kits[5] = new ContentNodeKit + { + ContentTypeId = 2, + Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, kit.Node.Path, 3, kit.Node.ParentContentId, DateTime.Now, 0), + DraftData = null, + PublishedData = new ContentData + { + Name = kit.PublishedData.Name, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = DateTime.Now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = new Dictionary() + } + }; + + kit = _source.Kits[6]; + _source.Kits[6] = new ContentNodeKit + { + ContentTypeId = 2, + Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, kit.Node.Path, 4, kit.Node.ParentContentId, DateTime.Now, 0), + DraftData = null, + PublishedData = new ContentData + { + Name = kit.PublishedData.Name, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = DateTime.Now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = new Dictionary() + } + }; + + kit = _source.Kits[7]; + _source.Kits[7] = new ContentNodeKit + { + ContentTypeId = 2, + Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, "-1,1,7", 1, 1, DateTime.Now, 0), + DraftData = null, + PublishedData = new ContentData + { + Name = kit.PublishedData.Name, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = DateTime.Now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = new Dictionary() + } + }; + + // notify + _snapshotService.Notify(new[] + { + // removal must come first + new ContentCacheRefresher.JsonPayload(2, TreeChangeTypes.RefreshBranch), + new ContentCacheRefresher.JsonPayload(1, TreeChangeTypes.RefreshBranch) + }, out _, out _); + + // changes that *I* make are immediately visible on the current snapshot + var documents = snapshot.Content.GetById(1).Children().ToArray(); + AssertDocuments(documents, "N7", "N4", "N5", "N6"); + + documents = snapshot.Content.GetById(2).Children().ToArray(); + AssertDocuments(documents, "N9", "N8"); + + Assert.AreEqual(1, snapshot.Content.GetById(7).Parent()?.Id); + } + + private void AssertDocuments(IPublishedContent[] documents, params string[] names) + { + Assert.AreEqual(names.Length, documents.Length); + for (var i = 0; i < names.Length; i++) + Assert.AreEqual(names[i], documents[i].Name()); + } + } +} diff --git a/src/Umbraco.Tests/Testing/Objects/TestDataSource.cs b/src/Umbraco.Tests/Testing/Objects/TestDataSource.cs index 26bfff0e1a..72fb89ab82 100644 --- a/src/Umbraco.Tests/Testing/Objects/TestDataSource.cs +++ b/src/Umbraco.Tests/Testing/Objects/TestDataSource.cs @@ -9,30 +9,38 @@ namespace Umbraco.Tests.Testing.Objects { 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); + Kits = kits.ToDictionary(x => x.Node.Id, x => x); } + public Dictionary Kits { get; } + + // note: it is important to clone the returned kits, as the inner + // ContentNode is directly reused and modified by the snapshot service + public ContentNodeKit GetContentSource(IScope scope, int id) - => _kits.TryGetValue(id, out var kit) ? kit : default; + => Kits.TryGetValue(id, out var kit) ? kit.Clone() : default; public IEnumerable GetAllContentSources(IScope scope) - => _kits.Values; + => Kits.Values + .OrderBy(x => x.Node.Level) + .Select(x => x.Clone()); public IEnumerable GetBranchContentSources(IScope scope, int id) - { - throw new NotImplementedException(); - } + => Kits.Values + .Where(x => x.Node.Path.EndsWith("," + id) || x.Node.Path.Contains("," + id + ",")) + .OrderBy(x => x.Node.Level).ThenBy(x => x.Node.SortOrder) + .Select(x => x.Clone()); public IEnumerable GetTypeContentSources(IScope scope, IEnumerable ids) - => _kits.Values.Where(x => ids.Contains(x.ContentTypeId)); + => Kits.Values + .Where(x => ids.Contains(x.ContentTypeId)) + .Select(x => x.Clone()); public ContentNodeKit GetMediaSource(IScope scope, int id) { diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index ddd67df6e5..cd3155c7c3 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -139,6 +139,7 @@ + diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs index 5d757fff7b..61fb5c12a3 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs @@ -36,5 +36,14 @@ namespace Umbraco.Web.PublishedCache.NuCache Node.SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor, variationContextAccessor); } + + public ContentNodeKit Clone() + => new ContentNodeKit + { + ContentTypeId = ContentTypeId, + DraftData = DraftData, + PublishedData = PublishedData, + Node = new ContentNode(Node) + }; } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDataSource.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDataSource.cs index 323d954980..d4702cf7aa 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDataSource.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDataSource.cs @@ -9,13 +9,13 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource internal interface IDataSource { ContentNodeKit GetContentSource(IScope scope, int id); - IEnumerable GetAllContentSources(IScope scope); - IEnumerable GetBranchContentSources(IScope scope, int id); + IEnumerable GetAllContentSources(IScope scope); // must order by level + IEnumerable GetBranchContentSources(IScope scope, int id); // must order by level, sortOrder IEnumerable GetTypeContentSources(IScope scope, IEnumerable ids); ContentNodeKit GetMediaSource(IScope scope, int id); - IEnumerable GetAllMediaSources(IScope scope); - IEnumerable GetBranchMediaSources(IScope scope, int id); + IEnumerable GetAllMediaSources(IScope scope); // must order by level + IEnumerable GetBranchMediaSources(IScope scope, int id); // must order by level, sortOrder IEnumerable GetTypeMediaSources(IScope scope, IEnumerable ids); } -} \ No newline at end of file +} From c17dbbe141f2485a1a6ac0a8686303554cba72f8 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 6 Jun 2019 12:16:55 +0200 Subject: [PATCH 24/75] Fix merge --- .../ValueConverters/MultiNodeTreePickerValueConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs index d402a0e714..47f8797295 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs @@ -153,7 +153,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return content; } - private static bool IsSingleNodePicker(PublishedPropertyType propertyType) + private static bool IsSingleNodePicker(IPublishedPropertyType propertyType) { return propertyType.DataType.ConfigurationAs().MaxNumber == 1; } From 7fd6bfa16337d26c4b184b9ede7694649ebabada Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 6 Jun 2019 16:54:00 +0200 Subject: [PATCH 25/75] Make IPublishedContent changes less breaking and more friendly --- .../PublishedContent/IPublishedContent.cs | 47 ++++--- .../PublishedContentWrapped.cs | 18 ++- .../PublishedContentExtensions.cs | 128 ++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../PublishedMediaCacheTests.cs | 22 +-- .../DictionaryPublishedContent.cs | 22 +-- .../PublishedContentCache.cs | 4 +- .../XmlPublishedContent.cs | 47 ++++--- .../Published/NestedContentTests.cs | 13 +- .../PublishedContent/NuCacheChildrenTests.cs | 30 ++-- .../PublishedContentDataTableTests.cs | 31 ++--- .../PublishedContentLanguageVariantTests.cs | 46 +++---- .../PublishedContentMoreTests.cs | 26 ++-- .../PublishedContent/PublishedMediaTests.cs | 6 +- .../PublishedContent/PublishedRouterTests.cs | 4 +- .../PublishedContent/RootNodeTests.cs | 2 +- .../SolidPublishedSnapshot.cs | 31 ++--- .../TestHelpers/Stubs/TestPublishedContent.cs | 42 ++---- .../Web/TemplateUtilitiesTests.cs | 2 +- src/Umbraco.Web.UI.Client/package-lock.json | 99 ++++++-------- .../ListChildPagesFromChangeableSource.cshtml | 2 +- .../ListChildPagesFromCurrentPage.cshtml | 2 +- .../ListChildPagesOrderedByDate.cshtml | 2 +- .../ListChildPagesOrderedByName.cshtml | 2 +- .../ListChildPagesOrderedByProperty.cshtml | 2 +- .../ListDescendantsFromCurrentPage.cshtml | 6 +- .../ListImagesFromMediaFolder.cshtml | 2 +- .../Templates/Navigation.cshtml | 2 +- .../Templates/SiteMap.cshtml | 2 +- .../Controllers/UmbLoginController.cs | 2 +- .../Editors/TemplateQueryController.cs | 10 +- .../PublishedContentHashtableConverter.cs | 56 ++++---- .../PublishedValueFallback.cs | 2 +- .../Models/PublishedContentBase.cs | 19 ++- .../PublishedCache/NuCache/ContentCache.cs | 4 +- .../NuCache/Navigable/NavigableContent.cs | 4 +- .../NuCache/PublishedContent.cs | 111 +++++---------- .../PublishedCache/PublishedMember.cs | 20 ++- src/Umbraco.Web/PublishedContentExtensions.cs | 48 ++----- src/Umbraco.Web/Routing/AliasUrlProvider.cs | 2 +- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 2 +- src/Umbraco.Web/Routing/PublishedRouter.cs | 2 +- .../Routing/RedirectTrackingComponent.cs | 4 +- .../Routing/UrlProviderExtensions.cs | 2 +- .../Templates/TemplateUtilities.cs | 4 +- 45 files changed, 481 insertions(+), 454 deletions(-) create mode 100644 src/Umbraco.Core/PublishedContentExtensions.cs diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 93487b6492..1c0d39a8b8 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; namespace Umbraco.Core.Models.PublishedContent { - /// /// /// Represents a published content item. @@ -27,16 +26,14 @@ namespace Umbraco.Core.Models.PublishedContent int Id { get; } /// - /// Gets the name of the content item. + /// Gets the name of the content item for the current culture. /// - /// The specific culture to get the name for. If null is used the current culture is used (Default is null). - string Name(string culture = null); + string Name { get; } /// - /// Gets the url segment of the content item. + /// Gets the url segment of the content item for the current culture. /// - /// The specific culture to get the url segment for. If null is used the current culture is used (Default is null). - string UrlSegment(string culture = null); + string UrlSegment { get; } /// /// Gets the sort order of the content item. @@ -94,20 +91,29 @@ namespace Umbraco.Core.Models.PublishedContent DateTime UpdateDate { get; } /// - /// Gets the culture date of the content item. + /// Gets the url of the content item for the current culture. /// - /// The specific culture to get the name for. If null is used the current culture is used (Default is null). - DateTime CultureDate(string culture = null); + /// + /// The value of this property is contextual. It depends on the 'current' request uri, + /// if any. + /// + string Url { get; } /// - /// Gets all available cultures. + /// Gets available culture infos. /// /// /// Contains only those culture that are available. For a published content, these are /// the cultures that are published. For a draft content, those that are 'available' ie /// have a non-empty content name. + /// Does not contain the invariant culture. // fixme? /// - IReadOnlyCollection Cultures { get; } + IReadOnlyDictionary Cultures { get; } + + /// + /// Gets the type of the content item (document, media...). + /// + PublishedItemType ItemType { get; } /// /// Gets a value indicating whether the content is draft. @@ -145,18 +151,17 @@ namespace Umbraco.Core.Models.PublishedContent /// Gets the parent of the content item. /// /// The parent of root content is null. - IPublishedContent Parent(); + IPublishedContent Parent { get; } /// - /// Gets the children of the content item. + /// Gets the children of the content item that are available for the current culture. /// - /// The specific culture to get the url children for. If null is used the current culture is used (Default is null). - /// - /// Gets children that are available for the specified culture. - /// Children are sorted by their sortOrder. - /// The '*' culture and supported and returns everything. - /// - IEnumerable Children(string culture = null); + IEnumerable Children { get; } + + /// + /// Gets all the children of the content item, regardless of whether they are available for the current culture. + /// + IEnumerable ChildrenForAllCultures { get; } #endregion } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 3da690a7e2..fb41c95419 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -58,10 +58,10 @@ namespace Umbraco.Core.Models.PublishedContent public virtual int Id => _content.Id; /// - public virtual string Name(string culture = null) => _content.Name(culture); + public virtual string Name => _content.Name; /// - public virtual string UrlSegment(string culture = null) => _content.UrlSegment(culture); + public virtual string UrlSegment => _content.UrlSegment; /// public virtual int SortOrder => _content.SortOrder; @@ -94,10 +94,13 @@ namespace Umbraco.Core.Models.PublishedContent public virtual DateTime UpdateDate => _content.UpdateDate; /// - public DateTime CultureDate(string culture = null) => _content.CultureDate(culture); + public virtual string Url => _content.Url; /// - public IReadOnlyCollection Cultures => _content.Cultures; + public IReadOnlyDictionary Cultures => _content.Cultures; + + /// + public virtual PublishedItemType ItemType => _content.ItemType; /// public virtual bool IsDraft(string culture = null) => _content.IsDraft(culture); @@ -110,10 +113,13 @@ namespace Umbraco.Core.Models.PublishedContent #region Tree /// - public virtual IPublishedContent Parent() => _content.Parent(); + public virtual IPublishedContent Parent => _content.Parent; /// - public virtual IEnumerable Children(string culture = null) => _content.Children(culture); + public virtual IEnumerable Children => _content.Children; + + /// + public virtual IEnumerable ChildrenForAllCultures => _content.ChildrenForAllCultures; #endregion diff --git a/src/Umbraco.Core/PublishedContentExtensions.cs b/src/Umbraco.Core/PublishedContentExtensions.cs new file mode 100644 index 0000000000..a8cb0cdaf2 --- /dev/null +++ b/src/Umbraco.Core/PublishedContentExtensions.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Composing; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core +{ + public static class PublishedContentExtensions + { + private static IVariationContextAccessor VariationContextAccessor => Current.VariationContextAccessor; + + /// + /// Determines whether the content has a culture. + /// + /// Culture is case-insensitive. + public static bool HasCulture(this IPublishedContent content, string culture) + => content.Cultures.ContainsKey(culture ?? string.Empty); + + /// + /// Determines whether the content is invariant, or has a culture. + /// + /// Culture is case-insensitive. + public static bool IsInvariantOrHasCulture(this IPublishedContent content, string culture) + => !content.ContentType.VariesByCulture() || content.Cultures.ContainsKey(culture ?? ""); + + /// + /// Filters a sequence of to return invariant items, and items that are published for the specified culture. + /// + /// The content items. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null). + internal static IEnumerable WhereIsInvariantOrHasCulture(this IEnumerable contents, string culture = null) + where T : class, IPublishedContent + { + if (contents == null) throw new ArgumentNullException(nameof(contents)); + + culture = culture ?? Current.VariationContextAccessor.VariationContext?.Culture ?? ""; + + // either does not vary by culture, or has the specified culture + return contents.Where(x => !ContentVariationExtensions.VariesByCulture((IPublishedContentType) x.ContentType) || HasCulture(x, culture)); + } + + /// + /// Gets the name of the content item. + /// + /// The content item. + /// The specific culture to get the name for. If null is used the current culture is used (Default is null). + public static string Name(this IPublishedContent content, string culture = null) + { + // invariant has invariant value (whatever the requested culture) + if (!content.ContentType.VariesByCulture()) + return "NAME??"; // fixme where should the invariant one come from? should Cultures contain it? + + // handle context culture for variant + if (culture == null) + culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; + + // get + return culture != "" && content.Cultures.TryGetValue(culture, out var infos) ? infos.Name : null; + } + + + /// + /// Gets the url segment of the content item. + /// + /// The content item. + /// The specific culture to get the url segment for. If null is used the current culture is used (Default is null). + public static string UrlSegment(this IPublishedContent content, string culture = null) + { + // invariant has invariant value (whatever the requested culture) + if (!content.ContentType.VariesByCulture()) + return "URLSEGMENT??"; // fixme where should the invariant one come from? should Cultures contain it? + + // handle context culture for variant + if (culture == null) + culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; + + // get + return culture != "" && content.Cultures.TryGetValue(culture, out var infos) ? infos.UrlSegment : null; + } + + /// + /// Gets the culture date of the content item. + /// + /// The content item. + /// The specific culture to get the name for. If null is used the current culture is used (Default is null). + public static DateTime CultureDate(this IPublishedContent content, string culture = null) + { + // invariant has invariant value (whatever the requested culture) + if (!content.ContentType.VariesByCulture()) + return content.UpdateDate; + + // handle context culture for variant + if (culture == null) + culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; + + // get + return culture != "" && content.Cultures.TryGetValue(culture, out var infos) ? infos.Date : DateTime.MinValue; + } + + + /// + /// Gets the children of the content item. + /// + /// The content item. + /// The specific culture to get the url children for. If null is used the current culture is used (Default is null). + /// + /// Gets children that are available for the specified culture. + /// Children are sorted by their sortOrder. + /// The '*' culture and supported and returns everything. + /// + public static IEnumerable Children(this IPublishedContent content, string culture = null) + { + // invariant has invariant value (whatever the requested culture) + if (!content.ContentType.VariesByCulture() && culture != "*") + culture = ""; + + // handle context culture for variant + if (culture == null) + culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; + + var children = content.ChildrenForAllCultures; + return culture == "*" + ? children + : children.Where(x => x.IsInvariantOrHasCulture(culture)); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index ae1c643b2b..d923065438 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -252,6 +252,7 @@ + diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index d31401c725..f3d9f895ef 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -108,10 +108,10 @@ namespace Umbraco.Tests.Cache.PublishedCache Assert.AreEqual(mRoot.ContentType.Alias, publishedMedia.ContentType.Alias); Assert.AreEqual(mRoot.ContentType.Id, publishedMedia.ContentType.Id); Assert.AreEqual(mRoot.Level, publishedMedia.Level); - Assert.AreEqual(mRoot.Name, publishedMedia.Name()); + Assert.AreEqual(mRoot.Name, publishedMedia.Name); Assert.AreEqual(mRoot.Path, publishedMedia.Path); Assert.AreEqual(mRoot.SortOrder, publishedMedia.SortOrder); - Assert.IsNull(publishedMedia.Parent()); + Assert.IsNull(publishedMedia.Parent); } [TestCase("id")] @@ -172,9 +172,9 @@ namespace Umbraco.Tests.Cache.PublishedCache child1, child2 }); - Assert.AreEqual(2, dicDoc.Children().Count()); - Assert.AreEqual(222333, dicDoc.Children().ElementAt(0).Id); - Assert.AreEqual(444555, dicDoc.Children().ElementAt(1).Id); + Assert.AreEqual(2, dicDoc.Children.Count()); + Assert.AreEqual(222333, dicDoc.Children.ElementAt(0).Id); + Assert.AreEqual(444555, dicDoc.Children.ElementAt(1).Id); } [Test] @@ -212,7 +212,7 @@ namespace Umbraco.Tests.Cache.PublishedCache var doc = store.CreateFromCacheValues(store.ConvertFromSearchResult(result)); DoAssert(doc, 1234, key, null, 0, "/media/test.jpg", "Image", 23, "Shannon", "Shannon", 0, 0, "-1,1234", DateTime.Parse("2012-07-17T10:34:09"), DateTime.Parse("2012-07-16T10:34:09"), 2); - Assert.AreEqual(null, doc.Parent()); + Assert.AreEqual(null, doc.Parent); } [Test] @@ -228,10 +228,10 @@ namespace Umbraco.Tests.Cache.PublishedCache var doc = cache.CreateFromCacheValues(cache.ConvertFromXPathNavigator(navigator, true)); DoAssert(doc, 2000, key, null, 2, "image1", "Image", 23, "Shannon", "Shannon", 33, 33, "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1); - Assert.AreEqual(null, doc.Parent()); - Assert.AreEqual(2, doc.Children().Count()); - Assert.AreEqual(2001, doc.Children().ElementAt(0).Id); - Assert.AreEqual(2002, doc.Children().ElementAt(1).Id); + Assert.AreEqual(null, doc.Parent); + Assert.AreEqual(2, doc.Children.Count()); + Assert.AreEqual(2001, doc.Children.ElementAt(0).Id); + Assert.AreEqual(2002, doc.Children.ElementAt(1).Id); } private XmlDocument GetMediaXml() @@ -395,7 +395,7 @@ namespace Umbraco.Tests.Cache.PublishedCache Assert.AreEqual(keyVal, doc.Key); Assert.AreEqual(templateIdVal, doc.TemplateId); Assert.AreEqual(sortOrderVal, doc.SortOrder); - Assert.AreEqual(urlNameVal, doc.UrlSegment()); + Assert.AreEqual(urlNameVal, doc.UrlSegment); Assert.AreEqual(nodeTypeAliasVal, doc.ContentType.Alias); Assert.AreEqual(nodeTypeIdVal, doc.ContentType.Id); Assert.AreEqual(writerNameVal, doc.WriterName); diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs index 3a39d23ccb..db8dc38d6d 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs @@ -136,7 +136,12 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private readonly Func _getProperty; private readonly IAppCache _appCache; - public override IPublishedContent Parent() => _getParent.Value; + /// + /// Returns 'Media' as the item type + /// + public override PublishedItemType ItemType => PublishedItemType.Media; + + public override IPublishedContent Parent => _getParent.Value; public int ParentId { get; private set; } @@ -148,15 +153,12 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache public override int SortOrder => _sortOrder; - public override string Name(string culture = null) => _name; + public override string Name => _name; - public override DateTime CultureDate(string culture = null) => UpdateDate; + private static readonly Lazy> NoCultures = new Lazy>(() => new Dictionary()); + public override IReadOnlyDictionary Cultures => NoCultures.Value; - // ReSharper disable once CollectionNeverUpdated.Local - private static readonly List EmptyListOfString = new List(); - public override IReadOnlyCollection Cultures => EmptyListOfString; - - public override string UrlSegment(string culture = null) => _urlName; + public override string UrlSegment => _urlName; public override string WriterName => _creatorName; @@ -180,7 +182,9 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache public override IEnumerable Properties => _properties; - public override IEnumerable Children(string culture = null) => _getChildren.Value; + public override IEnumerable Children => _getChildren.Value; + + public override IEnumerable ChildrenForAllCultures => Children; public override IPublishedProperty GetProperty(string alias) { diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs index 33348d071a..8de0209c69 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs @@ -268,14 +268,14 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache pathParts.Add(urlName); // move to parent node - n = n.Parent(); + n = n.Parent; hasDomains = n != null && _domainCache.HasAssigned(n.Id); } // no domain, respect HideTopLevelNodeFromPath for legacy purposes if (hasDomains == false && _globalSettings.HideTopLevelNodeFromPath) { - if (node.Parent() == null) + if (node.Parent == null) { var rootNode = GetByRoute(preview, "/", true); if (rootNode == null) diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index 2edb2bae32..4a60912757 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -71,12 +71,17 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private bool _isDraft; - public override IEnumerable Children(string culture = null) + public override IEnumerable Children { - EnsureNodeInitialized(andChildren: true); - return _children; + get + { + EnsureNodeInitialized(andChildren: true); + return _children; + } } + public override IEnumerable ChildrenForAllCultures => Children; + public override IPublishedProperty GetProperty(string alias) { EnsureNodeInitialized(); @@ -84,10 +89,15 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache return _properties.TryGetValue(alias, out property) ? property : null; } - public override IPublishedContent Parent() + public override PublishedItemType ItemType => PublishedItemType.Content; + + public override IPublishedContent Parent { - EnsureNodeInitialized(andParent: true); - return _parent; + get + { + EnsureNodeInitialized(andParent: true); + return _parent; + } } public override int Id @@ -126,17 +136,17 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache } } - public override string Name(string culture = null) + public override string Name { - EnsureNodeInitialized(); - return _name; + get + { + EnsureNodeInitialized(); + return _name; + } } - public override DateTime CultureDate(string culture = null) => UpdateDate; - - // ReSharper disable once CollectionNeverUpdated.Local - private static readonly List EmptyListOfString = new List(); - public override IReadOnlyCollection Cultures => EmptyListOfString; + private static readonly Lazy> NoCultures = new Lazy>(() => new Dictionary()); + public override IReadOnlyDictionary Cultures => NoCultures.Value; public override string WriterName { @@ -201,10 +211,13 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache } } - public override string UrlSegment(string culture = null) + public override string UrlSegment { - EnsureNodeInitialized(); - return _urlName; + get + { + EnsureNodeInitialized(); + return _urlName; + } } public override int Level diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index a1b736ae90..432247b09f 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -271,10 +271,12 @@ namespace Umbraco.Tests.Published } // ReSharper disable UnassignedGetOnlyAutoProperty + public override PublishedItemType ItemType { get; } public override bool IsDraft(string culture = null) => false; public override bool IsPublished(string culture = null) => true; - public override IPublishedContent Parent() => null; - public override IEnumerable Children(string culture = null) => Enumerable.Empty(); + public override IPublishedContent Parent { get; } + public override IEnumerable Children { get; } + public override IEnumerable ChildrenForAllCultures => Children; public override IPublishedContentType ContentType { get; } // ReSharper restore UnassignedGetOnlyAutoProperty @@ -282,10 +284,9 @@ namespace Umbraco.Tests.Published public override int Id { get; } public override int? TemplateId { get; } public override int SortOrder { get; } - public override string Name(string culture = null) => default; - public override DateTime CultureDate(string culture = null) => throw new NotSupportedException(); - public override IReadOnlyCollection Cultures => throw new NotSupportedException(); - public override string UrlSegment(string culture = null) => default; + public override string Name { get; } + public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); + public override string UrlSegment { get; } public override string WriterName { get; } public override string CreatorName { get; } public override int WriterId { get; } diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index d9cb8dbbc2..d77d1e4701 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -231,22 +231,22 @@ namespace Umbraco.Tests.PublishedContent var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); _snapshotAccessor.PublishedSnapshot = snapshot; - Assert.IsNull(snapshot.Content.GetById(1).Parent()); - Assert.IsNull(snapshot.Content.GetById(2).Parent()); - Assert.IsNull(snapshot.Content.GetById(3).Parent()); + Assert.IsNull(snapshot.Content.GetById(1).Parent); + Assert.IsNull(snapshot.Content.GetById(2).Parent); + Assert.IsNull(snapshot.Content.GetById(3).Parent); - Assert.AreEqual(1, snapshot.Content.GetById(4).Parent()?.Id); - Assert.AreEqual(1, snapshot.Content.GetById(5).Parent()?.Id); - Assert.AreEqual(1, snapshot.Content.GetById(6).Parent()?.Id); + Assert.AreEqual(1, snapshot.Content.GetById(4).Parent?.Id); + Assert.AreEqual(1, snapshot.Content.GetById(5).Parent?.Id); + Assert.AreEqual(1, snapshot.Content.GetById(6).Parent?.Id); - Assert.AreEqual(2, snapshot.Content.GetById(7).Parent()?.Id); - Assert.AreEqual(2, snapshot.Content.GetById(8).Parent()?.Id); - Assert.AreEqual(2, snapshot.Content.GetById(9).Parent()?.Id); + Assert.AreEqual(2, snapshot.Content.GetById(7).Parent?.Id); + Assert.AreEqual(2, snapshot.Content.GetById(8).Parent?.Id); + Assert.AreEqual(2, snapshot.Content.GetById(9).Parent?.Id); - Assert.AreEqual(3, snapshot.Content.GetById(10).Parent()?.Id); + Assert.AreEqual(3, snapshot.Content.GetById(10).Parent?.Id); - Assert.AreEqual(4, snapshot.Content.GetById(11).Parent()?.Id); - Assert.AreEqual(4, snapshot.Content.GetById(12).Parent()?.Id); + Assert.AreEqual(4, snapshot.Content.GetById(11).Parent?.Id); + Assert.AreEqual(4, snapshot.Content.GetById(12).Parent?.Id); } [Test] @@ -288,7 +288,7 @@ namespace Umbraco.Tests.PublishedContent documents = snapshot.Content.GetById(3).Children().ToArray(); AssertDocuments(documents); - Assert.IsNull(snapshot.Content.GetById(10).Parent()); + Assert.IsNull(snapshot.Content.GetById(10).Parent); } [Test] @@ -330,7 +330,7 @@ namespace Umbraco.Tests.PublishedContent documents = snapshot.Content.GetById(10).Children().ToArray(); AssertDocuments(documents, "N1"); - Assert.AreEqual(10, snapshot.Content.GetById(1).Parent()?.Id); + Assert.AreEqual(10, snapshot.Content.GetById(1).Parent?.Id); } [Test] @@ -509,7 +509,7 @@ namespace Umbraco.Tests.PublishedContent documents = snapshot.Content.GetById(2).Children().ToArray(); AssertDocuments(documents, "N9", "N8"); - Assert.AreEqual(1, snapshot.Content.GetById(7).Parent()?.Id); + Assert.AreEqual(1, snapshot.Content.GetById(7).Parent?.Id); } private void AssertDocuments(IPublishedContent[] documents, params string[] names) diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index e4bf02fec0..5c22295547 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -10,9 +10,9 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Web; +using PublishedContentExtensions = Umbraco.Web.PublishedContentExtensions; namespace Umbraco.Tests.PublishedContent { @@ -97,7 +97,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetContent(true, 1); //change a doc type alias - var c = (TestPublishedContent)doc.Children().ElementAt(0); + var c = (TestPublishedContent)doc.Children.ElementAt(0); c.ContentType = new PublishedContentType(22, "DontMatch", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var dt = doc.ChildrenAsTable(Current.Services, "Child"); @@ -139,6 +139,8 @@ namespace Umbraco.Tests.PublishedContent TemplateId = 5, UpdateDate = DateTime.Now, Path = "-1,3", + UrlSegment = "home-page", + Name = "Page" + Guid.NewGuid().ToString(), Version = Guid.NewGuid(), WriterId = 1, WriterName = "Shannon", @@ -146,8 +148,6 @@ namespace Umbraco.Tests.PublishedContent Level = 1, Children = new List() }; - d.SetName("Page" + Guid.NewGuid()); - d.SetUrlSegment("home-page"); d.Properties = new Collection(new List { new RawValueProperty(factory.CreatePropertyType("property1", 1), d, "value" + indexVals), @@ -183,26 +183,16 @@ namespace Umbraco.Tests.PublishedContent // l8tr... private class TestPublishedContent : IPublishedContent { - private readonly Dictionary _names = new Dictionary(); - private readonly Dictionary _urlSegments = new Dictionary(); - - public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => default; - - IPublishedContent IPublishedContent.Parent() => Parent; - - IEnumerable IPublishedContent.Children(string culture = null) => Children; - + public string Url { get; set; } + public PublishedItemType ItemType { get; set; } public IPublishedContent Parent { get; set; } public int Id { get; set; } public Guid Key { get; set; } public int? TemplateId { get; set; } public int SortOrder { get; set; } - public string Name(string culture = null) => _names.TryGetValue(culture ?? "", out var name) ? name : null; - public void SetName(string name, string culture = null) => _names[culture ?? ""] = name; - public DateTime CultureDate(string culture = null) => throw new NotSupportedException(); - public IReadOnlyCollection Cultures => throw new NotSupportedException(); - public string UrlSegment(string culture = null) => _urlSegments.TryGetValue(culture ?? "", out var urlSegment) ? urlSegment : null; - public void SetUrlSegment(string urlSegment, string culture = null) => _urlSegments[culture ?? ""] = urlSegment; + public string Name { get; set; } + public IReadOnlyDictionary Cultures => throw new NotSupportedException(); + public string UrlSegment { get; set; } public string WriterName { get; set; } public string CreatorName { get; set; } public int WriterId { get; set; } @@ -218,6 +208,7 @@ namespace Umbraco.Tests.PublishedContent public IEnumerable Properties { get; set; } public IEnumerable Children { get; set; } + public IEnumerable ChildrenForAllCultures => Children; public IPublishedProperty GetProperty(string alias) { @@ -232,7 +223,7 @@ namespace Umbraco.Tests.PublishedContent IPublishedContent content = this; while (content != null && (property == null || property.HasValue() == false)) { - content = content.Parent(); + content = content.Parent; property = content == null ? null : content.GetProperty(alias); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index dc726c5748..62447742ff 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -122,8 +122,11 @@ namespace Umbraco.Tests.PublishedContent { Id = 1, SortOrder = 0, + Name = "Content 1", + UrlSegment = "content-1", Path = "/1", Level = 1, + Url = "/content-1", ParentId = -1, ChildIds = new[] { 2 }, Properties = new Collection @@ -131,16 +134,16 @@ namespace Umbraco.Tests.PublishedContent prop1, prop2, noprop } }; - item1.SetName("Content 1"); - item1.SetUrlSegment("content-1"); - item1.SetUrl("/content-1"); var item2 = new SolidPublishedContent(contentType1) { Id = 2, SortOrder = 0, + Name = "Content 2", + UrlSegment = "content-2", Path = "/1/2", Level = 2, + Url = "/content-1/content-2", ParentId = 1, ChildIds = new int[] { 3 }, Properties = new Collection @@ -148,9 +151,6 @@ namespace Umbraco.Tests.PublishedContent prop3 } }; - item2.SetName("Content 2"); - item2.SetUrlSegment("content-2"); - item2.SetUrl("/content-1/content-2"); var prop4 = new SolidPublishedPropertyWithLanguageVariants { @@ -164,8 +164,11 @@ namespace Umbraco.Tests.PublishedContent { Id = 3, SortOrder = 0, + Name = "Content 3", + UrlSegment = "content-3", Path = "/1/2/3", Level = 3, + Url = "/content-1/content-2/content-3", ParentId = 2, ChildIds = new int[] { }, Properties = new Collection @@ -173,15 +176,12 @@ namespace Umbraco.Tests.PublishedContent prop4 } }; - item3.SetName("Content 3"); - item3.SetUrlSegment("content-3"); - item3.SetUrl("/content-1/content-2/content-3"); - item1.SetChildren(new List { item2 }); - item2.SetParent(item1); + item1.Children = new List { item2 }; + item2.Parent = item1; - item2.SetChildren(new List { item3 }); - item3.SetParent(item2); + item2.Children = new List { item3 }; + item3.Parent = item2; cache.Add(item1); cache.Add(item2); @@ -247,7 +247,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Do_Not_Get_Content_Recursively_Unless_Requested() { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); var value = content.Value("welcomeText2"); Assert.IsNull(value); } @@ -255,7 +255,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_Recursively() { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); var value = content.Value("welcomeText2", fallback: Fallback.ToAncestors); Assert.AreEqual("Welcome", value); } @@ -263,7 +263,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Do_Not_Get_Content_Recursively_Unless_Requested2() { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First().Children.First(); Assert.IsNull(content.GetProperty("welcomeText2")); var value = content.Value("welcomeText2"); Assert.IsNull(value); @@ -272,7 +272,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_Recursively2() { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First().Children.First(); Assert.IsNull(content.GetProperty("welcomeText2")); var value = content.Value("welcomeText2", fallback: Fallback.ToAncestors); Assert.AreEqual("Welcome", value); @@ -281,7 +281,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_Recursively3() { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First().Children.First(); Assert.IsNull(content.GetProperty("noprop")); var value = content.Value("noprop", fallback: Fallback.ToAncestors); // property has no value but we still get the value (ie, the converter would do something) @@ -292,7 +292,7 @@ namespace Umbraco.Tests.PublishedContent public void Can_Get_Content_With_Recursive_Priority() { Current.VariationContextAccessor.VariationContext = new VariationContext("nl"); - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); var value = content.Value("welcomeText", "nl", fallback: Fallback.To(Fallback.Ancestors, Fallback.Language)); @@ -303,7 +303,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Get_Content_With_Fallback_Language_Priority() { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); var value = content.Value("welcomeText", "nl", fallback: Fallback.ToLanguage); // No Dutch value is directly assigned. Check has fallen back to English value from language variant. @@ -313,14 +313,14 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Throws_For_Non_Supported_Fallback() { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); Assert.Throws(() => content.Value("welcomeText", "nl", fallback: Fallback.To(999))); } [Test] public void Can_Fallback_To_Default_Value() { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); // no Dutch value is assigned, so getting null var value = content.Value("welcomeText", "nl"); @@ -338,7 +338,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Can_Have_Custom_Default_Value() { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children().First(); + var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); // HACK: the value, pretend the converter would return something var prop = content.GetProperty("welcomeText") as SolidPublishedPropertyWithLanguageVariants; diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index abd4771547..440474ae74 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -29,8 +29,11 @@ namespace Umbraco.Tests.PublishedContent { Id = 1, SortOrder = 0, + Name = "Content 1", + UrlSegment = "content-1", Path = "/1", Level = 1, + Url = "/content-1", ParentId = -1, ChildIds = new int[] { }, Properties = new Collection @@ -44,17 +47,17 @@ namespace Umbraco.Tests.PublishedContent } } }; - content.SetName("Content 1"); - content.SetUrlSegment("content-1"); - content.SetUrl("/content-1"); cache.Add(content); content = new SolidPublishedContent(contentType2) { Id = 2, SortOrder = 1, + Name = "Content 2", + UrlSegment = "content-2", Path = "/2", Level = 1, + Url = "/content-2", ParentId = -1, ChildIds = new int[] { }, Properties = new Collection @@ -68,17 +71,17 @@ namespace Umbraco.Tests.PublishedContent } } }; - content.SetName("Content 2"); - content.SetUrlSegment("content-2"); - content.SetUrl("/content-2"); cache.Add(content); content = new SolidPublishedContent(contentType2Sub) { Id = 3, SortOrder = 2, + Name = "Content 2Sub", + UrlSegment = "content-2sub", Path = "/3", Level = 1, + Url = "/content-2sub", ParentId = -1, ChildIds = new int[] { }, Properties = new Collection @@ -92,9 +95,6 @@ namespace Umbraco.Tests.PublishedContent } } }; - content.SetName("Content 2Sub"); - content.SetUrlSegment("content-2sub"); - content.SetUrl("/content-2sub"); cache.Add(content); } @@ -114,17 +114,17 @@ namespace Umbraco.Tests.PublishedContent .ToIndexedArray(); var item = items[0]; - Assert.AreEqual("Content 1", item.Content.Name()); + Assert.AreEqual("Content 1", item.Content.Name); Assert.IsTrue(item.IsFirst()); Assert.IsFalse(item.IsLast()); item = items[1]; - Assert.AreEqual("Content 2", item.Content.Name()); + Assert.AreEqual("Content 2", item.Content.Name); Assert.IsFalse(item.IsFirst()); Assert.IsFalse(item.IsLast()); item = items[2]; - Assert.AreEqual("Content 2Sub", item.Content.Name()); + Assert.AreEqual("Content 2Sub", item.Content.Name); Assert.IsFalse(item.IsFirst()); Assert.IsTrue(item.IsLast()); } @@ -157,7 +157,7 @@ namespace Umbraco.Tests.PublishedContent var content = Current.UmbracoContext.Content.GetAtRoot() .OfType() .First(x => x.Prop1 == 1234); - Assert.AreEqual("Content 2", content.Name()); + Assert.AreEqual("Content 2", content.Name); Assert.AreEqual(1234, content.Prop1); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index c2cf22f85f..b789eb0ef8 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -416,13 +416,13 @@ namespace Umbraco.Tests.PublishedContent var mSubChild3 = MakeNewMedia("SubChild3", mType, user, mChild1.Id); var publishedRoot = GetNode(mRoot.Id); - Assert.AreEqual(null, publishedRoot.Parent()); + Assert.AreEqual(null, publishedRoot.Parent); var publishedChild1 = GetNode(mChild1.Id); - Assert.AreEqual(mRoot.Id, publishedChild1.Parent().Id); + Assert.AreEqual(mRoot.Id, publishedChild1.Parent.Id); var publishedSubChild1 = GetNode(mSubChild1.Id); - Assert.AreEqual(mChild1.Id, publishedSubChild1.Parent().Id); + Assert.AreEqual(mChild1.Id, publishedSubChild1.Parent.Id); } [Test] diff --git a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs index ea3a00371a..d8dbabb569 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs @@ -58,13 +58,13 @@ namespace Umbraco.Tests.PublishedContent { var pc = new Mock(); pc.Setup(content => content.Id).Returns(1); - pc.Setup(content => content.Name(It.IsAny())).Returns("test"); + pc.Setup(content => content.Name).Returns("test"); pc.Setup(content => content.WriterName).Returns("admin"); pc.Setup(content => content.CreatorName).Returns("admin"); pc.Setup(content => content.CreateDate).Returns(DateTime.Now); pc.Setup(content => content.UpdateDate).Returns(DateTime.Now); pc.Setup(content => content.Path).Returns("-1,1"); - pc.Setup(content => content.Parent()).Returns(() => null); + pc.Setup(content => content.Parent).Returns(() => null); pc.Setup(content => content.Properties).Returns(new Collection()); pc.Setup(content => content.ContentType).Returns(new PublishedContentType(22, "anything", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing)); return pc; diff --git a/src/Umbraco.Tests/PublishedContent/RootNodeTests.cs b/src/Umbraco.Tests/PublishedContent/RootNodeTests.cs index be94923ad0..4aad3d0acb 100644 --- a/src/Umbraco.Tests/PublishedContent/RootNodeTests.cs +++ b/src/Umbraco.Tests/PublishedContent/RootNodeTests.cs @@ -20,7 +20,7 @@ namespace Umbraco.Tests.PublishedContent content = ctx.Content.GetById(1046); Assert.IsNotNull(content); Assert.AreEqual(1, content.Level); - Assert.IsNull(content.Parent()); + Assert.IsNull(content.Parent); // non-existing content is null content = ctx.Content.GetById(666); diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index fbb2a7549b..ab5a65146a 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -102,7 +102,7 @@ namespace Umbraco.Tests.PublishedContent public override IEnumerable GetAtRoot(bool preview) { - return _content.Values.Where(x => x.Parent() == null); + return _content.Values.Where(x => x.Parent == null); } public override IPublishedContent GetSingleByXPath(bool preview, string xpath, Core.Xml.XPathVariable[] vars) @@ -158,10 +158,6 @@ namespace Umbraco.Tests.PublishedContent internal class SolidPublishedContent : IPublishedContent { - private readonly Dictionary _names = new Dictionary(); - private readonly Dictionary _urlSegments = new Dictionary(); - private readonly Dictionary _urls = new Dictionary(); - #region Constructor public SolidPublishedContent(IPublishedContentType contentType) @@ -184,12 +180,10 @@ namespace Umbraco.Tests.PublishedContent public Guid Key { get; set; } public int? TemplateId { get; set; } public int SortOrder { get; set; } - public string Name(string culture = null) => _names.TryGetValue(culture ?? "", out var name) ? name : null; - public void SetName(string name, string culture = null) => _names[culture ?? ""] = name; - public DateTime CultureDate(string culture = null) => throw new NotSupportedException(); - public IReadOnlyCollection Cultures => throw new NotSupportedException(); - public string UrlSegment(string culture = null) => _urlSegments.TryGetValue(culture ?? "", out var urlSegment) ? urlSegment : null; - public void SetUrlSegment(string urlSegment, string culture = null) => _urlSegments[culture ?? ""] = urlSegment; + public string Name { get; set; } + public PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException(); + public IReadOnlyDictionary Cultures => throw new NotSupportedException(); + public string UrlSegment { get; set; } public string WriterName { get; set; } public string CreatorName { get; set; } public int WriterId { get; set; } @@ -199,9 +193,9 @@ namespace Umbraco.Tests.PublishedContent public DateTime UpdateDate { get; set; } public Guid Version { get; set; } public int Level { get; set; } - public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => _urls.TryGetValue(culture ?? "", out var url) ? url : null; - public void SetUrl(string url, string culture = null) => _urls[culture ?? ""] = url; + public string Url { get; set; } + public PublishedItemType ItemType { get { return PublishedItemType.Content; } } public bool IsDraft(string culture = null) => false; public bool IsPublished(string culture = null) => true; @@ -212,12 +206,9 @@ namespace Umbraco.Tests.PublishedContent public int ParentId { get; set; } public IEnumerable ChildIds { get; set; } - private IPublishedContent _parent; - public IPublishedContent Parent() => _parent; - public void SetParent(IPublishedContent parent) => _parent = parent; - private IEnumerable _children; - public IEnumerable Children(string culture = null) => _children; - public void SetChildren(IEnumerable children) => _children = children; + public IPublishedContent Parent { get; set; } + public IEnumerable Children { get; set; } + public IEnumerable ChildrenForAllCultures => Children; #endregion @@ -244,7 +235,7 @@ namespace Umbraco.Tests.PublishedContent IPublishedContent content = this; while (content != null && (property == null || property.HasValue() == false)) { - content = content.Parent(); + content = content.Parent; property = content == null ? null : content.GetProperty(alias); } diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 1fb090e220..62f93d106f 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -7,38 +7,20 @@ namespace Umbraco.Tests.TestHelpers.Stubs { internal class TestPublishedContent : PublishedElement, IPublishedContent { - private readonly Dictionary _names = new Dictionary(); - private readonly Dictionary _urlSegments = new Dictionary(); - private readonly Dictionary _cultures; - - public TestPublishedContent(IPublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultures = null) + public TestPublishedContent(IPublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultures = null) : base(contentType, key, values, previewing) { Id = id; - _cultures = cultures ?? new Dictionary(); + Cultures = cultures ?? new Dictionary(); } public int Id { get; } public int? TemplateId { get; set; } public int SortOrder { get; set; } - public string Name(string culture = null) => _names.TryGetValue(culture ?? "", out var name) ? name : null; - public void SetName(string name, string culture = null) => _names[culture ?? ""] = name; + public string Name { get; set; } public IVariationContextAccessor VariationContextAccessor { get; set; } - public DateTime CultureDate(string culture = null) - { - // handle context culture - if (culture == null) - culture = VariationContextAccessor?.VariationContext?.Culture; - - // no invariant culture infos - if (culture == "" || Cultures == null) return UpdateDate; - - // get - return _cultures.TryGetValue(culture, out var date) ? date : DateTime.MinValue; - } - public IReadOnlyCollection Cultures { get; set; } - public string UrlSegment(string culture = null) => _urlSegments.TryGetValue(culture ?? "", out var urlSegment) ? urlSegment : null; - public void SetUrlSegment(string urlSegment, string culture = null) => _urlSegments[culture ?? ""] = urlSegment; + public IReadOnlyDictionary Cultures { get; set; } + public string UrlSegment { get; set; } public string DocumentTypeAlias => ContentType.Alias; public int DocumentTypeId { get; set; } public string WriterName { get; set; } @@ -50,15 +32,13 @@ namespace Umbraco.Tests.TestHelpers.Stubs public DateTime UpdateDate { get; set; } public Guid Version { get; set; } public int Level { get; set; } - public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => throw new NotSupportedException(); + public string Url { get; set; } + public PublishedItemType ItemType => ContentType.ItemType; public bool IsDraft(string culture = null) => false; public bool IsPublished(string culture = null) => true; - private IPublishedContent _parent; - public IPublishedContent Parent() => _parent; - public void SetParent(IPublishedContent parent) => _parent = parent; - private IEnumerable _children; - public IEnumerable Children(string culture = null) => _children; - public void SetChildren(IEnumerable children) => _children = children; + public IPublishedContent Parent { get; set; } + public IEnumerable Children { get; set; } + public IEnumerable ChildrenForAllCultures => Children; // copied from PublishedContentBase public IPublishedProperty GetProperty(string alias, bool recurse) @@ -70,7 +50,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs var firstNonNullProperty = property; while (content != null && (property == null || property.HasValue() == false)) { - content = content.Parent(); + content = content.Parent; property = content?.GetProperty(alias); if (firstNonNullProperty == null && property != null) firstNonNullProperty = property; } diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index c0a6e97dda..3a5405548b 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -99,7 +99,7 @@ namespace Umbraco.Tests.Web var snapshotService = Mock.Of(); Mock.Get(snapshotService).Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(snapshot); var media = Mock.Of(); - Mock.Get(media).Setup(x => x.Url(It.IsAny(), It.IsAny())).Returns("/media/1001/my-image.jpg"); + Mock.Get(media).Setup(x => x.Url).Returns("/media/1001/my-image.jpg"); var mediaCache = Mock.Of(); Mock.Get(mediaCache).Setup(x => x.GetById(It.IsAny())).Returns(media); diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index c1c85a9688..e8a9d6b1f7 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1222,7 +1222,7 @@ "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=", "dev": true }, "asap": { @@ -1284,7 +1284,7 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=", "dev": true }, "asynckit": { @@ -1536,7 +1536,7 @@ "bl": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "integrity": "sha1-oWCRFxcQPAdBDO9j71Gzl8Alr5w=", "dev": true, "requires": { "readable-stream": "^2.3.5", @@ -1551,7 +1551,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -1566,7 +1566,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -1740,7 +1740,7 @@ "buffer-alloc": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "integrity": "sha1-iQ3ZDZI6hz4I4Q5f1RpX5bfM4Ow=", "dev": true, "requires": { "buffer-alloc-unsafe": "^1.1.0", @@ -1750,7 +1750,7 @@ "buffer-alloc-unsafe": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "integrity": "sha1-vX3CauKXLQ7aJTvgYdupkjScGfA=", "dev": true }, "buffer-crc32": { @@ -2516,7 +2516,7 @@ "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", "dev": true }, "continuable-cache": { @@ -4045,7 +4045,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -4061,7 +4061,7 @@ }, "engine.io-client": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", "dev": true, "requires": { @@ -4081,7 +4081,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -4476,13 +4476,13 @@ "eventemitter3": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=", "dev": true }, "exec-buffer": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz", - "integrity": "sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA==", + "integrity": "sha1-sWhtvZBMfPmC5lLB9aebHlVzCCs=", "dev": true, "optional": true, "requires": { @@ -5244,7 +5244,7 @@ "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", "dev": true }, "fs-extra": { @@ -5309,8 +5309,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -5331,14 +5330,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5353,20 +5350,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -5483,8 +5477,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -5496,7 +5489,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5511,7 +5503,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5519,14 +5510,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -5545,7 +5534,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5626,8 +5614,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5639,7 +5626,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5725,8 +5711,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5762,7 +5747,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5782,7 +5766,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5826,14 +5809,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -7575,7 +7556,7 @@ "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "integrity": "sha1-d3asYn8+p3JQz8My2rfd9eT10R0=", "dev": true, "requires": { "isarray": "2.0.1" @@ -7778,7 +7759,7 @@ "http-proxy": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "integrity": "sha1-etOElGWPhGBeL220Q230EPTlvpo=", "dev": true, "requires": { "eventemitter3": "^3.0.0", @@ -9335,7 +9316,7 @@ "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", "dev": true }, "lpad-align": { @@ -9369,7 +9350,7 @@ "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", "dev": true, "requires": { "pify": "^3.0.0" @@ -12735,7 +12716,7 @@ "dependencies": { "minimist": { "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "dev": true }, @@ -13616,7 +13597,7 @@ "qjobs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=", "dev": true }, "qs": { @@ -14775,7 +14756,7 @@ }, "socket.io-parser": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", "dev": true, "requires": { @@ -14787,7 +14768,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -14920,7 +14901,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "optional": true, @@ -15328,7 +15309,7 @@ "strip-outer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", "dev": true, "requires": { "escape-string-regexp": "^1.0.2" @@ -15488,7 +15469,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -15731,7 +15712,7 @@ "to-buffer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", "dev": true }, "to-fast-properties": { @@ -15860,7 +15841,7 @@ "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=", "dev": true, "requires": { "media-typer": "0.3.0", @@ -15902,7 +15883,7 @@ "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "integrity": "sha1-n+FTahCmZKZSZqHjzPhf02MCvJw=", "dev": true }, "unc-path-regex": { @@ -16505,7 +16486,7 @@ "ws": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "integrity": "sha1-8c+E/i1ekB686U767OeF8YeiKPI=", "dev": true, "requires": { "async-limiter": "~1.0.0", diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml index 495141f821..46c8de695c 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromChangeableSource.cshtml @@ -19,7 +19,7 @@ { @* Get the starting page *@ var startNode = Umbraco.Content(startNodeId); - var selection = startNode.Children().Where(x => x.IsVisible()).ToArray(); + var selection = startNode.Children.Where(x => x.IsVisible()).ToArray(); if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromCurrentPage.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromCurrentPage.cshtml index 6ba7471899..e6606d6204 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromCurrentPage.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesFromCurrentPage.cshtml @@ -9,7 +9,7 @@ - It then generates links so the visitor can go to each page *@ -@{ var selection = Model.Content.Children().Where(x => x.IsVisible()).ToArray(); } +@{ var selection = Model.Content.Children.Where(x => x.IsVisible()).ToArray(); } @if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByDate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByDate.cshtml index 4ace3da7d1..2c2cc4422b 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByDate.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByDate.cshtml @@ -10,7 +10,7 @@ - It then generates links so the visitor can go to each page *@ -@{ var selection = Model.Content.Children().Where(x => x.IsVisible()).OrderByDescending(x => x.CreateDate).ToArray(); } +@{ var selection = Model.Content.Children.Where(x => x.IsVisible()).OrderByDescending(x => x.CreateDate).ToArray(); } @if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByName.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByName.cshtml index ecfd8ebe9d..d0398e7272 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByName.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByName.cshtml @@ -10,7 +10,7 @@ - It then generates links so the visitor can go to each page *@ -@{ var selection = Model.Content.Children().Where(x => x.IsVisible()).OrderBy(x => x.Name).ToArray(); } +@{ var selection = Model.Content.Children.Where(x => x.IsVisible()).OrderBy(x => x.Name).ToArray(); } @if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByProperty.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByProperty.cshtml index 667a384dfe..1bffae04c4 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByProperty.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListChildPagesOrderedByProperty.cshtml @@ -17,7 +17,7 @@ @if (propertyAlias != null) { - var selection = Model.Content.Children().Where(x => x.IsVisible()).OrderBy(x => x.Value(propertyAlias.ToString())).ToArray(); + var selection = Model.Content.Children.Where(x => x.IsVisible()).OrderBy(x => x.Value(propertyAlias.ToString())).ToArray(); if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListDescendantsFromCurrentPage.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListDescendantsFromCurrentPage.cshtml index 196db52d92..7ae917b41d 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListDescendantsFromCurrentPage.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListDescendantsFromCurrentPage.cshtml @@ -7,7 +7,7 @@ the page currently being viewed by the website visitor, displayed as nested unordered HTML lists. *@ -@{ var selection = Model.Content.Children().Where(x => x.IsVisible()).ToArray(); } +@{ var selection = Model.Content.Children.Where(x => x.IsVisible()).ToArray(); } @* Ensure that the Current Page has children *@ @if (selection.Length > 0) @@ -25,7 +25,7 @@ @* if this child page has any children, where the property umbracoNaviHide is not True *@ @{ - var children = item.Children().Where(x => x.IsVisible()).ToArray(); + var children = item.Children.Where(x => x.IsVisible()).ToArray(); if (children.Length > 0) { @* Call our helper to display the children *@ @@ -54,7 +54,7 @@ @* if the page has any children, where the property umbracoNaviHide is not True *@ @{ - var children = item.Children().Where(x => x.IsVisible()).ToArray(); + var children = item.Children.Where(x => x.IsVisible()).ToArray(); if (children.Length > 0) { @* Recurse and call our helper to display the children *@ diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml index b6067ff93a..51fdbadb00 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/ListImagesFromMediaFolder.cshtml @@ -18,7 +18,7 @@ { @* Get the media item associated with the id passed in *@ var media = Umbraco.Media(mediaId); - var selection = media.Children().ToArray(); + var selection = media.Children.ToArray(); if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml index 733c6be801..1c01eeb855 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Navigation.cshtml @@ -7,7 +7,7 @@ It also highlights the current active page/section in the navigation with the CSS class "current". *@ -@{ var selection = Model.Content.Root().Children().Where(x => x.IsVisible()).ToArray(); } +@{ var selection = Model.Content.Root().Children.Where(x => x.IsVisible()).ToArray(); } @if (selection.Length > 0) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml index a14d6fdc09..567ed5d07d 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/SiteMap.cshtml @@ -23,7 +23,7 @@ const int maxLevelForSitemap = 4; @* Select visible children *@ - var selection = node.Children().Where(x => x.IsVisible() && x.Level <= maxLevelForSitemap).ToArray(); + var selection = node.Children.Where(x => x.IsVisible() && x.Level <= maxLevelForSitemap).ToArray(); @* If any items are returned, render a list *@ if (selection.Length > 0) diff --git a/src/Umbraco.Web/Controllers/UmbLoginController.cs b/src/Umbraco.Web/Controllers/UmbLoginController.cs index 02130da8b5..2980b4a4c0 100644 --- a/src/Umbraco.Web/Controllers/UmbLoginController.cs +++ b/src/Umbraco.Web/Controllers/UmbLoginController.cs @@ -45,7 +45,7 @@ namespace Umbraco.Web.Controllers // if it's not a local url we'll redirect to the root of the current site return Redirect(Url.IsLocalUrl(model.RedirectUrl) ? model.RedirectUrl - : CurrentPage.AncestorOrSelf(1).Url()); + : CurrentPage.AncestorOrSelf(1).Url); } //redirect to current page by default diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs index b252f51fae..ed737e7749 100644 --- a/src/Umbraco.Web/Editors/TemplateQueryController.cs +++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs @@ -71,7 +71,7 @@ namespace Umbraco.Web.Editors QueryExpression = queryExpression.ToString(), ResultCount = results.Count, ExecutionTime = timer.ElapsedMilliseconds, - SampleResults = results.Take(20).Select(x => new TemplateQueryResult { Icon = "icon-file", Name = x.Name() }) + SampleResults = results.Take(20).Select(x => new TemplateQueryResult { Icon = "icon-file", Name = x.Name }) }; } @@ -186,12 +186,12 @@ namespace Umbraco.Web.Editors : contents.OrderByDescending(x => x.UpdateDate); case "name": return sortExpression.Direction == "ascending" - ? contents.OrderBy(x => x.Name()) - : contents.OrderByDescending(x => x.Name()); + ? contents.OrderBy(x => x.Name) + : contents.OrderByDescending(x => x.Name); default: return sortExpression.Direction == "ascending" - ? contents.OrderBy(x => x.Name()) - : contents.OrderByDescending(x => x.Name()); + ? contents.OrderBy(x => x.Name) + : contents.OrderByDescending(x => x.Name); } } diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index f641d6ca21..41f0e2fb65 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -35,9 +35,9 @@ namespace Umbraco.Web.Macros throw new ArgumentException("Document request has no node.", nameof(frequest)); PopulatePageData(frequest.PublishedContent.Id, - frequest.PublishedContent.Name(), frequest.PublishedContent.ContentType.Id, frequest.PublishedContent.ContentType.Alias, + frequest.PublishedContent.Name, frequest.PublishedContent.ContentType.Id, frequest.PublishedContent.ContentType.Alias, frequest.PublishedContent.WriterName, frequest.PublishedContent.CreatorName, frequest.PublishedContent.CreateDate, frequest.PublishedContent.UpdateDate, - frequest.PublishedContent.Path, frequest.PublishedContent.Parent()?.Id ?? -1); + frequest.PublishedContent.Path, frequest.PublishedContent.Parent?.Id ?? -1); if (frequest.HasTemplate) { @@ -57,9 +57,9 @@ namespace Umbraco.Web.Macros if (doc == null) throw new ArgumentNullException(nameof(doc)); PopulatePageData(doc.Id, - doc.Name(), doc.ContentType.Id, doc.ContentType.Alias, + doc.Name, doc.ContentType.Id, doc.ContentType.Alias, doc.WriterName, doc.CreatorName, doc.CreateDate, doc.UpdateDate, - doc.Path, doc.Parent()?.Id ?? -1); + doc.Path, doc.Parent?.Id ?? -1); if (doc.TemplateId.HasValue) { @@ -182,8 +182,8 @@ namespace Umbraco.Web.Macros { private readonly IContent _inner; private readonly IPublishedProperty[] _properties; + private IReadOnlyDictionary _cultureInfos; private readonly IVariationContextAccessor _variationContextAccessor; - private readonly IPublishedContent _parent; private static readonly IReadOnlyDictionary NoCultureInfos = new Dictionary(); @@ -215,7 +215,7 @@ namespace Umbraco.Web.Macros .Cast() .ToArray(); - _parent = new PagePublishedContent(_inner.ParentId); + Parent = new PagePublishedContent(_inner.ParentId); } public IPublishedContentType ContentType { get; } @@ -228,37 +228,25 @@ namespace Umbraco.Web.Macros public int SortOrder => _inner.SortOrder; - public string Name(string culture = null) => _inner.GetCultureName(culture); + public string Name => _inner.Name; - public DateTime CultureDate(string culture = null) - { - // invariant has invariant value (whatever the requested culture) - if (!ContentType.VariesByCulture()) - return UpdateDate; - - // handle context culture for variant - if (culture == null) - culture = _variationContextAccessor.VariationContext.Culture; - - // get - return culture != "" && _inner.PublishCultureInfos.TryGetValue(culture, out var infos) ? infos.Date : DateTime.MinValue; - } - - // ReSharper disable once CollectionNeverUpdated.Local - private static readonly List EmptyListOfString = new List(); - private IReadOnlyList _cultures; - - public IReadOnlyCollection Cultures + public IReadOnlyDictionary Cultures { get { if (!_inner.ContentType.VariesByCulture()) - return EmptyListOfString; - return _cultures ?? (_cultures = _inner.PublishCultureInfos.Values.Select(x => x.Culture).ToList()); + return NoCultureInfos; + + if (_cultureInfos != null) + return _cultureInfos; + + var urlSegmentProviders = Current.UrlSegmentProviders; // TODO inject + return _cultureInfos = _inner.PublishCultureInfos.Values + .ToDictionary(x => x.Culture, x => new PublishedCultureInfo(x.Culture, x.Name, _inner.GetUrlSegment(urlSegmentProviders, x.Culture), x.Date)); } } - public string UrlSegment(string culture = null) => throw new NotImplementedException(); + public string UrlSegment => throw new NotImplementedException(); public string WriterName { get; } @@ -276,7 +264,9 @@ namespace Umbraco.Web.Macros public int Level => _inner.Level; - public string Url(string culture = null, UrlMode mode = UrlMode.Auto) => throw new NotSupportedException(); + public string Url => throw new NotImplementedException(); + + public PublishedItemType ItemType => PublishedItemType.Content; public bool IsDraft(string culture = null) { @@ -288,9 +278,11 @@ namespace Umbraco.Web.Macros throw new NotImplementedException(); } - public IPublishedContent Parent() => _parent; + public IPublishedContent Parent { get; } - public IEnumerable Children(string culture = null) => throw new NotImplementedException(); + public IEnumerable Children => throw new NotImplementedException(); + + public IEnumerable ChildrenForAllCultures => throw new NotImplementedException(); public IEnumerable Properties => _properties; diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs index 6e0246f416..7b467b6d15 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -160,7 +160,7 @@ namespace Umbraco.Web.Models.PublishedContent IPublishedProperty property; // if we are here, content's property has no value do { - content = content.Parent(); + content = content.Parent; var propertyType = content?.ContentType.GetPropertyType(alias); diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 935c594285..cfc4fd1106 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Diagnostics; using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.PropertyEditors.ValueConverters; namespace Umbraco.Web.Models { @@ -34,10 +33,10 @@ namespace Umbraco.Web.Models public abstract int Id { get; } /// - public abstract string Name(string culture = null); + public virtual string Name => this.Name(); /// - public abstract string UrlSegment(string culture = null); + public virtual string UrlSegment => this.UrlSegment(); /// public abstract int SortOrder { get; } @@ -70,10 +69,13 @@ namespace Umbraco.Web.Models public abstract DateTime UpdateDate { get; } /// - public abstract DateTime CultureDate(string culture = null); + public virtual string Url => this.Url(); /// - public abstract IReadOnlyCollection Cultures { get; } + public abstract IReadOnlyDictionary Cultures { get; } + + /// + public abstract PublishedItemType ItemType { get; } /// public abstract bool IsDraft(string culture = null); @@ -86,10 +88,13 @@ namespace Umbraco.Web.Models #region Tree /// - public abstract IPublishedContent Parent(); + public abstract IPublishedContent Parent { get; } /// - public abstract IEnumerable Children(string culture = null); + public virtual IEnumerable Children => this.Children(); + + /// + public abstract IEnumerable ChildrenForAllCultures { get; } #endregion diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index c4277d6a79..4dcee5eb24 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -157,7 +157,7 @@ namespace Umbraco.Web.PublishedCache.NuCache pathParts.Add(urlSegment); // move to parent node - n = n.Parent(); + n = n.Parent; if (n != null) urlSegment = n.UrlSegment(culture); @@ -206,7 +206,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // "/foo" fails (looking for "/*/foo") we try also "/foo". // this does not make much sense anyway esp. if both "/foo/" and "/bar/foo" exist, but // that's the way it works pre-4.10 and we try to be backward compat for the time being - if (content.Parent() == null) + if (content.Parent == null) { var rootNode = GetByRoute(preview, "/", true); if (rootNode == null) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs index c9dd493ee6..51badc8b9a 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.Navigable var i = 0; _builtInValues = new [] { - XmlString(i++, _content.Name()), + XmlString(i++, _content.Name), XmlString(i++, _content.ParentId), XmlString(i++, _content.CreateDate), XmlString(i++, _content.UpdateDate), @@ -28,7 +28,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.Navigable XmlString(i++, _content.TemplateId), XmlString(i++, _content.WriterId), XmlString(i++, _content.CreatorId), - XmlString(i++, _content.UrlSegment()), + XmlString(i++, _content.UrlSegment), XmlString(i, _content.IsDraft()) }; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index c227c75952..67f219be21 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -110,7 +110,7 @@ namespace Umbraco.Web.PublishedCache.NuCache internal static Func GetMediaByIdFunc { get; set; } = (publishedShapshot, previewing, id) => publishedShapshot.Media.GetById(previewing, id); - private Func GetGetterById(PublishedItemType itemType) + private Func GetGetterById() { switch (ContentType.ItemType) { @@ -147,36 +147,6 @@ namespace Umbraco.Web.PublishedCache.NuCache /// public override int Id => _contentNode.Id; - /// - public override string Name(string culture = null) - { - // invariant has invariant value (whatever the requested culture) - if (!ContentType.VariesByCulture()) - return ContentData.Name; - - // handle context culture for variant - if (culture == null) - culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; - - // get - return culture != "" && ContentData.CultureInfos.TryGetValue(culture, out var infos) ? infos.Name : null; - } - - /// - public override string UrlSegment(string culture = null) - { - // invariant has invariant value (whatever the requested culture) - if (!ContentType.VariesByCulture()) - return _urlSegment; - - // handle context culture for variant - if (culture == null) - culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; - - // get - return ContentData.CultureInfos.TryGetValue(culture, out var infos) ? infos.UrlSegment : null; - } - /// public override int SortOrder => _contentNode.SortOrder; @@ -207,37 +177,31 @@ namespace Umbraco.Web.PublishedCache.NuCache /// public override DateTime UpdateDate => ContentData.VersionDate; - /// - public override DateTime CultureDate(string culture = null) - { - // invariant has invariant value (whatever the requested culture) - if (!ContentType.VariesByCulture()) - return UpdateDate; - - // handle context culture for variant - if (culture == null) - culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; - - // get - return culture != "" && ContentData.CultureInfos.TryGetValue(culture, out var infos) ? infos.Date : DateTime.MinValue; - } - // ReSharper disable once CollectionNeverUpdated.Local - private static readonly List EmptyListOfString = new List(); - private IReadOnlyCollection _cultures; + private static readonly IReadOnlyDictionary EmptyCultures = new Dictionary(); + private IReadOnlyDictionary _cultures; /// - public override IReadOnlyCollection Cultures + public override IReadOnlyDictionary Cultures { get { if (!ContentType.VariesByCulture()) - return EmptyListOfString; + return EmptyCultures; - return _cultures ?? (_cultures = new HashSet(ContentData.CultureInfos.Keys, StringComparer.OrdinalIgnoreCase)); + if (_cultures != null) return _cultures; + + if (ContentData.CultureInfos == null) + throw new Exception("panic: _contentDate.CultureInfos is null."); + + return _cultures = ContentData.CultureInfos + .ToDictionary(x => x.Key, x => new PublishedCultureInfo(x.Key, x.Value.Name, x.Value.UrlSegment, x.Value.Date), StringComparer.OrdinalIgnoreCase); } } + /// + public override PublishedItemType ItemType => _contentNode.ContentType.ItemType; + /// public override bool IsDraft(string culture = null) { @@ -288,38 +252,35 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Tree /// - public override IPublishedContent Parent() + public override IPublishedContent Parent { - var getById = GetGetterById(ContentType.ItemType); - var publishedSnapshot = _publishedSnapshotAccessor.PublishedSnapshot; - return getById(publishedSnapshot, IsPreviewing, ParentId); + get + { + var getById = GetGetterById(); + var publishedSnapshot = _publishedSnapshotAccessor.PublishedSnapshot; + return getById(publishedSnapshot, IsPreviewing, ParentId); + } } /// - public override IEnumerable Children(string culture = null) + public override IEnumerable ChildrenForAllCultures { - // invariant has invariant value (whatever the requested culture) - if (!ContentType.VariesByCulture() && culture != "*") - culture = ""; - - // handle context culture for variant - if (culture == null) - culture = VariationContextAccessor?.VariationContext?.Culture ?? ""; - - var getById = GetGetterById(ContentType.ItemType); - var publishedSnapshot = _publishedSnapshotAccessor.PublishedSnapshot; - var id = _contentNode.FirstChildContentId; - - while (id > 0) + get { - var content = getById(publishedSnapshot, IsPreviewing, id); - if (content == null) - throw new Exception("panic: failed to get content"); + var getById = GetGetterById(); + var publishedSnapshot = _publishedSnapshotAccessor.PublishedSnapshot; + var id = _contentNode.FirstChildContentId; + + while (id > 0) + { + var content = getById(publishedSnapshot, IsPreviewing, id); + if (content == null) + throw new Exception("panic: failed to get content"); - if (culture == "*" || content.IsInvariantOrHasCulture(culture)) yield return content; - id = UnwrapIPublishedContent(content)._contentNode.NextSiblingContentId; + id = UnwrapIPublishedContent(content)._contentNode.NextSiblingContentId; + } } } @@ -381,7 +342,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // includes all children, published or unpublished // NavigableNavigator takes care of selecting those it wants // note: this is not efficient - we do not try to be (would require a double-linked list) - internal IList ChildIds => Children().Select(x => x.Id).ToList(); + internal IList ChildIds => Children.Select(x => x.Id).ToList(); // used by Property // gets a value indicating whether the content or media exists in diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index 9c6f60da97..6e9ec61c62 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -73,13 +73,17 @@ namespace Umbraco.Web.PublishedCache #region IPublishedContent + public override PublishedItemType ItemType => PublishedItemType.Member; + public override bool IsDraft(string culture = null) => false; public override bool IsPublished(string culture = null) => true; - public override IPublishedContent Parent() => null; + public override IPublishedContent Parent => null; - public override IEnumerable Children(string culture = null) => Enumerable.Empty(); + public override IEnumerable Children => Enumerable.Empty(); + + public override IEnumerable ChildrenForAllCultures => Enumerable.Empty(); public override IEnumerable Properties => _properties; @@ -129,17 +133,11 @@ namespace Umbraco.Web.PublishedCache public override int SortOrder => 0; - public override string Name(string culture = null) - { - // member name does not vary, ignore culture - return _member.Name; - } + public override string Name => _member.Name; - public override DateTime CultureDate(string culture = null) => throw new NotSupportedException(); + public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public override IReadOnlyCollection Cultures => throw new NotSupportedException(); - - public override string UrlSegment(string culture = null) => throw new NotSupportedException(); + public override string UrlSegment => throw new NotSupportedException(); // TODO: ARGH! need to fix this - this is not good because it uses ApplicationContext.Current public override string WriterName => _member.GetCreatorProfile().Name; diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index eb38826193..fbfc52f4d8 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -167,36 +167,6 @@ namespace Umbraco.Web #region Variations - /// - /// Determines whether the content has a culture. - /// - /// Culture is case-insensitive. - public static bool HasCulture(this IPublishedContent content, string culture) - => content.Cultures.Contains(culture ?? string.Empty); - - /// - /// Determines whether the content is invariant, or has a culture. - /// - /// Culture is case-insensitive. - public static bool IsInvariantOrHasCulture(this IPublishedContent content, string culture) - => !content.ContentType.VariesByCulture() || content.Cultures.Contains(culture ?? ""); - - /// - /// Filters a sequence of to return invariant items, and items that are published for the specified culture. - /// - /// The content items. - /// The specific culture to filter for. If null is used the current culture is used. (Default is null). - internal static IEnumerable WhereIsInvariantOrHasCulture(this IEnumerable contents, string culture = null) - where T : class, IPublishedContent - { - if (contents == null) throw new ArgumentNullException(nameof(contents)); - - culture = culture ?? Current.VariationContextAccessor.VariationContext?.Culture ?? ""; - - // either does not vary by culture, or has the specified culture - return contents.Where(x => !x.ContentType.VariesByCulture() || x.HasCulture(culture)); - } - /// /// Gets the culture assigned to a document by domains, in the context of a current Uri. /// @@ -555,7 +525,7 @@ namespace Umbraco.Web /// This method is here for consistency purposes but does not make much sense. public static IPublishedContent Ancestor(this IPublishedContent content) { - return content.Parent(); + return content.Parent; } /// @@ -687,7 +657,7 @@ namespace Umbraco.Web { if (content == null) throw new ArgumentNullException(nameof(content)); if (orSelf) yield return content; - while ((content = content.Parent()) != null) + while ((content = content.Parent) != null) yield return content; } @@ -891,7 +861,7 @@ namespace Umbraco.Web where T : class, IPublishedContent { if (content == null) throw new ArgumentNullException(nameof(content)); - return content.Parent() as T; + return content.Parent as T; } #endregion @@ -1111,8 +1081,8 @@ namespace Umbraco.Web /// The siblings of the content including the node itself. public static IEnumerable SiblingsAndSelf(this IPublishedContent content, string culture = null) { - return content.Parent() != null - ? content.Parent().Children(culture) + return content.Parent != null + ? content.Parent.Children(culture) : PublishedSnapshot.Content.GetAtRoot().WhereIsInvariantOrHasCulture(culture); } @@ -1125,8 +1095,8 @@ namespace Umbraco.Web /// The siblings of the content including the node itself, of the given content type. public static IEnumerable SiblingsAndSelfOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) { - return content.Parent() != null - ? content.Parent().ChildrenOfType(contentTypeAlias, culture) + return content.Parent != null + ? content.Parent.ChildrenOfType(contentTypeAlias, culture) : PublishedSnapshot.Content.GetAtRoot().OfTypes(contentTypeAlias).WhereIsInvariantOrHasCulture(culture); } @@ -1140,8 +1110,8 @@ namespace Umbraco.Web public static IEnumerable SiblingsAndSelf(this IPublishedContent content, string culture = null) where T : class, IPublishedContent { - return content.Parent() != null - ? content.Parent().Children(culture) + return content.Parent != null + ? content.Parent.Children(culture) : PublishedSnapshot.Content.GetAtRoot().OfType().WhereIsInvariantOrHasCulture(culture); } diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index 354c315202..efd48af2d4 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -65,7 +65,7 @@ namespace Umbraco.Web.Routing while (domainUris == null && n != null) // n is null at root { // move to parent node - n = n.Parent(); + n = n.Parent; domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, n.Id, current, excludeDefault: false); } diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 4e9944a808..4092538481 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -87,7 +87,7 @@ namespace Umbraco.Web.Routing var domainUris = DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, n.Id, current, false); while (domainUris == null && n != null) // n is null at root { - n = n.Parent(); // move to parent node + n = n.Parent; // move to parent node domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, n.Id, current, excludeDefault: true); } diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index 1f15967996..6e768c28b6 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -276,7 +276,7 @@ namespace Umbraco.Web.Routing return true; // variant, ensure that the culture corresponding to the domain's language is published - return domainDocument.Cultures.Contains(domain.Culture.Name); + return domainDocument.Cultures.ContainsKey(domain.Culture.Name); } domains = domains.Where(IsPublishedContentDomain).ToList(); diff --git a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs b/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs index 9c6459da62..77aa3c65a1 100644 --- a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs +++ b/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs @@ -171,12 +171,12 @@ namespace Umbraco.Web.Routing if (entityContent == null) continue; // get the default affected cultures by going up the tree until we find the first culture variant entity (default to no cultures) - var defaultCultures = entityContent.AncestorsOrSelf()?.FirstOrDefault(a => a.Cultures.Any())?.Cultures.ToArray() + var defaultCultures = entityContent.AncestorsOrSelf()?.FirstOrDefault(a => a.Cultures.Any())?.Cultures.Keys.ToArray() ?? new[] {(string) null}; foreach (var x in entityContent.DescendantsOrSelf()) { // if this entity defines specific cultures, use those instead of the default ones - var cultures = x.Cultures.Any() ? x.Cultures : defaultCultures; + var cultures = x.Cultures.Any() ? x.Cultures.Keys : defaultCultures; foreach (var culture in cultures) { diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs index 27a27399bf..077680d2e2 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs @@ -188,7 +188,7 @@ namespace Umbraco.Web.Routing while (o != null) { l.Add(o.Name()); - o = o.Parent(); + o = o.Parent; } l.Reverse(); var s = "/" + string.Join("/", l) + " (id=" + pcr.PublishedContent.Id + ")"; diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index 06706e475b..c3a1fb128f 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -60,7 +60,7 @@ namespace Umbraco.Web.Templates if (guidUdi.EntityType == Constants.UdiEntityType.Document) newLink = urlProvider.GetUrl(guidUdi.Guid); else if (guidUdi.EntityType == Constants.UdiEntityType.Media) - newLink = mediaCache.GetById(guidUdi.Guid)?.Url(); + newLink = mediaCache.GetById(guidUdi.Guid)?.Url; if (newLink == null) newLink = "#"; @@ -171,7 +171,7 @@ namespace Umbraco.Web.Templates return match.Value; } - var url = media.Url(); + var url = media.Url; return $"{match.Groups[1].Value}{url}{match.Groups[3].Value}{udi}{match.Groups[5].Value}"; }); } From 14a056f4f4001ec2887eda750d482e851edbaa56 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 7 Jun 2019 11:15:58 +0200 Subject: [PATCH 26/75] Fix and cleanup --- .../PublishedContent/PublishedCultureInfos.cs | 3 +- .../PublishedContentExtensions.cs | 8 +- .../PublishedContentCache.cs | 2 +- .../PublishedMediaCache.cs | 2 +- .../XmlPublishedContent.cs | 11 +- .../Published/ConvertersTests.cs | 18 +- .../Published/NestedContentTests.cs | 68 ++---- .../PublishedContent/NuCacheChildrenTests.cs | 203 ++++++++++++++++-- .../PublishedContent/NuCacheTests.cs | 18 +- .../PublishedContentDataTableTests.cs | 62 +----- .../PublishedContent/PublishedContentTests.cs | 4 +- .../PublishedContent/PublishedMediaTests.cs | 2 +- .../SolidPublishedSnapshot.cs | 18 +- .../Routing/MediaUrlProviderTests.cs | 25 ++- src/Umbraco.Tests/Routing/UrlProviderTests.cs | 12 +- .../TestHelpers/Stubs/TestPublishedContent.cs | 66 ------ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 - .../PublishedCache/IPublishedCache.cs | 6 +- .../PublishedCache/NuCache/ContentCache.cs | 22 +- .../PublishedCache/NuCache/MediaCache.cs | 38 +--- .../NuCache/PublishedContent.cs | 9 +- .../NuCache/PublishedSnapshotService.cs | 16 +- .../PublishedCache/PublishedCacheBase.cs | 6 +- src/Umbraco.Web/PublishedContentExtensions.cs | 4 +- 24 files changed, 322 insertions(+), 302 deletions(-) delete mode 100644 src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs index eedc97dbfc..d5096158a7 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs @@ -13,10 +13,9 @@ namespace Umbraco.Core.Models.PublishedContent /// public PublishedCultureInfo(string culture, string name, string urlSegment, DateTime date) { - if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentNullOrEmptyException(nameof(culture)); if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); - Culture = culture; + Culture = culture ?? throw new ArgumentNullException(nameof(culture)); Name = name; UrlSegment = urlSegment; Date = date; diff --git a/src/Umbraco.Core/PublishedContentExtensions.cs b/src/Umbraco.Core/PublishedContentExtensions.cs index a8cb0cdaf2..f220f307d6 100644 --- a/src/Umbraco.Core/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/PublishedContentExtensions.cs @@ -37,7 +37,7 @@ namespace Umbraco.Core culture = culture ?? Current.VariationContextAccessor.VariationContext?.Culture ?? ""; // either does not vary by culture, or has the specified culture - return contents.Where(x => !ContentVariationExtensions.VariesByCulture((IPublishedContentType) x.ContentType) || HasCulture(x, culture)); + return contents.Where(x => !x.ContentType.VariesByCulture() || HasCulture(x, culture)); } /// @@ -49,7 +49,7 @@ namespace Umbraco.Core { // invariant has invariant value (whatever the requested culture) if (!content.ContentType.VariesByCulture()) - return "NAME??"; // fixme where should the invariant one come from? should Cultures contain it? + return content.Cultures.TryGetValue("", out var invariantInfos) ? invariantInfos.Name : null; // handle context culture for variant if (culture == null) @@ -69,7 +69,7 @@ namespace Umbraco.Core { // invariant has invariant value (whatever the requested culture) if (!content.ContentType.VariesByCulture()) - return "URLSEGMENT??"; // fixme where should the invariant one come from? should Cultures contain it? + return content.Cultures.TryGetValue("", out var invariantInfos) ? invariantInfos.UrlSegment : null; // handle context culture for variant if (culture == null) @@ -125,4 +125,4 @@ namespace Umbraco.Core : children.Where(x => x.IsInvariantOrHasCulture(culture)); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs index 8de0209c69..8ce6b10983 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs @@ -382,7 +382,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache return GetXml(preview).CreateNavigator().MoveToId(contentId.ToString(CultureInfo.InvariantCulture)); } - public override IEnumerable GetAtRoot(bool preview) + public override IEnumerable GetAtRoot(bool preview, string culture = null) { return ConvertToDocuments(GetXml(preview).SelectNodes(XPathStrings.RootDocuments), preview); } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs index ba43921f1c..999d7f040d 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs @@ -105,7 +105,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache return GetUmbracoMedia(contentId) != null; } - public override IEnumerable GetAtRoot(bool preview) + public override IEnumerable GetAtRoot(bool preview, string culture = null) { var searchProvider = GetSearchProviderSafe(); diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index 4a60912757..3697863cb4 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -145,8 +145,15 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache } } - private static readonly Lazy> NoCultures = new Lazy>(() => new Dictionary()); - public override IReadOnlyDictionary Cultures => NoCultures.Value; + private Dictionary _cultures; + + private Dictionary GetCultures() + { + EnsureNodeInitialized(); + return new Dictionary { { "", new PublishedCultureInfo("", _name, _urlName, _updateDate) } }; + } + + public override IReadOnlyDictionary Cultures => _cultures ?? (_cultures = GetCultures()); public override string WriterName { diff --git a/src/Umbraco.Tests/Published/ConvertersTests.cs b/src/Umbraco.Tests/Published/ConvertersTests.cs index 7ad81922e2..671129848c 100644 --- a/src/Umbraco.Tests/Published/ConvertersTests.cs +++ b/src/Umbraco.Tests/Published/ConvertersTests.cs @@ -4,16 +4,14 @@ using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Cache; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.Components; +using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.PublishedCache; @@ -127,7 +125,7 @@ namespace Umbraco.Tests.Published var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false); var cntType1 = contentTypeFactory.CreateContentType(1001, "cnt1", t => Enumerable.Empty()); - var cnt1 = new TestPublishedContent(cntType1, 1234, Guid.NewGuid(), new Dictionary(), false); + var cnt1 = new SolidPublishedContent(cntType1) { Id = 1234 }; cacheContent[cnt1.Id] = cnt1; Assert.AreSame(cnt1, element1.Value("prop1")); @@ -224,8 +222,16 @@ namespace Umbraco.Tests.Published var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); var element2 = new PublishedElement(elementType2, Guid.NewGuid(), new Dictionary { { "prop2", "1003" } }, false); - var cnt1 = new TestPublishedContent(contentType1, 1003, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); - var cnt2 = new TestPublishedContent(contentType2, 1004, Guid.NewGuid(), new Dictionary { { "prop2", "1003" } }, false); + var cnt1 = new SolidPublishedContent(contentType1) + { + Id = 1003, + Properties = new[] { new SolidPublishedProperty { Alias = "prop1", SolidHasValue = true, SolidValue = "val1" } } + }; + var cnt2 = new SolidPublishedContent(contentType1) + { + Id = 1004, + Properties = new[] { new SolidPublishedProperty { Alias = "prop2", SolidHasValue = true, SolidValue = "1003" } } + }; cacheContent[cnt1.Id] = cnt1.CreateModel(); cacheContent[cnt2.Id] = cnt2.CreateModel(); diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 432247b09f..9385b8955a 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; +using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; using Umbraco.Web; using Umbraco.Web.Models; @@ -167,12 +168,16 @@ namespace Umbraco.Tests.Published var key = Guid.NewGuid(); var keyA = Guid.NewGuid(); - var content = new TestPublishedContent(contentType1, key, new[] + var content = new SolidPublishedContent(contentType1) { - new TestPublishedProperty(contentType1.GetPropertyType("property1"), $@"[ + Key = key, + Properties = new [] + { + new TestPublishedProperty(contentType1.GetPropertyType("property1"), $@"[ {{ ""key"": ""{keyA}"", ""propertyN1"": ""foo"", ""ncContentTypeAlias"": ""contentN1"" }} ]") - }); + } + }; var value = content.Value("property1"); // nested single converter returns proper TestModel value @@ -194,13 +199,17 @@ namespace Umbraco.Tests.Published var key = Guid.NewGuid(); var keyA = Guid.NewGuid(); var keyB = Guid.NewGuid(); - var content = new TestPublishedContent(contentType2, key, new[] + var content = new SolidPublishedContent(contentType2) { - new TestPublishedProperty(contentType2.GetPropertyType("property2"), $@"[ + Key = key, + Properties = new[] + { + new TestPublishedProperty(contentType2.GetPropertyType("property2"), $@"[ {{ ""key"": ""{keyA}"", ""propertyN1"": ""foo"", ""ncContentTypeAlias"": ""contentN1"" }}, {{ ""key"": ""{keyB}"", ""propertyN1"": ""bar"", ""ncContentTypeAlias"": ""contentN1"" }} ]") - }); + } + }; var value = content.Value("property2"); // nested many converter returns proper IEnumerable value @@ -257,52 +266,5 @@ namespace Umbraco.Tests.Published 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(); } - - class TestPublishedContent : PublishedContentBase - { - public TestPublishedContent(IPublishedContentType contentType, Guid key, IEnumerable properties) - { - ContentType = contentType; - Key = key; - var propertiesA = properties.ToArray(); - Properties = propertiesA; - foreach (var property in propertiesA) - property.SetOwner(this); - } - - // ReSharper disable UnassignedGetOnlyAutoProperty - public override PublishedItemType ItemType { get; } - public override bool IsDraft(string culture = null) => false; - public override bool IsPublished(string culture = null) => true; - public override IPublishedContent Parent { get; } - public override IEnumerable Children { get; } - public override IEnumerable ChildrenForAllCultures => Children; - public override IPublishedContentType ContentType { get; } - // ReSharper restore UnassignedGetOnlyAutoProperty - - // ReSharper disable UnassignedGetOnlyAutoProperty - public override int Id { get; } - public override int? TemplateId { get; } - public override int SortOrder { get; } - public override string Name { get; } - public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public override string UrlSegment { get; } - public override string WriterName { get; } - public override string CreatorName { get; } - public override int WriterId { get; } - public override int CreatorId { get; } - public override string Path { get; } - public override DateTime CreateDate { get; } - public override DateTime UpdateDate { get; } - public override int Level { get; } - public override Guid Key { get; } - // ReSharper restore UnassignedGetOnlyAutoProperty - - public override IEnumerable Properties { get; } - public override IPublishedProperty GetProperty(string alias) - { - return Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); - } - } } } diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index d77d1e4701..7e8a4b18e1 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -20,6 +20,7 @@ using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects; using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Web; using Umbraco.Web.Cache; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.NuCache; @@ -33,20 +34,25 @@ namespace Umbraco.Tests.PublishedContent private IPublishedSnapshotService _snapshotService; private IVariationContextAccessor _variationAccesor; private IPublishedSnapshotAccessor _snapshotAccessor; - private ContentType _contentType; - private PropertyType _propertyType; + private ContentType _contentTypeInvariant; + private ContentType _contentTypeVariant; private TestDataSource _source; private void Init(IEnumerable kits) { Current.Reset(); - Current.UnlockConfigs(); - Current.Configs.Add(SettingsForTests.GenerateMockUmbracoSettings); - Current.Configs.Add(() => new GlobalSettings()); - var globalSettings = Current.Configs.Global(); - // create a data source for NuCache - _source = new TestDataSource(kits); + var factory = Mock.Of(); + Current.Factory = factory; + + var configs = new Configs(); + Mock.Get(factory).Setup(x => x.GetInstance(typeof(Configs))).Returns(configs); + var globalSettings = new GlobalSettings(); + configs.Add(SettingsForTests.GenerateMockUmbracoSettings); + configs.Add(() => globalSettings); + + var publishedModelFactory = new NoopPublishedModelFactory(); + Mock.Get(factory).Setup(x => x.GetInstance(typeof(IPublishedModelFactory))).Returns(publishedModelFactory); var runtime = Mock.Of(); Mock.Get(runtime).Setup(x => x.Level).Returns(RuntimeLevel.Run); @@ -59,13 +65,18 @@ namespace Umbraco.Tests.PublishedContent dataType }; - _propertyType = new PropertyType("Umbraco.Void.Editor", ValueStorageType.Nvarchar) { Alias = "prop", DataTypeId = 3, Variations = ContentVariation.Nothing }; - _contentType = new ContentType(-1) { Id = 2, Alias = "ctype", Variations = ContentVariation.Nothing }; - _contentType.AddPropertyType(_propertyType); + var propertyType = new PropertyType("Umbraco.Void.Editor", ValueStorageType.Nvarchar) { Alias = "prop", DataTypeId = 3, Variations = ContentVariation.Nothing }; + _contentTypeInvariant = new ContentType(-1) { Id = 2, Alias = "itype", Variations = ContentVariation.Nothing }; + _contentTypeInvariant.AddPropertyType(propertyType); + + propertyType = new PropertyType("Umbraco.Void.Editor", ValueStorageType.Nvarchar) { Alias = "prop", DataTypeId = 3, Variations = ContentVariation.Culture }; + _contentTypeVariant = new ContentType(-1) { Id = 3, Alias = "vtype", Variations = ContentVariation.Culture }; + _contentTypeVariant.AddPropertyType(propertyType); var contentTypes = new[] { - _contentType + _contentTypeInvariant, + _contentTypeVariant }; var contentTypeService = Mock.Of(); @@ -109,6 +120,9 @@ namespace Umbraco.Tests.PublishedContent _variationAccesor = new TestVariationContextAccessor(); _snapshotAccessor = new TestPublishedSnapshotAccessor(); + // create a data source for NuCache + _source = new TestDataSource(kits); + // at last, create the complete NuCache snapshot service! var options = new PublishedSnapshotService.Options { IgnoreLocalDb = true }; _snapshotService = new PublishedSnapshotService(options, @@ -133,9 +147,11 @@ namespace Umbraco.Tests.PublishedContent // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); + + Mock.Get(factory).Setup(x => x.GetInstance(typeof(IVariationContextAccessor))).Returns(_variationAccesor); } - private IEnumerable GetKits() + private IEnumerable GetInvariantKits() { var paths = new Dictionary { { -1, "-1" } }; @@ -146,10 +162,11 @@ namespace Umbraco.Tests.PublishedContent var path = paths[id] = parentPath + "," + id; var level = path.Count(x => x == ','); + var now = DateTime.Now; return new ContentNodeKit { - ContentTypeId = 2, + ContentTypeId = _contentTypeInvariant.Id, Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0), DraftData = null, PublishedData = new ContentData @@ -158,7 +175,7 @@ namespace Umbraco.Tests.PublishedContent Published = true, TemplateId = 0, VersionId = 1, - VersionDate = DateTime.Now, + VersionDate = now, WriterId = 0, Properties = new Dictionary(), CultureInfos = new Dictionary() @@ -184,6 +201,69 @@ namespace Umbraco.Tests.PublishedContent yield return CreateKit(12, 4, 2); } + private IEnumerable GetVariantKits() + { + var paths = new Dictionary { { -1, "-1" } }; + + Dictionary GetCultureInfos(int id, DateTime now) + { + var en = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; + var fr = new[] { 1, 3, 4, 6, 7, 9, 10, 12 }; + + var infos = new Dictionary(); + if (en.Contains(id)) + infos["en-US"] = new CultureVariation { Name = "N" + id + "-" + "en-US", Date = now, IsDraft = false }; + if (fr.Contains(id)) + infos["fr-FR"] = new CultureVariation { Name = "N" + id + "-" + "fr-FR", Date = now, IsDraft = false }; + return infos; + } + + ContentNodeKit CreateKit(int id, int parentId, int sortOrder) + { + if (!paths.TryGetValue(parentId, out var parentPath)) + throw new Exception("Unknown parent."); + + var path = paths[id] = parentPath + "," + id; + var level = path.Count(x => x == ','); + var now = DateTime.Now; + + return new ContentNodeKit + { + ContentTypeId = _contentTypeVariant.Id, + Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0), + DraftData = null, + PublishedData = new ContentData + { + Name = "N" + id, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = GetCultureInfos(id, now) + } + }; + } + + yield return CreateKit(1, -1, 1); + yield return CreateKit(2, -1, 2); + yield return CreateKit(3, -1, 3); + + yield return CreateKit(4, 1, 1); + yield return CreateKit(5, 1, 2); + yield return CreateKit(6, 1, 3); + + yield return CreateKit(7, 2, 3); + yield return CreateKit(8, 2, 2); + yield return CreateKit(9, 2, 1); + + yield return CreateKit(10, 3, 1); + + yield return CreateKit(11, 4, 1); + yield return CreateKit(12, 4, 2); + } + [Test] public void EmptyTest() { @@ -199,7 +279,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void ChildrenTest() { - Init(GetKits()); + Init(GetInvariantKits()); var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); _snapshotAccessor.PublishedSnapshot = snapshot; @@ -226,7 +306,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void ParentTest() { - Init(GetKits()); + Init(GetInvariantKits()); var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); _snapshotAccessor.PublishedSnapshot = snapshot; @@ -252,7 +332,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void MoveToRootTest() { - Init(GetKits()); + Init(GetInvariantKits()); // get snapshot var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); @@ -294,7 +374,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void MoveFromRootTest() { - Init(GetKits()); + Init(GetInvariantKits()); // get snapshot var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); @@ -336,7 +416,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void ReOrderTest() { - Init(GetKits()); + Init(GetInvariantKits()); // get snapshot var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); @@ -411,7 +491,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void MoveTest() { - Init(GetKits()); + Init(GetInvariantKits()); // get snapshot var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); @@ -512,11 +592,90 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(1, snapshot.Content.GetById(7).Parent?.Id); } + [Test] + public void VariantChildrenTest() + { + Init(GetVariantKits()); + + var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); + _snapshotAccessor.PublishedSnapshot = snapshot; + + _variationAccesor.VariationContext = new VariationContext("en-US"); + + var documents = snapshot.Content.GetAtRoot().ToArray(); + AssertDocuments(documents, "N1-en-US", "N2-en-US", "N3-en-US"); + + documents = snapshot.Content.GetById(1).Children().ToArray(); + AssertDocuments(documents, "N4-en-US", "N5-en-US", "N6-en-US"); + + documents = snapshot.Content.GetById(2).Children().ToArray(); + AssertDocuments(documents, "N9-en-US", "N8-en-US", "N7-en-US"); + + documents = snapshot.Content.GetById(3).Children().ToArray(); + AssertDocuments(documents, "N10-en-US"); + + documents = snapshot.Content.GetById(4).Children().ToArray(); + AssertDocuments(documents, "N11-en-US", "N12-en-US"); + + documents = snapshot.Content.GetById(10).Children().ToArray(); + AssertDocuments(documents); + + + _variationAccesor.VariationContext = new VariationContext("fr-FR"); + + documents = snapshot.Content.GetAtRoot().ToArray(); + AssertDocuments(documents, "N1-fr-FR", "N3-fr-FR"); + + documents = snapshot.Content.GetById(1).Children().ToArray(); + AssertDocuments(documents, "N4-fr-FR", "N6-fr-FR"); + + documents = snapshot.Content.GetById(2).Children().ToArray(); + AssertDocuments(documents, "N9-fr-FR", "N7-fr-FR"); + + documents = snapshot.Content.GetById(3).Children().ToArray(); + AssertDocuments(documents, "N10-fr-FR"); + + documents = snapshot.Content.GetById(4).Children().ToArray(); + AssertDocuments(documents, "N12-fr-FR"); + + documents = snapshot.Content.GetById(10).Children().ToArray(); + AssertDocuments(documents); + + documents = snapshot.Content.GetById(1).Children("*").ToArray(); + AssertDocuments(documents, "N4-fr-FR", null, "N6-fr-FR"); + AssertDocuments("en-US", documents, "N4-en-US", "N5-en-US", "N6-en-US"); + + documents = snapshot.Content.GetById(1).Children("en-US").ToArray(); + AssertDocuments(documents, "N4-fr-FR", null, "N6-fr-FR"); + AssertDocuments("en-US", documents, "N4-en-US", "N5-en-US", "N6-en-US"); + + documents = snapshot.Content.GetById(1).ChildrenForAllCultures.ToArray(); + AssertDocuments(documents, "N4-fr-FR", null, "N6-fr-FR"); + AssertDocuments("en-US", documents, "N4-en-US", "N5-en-US", "N6-en-US"); + + + documents = snapshot.Content.GetAtRoot("*").ToArray(); + AssertDocuments(documents, "N1-fr-FR", null, "N3-fr-FR"); + + documents = snapshot.Content.GetById(1).DescendantsOrSelf().ToArray(); + AssertDocuments(documents, "N1-fr-FR", "N4-fr-FR", "N12-fr-FR", "N6-fr-FR"); + + documents = snapshot.Content.GetById(1).DescendantsOrSelf("*").ToArray(); + AssertDocuments(documents, "N1-fr-FR", "N4-fr-FR", null /*11*/, "N12-fr-FR", null /*5*/, "N6-fr-FR"); + } + private void AssertDocuments(IPublishedContent[] documents, params string[] names) { Assert.AreEqual(names.Length, documents.Length); for (var i = 0; i < names.Length; i++) - Assert.AreEqual(names[i], documents[i].Name()); + Assert.AreEqual(names[i], documents[i].Name); + } + + private void AssertDocuments(string culture, IPublishedContent[] documents, params string[] names) + { + Assert.AreEqual(names.Length, documents.Length); + for (var i = 0; i < names.Length; i++) + Assert.AreEqual(names[i], documents[i].Name(culture)); } } } diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index dc035c1645..b66404c954 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -38,10 +38,18 @@ namespace Umbraco.Tests.PublishedContent private void Init() { Current.Reset(); - Current.UnlockConfigs(); - Current.Configs.Add(SettingsForTests.GenerateMockUmbracoSettings); - Current.Configs.Add(() => new GlobalSettings()); - var globalSettings = Current.Configs.Global(); + + var factory = Mock.Of(); + Current.Factory = factory; + + var configs = new Configs(); + Mock.Get(factory).Setup(x => x.GetInstance(typeof(Configs))).Returns(configs); + var globalSettings = new GlobalSettings(); + configs.Add(SettingsForTests.GenerateMockUmbracoSettings); + configs.Add(() => globalSettings); + + var publishedModelFactory = new NoopPublishedModelFactory(); + Mock.Get(factory).Setup(x => x.GetInstance(typeof(IPublishedModelFactory))).Returns(publishedModelFactory); // create a content node kit var kit = new ContentNodeKit @@ -184,6 +192,8 @@ namespace Umbraco.Tests.PublishedContent // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); + + Mock.Get(factory).Setup(x => x.GetInstance(typeof(IVariationContextAccessor))).Returns(_variationAccesor); } [Test] diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 5c22295547..cc455b8e5d 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -97,7 +97,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetContent(true, 1); //change a doc type alias - var c = (TestPublishedContent)doc.Children.ElementAt(0); + var c = (SolidPublishedContent)doc.Children.ElementAt(0); c.ContentType = new PublishedContentType(22, "DontMatch", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var dt = doc.ChildrenAsTable(Current.Services, "Child"); @@ -129,7 +129,8 @@ namespace Umbraco.Tests.PublishedContent var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); var contentTypeAlias = createChildren ? "Parent" : "Child"; - var d = new TestPublishedContent + var contentType = new PublishedContentType(22, contentTypeAlias, PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var d = new SolidPublishedContent(contentType) { CreateDate = DateTime.Now, CreatorId = 1, @@ -140,7 +141,7 @@ namespace Umbraco.Tests.PublishedContent UpdateDate = DateTime.Now, Path = "-1,3", UrlSegment = "home-page", - Name = "Page" + Guid.NewGuid().ToString(), + Name = "Page" + Guid.NewGuid(), Version = Guid.NewGuid(), WriterId = 1, WriterName = "Shannon", @@ -175,62 +176,7 @@ namespace Umbraco.Tests.PublishedContent new RawValueProperty(factory.CreatePropertyType("property3", 1), d, "value" + (indexVals + 2))); } - d.ContentType = new PublishedContentType(22, contentTypeAlias, PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); return d; } - - // note - could probably rewrite those tests using SolidPublishedContentCache - // l8tr... - private class TestPublishedContent : IPublishedContent - { - public string Url { get; set; } - public PublishedItemType ItemType { get; set; } - public IPublishedContent Parent { get; set; } - public int Id { get; set; } - public Guid Key { get; set; } - public int? TemplateId { get; set; } - public int SortOrder { get; set; } - public string Name { get; set; } - public IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public string UrlSegment { get; set; } - public string WriterName { get; set; } - public string CreatorName { get; set; } - public int WriterId { get; set; } - public int CreatorId { get; set; } - public string Path { get; set; } - public DateTime CreateDate { get; set; } - public DateTime UpdateDate { get; set; } - public Guid Version { get; set; } - public int Level { get; set; } - public bool IsDraft(string culture = null) => false; - public bool IsPublished(string culture = null) => true; - - public IEnumerable Properties { get; set; } - - public IEnumerable Children { get; set; } - public IEnumerable ChildrenForAllCultures => Children; - - public IPublishedProperty GetProperty(string alias) - { - return Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); - } - - public IPublishedProperty GetProperty(string alias, bool recurse) - { - var property = GetProperty(alias); - if (recurse == false) return property; - - IPublishedContent content = this; - while (content != null && (property == null || property.HasValue() == false)) - { - content = content.Parent; - property = content == null ? null : content.GetProperty(alias); - } - - return property; - } - - public IPublishedContentType ContentType { get; set; } - } } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index fd0837b0ab..f54971a197 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -821,7 +821,7 @@ namespace Umbraco.Tests.PublishedContent var level1_2 = GetNode(1175); var level1_3 = GetNode(4444); - _publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot()).Returns(new []{root}); + _publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot(It.IsAny())).Returns(new []{root}); CollectionAssertAreEqual(new []{root}, root.SiblingsAndSelf()); @@ -860,7 +860,7 @@ namespace Umbraco.Tests.PublishedContent var level1_2 = GetNode(1175); var level1_3 = GetNode(4444); - _publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot()).Returns(new []{root}); + _publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot(It.IsAny())).Returns(new []{root}); CollectionAssertAreEqual(new IPublishedContent[0], root.Siblings()); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index b789eb0ef8..f801d02c5b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -495,7 +495,7 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(nodeId, converted.Id); Assert.AreEqual(3, converted.Level); Assert.AreEqual(1, converted.SortOrder); - Assert.AreEqual("Sam's Umbraco Image", converted.Name()); + Assert.AreEqual("Sam's Umbraco Image", converted.Name); Assert.AreEqual("-1,1111,2222,2112", converted.Path); } diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index ab5a65146a..860b9b9179 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -100,7 +100,7 @@ namespace Umbraco.Tests.PublishedContent return _content.ContainsKey(contentId); } - public override IEnumerable GetAtRoot(bool preview) + public override IEnumerable GetAtRoot(bool preview, string culture = null) { return _content.Values.Where(x => x.Parent == null); } @@ -176,13 +176,19 @@ namespace Umbraco.Tests.PublishedContent #region Content + private Dictionary _cultures; + + private Dictionary GetCultures() + { + return new Dictionary { { "", new PublishedCultureInfo("", Name, UrlSegment, UpdateDate) } }; + } + public int Id { get; set; } public Guid Key { get; set; } public int? TemplateId { get; set; } public int SortOrder { get; set; } public string Name { get; set; } - public PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException(); - public IReadOnlyDictionary Cultures => throw new NotSupportedException(); + public IReadOnlyDictionary Cultures => _cultures ?? (_cultures = GetCultures()); public string UrlSegment { get; set; } public string WriterName { get; set; } public string CreatorName { get; set; } @@ -195,7 +201,7 @@ namespace Umbraco.Tests.PublishedContent public int Level { get; set; } public string Url { get; set; } - public PublishedItemType ItemType { get { return PublishedItemType.Content; } } + public PublishedItemType ItemType => PublishedItemType.Content; public bool IsDraft(string culture = null) => false; public bool IsPublished(string culture = null) => true; @@ -214,7 +220,7 @@ namespace Umbraco.Tests.PublishedContent #region ContentType - public IPublishedContentType ContentType { get; private set; } + public IPublishedContentType ContentType { get; set; } #endregion @@ -236,7 +242,7 @@ namespace Umbraco.Tests.PublishedContent while (content != null && (property == null || property.HasValue() == false)) { content = content.Parent; - property = content == null ? null : content.GetProperty(alias); + property = content?.GetProperty(alias); } return property; diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs index 9eee6eb32d..5af48e64b1 100644 --- a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using Moq; using Newtonsoft.Json; @@ -11,7 +10,6 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using Umbraco.Web.Routing; @@ -56,10 +54,10 @@ namespace Umbraco.Tests.Routing const string expected = "/media/rfeiw584/test.jpg"; var configuration = new ImageCropperConfiguration(); - var imageCropperValue = JsonConvert.SerializeObject(new ImageCropperValue + var imageCropperValue = new ImageCropperValue { Src = expected - }); + }; var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.ImageCropper, imageCropperValue, configuration); @@ -120,15 +118,28 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(daMediaUrl, resolvedUrl); } - private static TestPublishedContent CreatePublishedContent(string propertyEditorAlias, object propertyValue, object dataTypeConfiguration) + private static IPublishedContent CreatePublishedContent(string propertyEditorAlias, object propertyValue, object dataTypeConfiguration) { var umbracoFilePropertyType = CreatePropertyType(propertyEditorAlias, dataTypeConfiguration, ContentVariation.Nothing); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), new[] {umbracoFilePropertyType}, ContentVariation.Nothing); - return new TestPublishedContent(contentType, 1234, Guid.NewGuid(), - new Dictionary {{"umbracoFile", propertyValue } }, false); + return new SolidPublishedContent(contentType) + { + Id = 1234, + Key = Guid.NewGuid(), + Properties = new[] + { + new SolidPublishedProperty + { + Alias = "umbracoFile", + SolidValue = propertyValue, + SolidHasValue = true, + PropertyType = umbracoFilePropertyType + } + } + }; } private static PublishedPropertyType CreatePropertyType(string propertyEditorAlias, object dataTypeConfiguration, ContentVariation variation) diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index ca13e06f0a..02aa95cd2e 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -10,8 +10,8 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Tests.LegacyXmlPublishedCache; +using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -142,7 +142,7 @@ namespace Umbraco.Tests.Routing new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings: globalSettings.Object); - + var result = umbracoContext.UrlProvider.GetUrl(nodeId); Assert.AreEqual(niceUrlMatch, result); } @@ -159,7 +159,7 @@ namespace Umbraco.Tests.Routing var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); - var publishedContent = new TestPublishedContent(contentType, 1234, Guid.NewGuid(), new Dictionary(), false); + var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; var publishedContentCache = new Mock(); publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) @@ -204,7 +204,7 @@ namespace Umbraco.Tests.Routing var umbracoSettings = Current.Configs.Settings(); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); - var publishedContent = new TestPublishedContent(contentType, 1234, Guid.NewGuid(), new Dictionary(), false); + var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; var publishedContentCache = new Mock(); publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) @@ -258,7 +258,7 @@ namespace Umbraco.Tests.Routing var umbracoSettings = Current.Configs.Settings(); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); - var publishedContent = new TestPublishedContent(contentType, 1234, Guid.NewGuid(), new Dictionary(), false); + var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; var publishedContentCache = new Mock(); publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) @@ -332,7 +332,7 @@ namespace Umbraco.Tests.Routing }, globalSettings: globalSettings.Object); //mock the Umbraco settings that we need - + Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); umbracoContext.UrlProvider.Mode = UrlMode.Absolute; diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs deleted file mode 100644 index 62f93d106f..0000000000 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web.PublishedCache; - -namespace Umbraco.Tests.TestHelpers.Stubs -{ - internal class TestPublishedContent : PublishedElement, IPublishedContent - { - public TestPublishedContent(IPublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultures = null) - : base(contentType, key, values, previewing) - { - Id = id; - Cultures = cultures ?? new Dictionary(); - } - - public int Id { get; } - public int? TemplateId { get; set; } - public int SortOrder { get; set; } - public string Name { get; set; } - public IVariationContextAccessor VariationContextAccessor { get; set; } - public IReadOnlyDictionary Cultures { get; set; } - public string UrlSegment { get; set; } - public string DocumentTypeAlias => ContentType.Alias; - public int DocumentTypeId { get; set; } - public string WriterName { get; set; } - public string CreatorName { get; set; } - public int WriterId { get; set; } - public int CreatorId { get; set; } - public string Path { get; set; } - public DateTime CreateDate { get; set; } - public DateTime UpdateDate { get; set; } - public Guid Version { get; set; } - public int Level { get; set; } - public string Url { get; set; } - public PublishedItemType ItemType => ContentType.ItemType; - public bool IsDraft(string culture = null) => false; - public bool IsPublished(string culture = null) => true; - public IPublishedContent Parent { get; set; } - public IEnumerable Children { get; set; } - public IEnumerable ChildrenForAllCultures => Children; - - // copied from PublishedContentBase - public IPublishedProperty GetProperty(string alias, bool recurse) - { - 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; - } - } -} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index cd3155c7c3..4f4a83dc26 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -206,7 +206,6 @@ - diff --git a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs index 33094221f7..0370088f77 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs @@ -84,16 +84,18 @@ namespace Umbraco.Web.PublishedCache /// Gets contents at root. /// /// A value indicating whether to consider unpublished content. + /// A culture. /// The contents. /// The value of overrides defaults. - IEnumerable GetAtRoot(bool preview); + IEnumerable GetAtRoot(bool preview, string culture = null); /// /// Gets contents at root. /// + /// A culture. /// The contents. /// Considers published or unpublished content depending on defaults. - IEnumerable GetAtRoot(); + IEnumerable GetAtRoot(string culture = null); /// /// Gets a content resulting from an XPath query. diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index 4dcee5eb24..fa879b7879 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -1,20 +1,15 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Xml.XPath; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; -using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Services; using Umbraco.Core.Xml; using Umbraco.Core.Xml.XPath; using Umbraco.Web.PublishedCache.NuCache.Navigable; -using Umbraco.Web.Routing; namespace Umbraco.Web.PublishedCache.NuCache { @@ -25,6 +20,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IAppCache _elementsCache; private readonly IDomainCache _domainCache; private readonly IGlobalSettings _globalSettings; + private readonly IVariationContextAccessor _variationContextAccessor; #region Constructor @@ -33,7 +29,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // it's too late for UmbracoContext which has captured previewDefault and stuff into these ctor vars // but, no, UmbracoContext returns snapshot.Content which comes from elements SO a resync should create a new cache - public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache, IAppCache elementsCache, IDomainCache domainCache, IGlobalSettings globalSettings) + public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache, IAppCache elementsCache, IDomainCache domainCache, IGlobalSettings globalSettings, IVariationContextAccessor variationContextAccessor) : base(previewDefault) { _snapshot = snapshot; @@ -41,6 +37,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _elementsCache = elementsCache; _domainCache = domainCache; _globalSettings = globalSettings; + _variationContextAccessor = variationContextAccessor; } private bool HideTopLevelNodeFromPath => _globalSettings.HideTopLevelNodeFromPath; @@ -241,7 +238,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var guidUdi = contentId as GuidUdi; if (guidUdi == null) throw new ArgumentException($"Udi must be of type {typeof(GuidUdi).Name}.", nameof(contentId)); - + if (guidUdi.EntityType != Constants.UdiEntityType.Document) throw new ArgumentException($"Udi entity type must be \"{Constants.UdiEntityType.Document}\".", nameof(contentId)); @@ -256,11 +253,18 @@ namespace Umbraco.Web.PublishedCache.NuCache return preview || n.PublishedModel != null; } - public override IEnumerable GetAtRoot(bool preview) + IEnumerable INavigableData.GetAtRoot(bool preview) => GetAtRoot(preview); + + public override IEnumerable GetAtRoot(bool preview, string culture = null) { + // handle context culture for variant + if (culture == null) + culture = _variationContextAccessor?.VariationContext?.Culture ?? ""; + // both .Draft and .Published cannot be null at the same time // root is already sorted by sortOrder, and does not contain nulls - return _snapshot.GetAtRoot().Select(n => GetNodePublishedContent(n, preview)); + var atRoot = _snapshot.GetAtRoot().Select(n => GetNodePublishedContent(n, preview)); + return culture == "*" ? atRoot : atRoot.Where(x => x.IsInvariantOrHasCulture(culture)); } private static IPublishedContent GetNodePublishedContent(ContentNode node, bool preview) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs index fc04d9d1b3..182086ed7f 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs @@ -14,17 +14,15 @@ namespace Umbraco.Web.PublishedCache.NuCache internal class MediaCache : PublishedCacheBase, IPublishedMediaCache, INavigableData, IDisposable { private readonly ContentStore.Snapshot _snapshot; - private readonly IAppCache _snapshotCache; - private readonly IAppCache _elementsCache; + private readonly IVariationContextAccessor _variationContextAccessor; #region Constructors - public MediaCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache, IAppCache elementsCache) + public MediaCache(bool previewDefault, ContentStore.Snapshot snapshot, IVariationContextAccessor variationContextAccessor) : base(previewDefault) { _snapshot = snapshot; - _snapshotCache = snapshotCache; - _elementsCache = elementsCache; + _variationContextAccessor = variationContextAccessor; } #endregion @@ -65,30 +63,16 @@ namespace Umbraco.Web.PublishedCache.NuCache return n != null; } - public override IEnumerable GetAtRoot(bool preview) + IEnumerable INavigableData.GetAtRoot(bool preview) => GetAtRoot(preview); + + public override IEnumerable GetAtRoot(bool preview, string culture = null) { - if (PublishedSnapshotService.CacheContentCacheRoots == false) - return GetAtRootNoCache(); + // handle context culture for variant + if (culture == null) + culture = _variationContextAccessor?.VariationContext?.Culture ?? ""; - var cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing - ? _elementsCache - : _snapshotCache; - - if (cache == null) - return GetAtRootNoCache(); - - // note: ToArray is important here, we want to cache the result, not the function! - return (IEnumerable)cache.Get( - CacheKeys.MediaCacheRoots(false), // ignore preview, only 1 key! - () => GetAtRootNoCache().ToArray()); - } - - private IEnumerable GetAtRootNoCache() - { - var c = _snapshot.GetAtRoot(); - - // ignore preview, there's only draft for media - return c.Select(n => n.PublishedModel); + var atRoot = _snapshot.GetAtRoot().Select(x => x.PublishedModel); + return culture == "*" ? atRoot : atRoot.Where(x => x.IsInvariantOrHasCulture(culture)); } public override bool HasContent(bool preview) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 67f219be21..1995e8b424 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -186,11 +186,14 @@ namespace Umbraco.Web.PublishedCache.NuCache { get { - if (!ContentType.VariesByCulture()) - return EmptyCultures; - if (_cultures != null) return _cultures; + if (!ContentType.VariesByCulture()) + return _cultures = new Dictionary + { + { "", new PublishedCultureInfo("", ContentData.Name, _urlSegment, CreateDate) } + }; + if (ContentData.CultureInfos == null) throw new Exception("panic: _contentDate.CultureInfos is null."); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 5a3672ed57..7a7682a797 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -63,18 +63,6 @@ namespace Umbraco.Web.PublishedCache.NuCache // so making it configurable. public static readonly bool FullCacheWhenPreviewing = true; - // define constant - determines whether to cache the published content - // objects (in the elements cache, or snapshot cache, depending on preview) - // or to re-fetch them all the time. caching is faster but uses more - // memory. not sure what we want. - public static readonly bool CachePublishedContentChildren = true; - - // define constant - determines whether to cache the content cache root - // objects (in the elements cache, or snapshot cache, depending on preview) - // or to re-fetch them all the time. caching is faster but uses more - // memory - not sure what we want. - public static readonly bool CacheContentCacheRoots = true; - #region Constructors //private static int _singletonCheck; @@ -1070,8 +1058,8 @@ namespace Umbraco.Web.PublishedCache.NuCache return new PublishedSnapshot.PublishedSnapshotElements { - ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainCache, _globalSettings), - MediaCache = new MediaCache(previewDefault, mediaSnap, snapshotCache, elementsCache), + ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainCache, _globalSettings, VariationContextAccessor), + MediaCache = new MediaCache(previewDefault, mediaSnap, VariationContextAccessor), MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, memberTypeCache, PublishedSnapshotAccessor, VariationContextAccessor, _entitySerializer), DomainCache = domainCache, SnapshotCache = snapshotCache, diff --git a/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs b/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs index d726664db0..1f637663e5 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs @@ -37,11 +37,11 @@ namespace Umbraco.Web.PublishedCache public bool HasById(int contentId) => HasById(PreviewDefault, contentId); - public abstract IEnumerable GetAtRoot(bool preview); + public abstract IEnumerable GetAtRoot(bool preview, string culture = null); - public IEnumerable GetAtRoot() + public IEnumerable GetAtRoot(string culture = null) { - return GetAtRoot(PreviewDefault); + return GetAtRoot(PreviewDefault, culture); } public abstract IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars); diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index fbfc52f4d8..d7283a1e90 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -833,7 +833,7 @@ namespace Umbraco.Web if (content == null) throw new ArgumentNullException(nameof(content)); if (orSelf) yield return content; - foreach (var desc in content.Children(culture).SelectMany(x => x.EnumerateDescendants())) + foreach (var desc in content.Children(culture).SelectMany(x => x.EnumerateDescendants(culture))) yield return desc; } @@ -841,7 +841,7 @@ namespace Umbraco.Web { yield return content; - foreach (var desc in content.Children(culture).SelectMany(x => x.EnumerateDescendants())) + foreach (var desc in content.Children(culture).SelectMany(x => x.EnumerateDescendants(culture))) yield return desc; } From 8eafadc29b2fe50ff81b3f9ffe7b81d2df69dfa3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 12 Jun 2019 14:00:28 +1000 Subject: [PATCH 27/75] adds some more info to panic exceptions --- .../PublishedCache/NuCache/ContentStore.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 298b98ca05..f3c3c2bf7a 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -643,7 +643,7 @@ namespace Umbraco.Web.PublishedCache.NuCache while (id > 0) { if (!_contentNodes.TryGetValue(id, out var link) || link.Value == null) - throw new Exception("panic: failed to get child"); + throw new Exception("panic: failed to get child " + id); ClearBranchLocked(link.Value); id = link.Value.NextSiblingContentId; } @@ -667,7 +667,7 @@ namespace Umbraco.Web.PublishedCache.NuCache else { if (!_contentNodes.TryGetValue(content.ParentContentId, out parentLink) || parentLink.Value == null) - throw new Exception("panic: failed to get parent"); + throw new Exception("panic: failed to get parent " + content.ParentContentId); } var parent = parentLink.Value; @@ -680,11 +680,11 @@ namespace Umbraco.Web.PublishedCache.NuCache else { if (!_contentNodes.TryGetValue(parent.FirstChildContentId, out var link) || link.Value == null) - throw new Exception("panic: failed to get first child"); + throw new Exception("panic: failed to get first child " + parent.FirstChildContentId); while (link.Value.NextSiblingContentId != content.Id) if (!_contentNodes.TryGetValue(parent.NextSiblingContentId, out link) || link.Value == null) - throw new Exception("panic: failed to get next sibling"); + throw new Exception("panic: failed to get next sibling " + parent.NextSiblingContentId); var prevChild = GenCloneLocked(link); prevChild.NextSiblingContentId = content.NextSiblingContentId; @@ -735,7 +735,7 @@ namespace Umbraco.Web.PublishedCache.NuCache else { if (!_contentNodes.TryGetValue(content.ParentContentId, out parentLink) || parentLink.Value == null) - throw new Exception("panic: failed to get parent"); + throw new Exception("panic: failed to get parent " + content.ParentContentId); } var parent = parentLink.Value; @@ -748,7 +748,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } if (!_contentNodes.TryGetValue(parent.FirstChildContentId, out var prevChildLink) || prevChildLink.Value == null) - throw new Exception("panic: failed to get first child"); + throw new Exception("panic: failed to get first child " + parent.FirstChildContentId); var prevChild = prevChildLink.Value; @@ -764,7 +764,7 @@ namespace Umbraco.Web.PublishedCache.NuCache while (prevChild.NextSiblingContentId > 0) { if (!_contentNodes.TryGetValue(prevChild.NextSiblingContentId, out var link) || link.Value == null) - throw new Exception("panic: failed to get next child"); + throw new Exception("panic: failed to get next child " + prevChild.NextSiblingContentId); var nextChild = link.Value; if (nextChild.SortOrder > content.SortOrder) @@ -873,7 +873,7 @@ namespace Umbraco.Web.PublishedCache.NuCache while (id > 0) { if (!_contentNodes.TryGetValue(id, out var link) || link == null) - throw new Exception("panic: failed to get sibling"); + throw new Exception("panic: failed to get sibling " + id); yield return link.Value; id = link.Value.NextSiblingContentId; } From dec042fce4dbd1cbeeb33d8dc4794327db2e2098 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 12 Jun 2019 16:28:33 +1000 Subject: [PATCH 28/75] adds Id to panic exception --- src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 1995e8b424..de0c46f6ea 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -278,7 +278,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { var content = getById(publishedSnapshot, IsPreviewing, id); if (content == null) - throw new Exception("panic: failed to get content"); + throw new Exception("panic: failed to get content " + id); yield return content; From 9c252131f7ca0a64ec066e39fa6afdbfb99651d7 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 12 Jun 2019 08:55:03 +0200 Subject: [PATCH 29/75] Better exception message in NuCache --- .../PublishedCache/NuCache/ContentStore.cs | 56 ++++++++----------- .../NuCache/PublishedContent.cs | 2 +- 2 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index f3c3c2bf7a..f1f8f137fb 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -642,13 +642,22 @@ namespace Umbraco.Web.PublishedCache.NuCache var id = content.FirstChildContentId; while (id > 0) { - if (!_contentNodes.TryGetValue(id, out var link) || link.Value == null) - throw new Exception("panic: failed to get child " + id); + var link = GetLinkedNode(id, "child"); ClearBranchLocked(link.Value); id = link.Value.NextSiblingContentId; } } + // gets the link node + // throws (panic) if not found, or no value + private LinkedNode GetLinkedNode(int id, string description) + { + if (_contentNodes.TryGetValue(id, out var link) && link.Value != null) + return link; + + throw new Exception($"panic: failed to get {description} with id={id}"); + } + private LinkedNode GetParentLink(ContentNode content) { _contentNodes.TryGetValue(content.ParentContentId, out var link); // else null @@ -659,16 +668,9 @@ namespace Umbraco.Web.PublishedCache.NuCache private void RemoveNodeLocked(ContentNode content) { - LinkedNode parentLink; - if (content.ParentContentId < 0) - { - parentLink = _root; - } - else - { - if (!_contentNodes.TryGetValue(content.ParentContentId, out parentLink) || parentLink.Value == null) - throw new Exception("panic: failed to get parent " + content.ParentContentId); - } + var parentLink = content.ParentContentId < 0 + ? _root + : GetLinkedNode(content.ParentContentId, "parent"); var parent = parentLink.Value; @@ -679,12 +681,10 @@ namespace Umbraco.Web.PublishedCache.NuCache } else { - if (!_contentNodes.TryGetValue(parent.FirstChildContentId, out var link) || link.Value == null) - throw new Exception("panic: failed to get first child " + parent.FirstChildContentId); + var link = GetLinkedNode(parent.FirstChildContentId, "first child"); while (link.Value.NextSiblingContentId != content.Id) - if (!_contentNodes.TryGetValue(parent.NextSiblingContentId, out link) || link.Value == null) - throw new Exception("panic: failed to get next sibling " + parent.NextSiblingContentId); + link = GetLinkedNode(parent.NextSiblingContentId, "next child"); var prevChild = GenCloneLocked(link); prevChild.NextSiblingContentId = content.NextSiblingContentId; @@ -726,17 +726,9 @@ namespace Umbraco.Web.PublishedCache.NuCache private void AddNodeLocked(ContentNode content) { - LinkedNode parentLink; - - if (content.ParentContentId < 0) - { - parentLink = _root; - } - else - { - if (!_contentNodes.TryGetValue(content.ParentContentId, out parentLink) || parentLink.Value == null) - throw new Exception("panic: failed to get parent " + content.ParentContentId); - } + var parentLink = content.ParentContentId < 0 + ? _root + : GetLinkedNode(content.ParentContentId, "parent"); var parent = parentLink.Value; @@ -747,9 +739,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return; } - if (!_contentNodes.TryGetValue(parent.FirstChildContentId, out var prevChildLink) || prevChildLink.Value == null) - throw new Exception("panic: failed to get first child " + parent.FirstChildContentId); - + var prevChildLink = GetLinkedNode(parent.FirstChildContentId, "first child"); var prevChild = prevChildLink.Value; if (prevChild.SortOrder > content.SortOrder) @@ -763,8 +753,7 @@ namespace Umbraco.Web.PublishedCache.NuCache while (prevChild.NextSiblingContentId > 0) { - if (!_contentNodes.TryGetValue(prevChild.NextSiblingContentId, out var link) || link.Value == null) - throw new Exception("panic: failed to get next child " + prevChild.NextSiblingContentId); + var link = GetLinkedNode(prevChild.NextSiblingContentId, "next child"); var nextChild = link.Value; if (nextChild.SortOrder > content.SortOrder) @@ -872,8 +861,7 @@ namespace Umbraco.Web.PublishedCache.NuCache while (id > 0) { - if (!_contentNodes.TryGetValue(id, out var link) || link == null) - throw new Exception("panic: failed to get sibling " + id); + var link = GetLinkedNode(id, "sibling"); yield return link.Value; id = link.Value.NextSiblingContentId; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 1995e8b424..20372c074d 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -278,7 +278,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { var content = getById(publishedSnapshot, IsPreviewing, id); if (content == null) - throw new Exception("panic: failed to get content"); + throw new Exception($"panic: failed to get content with id={id}"); yield return content; From 94d2a5cbad2548358c88a6d942df5942e66b3c81 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 12 Jun 2019 15:48:19 +0200 Subject: [PATCH 30/75] Bugfix published cache and children --- .../PublishedContent/NuCacheChildrenTests.cs | 83 +++++++++++++++++++ .../Models/PublishedContentBase.cs | 2 +- .../PublishedCache/NuCache/ContentStore.cs | 50 +++++++---- 3 files changed, 119 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index 7e8a4b18e1..dd656b8019 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -664,6 +664,89 @@ namespace Umbraco.Tests.PublishedContent AssertDocuments(documents, "N1-fr-FR", "N4-fr-FR", null /*11*/, "N12-fr-FR", null /*5*/, "N6-fr-FR"); } + [Test] + public void RemoveTest() + { + Init(GetInvariantKits()); + + var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); + _snapshotAccessor.PublishedSnapshot = snapshot; + + var documents = snapshot.Content.GetAtRoot().ToArray(); + AssertDocuments(documents, "N1", "N2", "N3"); + + documents = snapshot.Content.GetById(1).Children().ToArray(); + AssertDocuments(documents, "N4", "N5", "N6"); + + documents = snapshot.Content.GetById(2).Children().ToArray(); + AssertDocuments(documents, "N9", "N8", "N7"); + + // notify + _snapshotService.Notify(new[] + { + new ContentCacheRefresher.JsonPayload(3, TreeChangeTypes.Remove), // remove last + new ContentCacheRefresher.JsonPayload(5, TreeChangeTypes.Remove), // remove middle + new ContentCacheRefresher.JsonPayload(9, TreeChangeTypes.Remove), // remove first + }, out _, out _); + + documents = snapshot.Content.GetAtRoot().ToArray(); + AssertDocuments(documents, "N1", "N2"); + + documents = snapshot.Content.GetById(1).Children().ToArray(); + AssertDocuments(documents, "N4", "N6"); + + documents = snapshot.Content.GetById(2).Children().ToArray(); + AssertDocuments(documents, "N8", "N7"); + + // notify + _snapshotService.Notify(new[] + { + new ContentCacheRefresher.JsonPayload(1, TreeChangeTypes.Remove), // remove first + new ContentCacheRefresher.JsonPayload(8, TreeChangeTypes.Remove), // remove + new ContentCacheRefresher.JsonPayload(7, TreeChangeTypes.Remove), // remove + }, out _, out _); + + documents = snapshot.Content.GetAtRoot().ToArray(); + AssertDocuments(documents, "N2"); + + documents = snapshot.Content.GetById(2).Children().ToArray(); + AssertDocuments(documents); + } + + [Test] + public void UpdateTest() + { + Init(GetInvariantKits()); + + var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); + _snapshotAccessor.PublishedSnapshot = snapshot; + + var documents = snapshot.Content.GetAtRoot().ToArray(); + AssertDocuments(documents, "N1", "N2", "N3"); + + documents = snapshot.Content.GetById(1).Children().ToArray(); + AssertDocuments(documents, "N4", "N5", "N6"); + + documents = snapshot.Content.GetById(2).Children().ToArray(); + AssertDocuments(documents, "N9", "N8", "N7"); + + // notify + _snapshotService.Notify(new[] + { + new ContentCacheRefresher.JsonPayload(1, TreeChangeTypes.RefreshBranch), + new ContentCacheRefresher.JsonPayload(2, TreeChangeTypes.RefreshNode), + }, out _, out _); + + documents = snapshot.Content.GetAtRoot().ToArray(); + AssertDocuments(documents, "N1", "N2", "N3"); + + documents = snapshot.Content.GetById(1).Children().ToArray(); + AssertDocuments(documents, "N4", "N5", "N6"); + + documents = snapshot.Content.GetById(2).Children().ToArray(); + AssertDocuments(documents, "N9", "N8", "N7"); + } + private void AssertDocuments(IPublishedContent[] documents, params string[] names) { Assert.AreEqual(names.Length, documents.Length); diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index cfc4fd1106..008bf10504 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.Models /// /// This base class does which (a) consistently resolves and caches the Url, (b) provides an implementation /// for this[alias], and (c) provides basic content set management. - [DebuggerDisplay("Content Id: {Id}}")] + [DebuggerDisplay("{Content Id: {Id}}")] public abstract class PublishedContentBase : IPublishedContent { #region ContentType diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index f1f8f137fb..e59d332525 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -511,6 +511,11 @@ namespace Umbraco.Web.PublishedCache.NuCache RemoveNodeLocked(existing); AddNodeLocked(kit.Node); } + else + { + // replacing existing, handle siblings + kit.Node.NextSiblingContentId = existing.NextSiblingContentId; + } _xmap[kit.Node.Uid] = kit.Node.Id; } @@ -604,7 +609,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // try to find the content // if it is not there, nothing to do - _contentNodes.TryGetValue(id, out LinkedNode link); // else null + _contentNodes.TryGetValue(id, out var link); // else null if (link?.Value == null) return false; var content = link.Value; @@ -674,6 +679,11 @@ namespace Umbraco.Web.PublishedCache.NuCache var parent = parentLink.Value; + // must have children + if (parent.FirstChildContentId < 0) + throw new Exception("panic: no children"); + + // if first, clone parent + remove first child if (parent.FirstChildContentId == content.Id) { parent = GenCloneLocked(parentLink); @@ -681,11 +691,13 @@ namespace Umbraco.Web.PublishedCache.NuCache } else { + // iterate children until the previous child var link = GetLinkedNode(parent.FirstChildContentId, "first child"); while (link.Value.NextSiblingContentId != content.Id) - link = GetLinkedNode(parent.NextSiblingContentId, "next child"); + link = GetLinkedNode(link.Value.NextSiblingContentId, "next child"); + // clone the previous child and replace next child var prevChild = GenCloneLocked(link); prevChild.NextSiblingContentId = content.NextSiblingContentId; } @@ -732,6 +744,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var parent = parentLink.Value; + // if parent has no children, clone parent + add as first child if (parent.FirstChildContentId < 0) { parent = GenCloneLocked(parentLink); @@ -739,39 +752,45 @@ namespace Umbraco.Web.PublishedCache.NuCache return; } - var prevChildLink = GetLinkedNode(parent.FirstChildContentId, "first child"); - var prevChild = prevChildLink.Value; + // get parent's first child + var childLink = GetLinkedNode(parent.FirstChildContentId, "first child"); + var child = childLink.Value; - if (prevChild.SortOrder > content.SortOrder) + // if first, clone parent + insert as first child + if (child.SortOrder > content.SortOrder) { content.NextSiblingContentId = parent.FirstChildContentId; - parent = GenCloneLocked(parentLink); parent.FirstChildContentId = content.Id; return; } - while (prevChild.NextSiblingContentId > 0) + // else lookup position + while (child.NextSiblingContentId > 0) { - var link = GetLinkedNode(prevChild.NextSiblingContentId, "next child"); - var nextChild = link.Value; + // get next child + var nextChildLink = GetLinkedNode(child.NextSiblingContentId, "next child"); + var nextChild = nextChildLink.Value; + // if here, clone previous + append/insert if (nextChild.SortOrder > content.SortOrder) { content.NextSiblingContentId = nextChild.Id; - prevChild = GenCloneLocked(prevChildLink); - prevChild.NextSiblingContentId = content.Id; + child = GenCloneLocked(childLink); + child.NextSiblingContentId = content.Id; return; } - prevChild = nextChild; - prevChildLink = link; + childLink = nextChildLink; + child = nextChild; } - prevChild = GenCloneLocked(prevChildLink); - prevChild.NextSiblingContentId = content.Id; + // if last, clone previous + append + child = GenCloneLocked(childLink); + child.NextSiblingContentId = content.Id; } + // replaces the root node private void SetRootLocked(ContentNode node) { if (_root.Gen != _liveGen) @@ -784,6 +803,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } } + // set a node (just the node, not the tree) private void SetValueLocked(ConcurrentDictionary> dict, TKey key, TValue value) where TValue : class { From fecadeec5135ef2f9a93ac4b7b4a303d83e0e2d6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 18 Jun 2019 11:57:36 +1000 Subject: [PATCH 31/75] Fixes issue with nucache and rebuilding and fixes ChildrenForAllCultures to deal with unpublished children --- .../PublishedCache/NuCache/ContentStore.cs | 25 ++++++++++-- .../NuCache/PublishedContent.cs | 39 +++++++++++++++++-- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index e59d332525..9eec95c10f 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -647,9 +647,16 @@ namespace Umbraco.Web.PublishedCache.NuCache var id = content.FirstChildContentId; while (id > 0) { - var link = GetLinkedNode(id, "child"); - ClearBranchLocked(link.Value); - id = link.Value.NextSiblingContentId; + if (TryGetLinkedNode(id, out var link)) + { + ClearBranchLocked(link.Value); + id = link.Value.NextSiblingContentId; + } + else + { + // break i guess? + id = 0; + } } } @@ -663,6 +670,18 @@ namespace Umbraco.Web.PublishedCache.NuCache throw new Exception($"panic: failed to get {description} with id={id}"); } + private bool TryGetLinkedNode(int id, out LinkedNode node) + { + if (_contentNodes.TryGetValue(id, out var link) && link.Value != null) + { + node = link; + return true; + } + + node = null; + return false; + } + private LinkedNode GetParentLink(ContentNode content) { _contentNodes.TryGetValue(content.ParentContentId, out var link); // else null diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 20372c074d..2e96513761 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -277,10 +277,43 @@ namespace Umbraco.Web.PublishedCache.NuCache while (id > 0) { var content = getById(publishedSnapshot, IsPreviewing, id); - if (content == null) - throw new Exception($"panic: failed to get content with id={id}"); - yield return content; + if (content != null) + { + yield return content; + } + else + { + //Why were we throwing here? It is perfectly legitimate that a child is not published and when IsPreviewing == false + //this will return null even if the item is in nucache but only with an unpublished flag. + //If we want to be very clear about something being wrong, then perhaps the exception should be thrown from within + //the Func that gets the child where it can validate if there really is nothing there when something is expected? + + //In the meantime, we cannot continue so we should break?... BUT doesn't that mean that if there was a sibling next to this that was published + //that it will now be excluded? + + //Well, in that case this is annoying, so the only thing i can think of is to get the preview version of it to get the + //next child and continue that way? + + if (IsPreviewing) + { + //if we're in preview mode and nothing is returned then something is wrong + throw new Exception($"panic: failed to get content with id={id}"); + } + else + { + //get the preview version since this item might not be published + content = getById(publishedSnapshot, true, id); + if (content == null) + { + //if we're in preview mode and nothing is returned then something is wrong + throw new Exception($"panic: failed to get content with id={id}"); + } + + //now we can continue with the next sibling id, but we aren't going to return this content item because it's not published + //and we're not previewing. + } + } id = UnwrapIPublishedContent(content)._contentNode.NextSiblingContentId; } From cb428bb288a4b786e474d86fd55017f2a64fd7a9 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 18 Jun 2019 08:11:18 +0200 Subject: [PATCH 32/75] https://umbraco.visualstudio.com/D-Team%20Tracker/_workitems/edit/1447: Catch the SoapException which happends when a timeout occurs on the server. Also change such that this check is only executed for super admins. --- src/Umbraco.Web/Editors/UpdateCheckController.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/UpdateCheckController.cs b/src/Umbraco.Web/Editors/UpdateCheckController.cs index cd11382d13..5193b08edc 100644 --- a/src/Umbraco.Web/Editors/UpdateCheckController.cs +++ b/src/Umbraco.Web/Editors/UpdateCheckController.cs @@ -20,7 +20,7 @@ namespace Umbraco.Web.Editors { var updChkCookie = Request.Headers.GetCookies("UMB_UPDCHK").FirstOrDefault(); var updateCheckCookie = updChkCookie != null ? updChkCookie["UMB_UPDCHK"].Value : ""; - if (GlobalSettings.VersionCheckPeriod > 0 && string.IsNullOrEmpty(updateCheckCookie) && Security.CurrentUser.IsAdmin()) + if (GlobalSettings.VersionCheckPeriod > 0 && string.IsNullOrEmpty(updateCheckCookie) && Security.CurrentUser.IsSuper()) { try { @@ -37,6 +37,11 @@ namespace Umbraco.Web.Editors //this occurs if the server is down or cannot be reached return null; } + catch (System.Web.Services.Protocols.SoapException) + { + //this occurs if the server has a timeout + return null; + } } return null; } From 067af64dfff486690aca612c4f83c9e954dced4a Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 18 Jun 2019 09:46:29 +0200 Subject: [PATCH 33/75] Cleanup ChildrenForAllCultures --- .../NuCache/PublishedContent.cs | 34 ++++--------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 2e96513761..cb7acdc164 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -121,7 +121,6 @@ namespace Umbraco.Web.PublishedCache.NuCache default: throw new Exception("panic: invalid item type"); } - } #endregion @@ -276,6 +275,7 @@ namespace Umbraco.Web.PublishedCache.NuCache while (id > 0) { + // is IsPreviewing is false, then this can return null var content = getById(publishedSnapshot, IsPreviewing, id); if (content != null) @@ -284,35 +284,15 @@ namespace Umbraco.Web.PublishedCache.NuCache } else { - //Why were we throwing here? It is perfectly legitimate that a child is not published and when IsPreviewing == false - //this will return null even if the item is in nucache but only with an unpublished flag. - //If we want to be very clear about something being wrong, then perhaps the exception should be thrown from within - //the Func that gets the child where it can validate if there really is nothing there when something is expected? - - //In the meantime, we cannot continue so we should break?... BUT doesn't that mean that if there was a sibling next to this that was published - //that it will now be excluded? - - //Well, in that case this is annoying, so the only thing i can think of is to get the preview version of it to get the - //next child and continue that way? - + // but if IsPreviewing is true, we should have a child if (IsPreviewing) - { - //if we're in preview mode and nothing is returned then something is wrong throw new Exception($"panic: failed to get content with id={id}"); - } - else - { - //get the preview version since this item might not be published - content = getById(publishedSnapshot, true, id); - if (content == null) - { - //if we're in preview mode and nothing is returned then something is wrong - throw new Exception($"panic: failed to get content with id={id}"); - } - //now we can continue with the next sibling id, but we aren't going to return this content item because it's not published - //and we're not previewing. - } + // if IsPreviewing is false, get the unpublished child nevertheless + // we need it to keep enumerating children! but we don't return it + content = getById(publishedSnapshot, true, id); + if (content == null) + throw new Exception($"panic: failed to get content with id={id}"); } id = UnwrapIPublishedContent(content)._contentNode.NextSiblingContentId; From 62e6168e036c8bbd3a36cf328d097229c87039a6 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 18 Jun 2019 11:36:53 +0200 Subject: [PATCH 34/75] Revert ContentStore changes - have to find the bug now --- .../PublishedCache/NuCache/ContentStore.cs | 25 +++---------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 9eec95c10f..e59d332525 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -647,16 +647,9 @@ namespace Umbraco.Web.PublishedCache.NuCache var id = content.FirstChildContentId; while (id > 0) { - if (TryGetLinkedNode(id, out var link)) - { - ClearBranchLocked(link.Value); - id = link.Value.NextSiblingContentId; - } - else - { - // break i guess? - id = 0; - } + var link = GetLinkedNode(id, "child"); + ClearBranchLocked(link.Value); + id = link.Value.NextSiblingContentId; } } @@ -670,18 +663,6 @@ namespace Umbraco.Web.PublishedCache.NuCache throw new Exception($"panic: failed to get {description} with id={id}"); } - private bool TryGetLinkedNode(int id, out LinkedNode node) - { - if (_contentNodes.TryGetValue(id, out var link) && link.Value != null) - { - node = link; - return true; - } - - node = null; - return false; - } - private LinkedNode GetParentLink(ContentNode content) { _contentNodes.TryGetValue(content.ParentContentId, out var link); // else null From 9614740410e167799be50bba4c030fb7bddff29a Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 18 Jun 2019 11:44:56 +0200 Subject: [PATCH 35/75] Added timeout for 2 seconds. Dont make sense to wait longer for this --- src/Umbraco.Web/Editors/UpdateCheckController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/UpdateCheckController.cs b/src/Umbraco.Web/Editors/UpdateCheckController.cs index 5193b08edc..b197fe8926 100644 --- a/src/Umbraco.Web/Editors/UpdateCheckController.cs +++ b/src/Umbraco.Web/Editors/UpdateCheckController.cs @@ -24,7 +24,8 @@ namespace Umbraco.Web.Editors { try { - var check = new org.umbraco.update.CheckForUpgrade(); + var check = new org.umbraco.update.CheckForUpgrade { Timeout = 2000 }; + var result = check.CheckUpgrade(UmbracoVersion.Current.Major, UmbracoVersion.Current.Minor, UmbracoVersion.Current.Build, From 832dc7f4a5cf70790253f30b395bb67e811c2351 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 18 Jun 2019 18:44:22 +0200 Subject: [PATCH 36/75] Changed superadmin back to admin again --- src/Umbraco.Web/Editors/UpdateCheckController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/UpdateCheckController.cs b/src/Umbraco.Web/Editors/UpdateCheckController.cs index b197fe8926..132526576b 100644 --- a/src/Umbraco.Web/Editors/UpdateCheckController.cs +++ b/src/Umbraco.Web/Editors/UpdateCheckController.cs @@ -20,7 +20,7 @@ namespace Umbraco.Web.Editors { var updChkCookie = Request.Headers.GetCookies("UMB_UPDCHK").FirstOrDefault(); var updateCheckCookie = updChkCookie != null ? updChkCookie["UMB_UPDCHK"].Value : ""; - if (GlobalSettings.VersionCheckPeriod > 0 && string.IsNullOrEmpty(updateCheckCookie) && Security.CurrentUser.IsSuper()) + if (GlobalSettings.VersionCheckPeriod > 0 && string.IsNullOrEmpty(updateCheckCookie) && Security.CurrentUser.IsAdmin()) { try { From 5c2b0647f3feaf23311bc26876a183bbb348bad5 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 19 Jun 2019 16:42:04 +1000 Subject: [PATCH 37/75] Gets the feature in v7 based on #2682 that wasn't merged upwards to v8 and found some issues along the way like not filtering the mini list view properly along with not returning the content type alias in the entity controller --- .../components/tree/umbtree.directive.js | 30 ++++++---- .../src/common/services/tree.service.js | 60 ++++++++++++------- .../treepicker/treepicker.controller.js | 14 +++++ .../treepicker/treepicker.html | 2 +- .../Models/Mapping/EntityMapDefinition.cs | 4 +- 5 files changed, 73 insertions(+), 37 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js index 7055d1a746..cfc1cfbfd3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtree.directive.js @@ -38,7 +38,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use treeOptionsClick: [], treeNodeAltSelect: [] }; - + //this is the API exposed by this directive, for either hosting controllers or for other directives vm.callbacks = { treeNodeExpanded: function (f) { @@ -82,7 +82,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use // since it saves on data retreival and DOM processing. // TODO: This isn't used!? var lastSection = ""; - + /** Helper function to emit tree events */ function emitEvent(eventName, args) { if (registeredCallbacks[eventName] && angular.isArray(registeredCallbacks[eventName])) { @@ -119,7 +119,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use $scope.cachekey = args.cacheKey; } } - + return loadTree(); } @@ -148,7 +148,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use if (!args.path) { throw "args.path cannot be null"; } - + if (angular.isString(args.path)) { args.path = args.path.replace('"', '').split(','); } @@ -172,8 +172,16 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use emitEvent("treeSynced", { node: data, activate: args.activate }); return $q.when({ node: data, activate: args.activate }); + }, function (data) { + return $q.reject(data); + }, function (data) { + //on notification + if (data.type === "treeNodeExpanded") { + //raise the event + emitEvent("treeNodeExpanded", { tree: $scope.tree, node: data.node, children: data.children }); + } }); - + } /** This will check the section tree loaded and return all actual root nodes based on a tree type (non group nodes, non section groups) */ @@ -201,7 +209,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use //given a tree alias, this will search the current section tree for the specified tree alias and set the current active tree to it's root node function loadActiveTree(treeAlias) { - + if (!$scope.tree) { throw "Err in umbtree.directive.loadActiveTree, $scope.tree is null"; } @@ -233,7 +241,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use function loadTree() { if (!$scope.loading && $scope.section) { $scope.loading = true; - + //default args var args = { section: $scope.section, tree: $scope.treealias, cacheKey: $scope.cachekey, isDialog: $scope.isdialog ? $scope.isdialog : false }; @@ -279,7 +287,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use if (forceReload || (node.hasChildren && node.children.length === 0)) { //get the children from the tree service return treeService.loadNodeChildren({ node: node, section: $scope.section, isDialog: $scope.isdialog }) - .then(function(data) { + .then(function (data) { //emit expanded event emitEvent("treeNodeExpanded", { tree: $scope.tree, node: node, children: data }); @@ -302,7 +310,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use // TODO: This is called constantly because as a method in a template it's re-evaluated pretty much all the time // it would be better if we could cache the processing. The problem is that some of these things are dynamic. - + var css = []; if (node.cssClasses) { _.each(node.cssClasses, function (c) { @@ -322,7 +330,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use }; /* helper to force reloading children of a tree node */ - $scope.loadChildren = function(node, forceReload) { + $scope.loadChildren = function (node, forceReload) { return loadChildren(node, forceReload); }; @@ -359,7 +367,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use $scope.altSelect = function (n, ev) { emitEvent("treeNodeAltSelect", { element: $element, tree: $scope.tree, node: n, event: ev }); }; - + //call the onInit method, if the result is a promise then load the tree after that resolves (if it's not a promise this will just resolve automatically). //NOTE: The promise cannot be rejected, else the tree won't be loaded and we'll get exceptions if some API calls syncTree or similar. $q.when($scope.onInit(), function (args) { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js index d61d1c3ba1..3e60b09ad9 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js @@ -41,7 +41,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS return { /** Internal method to return the tree cache */ - _getTreeCache: function() { + _getTreeCache: function () { return treeCache; }, @@ -97,7 +97,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS } //create a method outside of the loop to return the parent - otherwise jshint blows up - var funcParent = function() { + var funcParent = function () { return parentNode; }; @@ -168,7 +168,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS * * @param {String} treeAlias The tree alias to check */ - getTreePackageFolder: function(treeAlias) { + getTreePackageFolder: function (treeAlias) { //we determine this based on the server variables if (Umbraco.Sys.ServerVariables.umbracoPlugins && Umbraco.Sys.ServerVariables.umbracoPlugins.trees && @@ -220,7 +220,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS var self = this; this.clearCache({ cacheKey: args.cacheKey, - filter: function(cc) { + filter: function (cc) { //get the new parent node from the tree cache var parent = self.getDescendantNode(cc.root, args.childrenOf); if (parent) { @@ -288,7 +288,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS * @param {object} args.node The tree node * @param {object} args.section The current section */ - loadNodeChildren: function(args) { + loadNodeChildren: function (args) { if (!args) { throw "No args object defined for loadNodeChildren"; } @@ -303,7 +303,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS args.node.loading = true; return this.getChildren(args) - .then(function(data) { + .then(function (data) { //set state to done and expand (only if there actually are children!) args.node.loading = false; @@ -320,10 +320,10 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS return $q.when(data); - }, function(reason) { + }, function (reason) { //in case of error, emit event - eventsService.emit("treeService.treeNodeLoadError", {error: reason } ); + eventsService.emit("treeService.treeNodeLoadError", { error: reason }); //stop show the loading indicator args.node.loading = false; @@ -346,7 +346,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS * Removes a given node from the tree * @param {object} treeNode the node to remove */ - removeNode: function(treeNode) { + removeNode: function (treeNode) { if (!angular.isFunction(treeNode.parent)) { return; } @@ -359,7 +359,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS parent.children.splice(parent.children.indexOf(treeNode), 1); parent.hasChildren = parent.children.length !== 0; - + //Notify that the node has been removed eventsService.emit("treeService.removeNode", { node: treeNode }); }, @@ -374,7 +374,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS * Removes all child nodes from a given tree node * @param {object} treeNode the node to remove children from */ - removeChildNodes : function(treeNode) { + removeChildNodes: function (treeNode) { treeNode.expanded = false; treeNode.children = []; treeNode.hasChildren = false; @@ -413,7 +413,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS * @param {int} id id of descendant node * @param {string} treeAlias - optional tree alias, if fetching descendant node from a child of a listview document */ - getDescendantNode: function(treeNode, id, treeAlias) { + getDescendantNode: function (treeNode, id, treeAlias) { //validate if it is a section container since we'll need a treeAlias if it is one if (treeNode.isContainer === true && !treeAlias) { @@ -432,7 +432,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS var root = getTreeRoot(tn.children[c]); //only return if we found the root in this child, otherwise continue. - if(root){ + if (root) { return root; } } @@ -531,7 +531,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS * Gets the node's tree alias, this is done by looking up the meta-data of the current node's root node * @param {object} treeNode to retrive tree alias from */ - getTreeAlias : function(treeNode) { + getTreeAlias: function (treeNode) { var root = this.getTreeRoot(treeNode); if (root) { return root.metaData["treeAlias"]; @@ -570,7 +570,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS var self = this; return treeResource.loadApplication(args) - .then(function(data) { + .then(function (data) { //this will be called once the tree app data has loaded var result = { name: data.name, @@ -624,7 +624,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS } return treeResource.loadMenu(args.treeNode) - .then(function(data) { + .then(function (data) { //need to convert the icons to new ones for (var i = 0; i < data.length; i++) { data[i].cssclass = iconHelper.convertFromLegacyIcon(data[i].cssclass); @@ -677,7 +677,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS * Re-loads the single node from the server * @param {object} node Tree node to reload */ - reloadNode: function(node) { + reloadNode: function (node) { if (!node) { throw "node cannot be null"; } @@ -691,10 +691,10 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS //set the node to loading node.loading = true; - return this.getChildren({ node: node.parent(), section: node.section }).then(function(data) { + return this.getChildren({ node: node.parent(), section: node.section }).then(function (data) { //ok, now that we have the children, find the node we're reloading - var found = _.find(data, function(item) { + var found = _.find(data, function (item) { return item.id === node.id; }); if (found) { @@ -720,7 +720,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS else { return $q.reject(); } - }, function() { + }, function () { return $q.reject(); }); }, @@ -735,7 +735,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS * This will return the current node's path by walking up the tree * @param {object} node Tree node to retrieve path for */ - getPath: function(node) { + getPath: function (node) { if (!node) { throw "node cannot be null"; } @@ -760,7 +760,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS return reversePath.reverse(); }, - syncTree: function(args) { + syncTree: function (args) { if (!args) { throw "No args object defined for syncTree"; @@ -800,6 +800,8 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS } } + var deferred = $q.defer(); + //now that we have the first id to lookup, we can start the process var self = this; @@ -831,6 +833,10 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS else { //couldn't find it in the return self.loadNodeChildren({ node: node, section: node.section }).then(function (children) { + + //send back some progress to allow the caller to deal with expanded nodes + deferred.notify({ type: "treeNodeExpanded", node: node, children: children }) + //ok, got the children, let's find it var found = self.getChildNode(node, args.path[currPathIndex]); if (found) { @@ -858,8 +864,16 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS }; //start - return doSync(); + var wrappedPromise = doSync(); + //then wrap it + wrappedPromise.then(function (args) { + deferred.resolve(args); + }, function (args) { + deferred.reject(args); + }); + + return deferred.promise; } }; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js index 915abf62b0..7ba6e71f0c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js @@ -59,6 +59,8 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController", vm.submit = submit; vm.close = close; + var currentNode = $scope.model.currentNode; + function initDialogTree() { vm.dialogTreeApi.callbacks.treeLoaded(treeLoadedHandler); // TODO: Also deal with unexpanding!! @@ -160,6 +162,12 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController", } } } + + vm.filter = { + filterAdvanced: $scope.model.filterAdvanced, + filterExclude: $scope.model.filterExclude, + filter: $scope.model.filter + }; } /** @@ -256,6 +264,12 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController", vm.hasItems = args.tree.root.children.length > 0; tree = args.tree; + + var nodeHasPath = currentNode && currentNode.path; + var startNodeNotDefined = !vm.startNodeId; + if (startNodeNotDefined && nodeHasPath) { + vm.dialogTreeApi.syncTree({ path: currentNode.path, activate: false }); + } } //wires up selection diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html index c592b4ec3b..57ee805ba0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html @@ -74,7 +74,7 @@ start-node-id="vm.startNodeId" on-select="vm.selectListViewNode(node)" on-close="vm.closeMiniListView()" - entity-type-filter="filter"> + entity-type-filter="vm.filter"> diff --git a/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs index 2598523bd5..d5bc6adee9 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs @@ -45,10 +45,10 @@ namespace Umbraco.Web.Models.Mapping if (source.NodeObjectType == Constants.ObjectTypes.Member && target.Icon.IsNullOrWhiteSpace()) target.Icon = "icon-user"; - if (source.NodeObjectType == Constants.ObjectTypes.Media && source is IContentEntitySlim contentSlim) + if (source is IContentEntitySlim contentSlim) source.AdditionalData["ContentTypeAlias"] = contentSlim.ContentTypeAlias; - if (source.NodeObjectType == Constants.ObjectTypes.Media && source is IMediaEntitySlim mediaSlim) + if (source is IMediaEntitySlim mediaSlim) source.AdditionalData["MediaPath"] = mediaSlim.MediaPath; // NOTE: we're mapping the objects in AdditionalData by object reference here. From 5d4f2ec6973e7b55d714138abf7217d18d4008a8 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 21 Jun 2019 15:45:12 +1000 Subject: [PATCH 38/75] Fixes issue with ImageCropperTemplateExtensions since it wasn't re-mapped to the new method definition --- src/Umbraco.Web/ImageCropperTemplateExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/ImageCropperTemplateExtensions.cs b/src/Umbraco.Web/ImageCropperTemplateExtensions.cs index d7f457287a..656f1e05a2 100644 --- a/src/Umbraco.Web/ImageCropperTemplateExtensions.cs +++ b/src/Umbraco.Web/ImageCropperTemplateExtensions.cs @@ -130,7 +130,7 @@ namespace Umbraco.Web if (mediaItem.HasProperty(propertyAlias) == false || mediaItem.HasValue(propertyAlias) == false) return string.Empty; - var mediaItemUrl = mediaItem.MediaUrl(propertyAlias); + var mediaItemUrl = mediaItem.MediaUrl(propertyAlias: propertyAlias); //get the default obj from the value converter var cropperValue = mediaItem.Value(propertyAlias); From cd2ce2486afc264974ae3b974cc633d4dcfd92a2 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 21 Jun 2019 15:46:16 +1000 Subject: [PATCH 39/75] Fixes debugger display attribute --- src/Umbraco.Web/Models/PublishedContentBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 008bf10504..148bab11c0 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.Models /// /// This base class does which (a) consistently resolves and caches the Url, (b) provides an implementation /// for this[alias], and (c) provides basic content set management. - [DebuggerDisplay("{Content Id: {Id}}")] + [DebuggerDisplay("Content Id: {Id}")] public abstract class PublishedContentBase : IPublishedContent { #region ContentType From da1e2680cb640b98c15a1cc5a4aa60fa2ed2abd4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 21 Jun 2019 15:47:47 +1000 Subject: [PATCH 40/75] Fixes ContentStore when it's building the cache from sources, adds TODO notes, questions, concerns, etc.... --- .../PublishedCache/NuCache/ContentStore.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index e59d332525..38dd0952b8 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -305,6 +305,8 @@ namespace Umbraco.Web.PublishedCache.NuCache public void UpdateContentTypes(IEnumerable removedIds, IEnumerable refreshedTypes, IEnumerable kits) { + var isRebuilding = removedIds == null && kits == null; + var removedIdsA = removedIds?.ToArray() ?? Array.Empty(); var refreshedTypesA = refreshedTypes?.ToArray() ?? Array.Empty(); var refreshedIdsA = refreshedTypesA.Select(x => x.Id).ToArray(); @@ -315,6 +317,34 @@ namespace Umbraco.Web.PublishedCache.NuCache { Lock(lockInfo); + + if (isRebuilding) + { + //All calls to this method when these are null means that we are regenerating the cache and first only setting content types + //TODO: A different method should be used for this operation - there are 4 places where this is called with these two args as null + + //In this case we know we are regenerating everything, so clear it all + //TODO: From my understanding, we can't 'just' clear things like _contentNodes or _xmap, etc... since the cache can be in use on a diff generation? something like that. + + //TODO: Follow-up: Actually, in all cases where this is called with the 2 null params, a call is made to _contentStore.SetAll which performs this reset anyways so + // we don't really need to do much in this case at all. + + //ClearLocked(_contentNodes); + //ClearRootLocked(); + + //TODO: I'm unsure how this is handled behind the scenes - or maybe it simply doesn't need to be handled since it will never have a variying UDI -> ID map + // since the this mapping in the DB would never change? + //_xmap.Clear(); + + // At this point, all we need to do is perform update of refreshed content types + foreach (var type in refreshedTypesA) + { + SetValueLocked(_contentTypesById, type.Id, type); + SetValueLocked(_contentTypesByAlias, type.Alias, type); + } + return; + } + var removedContentTypeNodes = new List(); var refreshedContentTypeNodes = new List(); @@ -552,6 +582,9 @@ namespace Umbraco.Web.PublishedCache.NuCache foreach (var kit in kits.Where(x => ParentExistsLocked(x) && BuildKit(x))) { SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); + + //TODO: When this is called from LoadContentFromLocalDbLocked we are populating the cache from the localDB, + // so we shouldn't then need to spend the effort to tell it to update itself based on itself? if (_localDb != null) RegisterChange(kit.Node.Id, kit); AddNodeLocked(kit.Node); From 2870ce5e0a75ca3b0bb7479bfcc945cb7271fa47 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 21 Jun 2019 15:48:10 +1000 Subject: [PATCH 41/75] a perf change that in ContentCacheRefresher it doesn't iterate payloads if there is no real Id --- src/Umbraco.Web/Cache/ContentCacheRefresher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Cache/ContentCacheRefresher.cs b/src/Umbraco.Web/Cache/ContentCacheRefresher.cs index e4d2c2e4da..21b97d980d 100644 --- a/src/Umbraco.Web/Cache/ContentCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ContentCacheRefresher.cs @@ -49,7 +49,7 @@ namespace Umbraco.Web.Cache var idsRemoved = new HashSet(); var isolatedCache = AppCaches.IsolatedCaches.GetOrCreate(); - foreach (var payload in payloads) + foreach (var payload in payloads.Where(x => x.Id != default)) { isolatedCache.Clear(RepositoryCacheKeys.GetKey(payload.Id)); From d129989c3df5db98c3b28ec46a4872568f06f215 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 21 Jun 2019 16:07:07 +1000 Subject: [PATCH 42/75] Removes irrelevant GetAbsoluteMediaUrl method now that there is both a media url provider and urls have been refactored in this PR, this was overlooked with the media url provider PR --- src/Umbraco.Web/UrlHelperRenderExtensions.cs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/Umbraco.Web/UrlHelperRenderExtensions.cs b/src/Umbraco.Web/UrlHelperRenderExtensions.cs index 8fe0524209..5307aa5f86 100644 --- a/src/Umbraco.Web/UrlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/UrlHelperRenderExtensions.cs @@ -371,25 +371,6 @@ namespace Umbraco.Web return url.SurfaceAction(action, typeof (T), additionalRouteVals); } - /// - /// Generates a Absolute Media Item URL based on the current context - /// - /// - /// - /// - public static string GetAbsoluteMediaUrl(this UrlHelper urlHelper, IPublishedContent mediaItem) - { - if (urlHelper == null) throw new ArgumentNullException("urlHelper"); - if (mediaItem == null) throw new ArgumentNullException("mediaItem"); - - if (urlHelper.RequestContext.HttpContext.Request.Url != null) - { - var requestUrl = urlHelper.RequestContext.HttpContext.Request.Url.GetLeftPart(UriPartial.Authority); - return string.Format("{0}{1}", requestUrl, mediaItem.Url()); - } - return null; - } - /// /// This is used in methods like BeginUmbracoForm and SurfaceAction to generate an encrypted string which gets submitted in a request for which /// Umbraco can decrypt during the routing process in order to delegate the request to a specific MVC Controller. From f7aaad86c5ea29096bbce47689a0188416a99171 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Fri, 21 Jun 2019 15:56:36 +0200 Subject: [PATCH 43/75] Set focus on "Mandatory" after picking a property type when creating a new property --- .../infiniteeditors/propertysettings/propertysettings.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html index 93d7936326..fab6ba4069 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html @@ -91,6 +91,7 @@ From 71d076aba24b5deb0dae5358b89ba81a61b39932 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 25 Jun 2019 11:34:26 +0200 Subject: [PATCH 44/75] Dashboards not obeying c# access rules #5708 The invisible negator strikes again. --- src/Umbraco.Web/Services/DashboardService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Services/DashboardService.cs b/src/Umbraco.Web/Services/DashboardService.cs index 71969df475..0e33171f48 100644 --- a/src/Umbraco.Web/Services/DashboardService.cs +++ b/src/Umbraco.Web/Services/DashboardService.cs @@ -104,7 +104,7 @@ namespace Umbraco.Web.Services } } - if (!hasAccess || denyRules.Length == 0) + if (hasAccess || denyRules.Length == 0) return true; // check if this item has any deny arguments, if so check if the user is in one of the denied user groups, if so they will From 1da2c00f0a09ca2122b122d7224a6841bd7303b2 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 25 Jun 2019 11:41:33 +0200 Subject: [PATCH 45/75] Fix logic & make code easier to follow --- src/Umbraco.Web/Services/DashboardService.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Services/DashboardService.cs b/src/Umbraco.Web/Services/DashboardService.cs index 0e33171f48..794c6fa671 100644 --- a/src/Umbraco.Web/Services/DashboardService.cs +++ b/src/Umbraco.Web/Services/DashboardService.cs @@ -104,8 +104,9 @@ namespace Umbraco.Web.Services } } - if (hasAccess || denyRules.Length == 0) - return true; + // No need to check denyRules if there aren't any, just return current state + if (denyRules.Length == 0) + return hasAccess; // check if this item has any deny arguments, if so check if the user is in one of the denied user groups, if so they will // be denied to see it no matter what From a0966be565bc42916778977ed8c95abd5d1a5c58 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 25 Jun 2019 12:36:03 +0200 Subject: [PATCH 46/75] Fixes Macro Rendered in Richtext Editor when not configured to do so. #5700 --- src/Umbraco.Web/Editors/MacroRenderingController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/MacroRenderingController.cs b/src/Umbraco.Web/Editors/MacroRenderingController.cs index a2bbfe1dfd..eab2d997b5 100644 --- a/src/Umbraco.Web/Editors/MacroRenderingController.cs +++ b/src/Umbraco.Web/Editors/MacroRenderingController.cs @@ -108,7 +108,7 @@ namespace Umbraco.Web.Editors //if it isn't supposed to be rendered in the editor then return an empty string //currently we cannot render a macro if the page doesn't yet exist - if (pageId == -1 || publishedContent == null || !m.UseInEditor) + if (pageId == -1 || publishedContent == null || m.DontRender) { var response = Request.CreateResponse(); //need to create a specific content result formatted as HTML since this controller has been configured From e8d6daf59acdc30c86f5b4aa2bd6b1ee49ca5d9c Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 25 Jun 2019 12:54:16 +0200 Subject: [PATCH 47/75] NuCache - be more tolerant when cache is weird --- .../PublishedCache/NuCache/ContentStore.cs | 114 +++++++++++------- .../NuCache/PublishedSnapshotService.cs | 24 +++- 2 files changed, 87 insertions(+), 51 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 38dd0952b8..09a23ddf81 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -303,10 +303,35 @@ namespace Umbraco.Web.PublishedCache.NuCache } } + public void SetAllContentTypes(IEnumerable types) + { + var lockInfo = new WriteLockInfo(); + try + { + Lock(lockInfo); + + // clear all existing content types + ClearLocked(_contentTypesById); + ClearLocked(_contentTypesByAlias); + + // set all new content types + foreach (var type in types) + { + SetValueLocked(_contentTypesById, type.Id, type); + SetValueLocked(_contentTypesByAlias, type.Alias, type); + } + + // beware! at that point the cache is inconsistent, + // assuming we are going to SetAll content items! + } + finally + { + Release(lockInfo); + } + } + public void UpdateContentTypes(IEnumerable removedIds, IEnumerable refreshedTypes, IEnumerable kits) { - var isRebuilding = removedIds == null && kits == null; - var removedIdsA = removedIds?.ToArray() ?? Array.Empty(); var refreshedTypesA = refreshedTypes?.ToArray() ?? Array.Empty(); var refreshedIdsA = refreshedTypesA.Select(x => x.Id).ToArray(); @@ -317,34 +342,6 @@ namespace Umbraco.Web.PublishedCache.NuCache { Lock(lockInfo); - - if (isRebuilding) - { - //All calls to this method when these are null means that we are regenerating the cache and first only setting content types - //TODO: A different method should be used for this operation - there are 4 places where this is called with these two args as null - - //In this case we know we are regenerating everything, so clear it all - //TODO: From my understanding, we can't 'just' clear things like _contentNodes or _xmap, etc... since the cache can be in use on a diff generation? something like that. - - //TODO: Follow-up: Actually, in all cases where this is called with the 2 null params, a call is made to _contentStore.SetAll which performs this reset anyways so - // we don't really need to do much in this case at all. - - //ClearLocked(_contentNodes); - //ClearRootLocked(); - - //TODO: I'm unsure how this is handled behind the scenes - or maybe it simply doesn't need to be handled since it will never have a variying UDI -> ID map - // since the this mapping in the DB would never change? - //_xmap.Clear(); - - // At this point, all we need to do is perform update of refreshed content types - foreach (var type in refreshedTypesA) - { - SetValueLocked(_contentTypesById, type.Id, type); - SetValueLocked(_contentTypesByAlias, type.Alias, type); - } - return; - } - var removedContentTypeNodes = new List(); var refreshedContentTypeNodes = new List(); @@ -383,11 +380,10 @@ namespace Umbraco.Web.PublishedCache.NuCache // perform update of content with refreshed content type - from the kits // skip missing type, skip missing parents & un-buildable kits - what else could we do? - // kits are ordered by level, so ParentExits is ok here + // kits are ordered by level, so ParentExists is ok here var visited = new List(); foreach (var kit in kits.Where(x => refreshedIdsA.Contains(x.ContentTypeId) && - ParentExistsLocked(x) && BuildKit(x))) { // replacing the node: must preserve the parents @@ -464,13 +460,26 @@ namespace Umbraco.Web.PublishedCache.NuCache private bool BuildKit(ContentNodeKit kit) { + // make sure parent exists + if (!ParentExistsLocked(kit)) + { + _logger.Warn($"Skip item id={kit.Node.Id}, could not find parent id={kit.Node.ParentContentId}."); + return false; + } + // make sure the kit is valid if (kit.DraftData == null && kit.PublishedData == null) + { + _logger.Warn($"Skip item id={kit.Node.Id}, both draft and published data are null."); return false; + } // unknown = bad if (_contentTypesById.TryGetValue(kit.ContentTypeId, out var link) == false || link.Value == null) + { + _logger.Warn($"Skip item id={kit.Node.Id}, could not find content type id={kit.ContentTypeId}."); return false; + } // check whether parent is published var canBePublished = ParentPublishedLocked(kit); @@ -494,7 +503,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return link; } - public void Set(ContentNodeKit kit) + public bool Set(ContentNodeKit kit) { // ReSharper disable LocalizableElement if (kit.IsEmpty) @@ -514,9 +523,8 @@ namespace Umbraco.Web.PublishedCache.NuCache _contentNodes.TryGetValue(kit.Node.Id, out var link); var existing = link?.Value; - // else ignore, what else could we do? - if (ParentExistsLocked(kit) == false || BuildKit(kit) == false) - return; + if (!BuildKit(kit)) + return false; // moving? var moving = existing != null && existing.ParentContentId != kit.Node.ParentContentId; @@ -553,6 +561,8 @@ namespace Umbraco.Web.PublishedCache.NuCache { Release(lockInfo); } + + return true; } private void ClearRootLocked() @@ -564,9 +574,10 @@ namespace Umbraco.Web.PublishedCache.NuCache } // IMPORTANT kits must be sorted out by LEVEL - public void SetAll(IEnumerable kits) + public bool SetAll(IEnumerable kits, bool fromLocalDb = false) { var lockInfo = new WriteLockInfo(); + var ok = true; try { Lock(lockInfo); @@ -578,14 +589,18 @@ namespace Umbraco.Web.PublishedCache.NuCache //ClearLocked(_contentTypesById); //ClearLocked(_contentTypesByAlias); - // skip missing parents & un-buildable kits - what else could we do? - foreach (var kit in kits.Where(x => ParentExistsLocked(x) && BuildKit(x))) + foreach (var kit in kits) { + if (!BuildKit(kit)) + { + ok = false; + continue; // skip that one + } + _logger.Debug($"set {kit.Node.Id} with parent {kit.Node.ParentContentId}"); SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); - //TODO: When this is called from LoadContentFromLocalDbLocked we are populating the cache from the localDB, - // so we shouldn't then need to spend the effort to tell it to update itself based on itself? - if (_localDb != null) RegisterChange(kit.Node.Id, kit); + // don't refresh _localDb if we are reading from _localDb + if (!fromLocalDb && _localDb != null) RegisterChange(kit.Node.Id, kit); AddNodeLocked(kit.Node); _xmap[kit.Node.Uid] = kit.Node.Id; @@ -595,12 +610,15 @@ namespace Umbraco.Web.PublishedCache.NuCache { Release(lockInfo); } + + return ok; } // IMPORTANT kits must be sorted out by LEVEL and by SORT ORDER - public void SetBranch(int rootContentId, IEnumerable kits) + public bool SetBranch(int rootContentId, IEnumerable kits) { var lockInfo = new WriteLockInfo(); + var ok = true; try { Lock(lockInfo); @@ -617,9 +635,13 @@ namespace Umbraco.Web.PublishedCache.NuCache } // now add them all back - // skip missing parents & un-buildable kits - what else could we do? - foreach (var kit in kits.Where(x => ParentExistsLocked(x) && BuildKit(x))) + foreach (var kit in kits) { + if (!BuildKit(kit)) + { + ok = false; + continue; // skip that one + } SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); if (_localDb != null) RegisterChange(kit.Node.Id, kit); AddNodeLocked(kit.Node); @@ -631,6 +653,8 @@ namespace Umbraco.Web.PublishedCache.NuCache { Release(lockInfo); } + + return ok; } public bool Clear(int id) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 7a7682a797..8712c60a9b 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -331,7 +331,10 @@ namespace Umbraco.Web.PublishedCache.NuCache var contentTypes = _serviceContext.ContentTypeService.GetAll() .Select(x => _publishedContentTypeFactory.CreateContentType(x)); - _contentStore.UpdateContentTypes(null, contentTypes, null); + _contentStore.SetAllContentTypes(contentTypes); + + // beware! at that point the cache is inconsistent, + // assuming we are going to SetAll content items! _localContentDb?.Clear(); @@ -348,13 +351,16 @@ namespace Umbraco.Web.PublishedCache.NuCache { var contentTypes = _serviceContext.ContentTypeService.GetAll() .Select(x => _publishedContentTypeFactory.CreateContentType(x)); - _contentStore.UpdateContentTypes(null, contentTypes, null); + _contentStore.SetAllContentTypes(contentTypes); + + // beware! at that point the cache is inconsistent, + // assuming we are going to SetAll content items! _logger.Debug("Loading content from local db..."); var sw = Stopwatch.StartNew(); var kits = _localContentDb.Select(x => x.Value) .OrderBy(x => x.Node.Level); // IMPORTANT sort by level - _contentStore.SetAll(kits); + _contentStore.SetAll(kits, true); sw.Stop(); _logger.Debug("Loaded content from local db ({Duration}ms)", sw.ElapsedMilliseconds); } @@ -399,7 +405,10 @@ namespace Umbraco.Web.PublishedCache.NuCache var mediaTypes = _serviceContext.MediaTypeService.GetAll() .Select(x => _publishedContentTypeFactory.CreateContentType(x)); - _mediaStore.UpdateContentTypes(null, mediaTypes, null); + _mediaStore.SetAllContentTypes(mediaTypes); + + // beware! at that point the cache is inconsistent, + // assuming we are going to SetAll content items! _localMediaDb?.Clear(); @@ -416,13 +425,16 @@ namespace Umbraco.Web.PublishedCache.NuCache { var mediaTypes = _serviceContext.MediaTypeService.GetAll() .Select(x => _publishedContentTypeFactory.CreateContentType(x)); - _mediaStore.UpdateContentTypes(null, mediaTypes, null); + _mediaStore.SetAllContentTypes(mediaTypes); + + // beware! at that point the cache is inconsistent, + // assuming we are going to SetAll content items! _logger.Debug("Loading media from local db..."); var sw = Stopwatch.StartNew(); var kits = _localMediaDb.Select(x => x.Value) .OrderBy(x => x.Node.Level); // IMPORTANT sort by level - _mediaStore.SetAll(kits); + _mediaStore.SetAll(kits, true); sw.Stop(); _logger.Debug("Loaded media from local db ({Duration}ms)", sw.ElapsedMilliseconds); } From b306e41560b4de1b2a5d360e538cc404d10b20a7 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 25 Jun 2019 13:26:50 +0200 Subject: [PATCH 48/75] NuCache - failover to database when local db fails to load --- .../PublishedCache/NuCache/ContentStore.cs | 2 +- .../NuCache/PublishedSnapshotService.cs | 56 ++++++++++++++++--- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 09a23ddf81..f13fb21d33 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -596,7 +596,7 @@ namespace Umbraco.Web.PublishedCache.NuCache ok = false; continue; // skip that one } - _logger.Debug($"set {kit.Node.Id} with parent {kit.Node.ParentContentId}"); + _logger.Debug($"Set {kit.Node.Id} with parent {kit.Node.ParentContentId}"); SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); // don't refresh _localDb if we are reading from _localDb diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 8712c60a9b..642e969819 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -170,16 +170,24 @@ namespace Umbraco.Web.PublishedCache.NuCache try { + var okContent = false; + var okMedia = false; + if (_localDbExists) { - LockAndLoadContent(LoadContentFromLocalDbLocked); - LockAndLoadMedia(LoadMediaFromLocalDbLocked); + okContent = LockAndLoadContent(LoadContentFromLocalDbLocked); + if (!okContent) + _logger.Warn("Loading content from local db raised warnings, will reload from database."); + okMedia = LockAndLoadMedia(LoadMediaFromLocalDbLocked); + if (!okMedia) + _logger.Warn("Loading media from local db raised warnings, will reload from database."); } - else - { + + if (!okContent) LockAndLoadContent(LoadContentFromDatabaseLocked); + + if (!okMedia) LockAndLoadMedia(LoadMediaFromDatabaseLocked); - } LockAndLoadDomains(); } @@ -323,6 +331,21 @@ namespace Umbraco.Web.PublishedCache.NuCache } } + private bool LockAndLoadContent(Func action) + { + // first get a writer, then a scope + // if there already is a scope, the writer will attach to it + // otherwise, it will only exist here - cheap + using (_contentStore.GetScopedWriteLock(_scopeProvider)) + using (var scope = _scopeProvider.CreateScope()) + { + scope.ReadLock(Constants.Locks.ContentTree); + var ok = action(scope); + scope.Complete(); + return ok; + } + } + private void LoadContentFromDatabaseLocked(IScope scope) { // locks: @@ -347,7 +370,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _logger.Debug("Loaded content from database ({Duration}ms)", sw.ElapsedMilliseconds); } - private void LoadContentFromLocalDbLocked(IScope scope) + private bool LoadContentFromLocalDbLocked(IScope scope) { var contentTypes = _serviceContext.ContentTypeService.GetAll() .Select(x => _publishedContentTypeFactory.CreateContentType(x)); @@ -360,9 +383,10 @@ namespace Umbraco.Web.PublishedCache.NuCache var sw = Stopwatch.StartNew(); var kits = _localContentDb.Select(x => x.Value) .OrderBy(x => x.Node.Level); // IMPORTANT sort by level - _contentStore.SetAll(kits, true); + var ok = _contentStore.SetAll(kits, true); sw.Stop(); _logger.Debug("Loaded content from local db ({Duration}ms)", sw.ElapsedMilliseconds); + return ok; } // keep these around - might be useful @@ -399,6 +423,19 @@ namespace Umbraco.Web.PublishedCache.NuCache } } + private bool LockAndLoadMedia(Func action) + { + // see note in LockAndLoadContent + using (_mediaStore.GetScopedWriteLock(_scopeProvider)) + using (var scope = _scopeProvider.CreateScope()) + { + scope.ReadLock(Constants.Locks.MediaTree); + var ok = action(scope); + scope.Complete(); + return ok; + } + } + private void LoadMediaFromDatabaseLocked(IScope scope) { // locks & notes: see content @@ -421,7 +458,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _logger.Debug("Loaded media from database ({Duration}ms)", sw.ElapsedMilliseconds); } - private void LoadMediaFromLocalDbLocked(IScope scope) + private bool LoadMediaFromLocalDbLocked(IScope scope) { var mediaTypes = _serviceContext.MediaTypeService.GetAll() .Select(x => _publishedContentTypeFactory.CreateContentType(x)); @@ -434,9 +471,10 @@ namespace Umbraco.Web.PublishedCache.NuCache var sw = Stopwatch.StartNew(); var kits = _localMediaDb.Select(x => x.Value) .OrderBy(x => x.Node.Level); // IMPORTANT sort by level - _mediaStore.SetAll(kits, true); + var ok = _mediaStore.SetAll(kits, true); sw.Stop(); _logger.Debug("Loaded media from local db ({Duration}ms)", sw.ElapsedMilliseconds); + return ok; } // keep these around - might be useful From a0c51fb21287efb36e2ba78929e72a0b3742c2d7 Mon Sep 17 00:00:00 2001 From: Jannik Anker Date: Wed, 5 Jun 2019 15:03:03 +0200 Subject: [PATCH 49/75] Update da.xml Misc Danish language "fixes", including some in the new language variants stuff --- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index aac2f6ddd5..c200f1f35e 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -1279,17 +1279,17 @@ Mange hilsner fra Umbraco robotten Kompositioner Gruppe - Du har ikke tilføjet nogle grupper + Du har ikke tilføjet nogen grupper Tilføj gruppe Nedarvet fra Tilføj egenskab Påkrævet label - Aktiver listevisning - Konfigurer indholdet til at blive vist i en sorterbar og søgbar liste, dens børn vil ikke blive vist i træet + Aktivér listevisning + Konfigurér indholdet til at blive vist i en sortérbar og søgbar liste; undersider vil ikke blive vist i træet Tilladte skabeloner Vælg hvilke skabeloner der er tilladt at bruge på dette indhold Tillad på rodniveau - Kun dokumenttyper med denne indstilling aktiveret oprettes i rodniveau under inhold og mediearkiv + Kun dokumenttyper med denne indstilling aktiveret kan oprettes i rodniveau under indhold og mediearkiv Tilladte typer Tillad at oprette indhold af en specifik type under denne Vælg child node @@ -1326,20 +1326,20 @@ Mange hilsner fra Umbraco robotten Tilføj sprog Påkrævet sprog Egenskaber på dette sprog skal være udfyldt før noden kan blive udgivet. - Standard sprog - Et Umbraco site kan kun have et standard sprog. - Ved at skifte standardsprog kan resultere i standard indhold mangler. - Fallsback til - Ingen fallback sprog - For at tillade flersproget indhold til at falde tilbage på et andet sprog, hvis det ikke er tilgængelig i det anmodet sprog, vælg det her. - Fallback sprog + Standardsprog + Et Umbraco-site kan kun have ét standardsprog. + At skifte standardsprog kan resultere i at standardindhold mangler. + Fallback til + Intet fallback-sprog + For at tillade flersproget indhold, som ikke er tilgængeligt i det anmodede sprog, skal du her vælge et sprog at falde tilbage på. + Fallback-sprog Tilføj parameter - Rediger parameter + Redigér parameter Indtast makronavn Parametre - Definer de parametre, der skal være tilgængelige, når du bruger denne makro. + Definér de parametre der skal være tilgængelige, når du bruger denne makro. Vælg partial view makrofil From 40311f85902c98b6e8cbda927da586bc60d26f19 Mon Sep 17 00:00:00 2001 From: Rachel Breeze Date: Tue, 25 Jun 2019 13:50:40 +0100 Subject: [PATCH 50/75] V8: Accessibility improvements for textbox validation (#5664) --- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../components/application/umb-app-a11y.less | 10 ++++++++++ .../editor/umb-editor-content-header.html | 2 ++ .../propertyeditors/textbox/textbox.html | 19 ++++++++++++------- 4 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-a11y.less diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 05705ae8ce..db1bd30305 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -84,6 +84,7 @@ @import "accessibility/visually-hidden.less"; // Umbraco Components +@import "components/application/umb-app-a11y.less"; @import "components/application/umb-app-header.less"; @import "components/application/umb-app-content.less"; @import "components/application/umb-tour.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-a11y.less b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-a11y.less new file mode 100644 index 0000000000..7e127c5db1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-a11y.less @@ -0,0 +1,10 @@ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0,0,0,0); + border: 0; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html index 87e94193e1..37f363f50d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html @@ -26,6 +26,8 @@ umb-auto-focus val-server-field="{{serverValidationNameField}}" required + aria-required="true" + aria-invalid="{{contentForm.headerNameForm.headerName.$invalid ? true : false}}" autocomplete="off" maxlength="255" /> diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html index 92f02b9f5b..ff62629b1c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html @@ -4,19 +4,24 @@ class="umb-property-editor umb-textstring textstring" val-server="value" ng-required="model.validation.mandatory" + aria-required="model.validation.mandatory" + aria-invalid="False" ng-trim="false" ng-keyup="model.change()" /> - - - {{textboxFieldForm.textbox.errorMsg}} - Required - + +
+

{{model.label}} {{textboxFieldForm.textbox.errorMsg}}

+ +

Required

+
- %0% characters left. +

{{model.label}} %0% characters left.

+

%0% characters left.

- Maximum %0% characters, %1% too many. +

{{model.label}} Maximum %0% characters, %1% too many.

+
From a5fb2d20253df2a39e63e3fb6ae94d6bc67baaee Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 25 Jun 2019 14:53:58 +0200 Subject: [PATCH 51/75] Replace `visually-hidden` in favor of `sr-only` as discussed in #5664 --- .../src/less/accessibility/visually-hidden.less | 11 ----------- src/Umbraco.Web.UI.Client/src/less/belle.less | 3 --- .../views/components/application/umb-app-header.html | 4 ++-- 3 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/less/accessibility/visually-hidden.less diff --git a/src/Umbraco.Web.UI.Client/src/less/accessibility/visually-hidden.less b/src/Umbraco.Web.UI.Client/src/less/accessibility/visually-hidden.less deleted file mode 100644 index 592f9dcb21..0000000000 --- a/src/Umbraco.Web.UI.Client/src/less/accessibility/visually-hidden.less +++ /dev/null @@ -1,11 +0,0 @@ - -// Visually Hidden - used to remove an element from the view, whilst retaining accessibily for screen readers. More info available at https://a11yproject.com/posts/how-to-hide-content/ -// -------------------------------------------------- - -.visually-hidden { - position: absolute !important; - height: 1px; - width: 1px; - overflow: hidden; - clip: rect(1px, 1px, 1px, 1px); -} diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index db1bd30305..0b5e10379f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -80,9 +80,6 @@ @import "forms/umb-validation-label.less"; -// Umbraco Accessibility -@import "accessibility/visually-hidden.less"; - // Umbraco Components @import "components/application/umb-app-a11y.less"; @import "components/application/umb-app-header.less"; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html index a601940193..93c3a9b50d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html @@ -12,13 +12,13 @@
  • From 014eba971eb01c84b80cbc048228cd2d5afc0a5a Mon Sep 17 00:00:00 2001 From: Claus Date: Tue, 25 Jun 2019 15:29:32 +0200 Subject: [PATCH 52/75] adding a test for enum mapping between equal enums of different types. --- src/Umbraco.Tests/Mapping/MappingTests.cs | 70 +++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/Umbraco.Tests/Mapping/MappingTests.cs b/src/Umbraco.Tests/Mapping/MappingTests.cs index 79d383857a..7487667a58 100644 --- a/src/Umbraco.Tests/Mapping/MappingTests.cs +++ b/src/Umbraco.Tests/Mapping/MappingTests.cs @@ -172,6 +172,31 @@ namespace Umbraco.Tests.Mapping } } + [Test] + public void EnumMap() + { + var definitions = new MapDefinitionCollection(new IMapDefinition[] + { + new MapperDefinition4(), + }); + var mapper = new UmbracoMapper(definitions); + + var thing5 = new Thing5() + { + Fruit1 = Thing5Enum.Apple, + Fruit2 = Thing5Enum.Banana, + Fruit3= Thing5Enum.Cherry + }; + + var thing6 = mapper.Map(thing5); + + Assert.IsNotNull(thing6); + Assert.AreEqual(Thing6Enum.Apple, thing6.Fruit1); + Assert.AreEqual(Thing6Enum.Banana, thing6.Fruit2); + Assert.AreEqual(Thing6Enum.Cherry, thing6.Fruit3); + } + + private class Thing1 { public string Value { get; set; } @@ -188,6 +213,34 @@ namespace Umbraco.Tests.Mapping private class Thing4 { } + private class Thing5 + { + public Thing5Enum Fruit1 { get; set; } + public Thing5Enum Fruit2 { get; set; } + public Thing5Enum Fruit3 { get; set; } + } + + private enum Thing5Enum + { + Apple = 0, + Banana = 1, + Cherry = 2 + } + + private class Thing6 + { + public Thing6Enum Fruit1 { get; set; } + public Thing6Enum Fruit2 { get; set; } + public Thing6Enum Fruit3 { get; set; } + } + + private enum Thing6Enum + { + Apple = 0, + Banana = 1, + Cherry = 2 + } + private class MapperDefinition1 : IMapDefinition { public void DefineMaps(UmbracoMapper mapper) @@ -224,5 +277,22 @@ namespace Umbraco.Tests.Mapping mapper.Define(); } } + + private class MapperDefinition4 : IMapDefinition + { + public void DefineMaps(UmbracoMapper mapper) + { + mapper.Define((source, context) => new Thing6(), Map); + mapper.Define( + (source, context) => (Thing6Enum)source); + } + + private void Map(Thing5 source, Thing6 target, MapperContext context) + { + target.Fruit1 = context.Map(source.Fruit1); + target.Fruit2 = context.Map(source.Fruit2); + target.Fruit3 = context.Map(source.Fruit3); + } + } } } From bd8e34473682dfea437da1553c19e7449eda9914 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 13 Jun 2019 15:20:23 +0200 Subject: [PATCH 53/75] Support keyboard input in date/time picker --- .../propertyeditors/datepicker/datepicker.controller.js | 5 +++++ .../src/views/propertyeditors/datepicker/datepicker.html | 1 + 2 files changed, 6 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js index 62099734fb..869ca71eee 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js @@ -77,6 +77,11 @@ function dateTimePickerController($scope, notificationsService, assetsService, a setDate(date); setDatePickerVal(); }; + + $scope.inputChanged = function() { + setDate($scope.model.datetimePickerValue); + setDatePickerVal(); + } //here we declare a special method which will be called whenever the value has changed from the server //this is instead of doing a watch on the model.value = faster diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html index 0d3fad580e..99961110aa 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html @@ -16,6 +16,7 @@ id="{{model.alias}}" type="text" ng-model="model.datetimePickerValue" + ng-blur="inputChanged()" ng-required="model.validation.mandatory" val-server="value" class="datepickerinput"> From 8d67e39e9a8fc8039937ceaf099b8e496c60569e Mon Sep 17 00:00:00 2001 From: tiffy74 Date: Tue, 25 Jun 2019 14:58:56 +0100 Subject: [PATCH 54/75] secondary menu tab first commit (#5701) --- .../src/views/components/tabs/umb-tabs-nav.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/tabs/umb-tabs-nav.html b/src/Umbraco.Web.UI.Client/src/views/components/tabs/umb-tabs-nav.html index 2005666292..46fb11c310 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/tabs/umb-tabs-nav.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/tabs/umb-tabs-nav.html @@ -1,5 +1,5 @@ - From ad121bd758f13b37e92d4a55ee48d1872fe29299 Mon Sep 17 00:00:00 2001 From: Dennis Meinert Pedersen Date: Mon, 24 Jun 2019 20:29:58 +0200 Subject: [PATCH 55/75] Highlight when tabbing to umb-toggle --- .../less/components/buttons/umb-toggle.less | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less index 6f23677a1c..a621370d02 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less @@ -21,23 +21,23 @@ background-color: @inputBorder; position: relative; transition: background-color 120ms; - - .umb-toggle:hover &, + + .umb-toggle:hover &, .umb-toggle:focus & { border-color: @inputBorderFocus; } - + .umb-toggle.umb-toggle--checked & { border-color: @ui-btn; background-color: @ui-btn; - + &:hover { background-color: @ui-btn-hover; } } - - .umb-toggle.umb-toggle--checked:focus & { - border-color: black; + + .tabbing-active .umb-toggle:focus & { + box-shadow: 0 0 0 2px highlight; } } @@ -54,12 +54,12 @@ background-color: @white; border-radius: 8px; transition: transform 120ms ease-in-out, background-color 120ms; - + .umb-toggle.umb-toggle--checked & { transform: translateX(20px); background-color: white; } - + } From 90d591e5a8a3340b73a319c1f620ec04708eea02 Mon Sep 17 00:00:00 2001 From: Kim Rauff Schurmann Date: Tue, 25 Jun 2019 08:31:08 +0200 Subject: [PATCH 56/75] Fixed validation of fileextension. --- .../FileUploadPropertyValueEditor.cs | 2 +- .../ImageCropperPropertyValueEditor.cs | 2 +- .../UploadFileTypeValidator.cs | 34 +++++++++++++------ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs index 81239caec0..942f53b561 100644 --- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs @@ -99,7 +99,7 @@ namespace Umbraco.Web.PropertyEditors { // process the file // no file, invalid file, reject change - if (UploadFileTypeValidator.ValidateFileExtension(file.FileName) == false) + if (UploadFileTypeValidator.IsValidFileExtension(file.FileName) == false) return null; // get the filepath diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs index 7bea542521..a828f3a58f 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs @@ -142,7 +142,7 @@ namespace Umbraco.Web.PropertyEditors { // process the file // no file, invalid file, reject change - if (UploadFileTypeValidator.ValidateFileExtension(file.FileName) == false) + if (UploadFileTypeValidator.IsValidFileExtension(file.FileName) == false) return null; // get the filepath diff --git a/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs b/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs index 5394aca5ba..6855ab3bb8 100644 --- a/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs +++ b/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; +using System.Linq; using Newtonsoft.Json.Linq; using Umbraco.Core; using Umbraco.Core.Services; -using Umbraco.Core.Configuration; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Web.Composing; @@ -16,13 +16,27 @@ namespace Umbraco.Web.PropertyEditors { public IEnumerable Validate(object value, string valueType, object dataTypeConfiguration) { - if (!(value is JObject jobject) || jobject["selectedFiles"] == null) yield break; - - var fileNames = jobject["selectedFiles"].ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - - foreach (var fileName in fileNames) + string selectedFiles = null; + if (value is JObject jobject && jobject["selectedFiles"] is JToken jToken) { - if (ValidateFileExtension(fileName) == false) + selectedFiles = jToken.ToString(); + } + else if (valueType?.InvariantEquals(ValueTypes.String) == true) + { + selectedFiles = value as string; + + if (string.IsNullOrWhiteSpace(selectedFiles)) + yield break; + } + + var fileNames = selectedFiles?.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + if (fileNames == null || !fileNames.Any()) + yield break; + + foreach (string filename in fileNames) + { + if (IsValidFileExtension(filename) == false) { //we only store a single value for this editor so the 'member' or 'field' // we'll associate this error with will simply be called 'value' @@ -30,11 +44,11 @@ namespace Umbraco.Web.PropertyEditors } } } - - internal static bool ValidateFileExtension(string fileName) + + internal static bool IsValidFileExtension(string fileName) { if (fileName.IndexOf('.') <= 0) return false; - var extension = Path.GetExtension(fileName).TrimStart("."); + var extension = new FileInfo(fileName).Extension.TrimStart("."); return Current.Configs.Settings().Content.IsFileAllowedForUpload(extension); } } From 1c4b262c57fd27e4994d75411529846fe0855cf3 Mon Sep 17 00:00:00 2001 From: Rasmus Olsen <38912242+rolsendk@users.noreply.github.com> Date: Tue, 25 Jun 2019 18:38:46 +0200 Subject: [PATCH 57/75] Fix/5489 actions button left margin (#5718) --- .../directives/components/buttons/umbbutton.directive.js | 1 + .../src/less/utilities/_spacing.less | 9 +++++++++ .../src/views/components/buttons/umb-button.html | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js index 6127153a16..83904bd036 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js @@ -115,6 +115,7 @@ Use this directive to render an umbraco button. The directive can be used to gen vm.blockElement = false; vm.style = null; vm.innerState = "init"; + vm.generalActions = vm.labelKey === "general_actions"; vm.buttonLabel = vm.label; // is this a primary button style (i.e. anything but an 'info' button)? diff --git a/src/Umbraco.Web.UI.Client/src/less/utilities/_spacing.less b/src/Umbraco.Web.UI.Client/src/less/utilities/_spacing.less index 69bbeef0af..b9c8b909e8 100644 --- a/src/Umbraco.Web.UI.Client/src/less/utilities/_spacing.less +++ b/src/Umbraco.Web.UI.Client/src/less/utilities/_spacing.less @@ -57,3 +57,12 @@ .mb5 { margin-bottom: @spacing-extra-large; } .mb6 { margin-bottom: @spacing-extra-extra-large; } .mb7 { margin-bottom: @spacing-extra-extra-extra-large; } + +.ml0 { margin-left: @spacing-none; } +.ml1 { margin-left: @spacing-extra-small; } +.ml2 { margin-left: @spacing-small; } +.ml3 { margin-left: @spacing-medium; } +.ml4 { margin-left: @spacing-large; } +.ml5 { margin-left: @spacing-extra-large; } +.ml6 { margin-left: @spacing-extra-extra-large; } +.ml7 { margin-left: @spacing-extra-extra-extra-large; } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button.html b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button.html index 95c628376b..dcad858781 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button.html @@ -1,4 +1,4 @@ -
    +
    From c12bdaacbf510024183e840735ef6b1e1c9b92db Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 24 Jun 2019 20:43:44 +0200 Subject: [PATCH 58/75] Show the list view paging component even when there are less than 10 pages :) --- .../common/directives/components/umbpagination.directive.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpagination.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpagination.directive.js index ea57a3fad6..b49d47b979 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpagination.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbpagination.directive.js @@ -140,10 +140,9 @@ Use this directive to generate a pagination. tempPagination.push({ val: "...", isActive: false }, { name: lastLabel, val: scope.totalPages, isActive: false }); }); } - - scope.pagination = tempPagination; } + scope.pagination = tempPagination; } scope.next = function () { From 347c2e82385b1bddcf2ed3cc5b03e702a8d91dbd Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 26 Jun 2019 10:13:09 +0200 Subject: [PATCH 59/75] Upgrade to ModelsBuilder 8.1.0 --- build/NuSpecs/UmbracoCms.nuspec | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index 5cdacca419..0d18ac59fa 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -25,7 +25,7 @@ not want this to happen as the alpha of the next major is, really, the next major already. --> - + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 7ae996a6c5..1a1946b01a 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -104,7 +104,7 @@ - 8.0.4 + 8.1.0 From be587b20647c421ab26a5c655c5c7c45f6349ee8 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 26 Jun 2019 14:15:26 +0200 Subject: [PATCH 60/75] https://umbraco.visualstudio.com/D-Team%20Tracker/_workitems/edit/1085 - Creates a custom IAntiForgeryAdditionalDataProvider that provides and validates custom token data in the request which allows having a custom AF token per form created with BeginUmbracoForm --- ...oAntiForgeryAdditionalDataProviderTests.cs | 157 + src/Umbraco.Tests/Umbraco.Tests.csproj | 1237 ++++---- src/Umbraco.Web/HtmlHelperRenderExtensions.cs | 7 + src/Umbraco.Web/Mvc/RenderRouteHandler.cs | 35 +- src/Umbraco.Web/Runtime/WebFinalComponent.cs | 4 + ...mbracoAntiForgeryAdditionalDataProvider.cs | 92 + src/Umbraco.Web/Umbraco.Web.csproj | 2559 +++++++++-------- src/Umbraco.Web/UmbracoHelper.cs | 45 +- 8 files changed, 2202 insertions(+), 1934 deletions(-) create mode 100644 src/Umbraco.Tests/Security/UmbracoAntiForgeryAdditionalDataProviderTests.cs create mode 100644 src/Umbraco.Web/Security/UmbracoAntiForgeryAdditionalDataProvider.cs diff --git a/src/Umbraco.Tests/Security/UmbracoAntiForgeryAdditionalDataProviderTests.cs b/src/Umbraco.Tests/Security/UmbracoAntiForgeryAdditionalDataProviderTests.cs new file mode 100644 index 0000000000..c81c108e0d --- /dev/null +++ b/src/Umbraco.Tests/Security/UmbracoAntiForgeryAdditionalDataProviderTests.cs @@ -0,0 +1,157 @@ +using System.Collections.Specialized; +using System.Web; +using System.Web.Helpers; +using Moq; +using Newtonsoft.Json; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web.Mvc; +using Umbraco.Web.Security; + +namespace Umbraco.Tests.Security +{ + [TestFixture] + public class UmbracoAntiForgeryAdditionalDataProviderTests + { + [Test] + public void Test_Wrapped_Non_BeginUmbracoForm() + { + var wrapped = Mock.Of(x => x.GetAdditionalData(It.IsAny()) == "custom"); + var provider = new UmbracoAntiForgeryAdditionalDataProvider(wrapped); + + var httpContextFactory = new FakeHttpContextFactory("/hello/world"); + var data = provider.GetAdditionalData(httpContextFactory.HttpContext); + + Assert.IsTrue(data.DetectIsJson()); + var json = JsonConvert.DeserializeObject(data); + Assert.AreEqual(null, json.Ufprt); + Assert.IsTrue(json.Stamp != default); + Assert.AreEqual("custom", json.WrappedValue); + } + + [Test] + public void Null_Wrapped_Non_BeginUmbracoForm() + { + var provider = new UmbracoAntiForgeryAdditionalDataProvider(null); + + var httpContextFactory = new FakeHttpContextFactory("/hello/world"); + var data = provider.GetAdditionalData(httpContextFactory.HttpContext); + + Assert.IsTrue(data.DetectIsJson()); + var json = JsonConvert.DeserializeObject(data); + Assert.AreEqual(null, json.Ufprt); + Assert.IsTrue(json.Stamp != default); + Assert.AreEqual("default", json.WrappedValue); + } + + [Test] + public void Validate_Non_Json() + { + var provider = new UmbracoAntiForgeryAdditionalDataProvider(null); + + var httpContextFactory = new FakeHttpContextFactory("/hello/world"); + var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "hello"); + + Assert.IsFalse(isValid); + } + + [Test] + public void Validate_Invalid_Json() + { + var provider = new UmbracoAntiForgeryAdditionalDataProvider(null); + + var httpContextFactory = new FakeHttpContextFactory("/hello/world"); + var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '0'}"); + Assert.IsFalse(isValid); + + isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': ''}"); + Assert.IsFalse(isValid); + + isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'hello': 'world'}"); + Assert.IsFalse(isValid); + + } + + [Test] + public void Validate_No_Request_Ufprt() + { + var provider = new UmbracoAntiForgeryAdditionalDataProvider(null); + + var httpContextFactory = new FakeHttpContextFactory("/hello/world"); + //there is a ufprt in the additional data, but not in the request + var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': 'ASBVDFDFDFDF'}"); + Assert.IsFalse(isValid); + } + + [Test] + public void Validate_No_AdditionalData_Ufprt() + { + var provider = new UmbracoAntiForgeryAdditionalDataProvider(null); + + var httpContextFactory = new FakeHttpContextFactory("/hello/world"); + var requestMock = Mock.Get(httpContextFactory.HttpContext.Request); + requestMock.SetupGet(x => x["ufprt"]).Returns("ABCDEFG"); + + //there is a ufprt in the additional data, but not in the request + var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': ''}"); + Assert.IsFalse(isValid); + } + + [Test] + public void Validate_No_AdditionalData_Or_Request_Ufprt() + { + var provider = new UmbracoAntiForgeryAdditionalDataProvider(null); + + var httpContextFactory = new FakeHttpContextFactory("/hello/world"); + + //there is a ufprt in the additional data, but not in the request + var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': ''}"); + Assert.IsTrue(isValid); + } + + [Test] + public void Validate_Request_And_AdditionalData_Ufprt() + { + var provider = new UmbracoAntiForgeryAdditionalDataProvider(null); + + var routeParams1 = $"{RenderRouteHandler.ReservedAdditionalKeys.Controller}={HttpUtility.UrlEncode("Test")}&{RenderRouteHandler.ReservedAdditionalKeys.Action}={HttpUtility.UrlEncode("Index")}&{RenderRouteHandler.ReservedAdditionalKeys.Area}=Umbraco"; + var routeParams2 = $"{RenderRouteHandler.ReservedAdditionalKeys.Controller}={HttpUtility.UrlEncode("Test")}&{RenderRouteHandler.ReservedAdditionalKeys.Action}={HttpUtility.UrlEncode("Index")}&{RenderRouteHandler.ReservedAdditionalKeys.Area}=Umbraco"; + + var httpContextFactory = new FakeHttpContextFactory("/hello/world"); + var requestMock = Mock.Get(httpContextFactory.HttpContext.Request); + requestMock.SetupGet(x => x["ufprt"]).Returns(routeParams1.EncryptWithMachineKey()); + + var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': '" + routeParams2.EncryptWithMachineKey() + "'}"); + Assert.IsTrue(isValid); + + routeParams2 = $"{RenderRouteHandler.ReservedAdditionalKeys.Controller}={HttpUtility.UrlEncode("Invalid")}&{RenderRouteHandler.ReservedAdditionalKeys.Action}={HttpUtility.UrlEncode("Index")}&{RenderRouteHandler.ReservedAdditionalKeys.Area}=Umbraco"; + isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': '" + routeParams2.EncryptWithMachineKey() + "'}"); + Assert.IsFalse(isValid); + } + + [Test] + public void Validate_Wrapped_Request_And_AdditionalData_Ufprt() + { + var wrapped = Mock.Of(x => x.ValidateAdditionalData(It.IsAny(), "custom") == true); + var provider = new UmbracoAntiForgeryAdditionalDataProvider(wrapped); + + var routeParams1 = $"{RenderRouteHandler.ReservedAdditionalKeys.Controller}={HttpUtility.UrlEncode("Test")}&{RenderRouteHandler.ReservedAdditionalKeys.Action}={HttpUtility.UrlEncode("Index")}&{RenderRouteHandler.ReservedAdditionalKeys.Area}=Umbraco"; + var routeParams2 = $"{RenderRouteHandler.ReservedAdditionalKeys.Controller}={HttpUtility.UrlEncode("Test")}&{RenderRouteHandler.ReservedAdditionalKeys.Action}={HttpUtility.UrlEncode("Index")}&{RenderRouteHandler.ReservedAdditionalKeys.Area}=Umbraco"; + + var httpContextFactory = new FakeHttpContextFactory("/hello/world"); + var requestMock = Mock.Get(httpContextFactory.HttpContext.Request); + requestMock.SetupGet(x => x["ufprt"]).Returns(routeParams1.EncryptWithMachineKey()); + + var isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': '" + routeParams2.EncryptWithMachineKey() + "'}"); + Assert.IsFalse(isValid); + + isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'custom', 'Ufprt': '" + routeParams2.EncryptWithMachineKey() + "'}"); + Assert.IsTrue(isValid); + + routeParams2 = $"{RenderRouteHandler.ReservedAdditionalKeys.Controller}={HttpUtility.UrlEncode("Invalid")}&{RenderRouteHandler.ReservedAdditionalKeys.Action}={HttpUtility.UrlEncode("Index")}&{RenderRouteHandler.ReservedAdditionalKeys.Area}=Umbraco"; + isValid = provider.ValidateAdditionalData(httpContextFactory.HttpContext, "{'Stamp': '636970328040070330', 'WrappedValue': 'default', 'Ufprt': '" + routeParams2.EncryptWithMachineKey() + "'}"); + Assert.IsFalse(isValid); + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index ddd67df6e5..c49929ab64 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -1,619 +1,620 @@ - - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {5D3B8245-ADA6-453F-A008-50ED04BFE770} - Library - Properties - Umbraco.Tests - Umbraco.Tests - v4.7.2 - 512 - ..\ - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - latest - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - latest - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1.8.14 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - SqlResources.resx - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - ImportResources.resx - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - TestFiles.resx - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Designer - - - Designer - Always - - - Designer - Always - - - Always - - - - Designer - - - - Always - - - - - {31785BC3-256C-4613-B2F5-A1B0BDDED8C1} - Umbraco.Core - - - {651E1350-91B6-44B7-BD60-7207006D7003} - Umbraco.Web - - - {07fbc26b-2927-4a22-8d96-d644c667fecc} - Umbraco.Examine - - - - - ResXFileCodeGenerator - SqlResources.Designer.cs - Designer - - - ResXFileCodeGenerator - ImportResources.Designer.cs - Designer - - - ResXFileCodeGenerator - TestFiles.Designer.cs - Designer - - - - - Designer - Always - - - Always - - - - - - - Designer - - - - - - - - - - Designer - - - - - - - - - - - - - $(NuGetPackageFolders.Split(';')[0]) - - - - - - - - - + + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {5D3B8245-ADA6-453F-A008-50ED04BFE770} + Library + Properties + Umbraco.Tests + Umbraco.Tests + v4.7.2 + 512 + ..\ + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + latest + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + latest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.8.14 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + SqlResources.resx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + ImportResources.resx + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + TestFiles.resx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + Designer + Always + + + Designer + Always + + + Always + + + + Designer + + + + Always + + + + + {31785BC3-256C-4613-B2F5-A1B0BDDED8C1} + Umbraco.Core + + + {651E1350-91B6-44B7-BD60-7207006D7003} + Umbraco.Web + + + {07fbc26b-2927-4a22-8d96-d644c667fecc} + Umbraco.Examine + + + + + ResXFileCodeGenerator + SqlResources.Designer.cs + Designer + + + ResXFileCodeGenerator + ImportResources.Designer.cs + Designer + + + ResXFileCodeGenerator + TestFiles.Designer.cs + Designer + + + + + Designer + Always + + + Always + + + + + + + Designer + + + + + + + + + + Designer + + + + + + + + + + + + + $(NuGetPackageFolders.Split(';')[0]) + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index 99adf71742..274cc06dd2 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -223,6 +223,13 @@ namespace Umbraco.Web _method = method; _controllerName = controllerName; _encryptedString = UrlHelperRenderExtensions.CreateEncryptedRouteString(controllerName, controllerAction, area, additionalRouteVals); + + //For UmbracoForm's we want to add our routing string to the httpcontext items in the case where anti-forgery tokens are used. + //In which case our custom UmbracoAntiForgeryAdditionalDataProvider will kick in and validate the values in the request against + //the values that will be appended to the token. This essentially means that when anti-forgery tokens are used with UmbracoForm's forms, + //that each token is unique to the controller/action/area instead of the default ASP.Net implementation which is that the token is unique + //per user. + _viewContext.HttpContext.Items["ufprt"] = _encryptedString; } private readonly ViewContext _viewContext; diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs index 54215c2e8c..aa2e5f18a7 100644 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs @@ -19,7 +19,7 @@ namespace Umbraco.Web.Mvc public class RenderRouteHandler : IRouteHandler { // Define reserved dictionary keys for controller, action and area specified in route additional values data - private static class ReservedAdditionalKeys + internal static class ReservedAdditionalKeys { internal const string Controller = "c"; internal const string Action = "a"; @@ -134,36 +134,7 @@ namespace Umbraco.Web.Mvc return null; } - - string decryptedString; - try - { - decryptedString = encodedVal.DecryptWithMachineKey(); - } - catch (FormatException) - { - Current.Logger.Warn("A value was detected in the ufprt parameter but Umbraco could not decrypt the string"); - return null; - } - - var parsedQueryString = HttpUtility.ParseQueryString(decryptedString); - var decodedParts = new Dictionary(); - - foreach (var key in parsedQueryString.AllKeys) - { - decodedParts[key] = parsedQueryString[key]; - } - - //validate all required keys exist - - //the controller - if (decodedParts.All(x => x.Key != ReservedAdditionalKeys.Controller)) - return null; - //the action - if (decodedParts.All(x => x.Key != ReservedAdditionalKeys.Action)) - return null; - //the area - if (decodedParts.All(x => x.Key != ReservedAdditionalKeys.Area)) + if (!UmbracoHelper.DecryptAndValidateEncryptedRouteString(encodedVal, out var decodedParts)) return null; foreach (var item in decodedParts.Where(x => new[] { @@ -417,7 +388,7 @@ namespace Umbraco.Web.Mvc return new UmbracoMvcHandler(requestContext); } - + private SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext, string controllerName) { return _controllerFactory.GetControllerSessionBehavior(requestContext, controllerName); diff --git a/src/Umbraco.Web/Runtime/WebFinalComponent.cs b/src/Umbraco.Web/Runtime/WebFinalComponent.cs index 42ff0ee5e6..ba606e8d5e 100644 --- a/src/Umbraco.Web/Runtime/WebFinalComponent.cs +++ b/src/Umbraco.Web/Runtime/WebFinalComponent.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Web.Helpers; using System.Web.Http; using System.Web.Mvc; using System.Web.Routing; @@ -8,6 +9,7 @@ using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Web.Install; using Umbraco.Web.Mvc; +using Umbraco.Web.Security; using Umbraco.Web.WebApi; namespace Umbraco.Web.Runtime @@ -34,6 +36,8 @@ namespace Umbraco.Web.Runtime // ensure WebAPI is initialized, after everything GlobalConfiguration.Configuration.EnsureInitialized(); + + AntiForgeryConfig.AdditionalDataProvider = new UmbracoAntiForgeryAdditionalDataProvider(AntiForgeryConfig.AdditionalDataProvider); } public void Terminate() diff --git a/src/Umbraco.Web/Security/UmbracoAntiForgeryAdditionalDataProvider.cs b/src/Umbraco.Web/Security/UmbracoAntiForgeryAdditionalDataProvider.cs new file mode 100644 index 0000000000..c6ad4c6901 --- /dev/null +++ b/src/Umbraco.Web/Security/UmbracoAntiForgeryAdditionalDataProvider.cs @@ -0,0 +1,92 @@ +using System; +using Umbraco.Web.Mvc; +using Umbraco.Core; +using System.Web.Helpers; +using System.Web; +using Newtonsoft.Json; +using Umbraco.Web.Composing; + +namespace Umbraco.Web.Security +{ + /// + /// A custom to create a unique antiforgery token/validator per form created with BeginUmbracoForm + /// + public class UmbracoAntiForgeryAdditionalDataProvider : IAntiForgeryAdditionalDataProvider + { + private readonly IAntiForgeryAdditionalDataProvider _defaultProvider; + + /// + /// Constructor, allows wrapping a default provider + /// + /// + public UmbracoAntiForgeryAdditionalDataProvider(IAntiForgeryAdditionalDataProvider defaultProvider) + { + _defaultProvider = defaultProvider; + } + + public string GetAdditionalData(HttpContextBase context) + { + return JsonConvert.SerializeObject(new AdditionalData + { + Stamp = DateTime.UtcNow.Ticks, + //this value will be here if this is a BeginUmbracoForms form + Ufprt = context.Items["ufprt"]?.ToString(), + //if there was a wrapped provider, add it's value to the json, else just a static value + WrappedValue = _defaultProvider?.GetAdditionalData(context) ?? "default" + }); + } + + public bool ValidateAdditionalData(HttpContextBase context, string additionalData) + { + if (!additionalData.DetectIsJson()) + return false; //must be json + + AdditionalData json; + try + { + json = JsonConvert.DeserializeObject(additionalData); + } + catch + { + return false; //couldn't parse + } + + if (json.Stamp == default) return false; + + //if there was a wrapped provider, validate it, else validate the static value + var validateWrapped = _defaultProvider?.ValidateAdditionalData(context, json.WrappedValue) ?? json.WrappedValue == "default"; + if (!validateWrapped) + return false; + + var ufprtRequest = context.Request["ufprt"]?.ToString(); + + //if the custom BeginUmbracoForms route value is not there, then it's nothing more to validate + if (ufprtRequest.IsNullOrWhiteSpace() && json.Ufprt.IsNullOrWhiteSpace()) + return true; + + //if one or the other is null then something is wrong + if (!ufprtRequest.IsNullOrWhiteSpace() && json.Ufprt.IsNullOrWhiteSpace()) return false; + if (ufprtRequest.IsNullOrWhiteSpace() && !json.Ufprt.IsNullOrWhiteSpace()) return false; + + if (!UmbracoHelper.DecryptAndValidateEncryptedRouteString(json.Ufprt, out var additionalDataParts)) + return false; + + if (!UmbracoHelper.DecryptAndValidateEncryptedRouteString(ufprtRequest, out var requestParts)) + return false; + + //ensure they all match + return additionalDataParts.Count == requestParts.Count + && additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Controller] == requestParts[RenderRouteHandler.ReservedAdditionalKeys.Controller] + && additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Action] == requestParts[RenderRouteHandler.ReservedAdditionalKeys.Action] + && additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Area] == requestParts[RenderRouteHandler.ReservedAdditionalKeys.Area]; + } + + internal class AdditionalData + { + public string Ufprt { get; set; } + public long Stamp { get; set; } + public string WrappedValue { get; set; } + } + + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 7ebcf55155..ac30cc51b5 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1,1280 +1,1281 @@ - - - - - v4.7.2 - false - false - {651E1350-91B6-44B7-BD60-7207006D7003} - Library - Umbraco.Web - Umbraco.Web - ..\ - true - Off - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - latest - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\Umbraco.Web.xml - false - latest - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2.7.0.100 - - - - - - - - - - - - - - - - - - - - - 1.0.5 - - - - - {31785bc3-256c-4613-b2f5-a1b0bdded8c1} - Umbraco.Core - - - Umbraco.Examine - {07FBC26B-2927-4A22-8D96-D644C667FECC} - - - - - - - Properties\SolutionInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Reference.map - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Resources.resx - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ASPXCodeBehind - - - - - - - - - - - - - - True - True - Strings.resx - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - Code - - - True - True - Settings.settings - - - - - - - True - True - Reference.map - - - - - - Component - - - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - ResXFileCodeGenerator - Strings.Designer.cs - - - - - - - - - - - - - - - - - - MSDiscoCodeGenerator - Reference.cs - Designer - - - Mvc\web.config - - - MSDiscoCodeGenerator - Reference.cs - - - - - Reference.map - - - Reference.map - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - - - Dynamic - Web References\org.umbraco.our\ - http://our.umbraco.org/umbraco/webservices/api/repository.asmx - - - - - Settings - umbraco_org_umbraco_our_Repository - - - Dynamic - Web References\org.umbraco.update\ - http://update.umbraco.org/checkforupgrade.asmx - - - - - Settings - umbraco_org_umbraco_update_CheckForUpgrade - - - - - - - - $(PlatformTargetAsMSBuildArchitecture) - - - - - - - - - - - + + + + + v4.7.2 + false + false + {651E1350-91B6-44B7-BD60-7207006D7003} + Library + Umbraco.Web + Umbraco.Web + ..\ + true + Off + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + latest + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\Umbraco.Web.xml + false + latest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2.7.0.100 + + + + + + + + + + + + + + + + + + + + + 1.0.5 + + + + + {31785bc3-256c-4613-b2f5-a1b0bdded8c1} + Umbraco.Core + + + Umbraco.Examine + {07FBC26B-2927-4A22-8D96-D644C667FECC} + + + + + + + Properties\SolutionInfo.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Reference.map + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ASPXCodeBehind + + + + + + + + + + + + + + True + True + Strings.resx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + Code + + + True + True + Settings.settings + + + + + + + True + True + Reference.map + + + + + + Component + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + ResXFileCodeGenerator + Strings.Designer.cs + + + + + + + + + + + + + + + + + + MSDiscoCodeGenerator + Reference.cs + Designer + + + Mvc\web.config + + + MSDiscoCodeGenerator + Reference.cs + + + + + Reference.map + + + Reference.map + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + Dynamic + Web References\org.umbraco.our\ + http://our.umbraco.org/umbraco/webservices/api/repository.asmx + + + + + Settings + umbraco_org_umbraco_our_Repository + + + Dynamic + Web References\org.umbraco.update\ + http://update.umbraco.org/checkforupgrade.asmx + + + + + Settings + umbraco_org_umbraco_update_CheckForUpgrade + + + + + + + + $(PlatformTargetAsMSBuildArchitecture) + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 1c6eb28b92..3209ac2b59 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -8,7 +8,10 @@ using Umbraco.Core.Dictionary; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Xml; +using Umbraco.Web.Composing; +using Umbraco.Web.Mvc; using Umbraco.Web.Security; +using Constants = Umbraco.Core.Constants; namespace Umbraco.Web { @@ -228,7 +231,7 @@ namespace Umbraco.Web #endregion - + #region Member/Content/Media from Udi @@ -495,7 +498,7 @@ namespace Umbraco.Web /// The existing contents corresponding to the identifiers. /// If an identifier does not match an existing content, it will be missing in the returned value. public IEnumerable Content(IEnumerable ids) - { + { return ids.Select(id => ContentQuery.Content(id)).WhereNotNull(); } @@ -810,10 +813,42 @@ namespace Umbraco.Web #endregion - - + internal static bool DecryptAndValidateEncryptedRouteString(string ufprt, out IDictionary parts) + { + string decryptedString; + try + { + decryptedString = ufprt.DecryptWithMachineKey(); + } + catch (FormatException) + { + Current.Logger.Warn(typeof(UmbracoHelper), "A value was detected in the ufprt parameter but Umbraco could not decrypt the string"); + parts = null; + return false; + } - + var parsedQueryString = HttpUtility.ParseQueryString(decryptedString); + parts = new Dictionary(); + + foreach (var key in parsedQueryString.AllKeys) + { + parts[key] = parsedQueryString[key]; + } + + //validate all required keys exist + + //the controller + if (parts.All(x => x.Key != RenderRouteHandler.ReservedAdditionalKeys.Controller)) + return false; + //the action + if (parts.All(x => x.Key != RenderRouteHandler.ReservedAdditionalKeys.Action)) + return false; + //the area + if (parts.All(x => x.Key != RenderRouteHandler.ReservedAdditionalKeys.Area)) + return false; + + return true; + } } } From 975816ddd7c87ced0cc9178ecad5c9fa67e23f3a Mon Sep 17 00:00:00 2001 From: Claus Date: Wed, 26 Jun 2019 23:10:46 +0200 Subject: [PATCH 61/75] UmbracoMapper shouldn't throw an exception when trying to map a source property being null. --- src/Umbraco.Core/Mapping/UmbracoMapper.cs | 2 +- src/Umbraco.Tests/Mapping/MappingTests.cs | 48 ++++++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Mapping/UmbracoMapper.cs b/src/Umbraco.Core/Mapping/UmbracoMapper.cs index 2d495b38b5..8915ebcf74 100644 --- a/src/Umbraco.Core/Mapping/UmbracoMapper.cs +++ b/src/Umbraco.Core/Mapping/UmbracoMapper.cs @@ -191,7 +191,7 @@ namespace Umbraco.Core.Mapping private TTarget Map(object source, Type sourceType, MapperContext context) { if (source == null) - throw new ArgumentNullException(nameof(source)); + return default; var targetType = typeof(TTarget); diff --git a/src/Umbraco.Tests/Mapping/MappingTests.cs b/src/Umbraco.Tests/Mapping/MappingTests.cs index 7487667a58..e6a382692c 100644 --- a/src/Umbraco.Tests/Mapping/MappingTests.cs +++ b/src/Umbraco.Tests/Mapping/MappingTests.cs @@ -195,7 +195,24 @@ namespace Umbraco.Tests.Mapping Assert.AreEqual(Thing6Enum.Banana, thing6.Fruit2); Assert.AreEqual(Thing6Enum.Cherry, thing6.Fruit3); } - + + [Test] + public void NullPropertyMap() + { + var definitions = new MapDefinitionCollection(new IMapDefinition[] + { + new MapperDefinition5(), + }); + var mapper = new UmbracoMapper(definitions); + + var thing7 = new Thing7(); + + var thing8 = mapper.Map(thing7); + + Assert.IsNotNull(thing8); + Assert.IsNull(thing8.Things); + } + private class Thing1 { @@ -241,6 +258,16 @@ namespace Umbraco.Tests.Mapping Cherry = 2 } + private class Thing7 + { + public IEnumerable Things { get; set; } + } + + private class Thing8 + { + public IEnumerable Things { get; set; } + } + private class MapperDefinition1 : IMapDefinition { public void DefineMaps(UmbracoMapper mapper) @@ -294,5 +321,24 @@ namespace Umbraco.Tests.Mapping target.Fruit3 = context.Map(source.Fruit3); } } + + private class MapperDefinition5 : IMapDefinition + { + public void DefineMaps(UmbracoMapper mapper) + { + mapper.Define((source, context) => new Thing2(), Map1); + mapper.Define((source, context) => new Thing8(), Map2); + } + + private void Map1(Thing1 source, Thing2 target, MapperContext context) + { + target.Value = source.Value; + } + + private void Map2(Thing7 source, Thing8 target, MapperContext context) + { + target.Things = context.Map>(source.Things); + } + } } } From 857a8b163d33537b8d8eabca8dfa599b0c3275be Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 27 Jun 2019 08:03:08 +0200 Subject: [PATCH 62/75] build fix --- src/Umbraco.Tests/Umbraco.Tests.csproj | 620 +----------- src/Umbraco.Web/Umbraco.Web.csproj | 1282 +----------------------- 2 files changed, 3 insertions(+), 1899 deletions(-) diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 357d0c8a73..cc569d6989 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -147,6 +147,7 @@ + @@ -616,623 +617,4 @@ - - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {5D3B8245-ADA6-453F-A008-50ED04BFE770} - Library - Properties - Umbraco.Tests - Umbraco.Tests - v4.7.2 - 512 - ..\ - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - latest - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - latest - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1.8.14 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - SqlResources.resx - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - ImportResources.resx - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - TestFiles.resx - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Designer - - - Designer - Always - - - Designer - Always - - - Always - - - - Designer - - - - Always - - - - - {31785BC3-256C-4613-B2F5-A1B0BDDED8C1} - Umbraco.Core - - - {651E1350-91B6-44B7-BD60-7207006D7003} - Umbraco.Web - - - {07fbc26b-2927-4a22-8d96-d644c667fecc} - Umbraco.Examine - - - - - ResXFileCodeGenerator - SqlResources.Designer.cs - Designer - - - ResXFileCodeGenerator - ImportResources.Designer.cs - Designer - - - ResXFileCodeGenerator - TestFiles.Designer.cs - Designer - - - - - Designer - Always - - - Always - - - - - - - Designer - - - - - - - - - - Designer - - - - - - - - - - - - - $(NuGetPackageFolders.Split(';')[0]) - - - - - - - - - \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 0a5fcafb8d..8d1b106ba4 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -211,6 +211,7 @@ + @@ -219,6 +220,7 @@ + @@ -1273,1284 +1275,4 @@ - - - - - v4.7.2 - false - false - {651E1350-91B6-44B7-BD60-7207006D7003} - Library - Umbraco.Web - Umbraco.Web - ..\ - true - Off - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - latest - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\Umbraco.Web.xml - false - latest - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2.7.0.100 - - - - - - - - - - - - - - - - - - - - - 1.0.5 - - - - - {31785bc3-256c-4613-b2f5-a1b0bdded8c1} - Umbraco.Core - - - Umbraco.Examine - {07FBC26B-2927-4A22-8D96-D644C667FECC} - - - - - - - Properties\SolutionInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Reference.map - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Resources.resx - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ASPXCodeBehind - - - - - - - - - - - - - - True - True - Strings.resx - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - Code - - - True - True - Settings.settings - - - - - - - True - True - Reference.map - - - - - - Component - - - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - ResXFileCodeGenerator - Strings.Designer.cs - - - - - - - - - - - - - - - - - - MSDiscoCodeGenerator - Reference.cs - Designer - - - Mvc\web.config - - - MSDiscoCodeGenerator - Reference.cs - - - - - Reference.map - - - Reference.map - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - - - Dynamic - Web References\org.umbraco.our\ - http://our.umbraco.org/umbraco/webservices/api/repository.asmx - - - - - Settings - umbraco_org_umbraco_our_Repository - - - Dynamic - Web References\org.umbraco.update\ - http://update.umbraco.org/checkforupgrade.asmx - - - - - Settings - umbraco_org_umbraco_update_CheckForUpgrade - - - - - - - - $(PlatformTargetAsMSBuildArchitecture) - - - - - - - - - - - \ No newline at end of file From 01d5a120397cc8287050b4a64194e31dcefd1158 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 27 Jun 2019 18:00:45 +1000 Subject: [PATCH 63/75] when syncing the picker to a path, activate the node --- .../common/infiniteeditors/treepicker/treepicker.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js index aab8cb1bb5..259ae3011d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js @@ -276,7 +276,7 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController", var nodeHasPath = currentNode && currentNode.path; var startNodeNotDefined = !vm.startNodeId; if (startNodeNotDefined && nodeHasPath) { - vm.dialogTreeApi.syncTree({ path: currentNode.path, activate: false }); + vm.dialogTreeApi.syncTree({ path: currentNode.path, activate: true }); } } From ba27eff5cf094ce82fce799131b9e084c26dbf60 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 27 Jun 2019 10:24:53 +0200 Subject: [PATCH 64/75] Added missing using in macro template --- .../Umbraco/PartialViewMacros/Templates/Gallery.cshtml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml index 8e3222cd93..5aa09a3cb3 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml @@ -1,5 +1,6 @@ @using Umbraco.Core.Models.PublishedContent @using Umbraco.Web +@using Umbraco.Core @inherits Umbraco.Web.Macros.PartialViewMacroPage @* From 8c5f253b41e844ddb8582e4c78f829799785ee77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Vanbrabandt?= Date: Thu, 27 Jun 2019 11:45:48 +0200 Subject: [PATCH 65/75] V8: Add validation to the Version and UmbracoVersion properties (#5559) --- .../src/views/packages/edit.controller.js | 2 ++ src/Umbraco.Web.UI.Client/src/views/packages/edit.html | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index ad251120f7..623621c536 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -26,6 +26,8 @@ vm.buttonLabel = ""; + vm.versionRegex = /^(\d+\.)(\d+\.)(\*|\d+)$/; + const packageId = $routeParams.id; const create = $routeParams.create; diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index 1b09453804..2df95cec0d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -36,7 +36,7 @@ - + Required @@ -54,7 +54,7 @@ --> - + {{editPackageForm.umbracoVersion.errorMsg}} From 040ebfe9dd7d7053ccf4393f8a83c11fc3866b69 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 14 May 2019 21:51:53 +0200 Subject: [PATCH 66/75] Add auto-focus to the default action in overlay dialogs --- .../directives/components/buttons/umbbutton.directive.js | 3 ++- .../directives/components/forms/umbautofocus.directive.js | 8 +++++--- .../components/overlays/umboverlay.directive.js | 5 ++--- .../src/views/components/buttons/umb-button.html | 4 ++-- .../src/views/components/overlays/umb-overlay.html | 6 ++++-- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js index 83904bd036..f026a05c45 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js @@ -95,7 +95,8 @@ Use this directive to render an umbraco button. The directive can be used to gen size: "@?", alias: "@?", addEllipsis: "@?", - showCaret: "@?" + showCaret: "@?", + autoFocus: "@?" } }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbautofocus.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbautofocus.directive.js index 6fcdd99001..eb3503f799 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbautofocus.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbautofocus.directive.js @@ -9,8 +9,10 @@ angular.module("umbraco.directives") } }; - $timeout(function() { - update(); - }); + if (attr.umbAutoFocus !== "false") { + $timeout(function() { + update(); + }); + } }; }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js index 8c32d93c01..0135abd97c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js @@ -311,9 +311,8 @@ Opens an overlay to show a custom YSOD.
    var submitOnEnter = document.activeElement.hasAttribute("overlay-submit-on-enter"); var submitOnEnterValue = submitOnEnter ? document.activeElement.getAttribute("overlay-submit-on-enter") : ""; - if(clickableElements.indexOf(activeElementType) === 0) { - document.activeElement.trigger("click"); - event.preventDefault(); + if(clickableElements.indexOf(activeElementType) >= 0) { + // don't do anything, let the browser Enter key handle this } else if(activeElementType === "TEXTAREA" && !submitOnEnter) { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button.html b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button.html index dcad858781..8163b2807b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button.html @@ -15,7 +15,7 @@
    - -
    @@ -74,7 +75,8 @@ type="button" disabled="model.disableSubmitButton" action="submitForm(model)" - state="model.submitButtonState"> + state="model.submitButtonState" + auto-focus="true">
    From cb7300688cf6e6b6f25f96253e5ce2f78c4f07de Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Fri, 21 Jun 2019 15:53:56 +0200 Subject: [PATCH 67/75] Don't prompt for changes when cancelling empty properties --- .../directives/components/umbgroupsbuilder.directive.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js index 396699866c..a51afd200c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js @@ -474,6 +474,11 @@ if (!property.inherited) { var oldPropertyModel = angular.copy(property); + if (oldPropertyModel.allowCultureVariant === undefined) { + // this is necessary for comparison when detecting changes to the property + oldPropertyModel.allowCultureVariant = scope.model.allowCultureVariant; + oldPropertyModel.alias = ""; + } var propertyModel = angular.copy(property); var propertySettings = { From 80ffd026f61129d4316bbbfc25e1c1dc4cab27c0 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 27 Jun 2019 13:18:45 +0200 Subject: [PATCH 68/75] Reverts PR #5693 - we need to update the linkpicker search instead to return the correct results for us --- .../common/infiniteeditors/linkpicker/linkpicker.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js index 6568cfb567..afa6193227 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js @@ -150,7 +150,7 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController", function handleContentTarget(content) { $scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(contentEditingHelper.getAllProps(content.variants[0]))); - $scope.model.target.url = content.urls.filter(item => item.culture === $scope.currentNode.metaData.culture)[0].text; + $scope.model.target.url = content.urls[0].text; } function nodeExpandedHandler(args) { From 9d14c9e0b73af70613fab30695b560d68bdac2c1 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 18 Jun 2019 20:58:41 +0200 Subject: [PATCH 69/75] Don't explicitly allow deleting the current editor's own content --- src/Umbraco.Web/Trees/ContentTreeControllerBase.cs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 1b8f3b1434..30d34a1512 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -410,18 +410,7 @@ namespace Umbraco.Web.Trees internal IEnumerable GetAllowedUserMenuItemsForNode(IUmbracoEntity dd) { var permission = Services.UserService.GetPermissions(Security.CurrentUser, dd.Path); - // TODO: inject - var actions = Current.Actions.FromEntityPermission(permission) - .ToList(); - - var actionDelete = Current.Actions.GetAction(); - - // A user is allowed to delete their own stuff - var tryGetCurrentUserId = Security.GetUserId(); - if (tryGetCurrentUserId && dd.CreatorId == tryGetCurrentUserId.Result && actions.Contains(actionDelete) == false) - actions.Add(actionDelete); - - return actions.Select(x => new MenuItem(x)); + return Current.Actions.FromEntityPermission(permission).Select(x => new MenuItem(x)); } /// From 7a178be0312c4c273783d7dc7e7a8e163b6d0fd7 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 27 Jun 2019 13:29:35 +0200 Subject: [PATCH 70/75] Fix getting content AtRoot --- .../PublishedContent/NuCacheChildrenTests.cs | 87 +++++++++++++++++++ .../PublishedCache/NuCache/ContentCache.cs | 16 +++- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index dd656b8019..f3a520ead1 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -264,6 +264,74 @@ namespace Umbraco.Tests.PublishedContent yield return CreateKit(12, 4, 2); } + private IEnumerable GetVariantWithDraftKits() + { + var paths = new Dictionary { { -1, "-1" } }; + + Dictionary GetCultureInfos(int id, DateTime now) + { + var en = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; + var fr = new[] { 1, 3, 4, 6, 7, 9, 10, 12 }; + + var infos = new Dictionary(); + if (en.Contains(id)) + infos["en-US"] = new CultureVariation { Name = "N" + id + "-" + "en-US", Date = now, IsDraft = false }; + if (fr.Contains(id)) + infos["fr-FR"] = new CultureVariation { Name = "N" + id + "-" + "fr-FR", Date = now, IsDraft = false }; + return infos; + } + + ContentNodeKit CreateKit(int id, int parentId, int sortOrder) + { + if (!paths.TryGetValue(parentId, out var parentPath)) + throw new Exception("Unknown parent."); + + var path = paths[id] = parentPath + "," + id; + var level = path.Count(x => x == ','); + var now = DateTime.Now; + + ContentData CreateContentData(bool published) => new ContentData + { + Name = "N" + id, + Published = published, + TemplateId = 0, + VersionId = 1, + VersionDate = now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = GetCultureInfos(id, now) + }; + + var withDraft = id%2==0; + var withPublished = !withDraft; + + return new ContentNodeKit + { + ContentTypeId = _contentTypeVariant.Id, + Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0), + DraftData = withDraft ? CreateContentData(false) : null, + PublishedData = withPublished ? CreateContentData(true) : null + }; + } + + yield return CreateKit(1, -1, 1); + yield return CreateKit(2, -1, 2); + yield return CreateKit(3, -1, 3); + + yield return CreateKit(4, 1, 1); + yield return CreateKit(5, 1, 2); + yield return CreateKit(6, 1, 3); + + yield return CreateKit(7, 2, 3); + yield return CreateKit(8, 2, 2); + yield return CreateKit(9, 2, 1); + + yield return CreateKit(10, 3, 1); + + yield return CreateKit(11, 4, 1); + yield return CreateKit(12, 4, 2); + } + [Test] public void EmptyTest() { @@ -747,6 +815,25 @@ namespace Umbraco.Tests.PublishedContent AssertDocuments(documents, "N9", "N8", "N7"); } + [Test] + public void AtRootTest() + { + Init(GetVariantWithDraftKits()); + + var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); + _snapshotAccessor.PublishedSnapshot = snapshot; + + _variationAccesor.VariationContext = new VariationContext("en-US"); + + // N2 is draft only + + var documents = snapshot.Content.GetAtRoot().ToArray(); + AssertDocuments(documents, "N1-en-US", /*"N2-en-US",*/ "N3-en-US"); + + documents = snapshot.Content.GetAtRoot(true).ToArray(); + AssertDocuments(documents, "N1-en-US", "N2-en-US", "N3-en-US"); + } + private void AssertDocuments(IPublishedContent[] documents, params string[] names) { Assert.AreEqual(names.Length, documents.Length); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index fa879b7879..84edb9113c 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -261,10 +261,22 @@ namespace Umbraco.Web.PublishedCache.NuCache if (culture == null) culture = _variationContextAccessor?.VariationContext?.Culture ?? ""; + // _snapshot.GetAtRoot() returns all ContentNode at root // both .Draft and .Published cannot be null at the same time // root is already sorted by sortOrder, and does not contain nulls - var atRoot = _snapshot.GetAtRoot().Select(n => GetNodePublishedContent(n, preview)); - return culture == "*" ? atRoot : atRoot.Where(x => x.IsInvariantOrHasCulture(culture)); + // + // GetNodePublishedContent may return null if !preview and there is no + // published model, so we need to filter these nulls out + + var atRoot = _snapshot.GetAtRoot() + .Select(n => GetNodePublishedContent(n, preview)) + .WhereNotNull(); + + // if a culture is specified, we must ensure that it is avail/published + if (culture != "*") + atRoot = atRoot.Where(x => x.IsInvariantOrHasCulture(culture)); + + return atRoot; } private static IPublishedContent GetNodePublishedContent(ContentNode node, bool preview) From 5ebd90aba539064edd7049686369a44e654127b0 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Fri, 14 Jun 2019 11:05:55 +0200 Subject: [PATCH 71/75] Make searched media pickable in linkpicker --- .../mediapicker/mediapicker.controller.js | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 3a0deb812e..fdce8c135a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -350,20 +350,32 @@ angular.module("umbraco") mediaItem.thumbnail = mediaHelper.resolveFileFromEntity(mediaItem, true); mediaItem.image = mediaHelper.resolveFileFromEntity(mediaItem, false); // set properties to match a media object - if (mediaItem.metaData && - mediaItem.metaData.umbracoWidth && - mediaItem.metaData.umbracoHeight) { - - mediaItem.properties = [ - { - alias: "umbracoWidth", - value: mediaItem.metaData.umbracoWidth.Value - }, - { - alias: "umbracoHeight", - value: mediaItem.metaData.umbracoHeight.Value - } - ]; + if (mediaItem.metaData) { + mediaItem.properties = []; + if (mediaItem.metaData.umbracoWidth && mediaItem.metaData.umbracoHeight) { + mediaItem.properties.push( + { + alias: "umbracoWidth", + editor: mediaItem.metaData.umbracoWidth.PropertyEditorAlias, + value: mediaItem.metaData.umbracoWidth.Value + }, + { + alias: "umbracoHeight", + editor: mediaItem.metaData.umbracoHeight.PropertyEditorAlias, + value: mediaItem.metaData.umbracoHeight.Value + } + ); + } + if (mediaItem.metaData.umbracoFile) { + // this is required for resolving files through the mediahelper + mediaItem.properties.push( + { + alias: "umbracoFile", + editor: mediaItem.metaData.umbracoFile.PropertyEditorAlias, + value: mediaItem.metaData.umbracoFile.Value + } + ); + } } } From a8b5d69be046fd89a7a705f812d0dbca2d683e2d Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 19 Jun 2019 11:57:07 +0200 Subject: [PATCH 72/75] Fix confirmation for permissions dialog --- .../content/content.rights.controller.js | 33 ++++++++++++++----- .../src/views/content/rights.html | 18 ---------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.rights.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.rights.controller.js index a8f87ce2c9..60a8694eca 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.rights.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.rights.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function ContentRightsController($scope, $timeout, contentResource, localizationService, angularHelper, navigationService) { + function ContentRightsController($scope, $timeout, contentResource, localizationService, angularHelper, navigationService, overlayService) { var vm = this; var currentForm; @@ -11,7 +11,6 @@ vm.removedUserGroups = []; vm.viewState = "manageGroups"; vm.labels = {}; - vm.showNotification = false; vm.setViewSate = setViewSate; vm.editPermissions = editPermissions; @@ -20,7 +19,6 @@ vm.removePermissions = removePermissions; vm.cancelManagePermissions = cancelManagePermissions; vm.closeDialog = closeDialog; - vm.stay = stay; vm.discardChanges = discardChanges; function onInit() { @@ -164,14 +162,31 @@ }); } - function stay() { - vm.showNotification = false; - } - function closeDialog() { // check if form has been changed. If it has show discard changes notification if (currentForm && currentForm.$dirty) { - vm.showNotification = true; + localizationService.localizeMany(["prompt_unsavedChanges", "prompt_unsavedChangesWarning", "prompt_discardChanges", "prompt_stay"]).then( + function(values) { + var overlay = { + "view": "default", + "title": values[0], + "content": values[1], + "disableBackdropClick": true, + "disableEscKey": true, + "submitButtonLabel": values[2], + "closeButtonLabel": values[3], + submit: function () { + overlayService.close(); + navigationService.hideDialog(); + }, + close: function () { + overlayService.close(); + } + }; + + overlayService.open(overlay); + } + ); } else { navigationService.hideDialog(); } @@ -187,4 +202,4 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RightsController", ContentRightsController); -})(); \ No newline at end of file +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/rights.html b/src/Umbraco.Web.UI.Client/src/views/content/rights.html index 292db6f105..f430aad342 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/rights.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/rights.html @@ -62,24 +62,6 @@ -
    -
    -

    -

    - - - - -
    -
    - From 4d90322842e41529f78e2b76fc379e1b1c745c1f Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 27 Jun 2019 12:55:37 +0200 Subject: [PATCH 73/75] Fix broken on-outside-click --- .../common/directives/components/events/events.directive.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js index 3541a1cf68..15e74bbd90 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js @@ -136,7 +136,8 @@ angular.module('umbraco.directives') return; } - angularHelper.safeApply(scope, attrs.onOutsideClick); + // please to not use angularHelper.safeApply here, it won't work + scope.$apply(attrs.onOutsideClick); } From 405e9a79be0dddd8622610790110263d8fbd15ca Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 27 Jun 2019 13:07:38 +0100 Subject: [PATCH 74/75] V8: Make the content tree keyboard accessible (#5729) --- .../src/less/accessibility/sr-only.less | 24 +++++++++++ src/Umbraco.Web.UI.Client/src/less/belle.less | 4 +- .../components/application/umb-app-a11y.less | 10 ----- .../src/less/components/tree/umb-actions.less | 2 + .../less/components/tree/umb-tree-item.less | 20 +++++++++- .../src/less/components/tree/umb-tree.less | 40 +++++++++++-------- .../src/less/modals.less | 4 ++ .../application/umb-contextmenu.html | 8 +++- .../views/components/tree/umb-tree-item.html | 10 +++-- .../src/views/components/tree/umb-tree.html | 8 ++-- .../src/views/content/create.html | 35 ++++++++++------ 11 files changed, 113 insertions(+), 52 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/accessibility/sr-only.less delete mode 100644 src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-a11y.less diff --git a/src/Umbraco.Web.UI.Client/src/less/accessibility/sr-only.less b/src/Umbraco.Web.UI.Client/src/less/accessibility/sr-only.less new file mode 100644 index 0000000000..21ca5e6718 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/accessibility/sr-only.less @@ -0,0 +1,24 @@ + +// sr-only - based on the boot strap naming conventions used to remove an element from the view, whilst retaining accessibily for screen readers. More info available at https://getbootstrap.com/docs/4.0/utilities/screenreaders/ +// -------------------------------------------------- +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0,0,0,0); + border: 0; + + &--focusable:active, + &--focusable:focus, + &--hoverable:hover { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 0b5e10379f..bd1cdd5b4f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -80,8 +80,10 @@ @import "forms/umb-validation-label.less"; +// Umbraco Accessibility +@import "accessibility/sr-only.less"; + // Umbraco Components -@import "components/application/umb-app-a11y.less"; @import "components/application/umb-app-header.less"; @import "components/application/umb-app-content.less"; @import "components/application/umb-tour.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-a11y.less b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-a11y.less deleted file mode 100644 index 7e127c5db1..0000000000 --- a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-a11y.less +++ /dev/null @@ -1,10 +0,0 @@ -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0,0,0,0); - border: 0; -} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-actions.less b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-actions.less index 14544ded10..d6e792de73 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-actions.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-actions.less @@ -27,6 +27,7 @@ } .umb-action-link { + position: relative; white-space: nowrap; font-size: 15px; color: @black; @@ -34,6 +35,7 @@ text-decoration: none; cursor: pointer; display: flex; + width: 100%; align-items: center; body.touch & { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less index b372d41eb4..8f0b55f9ed 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less @@ -8,12 +8,28 @@ user-select: none; } - &:hover ins { + &:hover .umb-tree-item__arrow { visibility: visible; cursor: pointer } } +.umb-tree-item__arrow { + position: relative; + margin-left: -16px; + width: 16px; + height: 16px; + visibility: hidden; + text-decoration: none; + font-size: 12px; + line-height: 12px; + transition: color 120ms; + + &:hover { + color: @ui-option-type-hover; + } +} + .umb-tree-item > .umb-tree-item__inner { &:hover .umb-tree-item__label { @@ -95,7 +111,7 @@ a, .umb-tree-icon, - ins { + .umb-tree-item__arrow { color: @ui-active-type !important; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less index 202c9400da..5c54232200 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less @@ -15,20 +15,6 @@ text-decoration: none; } - ins { - margin: -4px 0 0 -16px; - width: 16px; - height: 16px; - visibility: hidden; - text-decoration: none; - font-size: 12px; - transition: color 120ms; - - &:hover { - color: @ui-option-type-hover; - } - } - i.noSpr { display: inline-block; margin-top: 1px; @@ -62,7 +48,7 @@ } body.touch .umb-tree { - ins { + .umb-tree-item__arrow { font-size: 14px; visibility: visible; padding: 7px; @@ -104,7 +90,13 @@ body.touch .umb-tree { } > .umb-options { - visibility: visible; + position: relative; + width: auto; + height: auto; + margin: 0 10px 0 auto; + padding: 9px 5px; + overflow: visible; + clip: auto; } .umb-tree-icon { @@ -178,7 +170,7 @@ body.touch .umb-tree { } .umb-options { - visibility: hidden; + position: relative; display: flex; flex: 0 0 auto; justify-content: flex-end; @@ -192,6 +184,20 @@ body.touch .umb-tree { background: @btnBackgroundHighlight; } + // NOTE - We're having to repeat ourselves here due to an .sr-only class appearing in umbraco/lib/font-awesome/css/font-awesome.min.css + &.sr-only--hoverable:hover, + &.sr-only--focusable:focus { + position: relative; + display: flex; + flex: 0 0 auto; + justify-content: flex-end; + padding: 9px 5px; + text-align: center; + margin: 0 10px 0 auto; + cursor: pointer; + border-radius: 3px; + } + i { height: 5px !important; width: 5px !important; diff --git a/src/Umbraco.Web.UI.Client/src/less/modals.less b/src/Umbraco.Web.UI.Client/src/less/modals.less index 52a573d8c4..84de751b12 100644 --- a/src/Umbraco.Web.UI.Client/src/less/modals.less +++ b/src/Umbraco.Web.UI.Client/src/less/modals.less @@ -113,6 +113,10 @@ bottom: 0px; padding: 20px; margin: 0; + + .btn.umb-outline { + position: relative + } } /*we will always make sure to wrap iframe dialogs in proper padding*/ diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-contextmenu.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-contextmenu.html index 92da12c423..a6d42d923b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-contextmenu.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-contextmenu.html @@ -6,10 +6,14 @@
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-item.html b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-item.html index e8d9839d45..d777b3db78 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-item.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-item.html @@ -1,16 +1,20 @@
  • -   + ng-click="load(node)">  + + Expand child items for {{node.name}} + {{node.name}} - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree.html b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree.html index c2559ef31a..0141ff264f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree.html @@ -8,9 +8,9 @@ {{tree.name}} - + - + - + From 17f73b88dc0c057054703ab15e9d84013021ee1b Mon Sep 17 00:00:00 2001 From: Marc Goodson Date: Thu, 27 Jun 2019 13:26:36 +0100 Subject: [PATCH 75/75] Urgent: Add Giphy embed provider support (#5731) --- src/Umbraco.Web/Media/EmbedProviders/Giphy.cs | 32 +++++++++++++++++++ src/Umbraco.Web/Runtime/WebInitialComposer.cs | 4 ++- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Web/Media/EmbedProviders/Giphy.cs diff --git a/src/Umbraco.Web/Media/EmbedProviders/Giphy.cs b/src/Umbraco.Web/Media/EmbedProviders/Giphy.cs new file mode 100644 index 0000000000..1069de749c --- /dev/null +++ b/src/Umbraco.Web/Media/EmbedProviders/Giphy.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Web.Media.EmbedProviders +{ + /// + /// Embed Provider for Giphy.com the popular online GIFs and animated sticker provider. + /// + public class Giphy : EmbedProviderBase + { + public override string ApiEndpoint => "https://giphy.com/services/oembed?url="; + + public override string[] UrlSchemeRegex => new string[] + { + @"giphy\.com/*", + @"gph\.is/*" + }; + + public override Dictionary RequestParams => new Dictionary(); + + public override string GetMarkup(string url, int maxWidth = 0, int maxHeight = 0) + { + var requestUrl = base.GetEmbedProviderUrl(url, maxWidth, maxHeight); + var oembed = base.GetJsonResponse(requestUrl); + + return oembed.GetHtml(); + } + } +} diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index a37f9c3588..e2b6313ca6 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -264,7 +264,9 @@ namespace Umbraco.Web.Runtime .Append() .Append() .Append() - .Append(); + .Append() + .Append(); + // replace with web implementation composition.RegisterUnique(); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 8d1b106ba4..e604ddba6f 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -149,6 +149,7 @@ +