diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs index c5c22484ad..2b70a63035 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs @@ -53,11 +53,21 @@ namespace Umbraco.Web.Models.ContentEditing [ReadOnly(true)] public string Culture { get; set; } + /// + /// The segment of the property + /// + /// + /// The segment value of a property can always be null but can only have a non-null value + /// when the property can be varied by segment. + /// + [DataMember(Name = "segment")] + [ReadOnly(true)] + public string Segment { get; set; } + /// /// Used internally during model mapping /// [IgnoreDataMember] internal IDataEditor PropertyEditor { get; set; } - } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicMapper.cs index 36c1b360b2..aacafd2f2a 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicMapper.cs @@ -70,8 +70,14 @@ namespace Umbraco.Web.Models.Mapping dest.Culture = culture; + var segment = context.GetSegment(); + + // The current segment can always be null, even if the propertyType *can* be varied by segment + // so the nullcheck like with culture is not performed here. + dest.Segment = segment; + // if no 'IncludeProperties' were specified or this property is set to be included - we will map the value and return. - dest.Value = editor.GetValueEditor().ToEditor(property, DataTypeService, culture); + dest.Value = editor.GetValueEditor().ToEditor(property, DataTypeService, culture, segment); } } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentVariantMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentVariantMapper.cs index c279ae2c70..848eaa0594 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentVariantMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentVariantMapper.cs @@ -21,27 +21,35 @@ namespace Umbraco.Web.Models.Mapping public IEnumerable Map(IContent source, MapperContext context) { - var result = new List(); - if (!source.ContentType.VariesByCulture()) + var variants = new List(); + + var variesByCulture = source.ContentType.VariesByCulture(); + var variesBySegment = source.ContentType.VariesBySegment(); + + if (!variesByCulture && !variesBySegment) { //this is invariant so just map the IContent instance to ContentVariationDisplay - result.Add(context.Map(source)); + variants.Add(context.Map(source)); + return variants; } - else + + if (variesByCulture && !variesBySegment) { + // Culture only + var allLanguages = _localizationService.GetAllLanguages().OrderBy(x => x.Id).ToList(); if (allLanguages.Count == 0) return Enumerable.Empty(); //this should never happen var langs = context.MapEnumerable(allLanguages).ToList(); //create a variant for each language, then we'll populate the values - var variants = langs.Select(x => + variants.AddRange(langs.Select(x => { //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.SetCulture(x.IsoCode); return context.Map(source); - }).ToList(); + })); for (int i = 0; i < langs.Count; i++) { @@ -63,10 +71,49 @@ namespace Umbraco.Web.Models.Mapping //Insert the default language as the first item variants.Insert(0, defaultLang); - - return variants; } - return result; + else if (variesBySegment && !variesByCulture) + { + // Segment only + throw new NotSupportedException("ContentVariantMapper not implemented for segment only!"); + } + else + { + // Culture and segment + + var allLanguages = _localizationService.GetAllLanguages().OrderBy(x => x.Id).ToList(); + if (allLanguages.Count == 0) return Enumerable.Empty(); //this should never happen + + var langs = context.MapEnumerable(allLanguages).ToList(); + + // All segments, including the unsegmented (= NULL) segment. + // TODO: The NULl segment might have to be changed to be empty string? + var segments = source.Properties + .SelectMany(p => p.Values.Select(v => v.Segment)) + .Distinct(); + + // Add all variants + foreach (var language in langs) + { + foreach (var segment in segments) + { + context.SetCulture(language.IsoCode); + context.SetSegment(segment); + + var variantDisplay = context.Map(source); + + variantDisplay.Language = language; + variantDisplay.Segment = segment; + variantDisplay.Name = source.GetCultureName(language.IsoCode); + + variants.Add(variantDisplay); + } + } + + // TODO: Sorting + } + + return variants; } } } diff --git a/src/Umbraco.Web/Models/Mapping/MapperContextExtensions.cs b/src/Umbraco.Web/Models/Mapping/MapperContextExtensions.cs index 1538f1a987..20a387c679 100644 --- a/src/Umbraco.Web/Models/Mapping/MapperContextExtensions.cs +++ b/src/Umbraco.Web/Models/Mapping/MapperContextExtensions.cs @@ -8,6 +8,7 @@ namespace Umbraco.Web.Models.Mapping internal static class MapperContextExtensions { private const string CultureKey = "Map.Culture"; + private const string SegmentKey = "Map.Segment"; private const string IncludedPropertiesKey = "Map.IncludedProperties"; /// @@ -18,6 +19,14 @@ namespace Umbraco.Web.Models.Mapping return context.HasItems && context.Items.TryGetValue(CultureKey, out var obj) && obj is string s ? s : null; } + /// + /// Gets the context segment. + /// + public static string GetSegment(this MapperContext context) + { + return context.HasItems && context.Items.TryGetValue(SegmentKey, out var obj) && obj is string s ? s : null; + } + /// /// Sets a context culture. /// @@ -26,6 +35,14 @@ namespace Umbraco.Web.Models.Mapping context.Items[CultureKey] = culture; } + /// + /// Sets a context segment. + /// + public static void SetSegment(this MapperContext context, string segment) + { + context.Items[SegmentKey] = segment; + } + /// /// Get included properties. /// @@ -42,4 +59,4 @@ namespace Umbraco.Web.Models.Mapping context.Items[IncludedPropertiesKey] = properties; } } -} \ No newline at end of file +}