Merge remote-tracking branch 'upstream' into v8/bugfix/enum-extensions

This commit is contained in:
Ronald Barendse
2020-01-22 11:04:11 +01:00
338 changed files with 9522 additions and 4936 deletions

View File

@@ -16,20 +16,32 @@ namespace Umbraco.Web.Models
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "alias")]
public string Alias { get; set; }
[DataMember(Name = "group")]
public string Group { get; set; }
[DataMember(Name = "groupOrder")]
public int GroupOrder { get; set; }
[DataMember(Name = "hidden")]
public bool Hidden { get; set; }
[DataMember(Name = "allowDisable")]
public bool AllowDisable { get; set; }
[DataMember(Name = "requiredSections")]
public List<string> RequiredSections { get; set; }
[DataMember(Name = "steps")]
public BackOfficeTourStep[] Steps { get; set; }
[DataMember(Name = "culture")]
public string Culture { get; set; }
[DataMember(Name = "contentType")]
public string ContentType { get; set; }
}
}

View File

@@ -53,11 +53,21 @@ namespace Umbraco.Web.Models.ContentEditing
[ReadOnly(true)]
public string Culture { get; set; }
/// <summary>
/// The segment of the property
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
[DataMember(Name = "segment")]
[ReadOnly(true)]
public string Segment { get; set; }
/// <summary>
/// Used internally during model mapping
/// </summary>
[IgnoreDataMember]
internal IDataEditor PropertyEditor { get; set; }
}
}

View File

@@ -29,6 +29,12 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "culture")]
public string Culture { get; set; }
/// <summary>
/// The segment of this variant, if this is invariant than this is null or empty
/// </summary>
[DataMember(Name = "segment")]
public string Segment { get; set; }
/// <summary>
/// Indicates if the variant should be updated
/// </summary>

View File

@@ -5,6 +5,7 @@ namespace Umbraco.Web.Models.ContentEditing
[DataContract(Name = "contentType", Namespace = "")]
public class MediaTypeDisplay : ContentTypeCompositionDisplay<PropertyTypeDisplay>
{
[DataMember(Name = "isSystemMediaType")]
public bool IsSystemMediaType { get; set; }
}
}

View File

@@ -24,7 +24,7 @@ namespace Umbraco.Web.Models.ContentEditing
/// </summary>
/// <remarks>Corresponds to the NodeObjectType in the umbracoNode table</remarks>
[DataMember(Name = "parentObjectType", IsRequired = true)]
public Guid ParentObjectType { get; set; }
public Guid? ParentObjectType { get; set; }
/// <summary>
/// Gets or sets the Parent's object type name.
@@ -38,7 +38,7 @@ namespace Umbraco.Web.Models.ContentEditing
/// </summary>
/// <remarks>Corresponds to the NodeObjectType in the umbracoNode table</remarks>
[DataMember(Name = "childObjectType", IsRequired = true)]
public Guid ChildObjectType { get; set; }
public Guid? ChildObjectType { get; set; }
/// <summary>
/// Gets or sets the Child's object type name.
@@ -47,13 +47,6 @@ namespace Umbraco.Web.Models.ContentEditing
[ReadOnly(true)]
public string ChildObjectTypeName { get; set; }
/// <summary>
/// Gets or sets the relations associated with this relation type.
/// </summary>
[DataMember(Name = "relations")]
[ReadOnly(true)]
public IEnumerable<RelationDisplay> Relations { get; set; }
/// <summary>
/// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes.
/// </summary>

View File

@@ -16,12 +16,12 @@ namespace Umbraco.Web.Models.ContentEditing
/// Gets or sets the parent object type ID.
/// </summary>
[DataMember(Name = "parentObjectType", IsRequired = false)]
public Guid ParentObjectType { get; set; }
public Guid? ParentObjectType { get; set; }
/// <summary>
/// Gets or sets the child object type ID.
/// </summary>
[DataMember(Name = "childObjectType", IsRequired = false)]
public Guid ChildObjectType { get; set; }
public Guid? ChildObjectType { get; set; }
}
}

View File

@@ -70,8 +70,13 @@ namespace Umbraco.Web.Models.Mapping
dest.Culture = culture;
// Get the segment, which is always allowed to be null even if the propertyType *can* be varied by segment.
// There is therefore no need to perform the null check like with culture above.
var segment = !property.PropertyType.VariesBySegment() ? null : context.GetSegment();
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);
}
}
}

View File

@@ -145,6 +145,7 @@ namespace Umbraco.Web.Models.Mapping
//default listview
target.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Media";
target.IsSystemMediaType = source.IsSystemMediaType();
if (string.IsNullOrEmpty(source.Name)) return;
@@ -488,7 +489,7 @@ namespace Umbraco.Web.Models.Mapping
target.Udi = MapContentTypeUdi(source);
target.UpdateDate = source.UpdateDate;
target.AllowedContentTypes = source.AllowedContentTypes.Select(x => x.Id.Value);
target.AllowedContentTypes = source.AllowedContentTypes.OrderBy(c => c.SortOrder).Select(x => x.Id.Value);
target.CompositeContentTypes = source.ContentTypeComposition.Select(x => x.Alias);
target.LockedCompositeContentTypes = MapLockedCompositions(source);
}

View File

@@ -21,52 +21,117 @@ namespace Umbraco.Web.Models.Mapping
public IEnumerable<ContentVariantDisplay> Map(IContent source, MapperContext context)
{
var result = new List<ContentVariantDisplay>();
if (!source.ContentType.VariesByCulture())
var variesByCulture = source.ContentType.VariesByCulture();
var variesBySegment = source.ContentType.VariesBySegment();
IList<ContentVariantDisplay> variants = new List<ContentVariantDisplay>();
if (!variesByCulture && !variesBySegment)
{
//this is invariant so just map the IContent instance to ContentVariationDisplay
result.Add(context.Map<ContentVariantDisplay>(source));
// this is invariant so just map the IContent instance to ContentVariationDisplay
var variantDisplay = context.Map<ContentVariantDisplay>(source);
variants.Add(variantDisplay);
}
else if (variesByCulture && !variesBySegment)
{
var languages = GetLanguages(context);
variants = languages
.Select(language => CreateVariantDisplay(context, source, language, null))
.ToList();
}
else if (variesBySegment && !variesByCulture)
{
// Segment only
var segments = GetSegments(source);
variants = segments
.Select(segment => CreateVariantDisplay(context, source, null, segment))
.ToList();
}
else
{
var allLanguages = _localizationService.GetAllLanguages().OrderBy(x => x.Id).ToList();
if (allLanguages.Count == 0) return Enumerable.Empty<ContentVariantDisplay>(); //this should never happen
// Culture and segment
var languages = GetLanguages(context).ToList();
var segments = GetSegments(source).ToList();
var langs = context.MapEnumerable<ILanguage, Language>(allLanguages).ToList();
//create a variant for each language, then we'll populate the values
var variants = langs.Select(x =>
if (languages.Count == 0 || segments.Count == 0)
{
//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<ContentVariantDisplay>(source);
}).ToList();
for (int i = 0; i < langs.Count; i++)
{
var x = langs[i];
var variant = variants[i];
variant.Language = x;
variant.Name = source.GetCultureName(x.IsoCode);
// This should not happen
throw new InvalidOperationException("No languages or segments available");
}
//Put the default language first in the list & then sort rest by a-z
var defaultLang = variants.SingleOrDefault(x => x.Language.IsDefault);
variants = languages
.SelectMany(language => segments
.Select(segment => CreateVariantDisplay(context, source, language, segment)))
.ToList();
}
//Remove the default language from the list for now
variants.Remove(defaultLang);
//Sort the remaining languages a-z
variants = variants.OrderBy(x => x.Language.Name).ToList();
//Insert the default language as the first item
variants.Insert(0, defaultLang);
return SortVariants(variants);
}
private IList<ContentVariantDisplay> SortVariants(IList<ContentVariantDisplay> variants)
{
if (variants == null || variants.Count <= 1)
{
return variants;
}
return result;
// Default variant first, then order by language, segment.
return variants
.OrderBy(v => IsDefaultLanguage(v) ? 0 : 1)
.ThenBy(v => IsDefaultSegment(v) ? 0 : 1)
.ThenBy(v => v?.Language?.Name)
.ThenBy(v => v.Segment)
.ToList();
}
private static bool IsDefaultSegment(ContentVariantDisplay variant)
{
return variant.Segment == null;
}
private static bool IsDefaultLanguage(ContentVariantDisplay variant)
{
return variant.Language == null || variant.Language.IsDefault;
}
private IEnumerable<Language> GetLanguages(MapperContext context)
{
var allLanguages = _localizationService.GetAllLanguages().OrderBy(x => x.Id).ToList();
if (allLanguages.Count == 0)
{
// This should never happen
return Enumerable.Empty<Language>();
}
else
{
return context.MapEnumerable<ILanguage, Language>(allLanguages).ToList();
}
}
/// <summary>
/// Returns all segments assigned to the content
/// </summary>
/// <param name="content"></param>
/// <returns>
/// Returns all segments assigned to the content including 'null' values
/// </returns>
private IEnumerable<string> GetSegments(IContent content)
{
return content.Properties.SelectMany(p => p.Values.Select(v => v.Segment)).Distinct();
}
private ContentVariantDisplay CreateVariantDisplay(MapperContext context, IContent content, Language language, string segment)
{
context.SetCulture(language?.IsoCode);
context.SetSegment(segment);
var variantDisplay = context.Map<ContentVariantDisplay>(content);
variantDisplay.Segment = segment;
variantDisplay.Language = language;
variantDisplay.Name = content.GetCultureName(language?.IsoCode);
return variantDisplay;
}
}
}

View File

@@ -234,11 +234,11 @@ namespace Umbraco.Web.Models.Mapping
{
switch (entity)
{
case ContentEntitySlim contentEntity:
// NOTE: this case covers both content and media entities
return contentEntity.ContentTypeIcon;
case MemberEntitySlim memberEntity:
case IMemberEntitySlim memberEntity:
return memberEntity.ContentTypeIcon.IfNullOrWhiteSpace(Constants.Icons.Member);
case IContentEntitySlim contentEntity:
// NOTE: this case covers both content and media entities
return contentEntity.ContentTypeIcon;
}
return null;

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Logging;
@@ -23,6 +24,7 @@ namespace Umbraco.Web.Models.Mapping
public void DefineMaps(UmbracoMapper mapper)
{
mapper.Define<IMacro, EntityBasic>((source, context) => new EntityBasic(), Map);
mapper.Define<IMacro, MacroDisplay>((source, context) => new MacroDisplay(), Map);
mapper.Define<IMacro, IEnumerable<MacroParameter>>((source, context) => context.MapEnumerable<IMacroProperty, MacroParameter>(source.Properties.Values));
mapper.Define<IMacroProperty, MacroParameter>((source, context) => new MacroParameter(), Map);
}
@@ -40,6 +42,23 @@ namespace Umbraco.Web.Models.Mapping
target.Udi = Udi.Create(Constants.UdiEntityType.Macro, source.Key);
}
private void Map(IMacro source, MacroDisplay target, MapperContext context)
{
target.Alias = source.Alias;
target.Icon = Constants.Icons.Macro;
target.Id = source.Id;
target.Key = source.Key;
target.Name = source.Name;
target.ParentId = -1;
target.Path = "-1," + source.Id;
target.Udi = Udi.Create(Constants.UdiEntityType.Macro, source.Key);
target.CacheByPage = source.CacheByPage;
target.CacheByUser = source.CacheByMember;
target.CachePeriod = source.CacheDuration;
target.UseInEditor = source.UseInEditor;
target.RenderInEditor = !source.DontRender;
target.View = source.MacroSource;
}
// Umbraco.Code.MapAll -Value
private void Map(IMacroProperty source, MacroParameter target, MapperContext context)
{

View File

@@ -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";
/// <summary>
@@ -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;
}
/// <summary>
/// Gets the context segment.
/// </summary>
public static string GetSegment(this MapperContext context)
{
return context.HasItems && context.Items.TryGetValue(SegmentKey, out var obj) && obj is string s ? s : null;
}
/// <summary>
/// Sets a context culture.
/// </summary>
@@ -26,6 +35,14 @@ namespace Umbraco.Web.Models.Mapping
context.Items[CultureKey] = culture;
}
/// <summary>
/// Sets a context segment.
/// </summary>
public static void SetSegment(this MapperContext context, string segment)
{
context.Items[SegmentKey] = segment;
}
/// <summary>
/// Get included properties.
/// </summary>
@@ -42,4 +59,4 @@ namespace Umbraco.Web.Models.Mapping
context.Items[IncludedPropertiesKey] = properties;
}
}
}
}

View File

@@ -1,12 +1,23 @@
using Umbraco.Core;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Mapping;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Models.Mapping
{
internal class RelationMapDefinition : IMapDefinition
{
private readonly IEntityService _entityService;
private readonly IRelationService _relationService;
public RelationMapDefinition(IEntityService entityService, IRelationService relationService)
{
_entityService = entityService;
_relationService = relationService;
}
public void DefineMaps(UmbracoMapper mapper)
{
mapper.Define<IRelationType, RelationTypeDisplay>((source, context) => new RelationTypeDisplay(), Map);
@@ -15,8 +26,8 @@ namespace Umbraco.Web.Models.Mapping
}
// Umbraco.Code.MapAll -Icon -Trashed -AdditionalData
// Umbraco.Code.MapAll -Relations -ParentId -Notifications
private static void Map(IRelationType source, RelationTypeDisplay target, MapperContext context)
// Umbraco.Code.MapAll -ParentId -Notifications
private void Map(IRelationType source, RelationTypeDisplay target, MapperContext context)
{
target.ChildObjectType = source.ChildObjectType;
target.Id = source.Id;
@@ -28,18 +39,32 @@ namespace Umbraco.Web.Models.Mapping
target.Udi = Udi.Create(Constants.UdiEntityType.RelationType, source.Key);
target.Path = "-1," + source.Id;
// Set the "friendly" names for the parent and child object types
target.ParentObjectTypeName = ObjectTypes.GetUmbracoObjectType(source.ParentObjectType).GetFriendlyName();
target.ChildObjectTypeName = ObjectTypes.GetUmbracoObjectType(source.ChildObjectType).GetFriendlyName();
// Set the "friendly" and entity names for the parent and child object types
if (source.ParentObjectType.HasValue)
{
var objType = ObjectTypes.GetUmbracoObjectType(source.ParentObjectType.Value);
target.ParentObjectTypeName = objType.GetFriendlyName();
}
if (source.ChildObjectType.HasValue)
{
var objType = ObjectTypes.GetUmbracoObjectType(source.ChildObjectType.Value);
target.ChildObjectTypeName = objType.GetFriendlyName();
}
}
// Umbraco.Code.MapAll -ParentName -ChildName
private static void Map(IRelation source, RelationDisplay target, MapperContext context)
private void Map(IRelation source, RelationDisplay target, MapperContext context)
{
target.ChildId = source.ChildId;
target.Comment = source.Comment;
target.CreateDate = source.CreateDate;
target.ParentId = source.ParentId;
var entities = _relationService.GetEntitiesFromRelation(source);
target.ParentName = entities.Item1.Name;
target.ChildName = entities.Item2.Name;
}
// Umbraco.Code.MapAll -CreateDate -UpdateDate -DeleteDate

View File

@@ -380,8 +380,8 @@ namespace Umbraco.Web.Models.Mapping
.ToDictionary(x => x.Key, x => (IEnumerable<Permission>)x.ToArray());
}
private static string MapContentTypeIcon(EntitySlim entity)
=> entity is ContentEntitySlim contentEntity ? contentEntity.ContentTypeIcon : null;
private static string MapContentTypeIcon(IEntitySlim entity)
=> entity is IContentEntitySlim contentEntity ? contentEntity.ContentTypeIcon : null;
private IEnumerable<EntityBasic> GetStartNodes(int[] startNodeIds, UmbracoObjectTypes objectType, string localizedKey, MapperContext context)
{