diff --git a/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs b/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs index e2e896f30d..a3cb1d229a 100644 --- a/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs +++ b/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs @@ -67,7 +67,7 @@ namespace Umbraco.Web.Editors.Binders //map the property dto collection with the culture of the current variant variant.PropertyCollectionDto = Mapper.Map( model.PersistedContent, - options => options.Items[ResolutionContextExtensions.CultureKey] = variant.Culture); + options => options.SetCulture(variant.Culture)); } //now map all of the saved values to the dto diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index b08d8826eb..a0ba648ddd 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -494,15 +494,14 @@ namespace Umbraco.Web.Editors Mapper.Map>(content, opts => { - opts.Items[ResolutionContextExtensions.CultureKey] = cultureName; + + opts.SetCulture(cultureName); // if there's a list of property aliases to map - we will make sure to store this in the mapping context. - if (string.IsNullOrWhiteSpace(includeProperties) == false) - { - opts.Items["IncludeProperties"] = includeProperties.Split(new[] { ", ", "," }, StringSplitOptions.RemoveEmptyEntries); - } - })); - + if (!includeProperties.IsNullOrWhiteSpace()) + opts.SetIncludedProperties(includeProperties.Split(new[] { ", ", "," }, StringSplitOptions.RemoveEmptyEntries)); + })) + .ToList(); // evaluate now return pagedResult; } diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs index 5742b7b17c..a627eab184 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs @@ -39,7 +39,7 @@ namespace Umbraco.Web.Models.Mapping { //We need to set the culture in the mapping context since this is needed to ensure that the correct property values //are resolved during the mapping - context.Items[ResolutionContextExtensions.CultureKey] = x.IsoCode; + context.Options.SetCulture(x.IsoCode); return context.Mapper.Map(source, null, context); }).ToList(); diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index 1da5bbbb44..d4d570f6af 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -96,31 +96,20 @@ namespace Umbraco.Web.Models.Mapping { public DateTime Resolve(IContent source, ContentItemBasic destination, DateTime destMember, ResolutionContext context) { - var culture = context.GetCulture(); + // invariant = global date + if (!source.ContentType.VariesByCulture()) return source.UpdateDate; - //a culture needs to be in the context for a variant content item - if (culture == null || source.ContentType.VariesByCulture() == false) - return source.UpdateDate; + // variant = depends on culture + var culture = context.Options.GetCulture(); - var pubDate = source.GetPublishDate(culture); - return pubDate.HasValue ? pubDate.Value : source.UpdateDate; - } - } + // if there's no culture here, the issue is somewhere else (UI, whatever) - throw! + if (culture == null) + throw new InvalidOperationException("Missing culture in mapping options."); - /// - /// Resolves the published flag for a content item/content variant - /// - private class PublishedResolver : IValueResolver, bool> - { - public bool Resolve(IContent source, ContentItemBasic destination, bool destMember, ResolutionContext context) - { - var culture = context.GetCulture(); - - //a culture needs to be in the context for a variant content item - if (culture == null || source.ContentType.VariesByCulture() == false) - return source.Published; - - return source.IsCulturePublished(culture); + // if we don't have a date for a culture, it means the culture is not available, and + // hey we should probably not be mapping it, but it's too late, return a fallback date + var date = source.GetCultureDate(culture); + return date ?? source.UpdateDate; } } @@ -131,25 +120,20 @@ namespace Umbraco.Web.Models.Mapping { public string Resolve(IContent source, ContentItemBasic destination, string destMember, ResolutionContext context) { - var culture = context.GetCulture(); + // invariant = only 1 name + if (!source.ContentType.VariesByCulture()) return source.Name; - //a culture needs to be in the context for a variant content item - if (culture == null || source.ContentType.VariesByCulture() == false) - return source.Name; + // variant = depends on culture + var culture = context.Options.GetCulture(); - if (source.CultureNames.TryGetValue(culture, out var name) && !string.IsNullOrWhiteSpace(name)) - { - return name; - } - else - { - return $"({ source.Name })"; - } + // if there's no culture here, the issue is somewhere else (UI, whatever) - throw! + if (culture == null) + throw new InvalidOperationException("Missing culture in mapping options."); + + // if we don't have a name for a culture, it means the culture is not available, and + // hey we should probably not be mapping it, but it's too late, return a fallback name + return source.CultureNames.TryGetValue(culture, out var name) && !name.IsNullOrWhiteSpace() ? name : $"(({source.Name}))"; } } - - } - - - + } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs index d9506897c8..006954fcb2 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs @@ -57,17 +57,12 @@ namespace Umbraco.Web.Models.Mapping // if there's a set of property aliases specified, we will check if the current property's value should be mapped. // if it isn't one of the ones specified in 'includeProperties', we will just return the result without mapping the Value. - if (context.Options.Items.ContainsKey("IncludeProperties")) - { - if (context.Options.Items["IncludeProperties"] is IEnumerable includeProperties - && includeProperties.Contains(property.Alias) == false) - { - return result; - } - } + var includedProperties = context.Options.GetIncludedProperties(); + if (includedProperties != null && !includedProperties.Contains(property.Alias)) + return result; //Get the culture from the context which will be set during the mapping operation for each property - var culture = context.GetCulture(); + var culture = context.Options.GetCulture(); //a culture needs to be in the context for a property type that can vary if (culture == null && property.PropertyType.VariesByCulture()) diff --git a/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs index b2b4408110..4f721999de 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentSavedStateResolver.cs @@ -37,7 +37,7 @@ namespace Umbraco.Web.Models.Mapping if (source.ContentType.VariesByCulture()) { //Get the culture from the context which will be set during the mapping operation for each variant - var culture = context.GetCulture(); + var culture = context.Options.GetCulture(); //a culture needs to be in the context for a variant content item if (culture == null) diff --git a/src/Umbraco.Web/Models/Mapping/MappingOperationOptionsExtensions.cs b/src/Umbraco.Web/Models/Mapping/MappingOperationOptionsExtensions.cs new file mode 100644 index 0000000000..e704272ddb --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/MappingOperationOptionsExtensions.cs @@ -0,0 +1,45 @@ +using AutoMapper; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// Provides extension methods for AutoMapper's . + /// + internal static class MappingOperationOptionsExtensions + { + private const string CultureKey = "MappingOperationOptions.Culture"; + private const string IncludedPropertiesKey = "MappingOperationOptions.IncludeProperties"; + + /// + /// Gets the context culture. + /// + public static string GetCulture(this IMappingOperationOptions options) + { + return options.Items.TryGetValue(CultureKey, out var obj) && obj is string s ? s : null; + } + + /// + /// Sets a context culture. + /// + public static void SetCulture(this IMappingOperationOptions options, string culture) + { + options.Items[CultureKey] = culture; + } + + /// + /// Get included properties. + /// + public static string[] GetIncludedProperties(this IMappingOperationOptions options) + { + return options.Items.TryGetValue(IncludedPropertiesKey, out var obj) && obj is string[] s ? s : null; + } + + /// + /// Sets included properties. + /// + public static void SetIncludedProperties(this IMappingOperationOptions options, string[] properties) + { + options.Items[IncludedPropertiesKey] = properties; + } + } +} diff --git a/src/Umbraco.Web/Models/Mapping/ResolutionContextExtensions.cs b/src/Umbraco.Web/Models/Mapping/ResolutionContextExtensions.cs deleted file mode 100644 index 27f00ce91c..0000000000 --- a/src/Umbraco.Web/Models/Mapping/ResolutionContextExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using AutoMapper; - -namespace Umbraco.Web.Models.Mapping -{ - /// - /// Extension methods for AutoMapper's - /// - internal static class ResolutionContextExtensions - { - public const string CultureKey = "ContextMapper.Culture"; - - /// - /// Returns the language Id in the mapping context if one is found - /// - /// - /// - public static string GetCulture(this ResolutionContext resolutionContext) - { - if (!resolutionContext.Options.Items.TryGetValue(CultureKey, out var obj)) return null; - - if (obj is string s) - return s; - - return null; - } - } -} - diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 771d4d7e9f..9eaaa2245e 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -142,6 +142,7 @@ + @@ -302,7 +303,6 @@ -