Merge remote-tracking branch 'origin/v8/feature/version-history-cleanup-ui' into v9/dev

# Conflicts:
#	src/Umbraco.Core/Composing/CompositionExtensions/Repositories.cs
#	src/Umbraco.Core/Composing/CompositionExtensions/Services.cs
#	src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
#	src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs
#	src/Umbraco.Core/ContentEditing/HistoryCleanup.cs
#	src/Umbraco.Core/Models/ContentEditing/DocumentTypeDisplay.cs
#	src/Umbraco.Core/Models/ContentType.cs
#	src/Umbraco.Core/Models/Mapping/ContentTypeMapDefinition.cs
#	src/Umbraco.Core/Persistence/Repositories/DocumentVersionRepository.cs
#	src/Umbraco.Core/Scheduling/ContentVersionCleanup.cs
#	src/Umbraco.Core/Services/DefaultContentVersionCleanupPolicy.cs
#	src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs
#	src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
#	src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs
#	src/Umbraco.Infrastructure/Services/Implement/ContentService.cs
#	src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config
#	src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs
#	src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs
#	src/Umbraco.Tests/Umbraco.Tests.csproj
#	src/Umbraco.Web.UI.Client/package-lock.json
#	src/Umbraco.Web.UI/config/umbracoSettings.Release.config
#	src/Umbraco.Web.UI/umbraco/config/lang/en.xml
#	src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
#	src/Umbraco.Web/Scheduling/SchedulerComponent.cs
#	src/Umbraco.Web/Umbraco.Web.csproj
#	tests/Umbraco.Tests.Common/Extensions/AutoMoqDataAttribute.cs
#	tests/Umbraco.Tests.UnitTests/Umbraco.Core/Persistence/Repositories/DocumentVersionRepository_Tests_Integration.cs
#	tests/Umbraco.Tests.UnitTests/Umbraco.Core/Scheduling/ContentVersionCleanup_Tests_UnitTests.cs
#	tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/ContentVersionCleanupService_Tests_Integration.cs
#	tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/ContentVersionCleanupService_Tests_UnitTests.cs
#	tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DefaultContentVersionCleanupPolicy_Tests_UnitTests.cs
This commit is contained in:
Bjarke Berg
2021-10-29 15:00:27 +02:00
44 changed files with 3354 additions and 1063 deletions

View File

@@ -29,6 +29,9 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
[DataMember(Name = "allowedContentTypes")]
public IEnumerable<int> AllowedContentTypes { get; set; }
[DataMember(Name = "historyCleanup")]
public HistoryCleanupViewModel HistoryCleanup { get; set; }
/// <summary>
/// Custom validation
/// </summary>

View File

@@ -6,11 +6,9 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
[DataContract(Name = "contentType", Namespace = "")]
public class DocumentTypeDisplay : ContentTypeCompositionDisplay<PropertyTypeDisplay>
{
public DocumentTypeDisplay()
{
public DocumentTypeDisplay() =>
//initialize collections so at least their never null
AllowedTemplates = new List<EntityBasic>();
}
//name, alias, icon, thumb, desc, inherited from the content type
@@ -18,8 +16,7 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
[DataMember(Name = "allowedTemplates")]
public IEnumerable<EntityBasic> AllowedTemplates { get; set; }
[DataMember(Name = "defaultTemplate")]
public EntityBasic DefaultTemplate { get; set; }
[DataMember(Name = "defaultTemplate")] public EntityBasic DefaultTemplate { get; set; }
[DataMember(Name = "allowCultureVariant")]
public bool AllowCultureVariant { get; set; }
@@ -27,7 +24,8 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
[DataMember(Name = "allowSegmentVariant")]
public bool AllowSegmentVariant { get; set; }
[DataMember(Name = "apps")]
public IEnumerable<ContentApp> ContentApps { get; set; }
[DataMember(Name = "apps")] public IEnumerable<ContentApp> ContentApps { get; set; }
[DataMember(Name = "historyCleanup")] public HistoryCleanupViewModel HistoryCleanup { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
using System.Runtime.Serialization;
namespace Umbraco.Cms.Core.Models.ContentEditing
{
[DataContract(Name = "historyCleanup", Namespace = "")]
public class HistoryCleanup
{
[DataMember(Name = "preventCleanup")] public bool PreventCleanup { get; set; }
[DataMember(Name = "keepAllVersionsNewerThanDays")]
public int? KeepAllVersionsNewerThanDays { get; set; }
[DataMember(Name = "keepLatestVersionPerDayForDays")]
public int? KeepLatestVersionPerDayForDays { get; set; }
}
}

View File

@@ -0,0 +1,17 @@
using System.Runtime.Serialization;
namespace Umbraco.Cms.Core.Models.ContentEditing
{
[DataContract(Name = "historyCleanup", Namespace = "")]
public class HistoryCleanupViewModel : HistoryCleanup
{
[DataMember(Name = "globalKeepAllVersionsNewerThanDays")]
public int? GlobalKeepAllVersionsNewerThanDays { get;set; }
[DataMember(Name = "globalKeepLatestVersionPerDayForDays")]
public int? GlobalKeepLatestVersionPerDayForDays { get; set;}
[DataMember(Name = "globalEnableCleanup")]
public bool GlobalEnableCleanup { get; set; }
}
}

View File

@@ -2,13 +2,14 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Strings;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Models
{
/// <summary>
/// Represents the content type that a <see cref="Content"/> object is based on
/// Represents the content type that a <see cref="Content" /> object is based on
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
@@ -16,54 +17,50 @@ namespace Umbraco.Cms.Core.Models
{
public const bool SupportsPublishingConst = true;
private int _defaultTemplate;
//Custom comparer for enumerable
private static readonly DelegateEqualityComparer<IEnumerable<ITemplate>> TemplateComparer = new(
(templates, enumerable) => templates.UnsortedSequenceEqual(enumerable),
templates => templates.GetHashCode());
private IEnumerable<ITemplate> _allowedTemplates;
private int _defaultTemplate;
/// <summary>
/// Constuctor for creating a ContentType with the parent's id.
/// Constuctor for creating a ContentType with the parent's id.
/// </summary>
/// <remarks>Only use this for creating ContentTypes at the root (with ParentId -1).</remarks>
/// <param name="parentId"></param>
public ContentType(IShortStringHelper shortStringHelper, int parentId) : base(shortStringHelper, parentId)
{
public ContentType(IShortStringHelper shortStringHelper, int parentId) : base(shortStringHelper, parentId) =>
_allowedTemplates = new List<ITemplate>();
}
/// <summary>
/// Constuctor for creating a ContentType with the parent as an inherited type.
/// Constuctor for creating a ContentType with the parent as an inherited type.
/// </summary>
/// <remarks>Use this to ensure inheritance from parent.</remarks>
/// <param name="parent"></param>
/// <param name="alias"></param>
public ContentType(IShortStringHelper shortStringHelper, IContentType parent, string alias)
: base(shortStringHelper, parent, alias)
{
: base(shortStringHelper, parent, alias) =>
_allowedTemplates = new List<ITemplate>();
}
/// <inheritdoc />
public override ISimpleContentType ToSimple() => new SimpleContentType(this);
/// <inheritdoc />
public override bool SupportsPublishing => SupportsPublishingConst;
//Custom comparer for enumerable
private static readonly DelegateEqualityComparer<IEnumerable<ITemplate>> TemplateComparer = new DelegateEqualityComparer<IEnumerable<ITemplate>>(
(templates, enumerable) => templates.UnsortedSequenceEqual(enumerable),
templates => templates.GetHashCode());
/// <inheritdoc />
public override ISimpleContentType ToSimple() => new SimpleContentType(this);
/// <summary>
/// Gets or sets the alias of the default Template.
/// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
/// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
/// Gets or sets the alias of the default Template.
/// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
/// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
/// we should not store direct entity
/// </summary>
[IgnoreDataMember]
public ITemplate DefaultTemplate
{
get { return AllowedTemplates.FirstOrDefault(x => x != null && x.Id == DefaultTemplateId); }
}
public ITemplate DefaultTemplate =>
AllowedTemplates.FirstOrDefault(x => x != null && x.Id == DefaultTemplateId);
[DataMember]
@@ -74,9 +71,9 @@ namespace Umbraco.Cms.Core.Models
}
/// <summary>
/// Gets or Sets a list of Templates which are allowed for the ContentType
/// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
/// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
/// Gets or Sets a list of Templates which are allowed for the ContentType
/// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
/// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
/// we should not store direct entity
/// </summary>
[DataMember]
@@ -85,41 +82,42 @@ namespace Umbraco.Cms.Core.Models
get => _allowedTemplates;
set
{
SetPropertyValueAndDetectChanges(value, ref _allowedTemplates, nameof(AllowedTemplates), TemplateComparer);
SetPropertyValueAndDetectChanges(value, ref _allowedTemplates, nameof(AllowedTemplates),
TemplateComparer);
if (_allowedTemplates.Any(x => x.Id == _defaultTemplate) == false)
{
DefaultTemplateId = 0;
}
}
}
public HistoryCleanup HistoryCleanup { get; set; }
/// <summary>
/// Determines if AllowedTemplates contains templateId
/// Determines if AllowedTemplates contains templateId
/// </summary>
/// <param name="templateId">The template id to check</param>
/// <returns>True if AllowedTemplates contains the templateId else False</returns>
public bool IsAllowedTemplate(int templateId)
{
return AllowedTemplates == null
public bool IsAllowedTemplate(int templateId) =>
AllowedTemplates == null
? false
: AllowedTemplates.Any(t => t.Id == templateId);
}
/// <summary>
/// Determines if AllowedTemplates contains templateId
/// Determines if AllowedTemplates contains templateId
/// </summary>
/// <param name="templateAlias">The template alias to check</param>
/// <returns>True if AllowedTemplates contains the templateAlias else False</returns>
public bool IsAllowedTemplate(string templateAlias)
{
return AllowedTemplates == null
public bool IsAllowedTemplate(string templateAlias) =>
AllowedTemplates == null
? false
: AllowedTemplates.Any(t => t.Alias.Equals(templateAlias, StringComparison.InvariantCultureIgnoreCase));
}
/// <summary>
/// Sets the default template for the ContentType
/// Sets the default template for the ContentType
/// </summary>
/// <param name="template">Default <see cref="ITemplate"/></param>
/// <param name="template">Default <see cref="ITemplate" /></param>
public void SetDefaultTemplate(ITemplate template)
{
if (template == null)
@@ -138,17 +136,19 @@ namespace Umbraco.Cms.Core.Models
}
/// <summary>
/// Removes a template from the list of allowed templates
/// Removes a template from the list of allowed templates
/// </summary>
/// <param name="template"><see cref="ITemplate"/> to remove</param>
/// <param name="template"><see cref="ITemplate" /> to remove</param>
/// <returns>True if template was removed, otherwise False</returns>
public bool RemoveTemplate(ITemplate template)
{
if (DefaultTemplateId == template.Id)
DefaultTemplateId = default(int);
{
DefaultTemplateId = default;
}
var templates = AllowedTemplates.ToList();
var remove = templates.FirstOrDefault(x => x.Id == template.Id);
ITemplate remove = templates.FirstOrDefault(x => x.Id == template.Id);
var result = templates.Remove(remove);
AllowedTemplates = templates;
@@ -156,6 +156,7 @@ namespace Umbraco.Cms.Core.Models
}
/// <inheritdoc />
IContentType IContentType.DeepCloneWithResetIdentities(string newAlias) => (IContentType)DeepCloneWithResetIdentities(newAlias);
IContentType IContentType.DeepCloneWithResetIdentities(string newAlias) =>
(IContentType)DeepCloneWithResetIdentities(newAlias);
}
}

View File

@@ -0,0 +1,13 @@
using System;
namespace Umbraco.Core.Models
{
public class ContentVersionCleanupPolicySettings
{
public int ContentTypeId { get; set; }
public int? KeepAllVersionsNewerThanDays { get; set; }
public int? KeepLatestVersionPerDayForDays { get; set; }
public bool PreventCleanup { get; set; }
public DateTime Updated { get; set; }
}
}

View File

@@ -0,0 +1,24 @@
using System;
namespace Umbraco.Cms.Core.Models
{
public class HistoricContentVersionMeta
{
public int ContentId { get; }
public int ContentTypeId { get; }
public int VersionId { get; }
public DateTime VersionDate { get; }
public HistoricContentVersionMeta() { }
public HistoricContentVersionMeta(int contentId, int contentTypeId, int versionId, DateTime versionDate)
{
ContentId = contentId;
ContentTypeId = contentTypeId;
VersionId = versionId;
VersionDate = versionDate;
}
public override string ToString() => $"HistoricContentVersionMeta(versionId: {VersionId}, versionDate: {VersionDate:s}";
}
}

View File

@@ -1,56 +1,62 @@
using System.Collections.Generic;
using Umbraco.Cms.Core.Models.ContentEditing;
namespace Umbraco.Cms.Core.Models
{
/// <summary>
/// Defines a ContentType, which Content is based on
/// Defines a ContentType, which Content is based on
/// </summary>
public interface IContentType : IContentTypeComposition
{
/// <summary>
/// Internal property to store the Id of the default template
/// Internal property to store the Id of the default template
/// </summary>
int DefaultTemplateId { get; set; }
/// <summary>
/// Gets the default Template of the ContentType
/// Gets the default Template of the ContentType
/// </summary>
ITemplate DefaultTemplate { get; }
/// <summary>
/// Gets or Sets a list of Templates which are allowed for the ContentType
/// Gets or Sets a list of Templates which are allowed for the ContentType
/// </summary>
IEnumerable<ITemplate> AllowedTemplates { get; set; }
/// <summary>
/// Determines if AllowedTemplates contains templateId
/// Gets or Sets the history cleanup configuration
/// </summary>
HistoryCleanup HistoryCleanup { get; set; }
/// <summary>
/// Determines if AllowedTemplates contains templateId
/// </summary>
/// <param name="templateId">The template id to check</param>
/// <returns>True if AllowedTemplates contains the templateId else False</returns>
bool IsAllowedTemplate(int templateId);
/// <summary>
/// Determines if AllowedTemplates contains templateId
/// Determines if AllowedTemplates contains templateId
/// </summary>
/// <param name="templateAlias">The template alias to check</param>
/// <returns>True if AllowedTemplates contains the templateAlias else False</returns>
bool IsAllowedTemplate(string templateAlias);
/// <summary>
/// Sets the default template for the ContentType
/// Sets the default template for the ContentType
/// </summary>
/// <param name="template">Default <see cref="ITemplate"/></param>
/// <param name="template">Default <see cref="ITemplate" /></param>
void SetDefaultTemplate(ITemplate template);
/// <summary>
/// Removes a template from the list of allowed templates
/// Removes a template from the list of allowed templates
/// </summary>
/// <param name="template"><see cref="ITemplate"/> to remove</param>
/// <param name="template"><see cref="ITemplate" /> to remove</param>
/// <returns>True if template was removed, otherwise False</returns>
bool RemoveTemplate(ITemplate template);
/// <summary>
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
/// </summary>
/// <param name="newAlias"></param>
/// <returns></returns>

View File

@@ -2,9 +2,9 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Exceptions;
using Umbraco.Cms.Core.Hosting;
@@ -13,31 +13,49 @@ using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
using Umbraco.Cms.Web.Common.DependencyInjection;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Models.Mapping
{
/// <summary>
/// Defines mappings for content/media/members type mappings
/// Defines mappings for content/media/members type mappings
/// </summary>
public class ContentTypeMapDefinition : IMapDefinition
{
private readonly CommonMapper _commonMapper;
private readonly PropertyEditorCollection _propertyEditors;
private readonly IContentTypeService _contentTypeService;
private readonly IDataTypeService _dataTypeService;
private readonly IFileService _fileService;
private readonly IContentTypeService _contentTypeService;
private readonly IMediaTypeService _mediaTypeService;
private readonly IMemberTypeService _memberTypeService;
private readonly ILoggerFactory _loggerFactory;
private readonly ILogger<ContentTypeMapDefinition> _logger;
private readonly IShortStringHelper _shortStringHelper;
private readonly GlobalSettings _globalSettings;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly ILogger<ContentTypeMapDefinition> _logger;
private readonly ILoggerFactory _loggerFactory;
private readonly IMediaTypeService _mediaTypeService;
private readonly IMemberTypeService _memberTypeService;
private readonly PropertyEditorCollection _propertyEditors;
private readonly IShortStringHelper _shortStringHelper;
private ContentSettings _contentSettings;
public ContentTypeMapDefinition(CommonMapper commonMapper, PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, IFileService fileService,
IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService,
ILoggerFactory loggerFactory, IShortStringHelper shortStringHelper, IOptions<GlobalSettings> globalSettings, IHostingEnvironment hostingEnvironment)
[Obsolete("Use ctor with all params injected")]
public ContentTypeMapDefinition(CommonMapper commonMapper, PropertyEditorCollection propertyEditors,
IDataTypeService dataTypeService, IFileService fileService,
IContentTypeService contentTypeService, IMediaTypeService mediaTypeService,
IMemberTypeService memberTypeService,
ILoggerFactory loggerFactory, IShortStringHelper shortStringHelper, IOptions<GlobalSettings> globalSettings,
IHostingEnvironment hostingEnvironment)
: this(commonMapper, propertyEditors, dataTypeService, fileService, contentTypeService, mediaTypeService, memberTypeService, loggerFactory, shortStringHelper, globalSettings, hostingEnvironment, StaticServiceProvider.Instance.GetRequiredService<IOptionsMonitor<ContentSettings>>())
{
}
public ContentTypeMapDefinition(CommonMapper commonMapper, PropertyEditorCollection propertyEditors,
IDataTypeService dataTypeService, IFileService fileService,
IContentTypeService contentTypeService, IMediaTypeService mediaTypeService,
IMemberTypeService memberTypeService,
ILoggerFactory loggerFactory, IShortStringHelper shortStringHelper, IOptions<GlobalSettings> globalSettings,
IHostingEnvironment hostingEnvironment, IOptionsMonitor<ContentSettings> contentSettings)
{
_commonMapper = commonMapper;
_propertyEditors = propertyEditors;
@@ -51,13 +69,19 @@ namespace Umbraco.Cms.Core.Models.Mapping
_shortStringHelper = shortStringHelper;
_globalSettings = globalSettings.Value;
_hostingEnvironment = hostingEnvironment;
_contentSettings = contentSettings.CurrentValue;
contentSettings.OnChange(x => _contentSettings = x);
}
public void DefineMaps(IUmbracoMapper mapper)
{
mapper.Define<DocumentTypeSave, IContentType>((source, context) => new ContentType(_shortStringHelper, source.ParentId), Map);
mapper.Define<MediaTypeSave, IMediaType>((source, context) => new MediaType(_shortStringHelper, source.ParentId), Map);
mapper.Define<MemberTypeSave, IMemberType>((source, context) => new MemberType(_shortStringHelper, source.ParentId), Map);
mapper.Define<DocumentTypeSave, IContentType>(
(source, context) => new ContentType(_shortStringHelper, source.ParentId), Map);
mapper.Define<MediaTypeSave, IMediaType>(
(source, context) => new MediaType(_shortStringHelper, source.ParentId), Map);
mapper.Define<MemberTypeSave, IMemberType>(
(source, context) => new MemberType(_shortStringHelper, source.ParentId), Map);
mapper.Define<IContentType, DocumentTypeDisplay>((source, context) => new DocumentTypeDisplay(), Map);
mapper.Define<IMediaType, MediaTypeDisplay>((source, context) => new MediaTypeDisplay(), Map);
@@ -66,14 +90,20 @@ namespace Umbraco.Cms.Core.Models.Mapping
mapper.Define<PropertyTypeBasic, IPropertyType>(
(source, context) =>
{
var dataType = _dataTypeService.GetDataType(source.DataTypeId);
if (dataType == null) throw new NullReferenceException("No data type found with id " + source.DataTypeId);
IDataType dataType = _dataTypeService.GetDataType(source.DataTypeId);
if (dataType == null)
{
throw new NullReferenceException("No data type found with id " + source.DataTypeId);
}
return new PropertyType(_shortStringHelper, dataType, source.Alias);
}, Map);
// TODO: isPublishing in ctor?
mapper.Define<PropertyGroupBasic<PropertyTypeBasic>, PropertyGroup>((source, context) => new PropertyGroup(false), Map);
mapper.Define<PropertyGroupBasic<MemberPropertyTypeBasic>, PropertyGroup>((source, context) => new PropertyGroup(false), Map);
mapper.Define<PropertyGroupBasic<PropertyTypeBasic>, PropertyGroup>(
(source, context) => new PropertyGroup(false), Map);
mapper.Define<PropertyGroupBasic<MemberPropertyTypeBasic>, PropertyGroup>(
(source, context) => new PropertyGroup(false), Map);
mapper.Define<IContentTypeComposition, ContentTypeBasic>((source, context) => new ContentTypeBasic(), Map);
mapper.Define<IContentType, ContentTypeBasic>((source, context) => new ContentTypeBasic(), Map);
@@ -84,11 +114,14 @@ namespace Umbraco.Cms.Core.Models.Mapping
mapper.Define<MediaTypeSave, MediaTypeDisplay>((source, context) => new MediaTypeDisplay(), Map);
mapper.Define<MemberTypeSave, MemberTypeDisplay>((source, context) => new MemberTypeDisplay(), Map);
mapper.Define<PropertyGroupBasic<PropertyTypeBasic>, PropertyGroupDisplay<PropertyTypeDisplay>>((source, context) => new PropertyGroupDisplay<PropertyTypeDisplay>(), Map);
mapper.Define<PropertyGroupBasic<MemberPropertyTypeBasic>, PropertyGroupDisplay<MemberPropertyTypeDisplay>>((source, context) => new PropertyGroupDisplay<MemberPropertyTypeDisplay>(), Map);
mapper.Define<PropertyGroupBasic<PropertyTypeBasic>, PropertyGroupDisplay<PropertyTypeDisplay>>(
(source, context) => new PropertyGroupDisplay<PropertyTypeDisplay>(), Map);
mapper.Define<PropertyGroupBasic<MemberPropertyTypeBasic>, PropertyGroupDisplay<MemberPropertyTypeDisplay>>(
(source, context) => new PropertyGroupDisplay<MemberPropertyTypeDisplay>(), Map);
mapper.Define<PropertyTypeBasic, PropertyTypeDisplay>((source, context) => new PropertyTypeDisplay(), Map);
mapper.Define<MemberPropertyTypeBasic, MemberPropertyTypeDisplay>((source, context) => new MemberPropertyTypeDisplay(), Map);
mapper.Define<MemberPropertyTypeBasic, MemberPropertyTypeDisplay>(
(source, context) => new MemberPropertyTypeDisplay(), Map);
}
// no MapAll - take care
@@ -97,13 +130,17 @@ namespace Umbraco.Cms.Core.Models.Mapping
MapSaveToTypeBase<DocumentTypeSave, PropertyTypeBasic>(source, target, context);
MapComposition(source, target, alias => _contentTypeService.Get(alias));
target.HistoryCleanup = source.HistoryCleanup;
target.AllowedTemplates = source.AllowedTemplates
.Where(x => x != null)
.Select(_fileService.GetTemplate)
.Where(x => x != null)
.ToArray();
target.SetDefaultTemplate(source.DefaultTemplate == null ? null : _fileService.GetTemplate(source.DefaultTemplate));
target.SetDefaultTemplate(source.DefaultTemplate == null
? null
: _fileService.GetTemplate(source.DefaultTemplate));
}
// no MapAll - take care
@@ -119,11 +156,16 @@ namespace Umbraco.Cms.Core.Models.Mapping
MapSaveToTypeBase<MemberTypeSave, MemberPropertyTypeBasic>(source, target, context);
MapComposition(source, target, alias => _memberTypeService.Get(alias));
foreach (var propertyType in source.Groups.SelectMany(x => x.Properties))
foreach (MemberPropertyTypeBasic propertyType in source.Groups.SelectMany(x => x.Properties))
{
var localCopy = propertyType;
var destProp = target.PropertyTypes.SingleOrDefault(x => x.Alias.InvariantEquals(localCopy.Alias));
if (destProp == null) continue;
MemberPropertyTypeBasic localCopy = propertyType;
IPropertyType destProp =
target.PropertyTypes.SingleOrDefault(x => x.Alias.InvariantEquals(localCopy.Alias));
if (destProp == null)
{
continue;
}
target.SetMemberCanEditProperty(localCopy.Alias, localCopy.MemberCanEditProperty);
target.SetMemberCanViewProperty(localCopy.Alias, localCopy.MemberCanViewProperty);
target.SetIsSensitiveProperty(localCopy.Alias, localCopy.IsSensitiveData);
@@ -135,6 +177,15 @@ namespace Umbraco.Cms.Core.Models.Mapping
{
MapTypeToDisplayBase<DocumentTypeDisplay, PropertyTypeDisplay>(source, target);
target.HistoryCleanup = new HistoryCleanupViewModel
{
PreventCleanup = source.HistoryCleanup.PreventCleanup,
KeepAllVersionsNewerThanDays = source.HistoryCleanup.KeepAllVersionsNewerThanDays,
KeepLatestVersionPerDayForDays = source.HistoryCleanup.KeepLatestVersionPerDayForDays,
GlobalKeepAllVersionsNewerThanDays = _contentSettings.ContentVersionCleanupPolicy.KeepAllVersionsNewerThanDays,
GlobalKeepLatestVersionPerDayForDays = _contentSettings.ContentVersionCleanupPolicy.KeepLatestVersionPerDayForDays
};
target.AllowCultureVariant = source.VariesByCulture();
target.AllowSegmentVariant = source.VariesBySegment();
target.ContentApps = _commonMapper.GetContentApps(source);
@@ -143,16 +194,23 @@ namespace Umbraco.Cms.Core.Models.Mapping
target.AllowedTemplates = context.MapEnumerable<ITemplate, EntityBasic>(source.AllowedTemplates);
if (source.DefaultTemplate != null)
{
target.DefaultTemplate = context.Map<EntityBasic>(source.DefaultTemplate);
}
//default listview
target.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Content";
if (string.IsNullOrEmpty(source.Alias)) return;
if (string.IsNullOrEmpty(source.Alias))
{
return;
}
var name = Constants.Conventions.DataTypes.ListViewPrefix + source.Alias;
if (_dataTypeService.GetDataType(name) != null)
{
target.ListViewEditorName = name;
}
}
// no MapAll - take care
@@ -164,11 +222,16 @@ namespace Umbraco.Cms.Core.Models.Mapping
target.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Media";
target.IsSystemMediaType = source.IsSystemMediaType();
if (string.IsNullOrEmpty(source.Name)) return;
if (string.IsNullOrEmpty(source.Name))
{
return;
}
var name = Constants.Conventions.DataTypes.ListViewPrefix + source.Name;
if (_dataTypeService.GetDataType(name) != null)
{
target.ListViewEditorName = name;
}
}
// no MapAll - take care
@@ -177,11 +240,16 @@ namespace Umbraco.Cms.Core.Models.Mapping
MapTypeToDisplayBase<MemberTypeDisplay, MemberPropertyTypeDisplay>(source, target);
//map the MemberCanEditProperty,MemberCanViewProperty,IsSensitiveData
foreach (var propertyType in source.PropertyTypes)
foreach (IPropertyType propertyType in source.PropertyTypes)
{
var localCopy = propertyType;
var displayProp = target.Groups.SelectMany(dest => dest.Properties).SingleOrDefault(dest => dest.Alias.InvariantEquals(localCopy.Alias));
if (displayProp == null) continue;
IPropertyType localCopy = propertyType;
MemberPropertyTypeDisplay displayProp = target.Groups.SelectMany(dest => dest.Properties)
.SingleOrDefault(dest => dest.Alias.InvariantEquals(localCopy.Alias));
if (displayProp == null)
{
continue;
}
displayProp.MemberCanEditProperty = source.MemberCanEditProperty(localCopy.Alias);
displayProp.MemberCanViewProperty = source.MemberCanViewProperty(localCopy.Alias);
displayProp.IsSensitiveData = source.IsSensitiveProperty(localCopy.Alias);
@@ -216,28 +284,20 @@ namespace Umbraco.Cms.Core.Models.Mapping
}
// no MapAll - uses the IContentTypeBase map method, which has MapAll
private void Map(IContentTypeComposition source, ContentTypeBasic target, MapperContext context)
{
private void Map(IContentTypeComposition source, ContentTypeBasic target, MapperContext context) =>
Map(source, target, Constants.UdiEntityType.MemberType);
}
// no MapAll - uses the IContentTypeBase map method, which has MapAll
private void Map(IContentType source, ContentTypeBasic target, MapperContext context)
{
private void Map(IContentType source, ContentTypeBasic target, MapperContext context) =>
Map(source, target, Constants.UdiEntityType.DocumentType);
}
// no MapAll - uses the IContentTypeBase map method, which has MapAll
private void Map(IMediaType source, ContentTypeBasic target, MapperContext context)
{
private void Map(IMediaType source, ContentTypeBasic target, MapperContext context) =>
Map(source, target, Constants.UdiEntityType.MediaType);
}
// no MapAll - uses the IContentTypeBase map method, which has MapAll
private void Map(IMemberType source, ContentTypeBasic target, MapperContext context)
{
private void Map(IMemberType source, ContentTypeBasic target, MapperContext context) =>
Map(source, target, Constants.UdiEntityType.MemberType);
}
// Umbraco.Code.MapAll -CreateDate -DeleteDate -UpdateDate
// Umbraco.Code.MapAll -SupportsPublishing -Key -PropertyEditorAlias -ValueStorageType -Variations
@@ -254,10 +314,14 @@ namespace Umbraco.Cms.Core.Models.Mapping
target.SetVariesBy(ContentVariation.Segment, source.AllowSegmentVariant);
if (source.Id > 0)
{
target.Id = source.Id;
}
if (source.GroupId > 0)
{
target.PropertyGroupId = new Lazy<int>(() => source.GroupId, false);
}
target.Alias = source.Alias;
target.Description = source.Description;
@@ -268,18 +332,19 @@ namespace Umbraco.Cms.Core.Models.Mapping
// no MapAll - take care
private void Map(DocumentTypeSave source, DocumentTypeDisplay target, MapperContext context)
{
MapTypeToDisplayBase<DocumentTypeSave, PropertyTypeBasic, DocumentTypeDisplay, PropertyTypeDisplay>(source, target, context);
MapTypeToDisplayBase<DocumentTypeSave, PropertyTypeBasic, DocumentTypeDisplay, PropertyTypeDisplay>(source,
target, context);
//sync templates
var destAllowedTemplateAliases = target.AllowedTemplates.Select(x => x.Alias);
IEnumerable<string> destAllowedTemplateAliases = target.AllowedTemplates.Select(x => x.Alias);
//if the dest is set and it's the same as the source, then don't change
if (destAllowedTemplateAliases.SequenceEqual(source.AllowedTemplates) == false)
{
var templates = _fileService.GetTemplates(source.AllowedTemplates.ToArray());
IEnumerable<ITemplate> templates = _fileService.GetTemplates(source.AllowedTemplates.ToArray());
target.AllowedTemplates = source.AllowedTemplates
.Select(x =>
{
var template = templates.SingleOrDefault(t => t.Alias == x);
ITemplate template = templates.SingleOrDefault(t => t.Alias == x);
return template != null
? context.Map<EntityBasic>(template)
: null;
@@ -293,7 +358,7 @@ namespace Umbraco.Cms.Core.Models.Mapping
//if the dest is set and it's the same as the source, then don't change
if (target.DefaultTemplate == null || source.DefaultTemplate != target.DefaultTemplate.Alias)
{
var template = _fileService.GetTemplate(source.DefaultTemplate);
ITemplate template = _fileService.GetTemplate(source.DefaultTemplate);
target.DefaultTemplate = template == null ? null : context.Map<EntityBasic>(template);
}
}
@@ -304,22 +369,24 @@ namespace Umbraco.Cms.Core.Models.Mapping
}
// no MapAll - take care
private void Map(MediaTypeSave source, MediaTypeDisplay target, MapperContext context)
{
MapTypeToDisplayBase<MediaTypeSave, PropertyTypeBasic, MediaTypeDisplay, PropertyTypeDisplay>(source, target, context);
}
private void Map(MediaTypeSave source, MediaTypeDisplay target, MapperContext context) =>
MapTypeToDisplayBase<MediaTypeSave, PropertyTypeBasic, MediaTypeDisplay, PropertyTypeDisplay>(source,
target, context);
// no MapAll - take care
private void Map(MemberTypeSave source, MemberTypeDisplay target, MapperContext context)
{
MapTypeToDisplayBase<MemberTypeSave, MemberPropertyTypeBasic, MemberTypeDisplay, MemberPropertyTypeDisplay>(source, target, context);
}
private void Map(MemberTypeSave source, MemberTypeDisplay target, MapperContext context) =>
MapTypeToDisplayBase<MemberTypeSave, MemberPropertyTypeBasic, MemberTypeDisplay, MemberPropertyTypeDisplay>(
source, target, context);
// Umbraco.Code.MapAll -CreateDate -UpdateDate -DeleteDate -Key -PropertyTypes
private static void Map(PropertyGroupBasic<PropertyTypeBasic> source, PropertyGroup target, MapperContext context)
private static void Map(PropertyGroupBasic<PropertyTypeBasic> source, PropertyGroup target,
MapperContext context)
{
if (source.Id > 0)
{
target.Id = source.Id;
}
target.Key = source.Key;
target.Type = source.Type;
target.Name = source.Name;
@@ -328,10 +395,14 @@ namespace Umbraco.Cms.Core.Models.Mapping
}
// Umbraco.Code.MapAll -CreateDate -UpdateDate -DeleteDate -Key -PropertyTypes
private static void Map(PropertyGroupBasic<MemberPropertyTypeBasic> source, PropertyGroup target, MapperContext context)
private static void Map(PropertyGroupBasic<MemberPropertyTypeBasic> source, PropertyGroup target,
MapperContext context)
{
if (source.Id > 0)
{
target.Id = source.Id;
}
target.Key = source.Key;
target.Type = source.Type;
target.Name = source.Name;
@@ -340,11 +411,15 @@ namespace Umbraco.Cms.Core.Models.Mapping
}
// Umbraco.Code.MapAll -ContentTypeId -ParentTabContentTypes -ParentTabContentTypeNames
private static void Map(PropertyGroupBasic<PropertyTypeBasic> source, PropertyGroupDisplay<PropertyTypeDisplay> target, MapperContext context)
private static void Map(PropertyGroupBasic<PropertyTypeBasic> source,
PropertyGroupDisplay<PropertyTypeDisplay> target, MapperContext context)
{
target.Inherited = source.Inherited;
if (source.Id > 0)
{
target.Id = source.Id;
}
target.Key = source.Key;
target.Type = source.Type;
target.Name = source.Name;
@@ -354,17 +429,22 @@ namespace Umbraco.Cms.Core.Models.Mapping
}
// Umbraco.Code.MapAll -ContentTypeId -ParentTabContentTypes -ParentTabContentTypeNames
private static void Map(PropertyGroupBasic<MemberPropertyTypeBasic> source, PropertyGroupDisplay<MemberPropertyTypeDisplay> target, MapperContext context)
private static void Map(PropertyGroupBasic<MemberPropertyTypeBasic> source,
PropertyGroupDisplay<MemberPropertyTypeDisplay> target, MapperContext context)
{
target.Inherited = source.Inherited;
if (source.Id > 0)
{
target.Id = source.Id;
}
target.Key = source.Key;
target.Type = source.Type;
target.Name = source.Name;
target.Alias = source.Alias;
target.SortOrder = source.SortOrder;
target.Properties = context.MapEnumerable<MemberPropertyTypeBasic, MemberPropertyTypeDisplay>(source.Properties);
target.Properties =
context.MapEnumerable<MemberPropertyTypeBasic, MemberPropertyTypeDisplay>(source.Properties);
}
// Umbraco.Code.MapAll -Editor -View -Config -ContentTypeId -ContentTypeName -Locked -DataTypeIcon -DataTypeName
@@ -409,7 +489,8 @@ namespace Umbraco.Cms.Core.Models.Mapping
// Umbraco.Code.MapAll -CreatorId -Level -SortOrder -Variations
// Umbraco.Code.MapAll -CreateDate -UpdateDate -DeleteDate
// Umbraco.Code.MapAll -ContentTypeComposition (done by AfterMapSaveToType)
private static void MapSaveToTypeBase<TSource, TSourcePropertyType>(TSource source, IContentTypeComposition target, MapperContext context)
private static void MapSaveToTypeBase<TSource, TSourcePropertyType>(TSource source,
IContentTypeComposition target, MapperContext context)
where TSource : ContentTypeSave<TSourcePropertyType>
where TSourcePropertyType : PropertyTypeBasic
{
@@ -418,7 +499,9 @@ namespace Umbraco.Cms.Core.Models.Mapping
var id = Convert.ToInt32(source.Id);
if (id > 0)
{
target.Id = id;
}
target.Alias = source.Alias;
target.Description = source.Description;
@@ -457,18 +540,19 @@ namespace Umbraco.Cms.Core.Models.Mapping
// - managing the content type's PropertyTypes collection (for generic properties)
// handle actual groups (non-generic-properties)
var destOrigGroups = target.PropertyGroups.ToArray(); // local groups
var destOrigProperties = target.PropertyTypes.ToArray(); // all properties, in groups or not
PropertyGroup[] destOrigGroups = target.PropertyGroups.ToArray(); // local groups
IPropertyType[] destOrigProperties = target.PropertyTypes.ToArray(); // all properties, in groups or not
var destGroups = new List<PropertyGroup>();
var sourceGroups = source.Groups.Where(x => x.IsGenericProperties == false).ToArray();
PropertyGroupBasic<TSourcePropertyType>[] sourceGroups =
source.Groups.Where(x => x.IsGenericProperties == false).ToArray();
var sourceGroupParentAliases = sourceGroups.Select(x => x.GetParentAlias()).Distinct().ToArray();
foreach (var sourceGroup in sourceGroups)
foreach (PropertyGroupBasic<TSourcePropertyType> sourceGroup in sourceGroups)
{
// get the dest group
var destGroup = MapSaveGroup(sourceGroup, destOrigGroups, context);
PropertyGroup destGroup = MapSaveGroup(sourceGroup, destOrigGroups, context);
// handle local properties
var destProperties = sourceGroup.Properties
IPropertyType[] destProperties = sourceGroup.Properties
.Where(x => x.Inherited == false)
.Select(x => MapSaveProperty(x, destOrigProperties, context))
.ToArray();
@@ -476,7 +560,9 @@ namespace Umbraco.Cms.Core.Models.Mapping
// if the group has no local properties and is not used as parent, skip it, ie sort-of garbage-collect
// local groups which would not have local properties anymore
if (destProperties.Length == 0 && !sourceGroupParentAliases.Contains(sourceGroup.Alias))
{
continue;
}
// ensure no duplicate alias, then assign the group properties collection
EnsureUniqueAliases(destProperties);
@@ -492,11 +578,12 @@ namespace Umbraco.Cms.Core.Models.Mapping
// the old groups - they are just gone and will be cleared by the repository
// handle non-grouped (ie generic) properties
var genericPropertiesGroup = source.Groups.FirstOrDefault(x => x.IsGenericProperties);
PropertyGroupBasic<TSourcePropertyType> genericPropertiesGroup =
source.Groups.FirstOrDefault(x => x.IsGenericProperties);
if (genericPropertiesGroup != null)
{
// handle local properties
var destProperties = genericPropertiesGroup.Properties
IPropertyType[] destProperties = genericPropertiesGroup.Properties
.Where(x => x.Inherited == false)
.Select(x => MapSaveProperty(x, destOrigProperties, context))
.ToArray();
@@ -547,7 +634,8 @@ namespace Umbraco.Cms.Core.Models.Mapping
{
MapTypeToDisplayBase(source, target);
var groupsMapper = new PropertyTypeGroupMapper<TTargetPropertyType>(_propertyEditors, _dataTypeService, _shortStringHelper, _loggerFactory.CreateLogger<PropertyTypeGroupMapper<TTargetPropertyType>>());
var groupsMapper = new PropertyTypeGroupMapper<TTargetPropertyType>(_propertyEditors, _dataTypeService,
_shortStringHelper, _loggerFactory.CreateLogger<PropertyTypeGroupMapper<TTargetPropertyType>>());
target.Groups = groupsMapper.Map(source);
}
@@ -580,7 +668,8 @@ namespace Umbraco.Cms.Core.Models.Mapping
}
// no MapAll - relies on the non-generic method
private void MapTypeToDisplayBase<TSource, TSourcePropertyType, TTarget, TTargetPropertyType>(TSource source, TTarget target, MapperContext context)
private void MapTypeToDisplayBase<TSource, TSourcePropertyType, TTarget, TTargetPropertyType>(TSource source,
TTarget target, MapperContext context)
where TSource : ContentTypeSave<TSourcePropertyType>
where TSourcePropertyType : PropertyTypeBasic
where TTarget : ContentTypeCompositionDisplay<TTargetPropertyType>
@@ -588,35 +677,49 @@ namespace Umbraco.Cms.Core.Models.Mapping
{
MapTypeToDisplayBase(source, target);
target.Groups = context.MapEnumerable<PropertyGroupBasic<TSourcePropertyType>, PropertyGroupDisplay<TTargetPropertyType>>(source.Groups);
target.Groups =
context
.MapEnumerable<PropertyGroupBasic<TSourcePropertyType>, PropertyGroupDisplay<TTargetPropertyType>>(
source.Groups);
}
private IEnumerable<string> MapLockedCompositions(IContentTypeComposition source)
{
// get ancestor ids from path of parent if not root
if (source.ParentId == Constants.System.Root)
{
return Enumerable.Empty<string>();
}
var parent = _contentTypeService.Get(source.ParentId);
IContentType parent = _contentTypeService.Get(source.ParentId);
if (parent == null)
{
return Enumerable.Empty<string>();
}
var aliases = new List<string>();
var ancestorIds = parent.Path.Split(Constants.CharArrays.Comma).Select(s => int.Parse(s, CultureInfo.InvariantCulture));
IEnumerable<int> ancestorIds = parent.Path.Split(Constants.CharArrays.Comma)
.Select(s => int.Parse(s, CultureInfo.InvariantCulture));
// loop through all content types and return ordered aliases of ancestors
var allContentTypes = _contentTypeService.GetAll().ToArray();
IContentType[] allContentTypes = _contentTypeService.GetAll().ToArray();
foreach (var ancestorId in ancestorIds)
{
var ancestor = allContentTypes.FirstOrDefault(x => x.Id == ancestorId);
IContentType ancestor = allContentTypes.FirstOrDefault(x => x.Id == ancestorId);
if (ancestor != null)
{
aliases.Add(ancestor.Alias);
}
}
return aliases.OrderBy(x => x);
}
public static Udi MapContentTypeUdi(IContentTypeComposition source)
{
if (source == null) return null;
if (source == null)
{
return null;
}
string udiType;
switch (source)
@@ -637,7 +740,8 @@ namespace Umbraco.Cms.Core.Models.Mapping
return Udi.Create(udiType, source.Key);
}
private static PropertyGroup MapSaveGroup<TPropertyType>(PropertyGroupBasic<TPropertyType> sourceGroup, IEnumerable<PropertyGroup> destOrigGroups, MapperContext context)
private static PropertyGroup MapSaveGroup<TPropertyType>(PropertyGroupBasic<TPropertyType> sourceGroup,
IEnumerable<PropertyGroup> destOrigGroups, MapperContext context)
where TPropertyType : PropertyTypeBasic
{
PropertyGroup destGroup;
@@ -663,7 +767,8 @@ namespace Umbraco.Cms.Core.Models.Mapping
return destGroup;
}
private static IPropertyType MapSaveProperty(PropertyTypeBasic sourceProperty, IEnumerable<IPropertyType> destOrigProperties, MapperContext context)
private static IPropertyType MapSaveProperty(PropertyTypeBasic sourceProperty,
IEnumerable<IPropertyType> destOrigProperties, MapperContext context)
{
IPropertyType destProperty;
if (sourceProperty.Id > 0)
@@ -690,43 +795,52 @@ namespace Umbraco.Cms.Core.Models.Mapping
private static void EnsureUniqueAliases(IEnumerable<IPropertyType> properties)
{
var propertiesA = properties.ToArray();
IPropertyType[] propertiesA = properties.ToArray();
var distinctProperties = propertiesA
.Select(x => x.Alias?.ToUpperInvariant())
.Distinct()
.Count();
if (distinctProperties != propertiesA.Length)
{
throw new InvalidOperationException("Cannot map properties due to alias conflict.");
}
}
private static void EnsureUniqueAliases(IEnumerable<PropertyGroup> groups)
{
var groupsA = groups.ToArray();
PropertyGroup[] groupsA = groups.ToArray();
var distinctProperties = groupsA
.Select(x => x.Alias)
.Distinct()
.Count();
if (distinctProperties != groupsA.Length)
{
throw new InvalidOperationException("Cannot map groups due to alias conflict.");
}
}
private static void MapComposition(ContentTypeSave source, IContentTypeComposition target, Func<string, IContentTypeComposition> getContentType)
private static void MapComposition(ContentTypeSave source, IContentTypeComposition target,
Func<string, IContentTypeComposition> getContentType)
{
var current = target.CompositionAliases().ToArray();
var proposed = source.CompositeContentTypes;
IEnumerable<string> proposed = source.CompositeContentTypes;
var remove = current.Where(x => !proposed.Contains(x));
var add = proposed.Where(x => !current.Contains(x));
IEnumerable<string> remove = current.Where(x => !proposed.Contains(x));
IEnumerable<string> add = proposed.Where(x => !current.Contains(x));
foreach (var alias in remove)
{
target.RemoveContentType(alias);
}
foreach (var alias in add)
{
// TODO: Remove N+1 lookup
var contentType = getContentType(alias);
IContentTypeComposition contentType = getContentType(alias);
if (contentType != null)
{
target.AddContentType(contentType);
}
}
}
}