Merge remote-tracking branch 'origin/v10/dev' into v10/feature/nullable-reference-types-in-Umbraco-Core

# Conflicts:
#	build/build.ps1
#	src/Umbraco.Core/Configuration/ConfigConnectionString.cs
#	src/Umbraco.Core/Configuration/Models/ConnectionStrings.cs
#	src/Umbraco.Core/Install/InstallSteps/TelemetryIdentifierStep.cs
#	src/Umbraco.Core/Models/ContentType.cs
#	src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
#	tests/Umbraco.Tests.AcceptanceTest/package.json
This commit is contained in:
Nikolaj Geisle
2022-03-16 13:00:38 +01:00
480 changed files with 11569 additions and 6721 deletions

View File

@@ -198,7 +198,7 @@ namespace Umbraco.Cms.Core.Models
public bool IsCulturePublished(string culture)
// just check _publishInfos
// a non-available culture could not become published anyways
=> _publishInfos != null && _publishInfos.ContainsKey(culture);
=> !culture.IsNullOrWhiteSpace() && _publishInfos != null && _publishInfos.ContainsKey(culture);
/// <inheritdoc />
public bool IsCultureEdited(string culture)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
@@ -17,6 +17,7 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
{
Notifications = new List<BackOfficeNotification>();
Translations = new List<DictionaryTranslationDisplay>();
ContentApps = new List<ContentApp>();
}
/// <inheritdoc />
@@ -37,5 +38,11 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
/// </summary>
[DataMember(Name = "translations")]
public List<DictionaryTranslationDisplay> Translations { get; private set; }
/// <summary>
/// Apps for the dictionary item
/// </summary>
[DataMember(Name = "apps")]
public List<ContentApp> ContentApps { get; private set; }
}
}

View File

@@ -1,17 +1,34 @@
using System.Runtime.Serialization;
using Umbraco.Cms.Core.Models.Entities;
namespace Umbraco.Cms.Core.Models.ContentEditing
{
[DataContract(Name = "historyCleanup", Namespace = "")]
public class HistoryCleanup
public class HistoryCleanup : BeingDirtyBase
{
private bool _preventCleanup;
private int? _keepAllVersionsNewerThanDays;
private int? _keepLatestVersionPerDayForDays;
[DataMember(Name = "preventCleanup")]
public bool PreventCleanup { get; set; }
public bool PreventCleanup
{
get => _preventCleanup;
set => SetPropertyValueAndDetectChanges(value, ref _preventCleanup, nameof(PreventCleanup));
}
[DataMember(Name = "keepAllVersionsNewerThanDays")]
public int? KeepAllVersionsNewerThanDays { get; set; }
public int? KeepAllVersionsNewerThanDays
{
get => _keepAllVersionsNewerThanDays;
set => SetPropertyValueAndDetectChanges(value, ref _keepAllVersionsNewerThanDays, nameof(KeepAllVersionsNewerThanDays));
}
[DataMember(Name = "keepLatestVersionPerDayForDays")]
public int? KeepLatestVersionPerDayForDays { get; set; }
public int? KeepLatestVersionPerDayForDays
{
get => _keepLatestVersionPerDayForDays;
set => SetPropertyValueAndDetectChanges(value, ref _keepLatestVersionPerDayForDays, nameof(KeepLatestVersionPerDayForDays));
}
}
}

View File

@@ -55,5 +55,11 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
/// </summary>
[DataMember(Name = "notifications")]
public List<BackOfficeNotification> Notifications { get; private set; }
/// <summary>
/// Gets or sets a boolean indicating whether the RelationType should be returned in "Used by"-queries.
/// </summary>
[DataMember(Name = "isDependency", IsRequired = true)]
public bool IsDependency { get; set; }
}
}

View File

@@ -23,5 +23,11 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
/// </summary>
[DataMember(Name = "childObjectType", IsRequired = false)]
public Guid? ChildObjectType { get; set; }
/// <summary>
/// Gets or sets a boolean indicating whether the RelationType should be returned in "Used by"-queries.
/// </summary>
[DataMember(Name = "isDependency", IsRequired = true)]
public bool IsDependency { get; set; }
}
}

View File

@@ -64,8 +64,8 @@ namespace Umbraco.Cms.Core.Models
/// we should not store direct entity
/// </summary>
[IgnoreDataMember]
public ITemplate? DefaultTemplate =>
AllowedTemplates?.FirstOrDefault(x => x != null && x.Id == DefaultTemplateId);
public ITemplate DefaultTemplate =>
AllowedTemplates.FirstOrDefault(x => x != null && x.Id == DefaultTemplateId);
[DataMember]
@@ -82,21 +82,27 @@ namespace Umbraco.Cms.Core.Models
/// we should not store direct entity
/// </summary>
[DataMember]
public IEnumerable<ITemplate>? AllowedTemplates
public IEnumerable<ITemplate> AllowedTemplates
{
get => _allowedTemplates;
set
{
SetPropertyValueAndDetectChanges(value, ref _allowedTemplates, nameof(AllowedTemplates), TemplateComparer);
if (_allowedTemplates?.Any(x => x.Id == _defaultTemplate) == false)
if (_allowedTemplates.Any(x => x.Id == _defaultTemplate) == false)
{
DefaultTemplateId = 0;
}
}
}
public HistoryCleanup? HistoryCleanup { get; set; }
private HistoryCleanup? _historyCleanup;
public HistoryCleanup? HistoryCleanup
{
get => _historyCleanup;
set => SetPropertyValueAndDetectChanges(value, ref _historyCleanup, nameof(HistoryCleanup));
}
/// <summary>
/// Determines if AllowedTemplates contains templateId
@@ -162,5 +168,8 @@ namespace Umbraco.Cms.Core.Models
/// <inheritdoc />
IContentType IContentType.DeepCloneWithResetIdentities(string newAlias) =>
(IContentType)DeepCloneWithResetIdentities(newAlias);
/// <inheritdoc/>
public override bool IsDirty() => base.IsDirty() || HistoryCleanup.IsDirty();
}
}

View File

@@ -4,6 +4,15 @@ using Umbraco.Cms.Core.Models.Entities;
namespace Umbraco.Cms.Core.Models
{
public interface IRelationTypeWithIsDependency : IRelationType
{
/// <summary>
/// Gets or sets a boolean indicating whether the RelationType should be returned in "Used by"-queries.
/// </summary>
[DataMember]
bool IsDependency { get; set; }
}
public interface IRelationType : IEntity, IRememberBeingDirty
{
/// <summary>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Cms.Core.ContentApps;
@@ -48,6 +48,11 @@ namespace Umbraco.Cms.Core.Models.Mapping
}
public IEnumerable<ContentApp> GetContentApps(IUmbracoEntity source)
{
return GetContentAppsForEntity(source);
}
public IEnumerable<ContentApp> GetContentAppsForEntity(IEntity source)
{
var apps = _contentAppDefinitions.GetContentAppsFor(source).ToArray();

View File

@@ -133,7 +133,7 @@ namespace Umbraco.Cms.Core.Models.Mapping
if (target is IContentTypeWithHistoryCleanup targetWithHistoryCleanup)
{
targetWithHistoryCleanup.HistoryCleanup = source.HistoryCleanup;
MapHistoryCleanup(source, targetWithHistoryCleanup);
}
target.AllowedTemplates = source.AllowedTemplates?
@@ -147,6 +147,34 @@ namespace Umbraco.Cms.Core.Models.Mapping
: _fileService.GetTemplate(source.DefaultTemplate));
}
private static void MapHistoryCleanup(DocumentTypeSave source, IContentTypeWithHistoryCleanup target)
{
// If source history cleanup is null we don't have to map all properties
if (source.HistoryCleanup is null)
{
target.HistoryCleanup = null;
return;
}
// We need to reset the dirty properties, because it is otherwise true, just because the json serializer has set properties
target.HistoryCleanup.ResetDirtyProperties(false);
if (target.HistoryCleanup.PreventCleanup != source.HistoryCleanup.PreventCleanup)
{
target.HistoryCleanup.PreventCleanup = source.HistoryCleanup.PreventCleanup;
}
if (target.HistoryCleanup.KeepAllVersionsNewerThanDays != source.HistoryCleanup.KeepAllVersionsNewerThanDays)
{
target.HistoryCleanup.KeepAllVersionsNewerThanDays = source.HistoryCleanup.KeepAllVersionsNewerThanDays;
}
if (target.HistoryCleanup.KeepLatestVersionPerDayForDays !=
source.HistoryCleanup.KeepLatestVersionPerDayForDays)
{
target.HistoryCleanup.KeepLatestVersionPerDayForDays = source.HistoryCleanup.KeepLatestVersionPerDayForDays;
}
}
// no MapAll - take care
private void Map(MediaTypeSave source, IMediaType target, MapperContext context)
{
@@ -196,7 +224,7 @@ namespace Umbraco.Cms.Core.Models.Mapping
target.AllowCultureVariant = source.VariesByCulture();
target.AllowSegmentVariant = source.VariesBySegment();
target.ContentApps = _commonMapper.GetContentApps(source);
target.ContentApps = _commonMapper.GetContentAppsForEntity(source);
//sync templates
if (source.AllowedTemplates is not null)
@@ -331,7 +359,10 @@ namespace Umbraco.Cms.Core.Models.Mapping
if (source.GroupId > 0)
{
target.PropertyGroupId = new Lazy<int>(() => source.GroupId, false);
if (target.PropertyGroupId?.Value != source.GroupId)
{
target.PropertyGroupId = new Lazy<int>(() => source.GroupId, false);
}
}
target.Alias = source.Alias;
@@ -526,7 +557,15 @@ namespace Umbraco.Cms.Core.Models.Mapping
target.Thumbnail = source.Thumbnail;
target.AllowedAsRoot = source.AllowAsRoot;
target.AllowedContentTypes = source.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i));
bool allowedContentTypesUnchanged = target.AllowedContentTypes.Select(x => x.Id.Value)
.SequenceEqual(source.AllowedContentTypes);
if (allowedContentTypesUnchanged is false)
{
target.AllowedContentTypes = source.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i));
}
if (!(target is IMemberType))
{
@@ -577,13 +616,21 @@ namespace Umbraco.Cms.Core.Models.Mapping
// ensure no duplicate alias, then assign the group properties collection
EnsureUniqueAliases(destProperties);
destGroup.PropertyTypes = new PropertyTypeCollection(isPublishing, destProperties);
if (destGroup.PropertyTypes.SupportsPublishing != isPublishing || destGroup.PropertyTypes.SequenceEqual(destProperties) is false)
{
destGroup.PropertyTypes = new PropertyTypeCollection(isPublishing, destProperties);
}
destGroups.Add(destGroup);
}
// ensure no duplicate name, then assign the groups collection
EnsureUniqueAliases(destGroups);
target.PropertyGroups = new PropertyGroupCollection(destGroups);
if (target.PropertyGroups.SequenceEqual(destGroups) is false)
{
target.PropertyGroups = new PropertyGroupCollection(destGroups);
}
// because the property groups collection was rebuilt, there is no need to remove
// the old groups - they are just gone and will be cleared by the repository

View File

@@ -1,6 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Services;
@@ -14,12 +15,20 @@ namespace Umbraco.Cms.Core.Models.Mapping
public class DictionaryMapDefinition : IMapDefinition
{
private readonly ILocalizationService _localizationService;
private readonly CommonMapper _commonMapper;
[Obsolete("Use the constructor with the CommonMapper")]
public DictionaryMapDefinition(ILocalizationService localizationService)
{
_localizationService = localizationService;
}
public DictionaryMapDefinition(ILocalizationService localizationService, CommonMapper commonMapper)
{
_localizationService = localizationService;
_commonMapper = commonMapper;
}
public void DefineMaps(IUmbracoMapper mapper)
{
mapper.Define<IDictionaryItem, EntityBasic>((source, context) => new EntityBasic(), Map);
@@ -44,6 +53,10 @@ namespace Umbraco.Cms.Core.Models.Mapping
target.Name = source.ItemKey;
target.ParentId = source.ParentId ?? Guid.Empty;
target.Udi = Udi.Create(Constants.UdiEntityType.DictionaryItem, source.Key);
if (_commonMapper != null)
{
target.ContentApps.AddRange(_commonMapper.GetContentAppsForEntity(source));
}
// build up the path to make it possible to set active item in tree
// TODO: check if there is a better way

View File

@@ -30,6 +30,11 @@ namespace Umbraco.Cms.Core.Models.Mapping
target.ChildObjectType = source.ChildObjectType;
target.Id = source.Id;
target.IsBidirectional = source.IsBidirectional;
if (source is IRelationTypeWithIsDependency sourceWithIsDependency)
{
target.IsDependency = sourceWithIsDependency.IsDependency;
}
target.Key = source.Key;
target.Name = source.Name;
target.Alias = source.Alias;
@@ -74,6 +79,11 @@ namespace Umbraco.Cms.Core.Models.Mapping
target.ChildObjectType = source.ChildObjectType;
target.Id = source.Id.TryConvertTo<int>().Result;
target.IsBidirectional = source.IsBidirectional;
if (target is IRelationTypeWithIsDependency targetWithIsDependency)
{
targetWithIsDependency.IsDependency = source.IsDependency;
}
target.Key = source.Key;
target.Name = source.Name;
target.ParentObjectType = source.ParentObjectType;

View File

@@ -0,0 +1,44 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Cms.Core.Models.Entities;
namespace Umbraco.Cms.Core.Models
{
[DataContract(Name = "relationItem", Namespace = "")]
public class RelationItem
{
[DataMember(Name = "id")]
public int NodeId { get; set; }
[DataMember(Name = "key")]
public Guid NodeKey { get; set; }
[DataMember(Name = "name")]
public string NodeName { get; set; }
[DataMember(Name = "type")]
public string NodeType { get; set; }
[DataMember(Name = "udi")]
public Udi NodeUdi => Udi.Create(NodeType, NodeKey);
[DataMember(Name = "icon")]
public string ContentTypeIcon { get; set; }
[DataMember(Name = "alias")]
public string ContentTypeAlias { get; set; }
[DataMember(Name = "contentTypeName")]
public string ContentTypeName { get; set; }
[DataMember(Name = "relationTypeName")]
public string RelationTypeName { get; set; }
[DataMember(Name = "relationTypeIsBidirectional")]
public bool RelationTypeIsBidirectional { get; set; }
[DataMember(Name = "relationTypeIsDependency")]
public bool RelationTypeIsDependency { get; set; }
}
}

View File

@@ -9,20 +9,28 @@ namespace Umbraco.Cms.Core.Models
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
public class RelationType : EntityBase, IRelationType
public class RelationType : EntityBase, IRelationType, IRelationTypeWithIsDependency
{
private string _name;
private string _alias;
private bool _isBidirectional;
private bool _isDependency;
private Guid? _parentObjectType;
private Guid? _childObjectType;
public RelationType(string alias, string name)
: this(name: name, alias: alias, false, null, null)
: this(name: name, alias: alias, false, null, null, false)
{
}
[Obsolete("Use ctor with isDependency parameter")]
public RelationType(string name, string alias, bool isBidrectional, Guid? parentObjectType, Guid? childObjectType)
:this(name,alias,isBidrectional, parentObjectType, childObjectType, false)
{
}
public RelationType(string name, string alias, bool isBidrectional, Guid? parentObjectType, Guid? childObjectType, bool isDependency)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name));
@@ -32,6 +40,7 @@ namespace Umbraco.Cms.Core.Models
_name = name;
_alias = alias;
_isBidirectional = isBidrectional;
_isDependency = isDependency;
_parentObjectType = parentObjectType;
_childObjectType = childObjectType;
}
@@ -88,5 +97,11 @@ namespace Umbraco.Cms.Core.Models
set => SetPropertyValueAndDetectChanges(value, ref _childObjectType, nameof(ChildObjectType));
}
public bool IsDependency
{
get => _isDependency;
set => SetPropertyValueAndDetectChanges(value, ref _isDependency, nameof(IsDependency));
}
}
}