From d865448177f7cc2f70860afe58cad98a974f3904 Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Fri, 15 Oct 2021 15:45:21 +0100 Subject: [PATCH 01/26] Added database migration to support version cleanup feature --- .../Install/DatabaseSchemaCreator.cs | 3 +- .../Migrations/Upgrade/UmbracoPlan.cs | 2 ++ .../AddContentVersionCleanupFeature.cs | 20 +++++++++++ .../Persistence/Constants-DatabaseSchema.cs | 2 ++ .../Dtos/ContentVersionCleanupPolicyDto.cs | 34 +++++++++++++++++++ .../Persistence/Dtos/ContentVersionDto.cs | 4 +++ src/Umbraco.Core/Umbraco.Core.csproj | 2 ++ 7 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_18_0/AddContentVersionCleanupFeature.cs create mode 100644 src/Umbraco.Core/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs index c3756cfaad6b..2e195fe1d3c9 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs @@ -85,7 +85,8 @@ public DatabaseSchemaCreator(IUmbracoDatabase database, ILogger logger) typeof (AuditEntryDto), typeof (ContentVersionCultureVariationDto), typeof (DocumentCultureVariationDto), - typeof (ContentScheduleDto) + typeof (ContentScheduleDto), + typeof (ContentVersionCleanupPolicyDto) }; /// diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index 8e3e5a486203..a557c7e78a61 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Migrations.Upgrade.V_8_10_0; using Umbraco.Core.Migrations.Upgrade.V_8_15_0; using Umbraco.Core.Migrations.Upgrade.V_8_17_0; +using Umbraco.Core.Migrations.Upgrade.V_8_18_0; namespace Umbraco.Core.Migrations.Upgrade { @@ -211,6 +212,7 @@ protected void DefinePlan() To("{153865E9-7332-4C2A-9F9D-F20AEE078EC7}"); //FINAL + To("{8BAF5E6C-DCB7-41AE-824F-4215AE4F1F98}"); } } } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_18_0/AddContentVersionCleanupFeature.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_18_0/AddContentVersionCleanupFeature.cs new file mode 100644 index 000000000000..bfa204ec2423 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_18_0/AddContentVersionCleanupFeature.cs @@ -0,0 +1,20 @@ +using System.Linq; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_18_0 +{ + class AddContentVersionCleanupFeature : MigrationBase + { + public AddContentVersionCleanupFeature(IMigrationContext context) + : base(context) { } + + public override void Migrate() + { + Create.Table().Do(); + + // What's this about, we worry someone else edited table with same change? + var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList(); + AddColumnIfNotExists(columns, "preventCleanup"); + } + } +} diff --git a/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs b/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs index f430a8894ca5..1c00f2736572 100644 --- a/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs +++ b/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs @@ -26,6 +26,8 @@ public static class Tables public const string Content = TableNamePrefix + "Content"; public const string ContentVersion = TableNamePrefix + "ContentVersion"; public const string ContentVersionCultureVariation = TableNamePrefix + "ContentVersionCultureVariation"; + public const string ContentVersionCleanupPolicy = TableNamePrefix + "ContentVersionCleanupPolicy"; + public const string Document = TableNamePrefix + "Document"; public const string DocumentCultureVariation = TableNamePrefix + "DocumentCultureVariation"; public const string DocumentVersion = TableNamePrefix + "DocumentVersion"; diff --git a/src/Umbraco.Core/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs b/src/Umbraco.Core/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs new file mode 100644 index 000000000000..9b016138ff1b --- /dev/null +++ b/src/Umbraco.Core/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs @@ -0,0 +1,34 @@ +using System; +using System.Data; +using NPoco; +using Umbraco.Core.Persistence.DatabaseAnnotations; + +namespace Umbraco.Core.Persistence.Dtos +{ + [TableName(TableName)] + [PrimaryKey("contentTypeId")] + [ExplicitColumns] + internal class ContentVersionCleanupPolicyDto + { + public const string TableName = Constants.DatabaseSchema.Tables.ContentVersionCleanupPolicy; + + [Column("contentTypeId")] + [PrimaryKeyColumn(AutoIncrement = false)] + [ForeignKey(typeof(ContentTypeDto), Column = "nodeId", OnDelete = Rule.Cascade)] + public int ContentTypeId { get; set; } + + [Column("keepAllVersionsNewerThanDays")] + [NullSetting(NullSetting = NullSettings.Null)] + public int? KeepAllVersionsNewerThanDays { get; set; } + + [Column("keepLatestVersionPerDayForDays")] + [NullSetting(NullSetting = NullSettings.Null)] + public int? KeepLatestVersionPerDayForDays { get; set; } + + [Column("preventCleanup")] + public bool PreventCleanup { get; set; } + + [Column("updated")] + public DateTime Updated { get; set; } + } +} diff --git a/src/Umbraco.Core/Persistence/Dtos/ContentVersionDto.cs b/src/Umbraco.Core/Persistence/Dtos/ContentVersionDto.cs index f9bf283be9d7..02ae38ac3502 100644 --- a/src/Umbraco.Core/Persistence/Dtos/ContentVersionDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/ContentVersionDto.cs @@ -49,5 +49,9 @@ internal class ContentVersionDto [ResultColumn] [Reference(ReferenceType.OneToOne, ColumnName = "NodeId", ReferenceMemberName = "NodeId")] public ContentDto ContentDto { get; set; } + + [Column("preventCleanup")] + [Constraint(Default = "0")] + public bool PreventCleanup { get; set; } } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index e2679c222351..d6b373cbcf25 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -148,6 +148,7 @@ + @@ -174,6 +175,7 @@ + From a1ac730633448d618aafcc39154ff9936a058b9e Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Mon, 18 Oct 2021 15:26:38 +0100 Subject: [PATCH 02/26] Added XML configuration support for cleanup policy --- .../UmbracoSettings/ContentElement.cs | 5 +++++ .../ContentVersionCleanupPolicyElement.cs | 16 ++++++++++++++++ .../UmbracoSettings/IContentSection.cs | 2 ++ .../IContentVersionCleanupPolicySettings.cs | 9 +++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 2 ++ .../config/umbracoSettings.Release.config | 2 ++ 6 files changed, 36 insertions(+) create mode 100644 src/Umbraco.Core/Configuration/UmbracoSettings/ContentVersionCleanupPolicyElement.cs create mode 100644 src/Umbraco.Core/Configuration/UmbracoSettings/IContentVersionCleanupPolicySettings.cs diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs index 7c6ff4405f06..12cc890c2b58 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs @@ -22,6 +22,9 @@ internal class ContentElement : UmbracoConfigurationElement, IContentSection [ConfigurationProperty("notifications", IsRequired = true)] internal NotificationsElement Notifications => (NotificationsElement) base["notifications"]; + [ConfigurationProperty("contentVersionCleanupPolicy", IsRequired = false)] + internal ContentVersionCleanupPolicyElement ContentVersionCleanupPolicy => (ContentVersionCleanupPolicyElement) this["contentVersionCleanupPolicy"]; + [ConfigurationProperty("PreviewBadge")] internal InnerTextConfigurationElement PreviewBadge => GetOptionalTextElement("PreviewBadge", DefaultPreviewBadge); @@ -61,6 +64,8 @@ internal class ContentElement : UmbracoConfigurationElement, IContentSection IEnumerable IContentSection.AllowedUploadFiles => AllowedUploadFiles; + IContentVersionCleanupPolicySettings IContentSection.ContentVersionCleanupPolicySettings => ContentVersionCleanupPolicy; + bool IContentSection.ShowDeprecatedPropertyEditors => ShowDeprecatedPropertyEditors; string IContentSection.LoginBackgroundImage => LoginBackgroundImage; diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentVersionCleanupPolicyElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentVersionCleanupPolicyElement.cs new file mode 100644 index 000000000000..a736ccfd341a --- /dev/null +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentVersionCleanupPolicyElement.cs @@ -0,0 +1,16 @@ +using System.Configuration; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + internal class ContentVersionCleanupPolicyElement : UmbracoConfigurationElement, IContentVersionCleanupPolicySettings + { + [ConfigurationProperty("enable", DefaultValue = false)] + public bool EnableCleanup => (bool)this["enable"]; + + [ConfigurationProperty("keepAllVersionsNewerThanDays", DefaultValue = 2)] + public int KeepAllVersionsNewerThanDays => (int)this["keepAllVersionsNewerThanDays"]; + + [ConfigurationProperty("keepLatestVersionPerDayForDays", DefaultValue = 30)] + public int KeepLatestVersionPerDayForDays => (int)this["keepLatestVersionPerDayForDays"]; + } +} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs index 0f52adf02ec5..97893e0a9148 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs @@ -25,6 +25,8 @@ public interface IContentSection : IUmbracoConfigurationSection IEnumerable AllowedUploadFiles { get; } + IContentVersionCleanupPolicySettings ContentVersionCleanupPolicySettings { get; } + /// /// Gets a value indicating whether to show deprecated property editors in /// a datatype list of available editors. diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentVersionCleanupPolicySettings.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentVersionCleanupPolicySettings.cs new file mode 100644 index 000000000000..66e9e17d97b1 --- /dev/null +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentVersionCleanupPolicySettings.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IContentVersionCleanupPolicySettings + { + bool EnableCleanup { get; } + int KeepAllVersionsNewerThanDays { get; } + int KeepLatestVersionPerDayForDays { get; } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index d6b373cbcf25..696de73fd01c 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -130,6 +130,8 @@ + + diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config index 5ce2c24d41c7..f41eb10ce1d3 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config @@ -190,6 +190,8 @@ assets/img/application/umbraco_logo_white.svg + + From bba089c24c1bd26a5769f84ed4646f3a1c08e438 Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Mon, 18 Oct 2021 21:56:18 +0100 Subject: [PATCH 03/26] Implemented ContentVersionCleanup scheduled task. Note: adding ref to Microsoft.NET.Test.Sdk fixes AutoFixture AutoDataAttribute (and sub classes) --- .../CompositionExtensions/Repositories.cs | 1 + .../CompositionExtensions/Services.cs | 7 +- .../UmbracoSettings/ContentElement.cs | 6 +- ...sionCleanupPolicyGlobalSettingsElement.cs} | 2 +- .../UmbracoSettings/IContentSection.cs | 2 +- ...tentVersionCleanupPolicyGlobalSettings.cs} | 2 +- .../ContentVersionCleanupPolicySettings.cs | 13 + .../Models/HistoricContentVersionMeta.cs | 24 ++ .../Dtos/ContentVersionCleanupPolicyDto.cs | 3 +- .../IDocumentVersionRepository.cs | 23 ++ .../Implement/DocumentVersionRepository.cs | 94 +++++++ .../Services/IContentVersionCleanupPolicy.cs | 17 ++ .../Services/IContentVersionCleanupService.cs | 14 + .../Services/Implement/ContentService.cs | 113 +++++++- .../DefaultContentVersionCleanupPolicy.cs | 90 ++++++ src/Umbraco.Core/Umbraco.Core.csproj | 11 +- .../UmbracoSettings/umbracoSettings.config | 2 + ...mentVersionRepository_Tests_Integration.cs | 125 +++++++++ .../ContentVersionCleanup_Tests_UnitTests.cs | 127 +++++++++ ...VersionCleanupService_Tests_Integration.cs | 106 +++++++ ...ntVersionCleanupService_Tests_UnitTests.cs | 264 ++++++++++++++++++ ...entVersionCleanupPolicy_Tests_UnitTests.cs | 237 ++++++++++++++++ .../TestHelpers/Entities/MockedContent.cs | 10 +- .../Entities/MockedContentTypes.cs | 1 + .../Testing/AutoMoqDataAttribute.cs | 13 + src/Umbraco.Tests/Umbraco.Tests.csproj | 15 +- .../config/umbracoSettings.Release.config | 2 +- .../Scheduling/ContentVersionCleanup.cs | 79 ++++++ .../Scheduling/SchedulerComponent.cs | 37 ++- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 30 files changed, 1416 insertions(+), 25 deletions(-) rename src/Umbraco.Core/Configuration/UmbracoSettings/{ContentVersionCleanupPolicyElement.cs => ContentVersionCleanupPolicyGlobalSettingsElement.cs} (80%) rename src/Umbraco.Core/Configuration/UmbracoSettings/{IContentVersionCleanupPolicySettings.cs => IContentVersionCleanupPolicyGlobalSettings.cs} (76%) create mode 100644 src/Umbraco.Core/Models/ContentVersionCleanupPolicySettings.cs create mode 100644 src/Umbraco.Core/Models/HistoricContentVersionMeta.cs create mode 100644 src/Umbraco.Core/Persistence/Repositories/IDocumentVersionRepository.cs create mode 100644 src/Umbraco.Core/Persistence/Repositories/Implement/DocumentVersionRepository.cs create mode 100644 src/Umbraco.Core/Services/IContentVersionCleanupPolicy.cs create mode 100644 src/Umbraco.Core/Services/IContentVersionCleanupService.cs create mode 100644 src/Umbraco.Core/Services/Implement/DefaultContentVersionCleanupPolicy.cs create mode 100644 src/Umbraco.Tests/Persistence/Repositories/DocumentVersionRepository_Tests_Integration.cs create mode 100644 src/Umbraco.Tests/Scheduling/ContentVersionCleanup_Tests_UnitTests.cs create mode 100644 src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_Integration.cs create mode 100644 src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_UnitTests.cs create mode 100644 src/Umbraco.Tests/Services/DefaultContentVersionCleanupPolicy_Tests_UnitTests.cs create mode 100644 src/Umbraco.Tests/Testing/AutoMoqDataAttribute.cs create mode 100644 src/Umbraco.Web/Scheduling/ContentVersionCleanup.cs diff --git a/src/Umbraco.Core/Composing/CompositionExtensions/Repositories.cs b/src/Umbraco.Core/Composing/CompositionExtensions/Repositories.cs index 00b29dd97fb6..77549ed48cce 100644 --- a/src/Umbraco.Core/Composing/CompositionExtensions/Repositories.cs +++ b/src/Umbraco.Core/Composing/CompositionExtensions/Repositories.cs @@ -49,6 +49,7 @@ public static Composition ComposeRepositories(this Composition composition) composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(); + composition.RegisterUnique(); return composition; } diff --git a/src/Umbraco.Core/Composing/CompositionExtensions/Services.cs b/src/Umbraco.Core/Composing/CompositionExtensions/Services.cs index d97845928dcb..56a9b2f8aede 100644 --- a/src/Umbraco.Core/Composing/CompositionExtensions/Services.cs +++ b/src/Umbraco.Core/Composing/CompositionExtensions/Services.cs @@ -31,7 +31,12 @@ public static Composition ComposeServices(this Composition composition) composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(); - composition.RegisterUnique(); + + composition.RegisterUnique(); + composition.RegisterUnique(factory => factory.GetInstance()); + composition.RegisterUnique(factory => factory.GetInstance()); + composition.RegisterUnique(); + composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(); diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs index 12cc890c2b58..fba46c077e76 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs @@ -22,8 +22,8 @@ internal class ContentElement : UmbracoConfigurationElement, IContentSection [ConfigurationProperty("notifications", IsRequired = true)] internal NotificationsElement Notifications => (NotificationsElement) base["notifications"]; - [ConfigurationProperty("contentVersionCleanupPolicy", IsRequired = false)] - internal ContentVersionCleanupPolicyElement ContentVersionCleanupPolicy => (ContentVersionCleanupPolicyElement) this["contentVersionCleanupPolicy"]; + [ConfigurationProperty("contentVersionCleanupPolicyGlobalSettings", IsRequired = false)] + internal ContentVersionCleanupPolicyGlobalSettingsElement ContentVersionCleanupPolicyGlobalSettingsElement => (ContentVersionCleanupPolicyGlobalSettingsElement) this["contentVersionCleanupPolicyGlobalSettings"]; [ConfigurationProperty("PreviewBadge")] internal InnerTextConfigurationElement PreviewBadge => GetOptionalTextElement("PreviewBadge", DefaultPreviewBadge); @@ -64,7 +64,7 @@ internal class ContentElement : UmbracoConfigurationElement, IContentSection IEnumerable IContentSection.AllowedUploadFiles => AllowedUploadFiles; - IContentVersionCleanupPolicySettings IContentSection.ContentVersionCleanupPolicySettings => ContentVersionCleanupPolicy; + IContentVersionCleanupPolicyGlobalSettings IContentSection.ContentVersionCleanupPolicyGlobalSettings => ContentVersionCleanupPolicyGlobalSettingsElement; bool IContentSection.ShowDeprecatedPropertyEditors => ShowDeprecatedPropertyEditors; diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentVersionCleanupPolicyElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentVersionCleanupPolicyGlobalSettingsElement.cs similarity index 80% rename from src/Umbraco.Core/Configuration/UmbracoSettings/ContentVersionCleanupPolicyElement.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/ContentVersionCleanupPolicyGlobalSettingsElement.cs index a736ccfd341a..ad32594e39b1 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentVersionCleanupPolicyElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentVersionCleanupPolicyGlobalSettingsElement.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class ContentVersionCleanupPolicyElement : UmbracoConfigurationElement, IContentVersionCleanupPolicySettings + internal class ContentVersionCleanupPolicyGlobalSettingsElement : UmbracoConfigurationElement, IContentVersionCleanupPolicyGlobalSettings { [ConfigurationProperty("enable", DefaultValue = false)] public bool EnableCleanup => (bool)this["enable"]; diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs index 97893e0a9148..d8ef2bb94353 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs @@ -25,7 +25,7 @@ public interface IContentSection : IUmbracoConfigurationSection IEnumerable AllowedUploadFiles { get; } - IContentVersionCleanupPolicySettings ContentVersionCleanupPolicySettings { get; } + IContentVersionCleanupPolicyGlobalSettings ContentVersionCleanupPolicyGlobalSettings { get; } /// /// Gets a value indicating whether to show deprecated property editors in diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentVersionCleanupPolicySettings.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentVersionCleanupPolicyGlobalSettings.cs similarity index 76% rename from src/Umbraco.Core/Configuration/UmbracoSettings/IContentVersionCleanupPolicySettings.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/IContentVersionCleanupPolicyGlobalSettings.cs index 66e9e17d97b1..20198d995df8 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentVersionCleanupPolicySettings.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentVersionCleanupPolicyGlobalSettings.cs @@ -1,6 +1,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface IContentVersionCleanupPolicySettings + public interface IContentVersionCleanupPolicyGlobalSettings { bool EnableCleanup { get; } int KeepAllVersionsNewerThanDays { get; } diff --git a/src/Umbraco.Core/Models/ContentVersionCleanupPolicySettings.cs b/src/Umbraco.Core/Models/ContentVersionCleanupPolicySettings.cs new file mode 100644 index 000000000000..90ff0572a0c8 --- /dev/null +++ b/src/Umbraco.Core/Models/ContentVersionCleanupPolicySettings.cs @@ -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; } + } +} diff --git a/src/Umbraco.Core/Models/HistoricContentVersionMeta.cs b/src/Umbraco.Core/Models/HistoricContentVersionMeta.cs new file mode 100644 index 000000000000..7eb329371136 --- /dev/null +++ b/src/Umbraco.Core/Models/HistoricContentVersionMeta.cs @@ -0,0 +1,24 @@ +using System; + +namespace Umbraco.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}"; + } +} diff --git a/src/Umbraco.Core/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs b/src/Umbraco.Core/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs index 9b016138ff1b..abe7432775c0 100644 --- a/src/Umbraco.Core/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs @@ -6,14 +6,13 @@ namespace Umbraco.Core.Persistence.Dtos { [TableName(TableName)] - [PrimaryKey("contentTypeId")] + [PrimaryKey("contentTypeId", AutoIncrement = false)] [ExplicitColumns] internal class ContentVersionCleanupPolicyDto { public const string TableName = Constants.DatabaseSchema.Tables.ContentVersionCleanupPolicy; [Column("contentTypeId")] - [PrimaryKeyColumn(AutoIncrement = false)] [ForeignKey(typeof(ContentTypeDto), Column = "nodeId", OnDelete = Rule.Cascade)] public int ContentTypeId { get; set; } diff --git a/src/Umbraco.Core/Persistence/Repositories/IDocumentVersionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDocumentVersionRepository.cs new file mode 100644 index 000000000000..a299713985ff --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/IDocumentVersionRepository.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Persistence.Repositories +{ + public interface IDocumentVersionRepository : IRepository + { + /// + /// Gets a list of all historic content versions. + /// + public IReadOnlyCollection GetDocumentVersionsEligibleForCleanup(); + + /// + /// Gets cleanup policy override settings per content type. + /// + public IReadOnlyCollection GetCleanupPolicies(); + + /// + /// Deletes multiple content versions by ID. + /// + void DeleteVersions(IEnumerable versionIds); + } +} diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentVersionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentVersionRepository.cs new file mode 100644 index 000000000000..761b83924f8a --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentVersionRepository.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.Scoping; + +namespace Umbraco.Core.Persistence.Repositories.Implement +{ + internal class DocumentVersionRepository : IDocumentVersionRepository + { + private readonly IScopeAccessor _scopeAccessor; + + public DocumentVersionRepository(IScopeAccessor scopeAccessor) + { + _scopeAccessor = scopeAccessor ?? throw new ArgumentNullException(nameof(scopeAccessor)); + } + + /// + /// + /// Never includes current draft version.
+ /// Never includes current published version.
+ /// Never includes versions marked as "preventCleanup".
+ ///
+ public IReadOnlyCollection GetDocumentVersionsEligibleForCleanup() + { + var query = _scopeAccessor.AmbientScope.SqlContext.Sql(); + + query.Select(@"umbracoDocument.nodeId as contentId, + umbracoContent.contentTypeId as contentTypeId, + umbracoContentVersion.id as versionId, + umbracoContentVersion.versionDate as versionDate") + .From() + .InnerJoin() + .On(left => left.NodeId, right => right.NodeId) + .InnerJoin() + .On(left => left.NodeId, right => right.NodeId) + .InnerJoin() + .On(left => left.Id, right => right.Id) + .Where(x => !x.Current) // Never delete current draft version + .Where(x => !x.PreventCleanup) // Never delete "pinned" versions + .Where(x => !x.Published); // Never delete published version + + return _scopeAccessor.AmbientScope.Database.Fetch(query); + } + + /// + public IReadOnlyCollection GetCleanupPolicies() + { + var query = _scopeAccessor.AmbientScope.SqlContext.Sql(); + + query.Select() + .From(); + + return _scopeAccessor.AmbientScope.Database.Fetch(query); + } + + /// + /// + /// Deletes in batches of + /// + public void DeleteVersions(IEnumerable versionIds) + { + foreach (var group in versionIds.InGroupsOf(Constants.Sql.MaxParameterCount)) + { + var groupedVersionIds = group.ToList(); + + // Note: We had discussed doing this in a single SQL Command. + // If you can work out how to make that work with SQL CE, let me know! + // Can use test PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExceptActive to try things out. + + var query = _scopeAccessor.AmbientScope.SqlContext.Sql() + .Delete() + .WhereIn(x => x.VersionId, groupedVersionIds); + _scopeAccessor.AmbientScope.Database.Execute(query); + + query = _scopeAccessor.AmbientScope.SqlContext.Sql() + .Delete() + .WhereIn(x => x.VersionId, groupedVersionIds); + _scopeAccessor.AmbientScope.Database.Execute(query); + + query = _scopeAccessor.AmbientScope.SqlContext.Sql() + .Delete() + .WhereIn(x => x.Id, groupedVersionIds); + _scopeAccessor.AmbientScope.Database.Execute(query); + + query = _scopeAccessor.AmbientScope.SqlContext.Sql() + .Delete() + .WhereIn(x => x.Id, groupedVersionIds); + _scopeAccessor.AmbientScope.Database.Execute(query); + } + } + } +} diff --git a/src/Umbraco.Core/Services/IContentVersionCleanupPolicy.cs b/src/Umbraco.Core/Services/IContentVersionCleanupPolicy.cs new file mode 100644 index 000000000000..924148181626 --- /dev/null +++ b/src/Umbraco.Core/Services/IContentVersionCleanupPolicy.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + /// + /// Used to filter historic content versions for cleanup. + /// + public interface IContentVersionCleanupPolicy + { + /// + /// Filters a set of candidates historic content versions for cleanup according to policy settings. + /// + IEnumerable Apply(DateTime asAtDate, IEnumerable items); + } +} diff --git a/src/Umbraco.Core/Services/IContentVersionCleanupService.cs b/src/Umbraco.Core/Services/IContentVersionCleanupService.cs new file mode 100644 index 000000000000..8b7e826f55f4 --- /dev/null +++ b/src/Umbraco.Core/Services/IContentVersionCleanupService.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + public interface IContentVersionCleanupService + { + /// + /// Removes historic content versions according to a policy. + /// + IReadOnlyCollection PerformContentVersionCleanup(DateTime asAtDate); + } +} diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index b50200c842bd..5f6782e0b477 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Services.Implement /// /// Implements the content service. /// - public class ContentService : RepositoryService, IContentService + public class ContentService : RepositoryService, IContentService, IContentVersionCleanupService { private readonly IDocumentRepository _documentRepository; private readonly IEntityRepository _entityRepository; @@ -1438,7 +1438,7 @@ private void PerformScheduledPublishingExpiration(DateTime date, List(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result); + Logger.Error(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result); results.Add(result); } @@ -2452,7 +2452,7 @@ public ContentDataIntegrityReport CheckDataIntegrity(ContentDataIntegrityReportO if (report.FixedIssues.Count > 0) { //The event args needs a content item so we'll make a fake one with enough properties to not cause a null ref - var root = new Content("root", -1, new ContentType(-1)) {Id = -1, Key = Guid.Empty}; + var root = new Content("root", -1, new ContentType(-1)) { Id = -1, Key = Guid.Empty }; scope.Events.Dispatch(TreeChanged, this, new TreeChange.EventArgs(new TreeChange(root, TreeChangeTypes.RefreshAll))); } @@ -3201,7 +3201,7 @@ public OperationResult Rollback(int id, int versionId, string culture = "*", int if (rollbackSaveResult.Success == false) { //Log the error/warning - Logger.Error("User '{UserId}' was unable to rollback content '{ContentId}' to version '{VersionId}'", userId, id, versionId); + Logger.Error("User '{UserId}' was unable to rollback content '{ContentId}' to version '{VersionId}'", userId, id, versionId); } else { @@ -3210,7 +3210,7 @@ public OperationResult Rollback(int id, int versionId, string culture = "*", int scope.Events.Dispatch(RolledBack, this, rollbackEventArgs); //Logging & Audit message - Logger.Info("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId); + Logger.Info("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId); Audit(AuditType.RollBack, userId, id, $"Content '{content.Name}' was rolled back to version '{versionId}'"); } @@ -3222,7 +3222,110 @@ public OperationResult Rollback(int id, int versionId, string culture = "*", int #endregion + /// + /// + /// In v9 this can live in another class as we publish the notifications via IEventAggregator. + /// But for v8 must be here for access to the static events. + /// + public IReadOnlyCollection PerformContentVersionCleanup(DateTime asAtDate) + { + return CleanupDocumentVersions(asAtDate); + // Media - ignored + // Members - ignored + } + /// + /// v9 - move to another class + /// + private IReadOnlyCollection CleanupDocumentVersions(DateTime asAtDate) + { + // NOTE: v9 - don't service locate + var documentVersionRepository = Composing.Current.Factory.GetInstance(); + + // NOTE: v9 - don't service locate + var cleanupPolicy = Composing.Current.Factory.GetInstance(); + + List versionsToDelete; + + /* Why so many scopes? + * + * We could just work out the set to delete at SQL infra level which was the original plan, however we agreed that really we should fire + * ContentService.DeletingVersions so people can hook & cancel if required. + * + * On first time run of cleanup on a site with a lot of history there may be a lot of historic ContentVersions to remove e.g. 200K for our.umbraco.com. + * If we weren't supporting SQL CE we could do TVP, or use temp tables to bulk delete with joins to our list of version ids to nuke. + * (much nicer, we can kill 100k in sub second time-frames). + * + * However we are supporting SQL CE, so the easiest thing to do is use the Umbraco InGroupsOf helper to create a query with 2K args of version + * ids to delete at a time. + * + * This is already done at the repository level, however if we only had a single scope at service level we're still locking + * the ContentVersions table (and other related tables) for a couple of minutes which makes the back office unusable. + * + * As a quick fix, we can also use InGroupsOf at service level, create a scope per group to give other connections a chance + * to grab the locks and execute their queries. + * + * This makes the back office a tiny bit sluggish during first run but it is usable for loading tree and publishing content. + * + * There are optimizations we can do, we could add a bulk delete for SqlServerSyntaxProvider which differs in implementation + * and fallback to this naive approach only for SQL CE, however we agreed it is not worth the effort as this is a one time pain, + * subsequent runs shouldn't have huge numbers of versions to cleanup. + * + * tl;dr lots of scopes to enable other connections to use the DB whilst we work. + */ + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + var allHistoricVersions = documentVersionRepository.GetDocumentVersionsEligibleForCleanup(); + Logger.Debug("Discovered {count} candidate(s) for ContentVersion cleanup.", allHistoricVersions.Count); + versionsToDelete = new List(allHistoricVersions.Count); + + var filteredContentVersions = cleanupPolicy.Apply(asAtDate, allHistoricVersions); + + foreach (var version in filteredContentVersions) + { + var args = new DeleteRevisionsEventArgs(version.ContentId, version.VersionId); + + if (scope.Events.DispatchCancelable(ContentService.DeletingVersions, this, args)) + { + Logger.Debug("Delete cancelled for ContentVersion [{versionId}]", version.VersionId); + continue; + } + + versionsToDelete.Add(version); + } + } + + if (!versionsToDelete.Any()) + { + Logger.Debug("No remaining ContentVersions for cleanup.", versionsToDelete.Count); + return Array.Empty(); + } + + Logger.Debug("Removing {count} ContentVersion(s).", versionsToDelete.Count); + + foreach (var group in versionsToDelete.InGroupsOf(Constants.Sql.MaxParameterCount)) + { + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + scope.WriteLock(Constants.Locks.ContentTree); + var groupEnumerated = group.ToList(); + documentVersionRepository.DeleteVersions(groupEnumerated.Select(x => x.VersionId)); + + foreach (var version in groupEnumerated) + { + var args = new DeleteRevisionsEventArgs(version.ContentId, version.VersionId); + scope.Events.Dispatch(ContentService.DeletedVersions, this, args); + } + } + } + + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + Audit(AuditType.Delete, Constants.Security.SuperUserId, -1, $"Removed {versionsToDelete.Count} ContentVersion(s) according to cleanup policy."); + } + + return versionsToDelete; + } } } diff --git a/src/Umbraco.Core/Services/Implement/DefaultContentVersionCleanupPolicy.cs b/src/Umbraco.Core/Services/Implement/DefaultContentVersionCleanupPolicy.cs new file mode 100644 index 000000000000..014c6ff1133e --- /dev/null +++ b/src/Umbraco.Core/Services/Implement/DefaultContentVersionCleanupPolicy.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Scoping; + +namespace Umbraco.Core.Services.Implement +{ + public class DefaultContentVersionCleanupPolicy : IContentVersionCleanupPolicy + { + private readonly IContentSection _contentSection; + private readonly IScopeProvider _scopeProvider; + private readonly IDocumentVersionRepository _documentVersionRepository; + + public DefaultContentVersionCleanupPolicy(IContentSection contentSection, IScopeProvider scopeProvider, IDocumentVersionRepository documentVersionRepository) + { + _contentSection = contentSection ?? throw new ArgumentNullException(nameof(contentSection)); + _scopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider)); + _documentVersionRepository = documentVersionRepository ?? throw new ArgumentNullException(nameof(documentVersionRepository)); + } + + public IEnumerable Apply(DateTime asAtDate, IEnumerable items) + { + // Note: Not checking global enable flag, that's handled in the scheduled job. + // If this method is called and policy is globally disabled someone has chosen to run in code. + + var globalPolicy = _contentSection.ContentVersionCleanupPolicyGlobalSettings; + + var theRest = new List(); + + using(_scopeProvider.CreateScope(autoComplete: true)) + { + var policyOverrides = _documentVersionRepository.GetCleanupPolicies() + .ToDictionary(x => x.ContentTypeId); + + foreach (var version in items) + { + var age = asAtDate - version.VersionDate; + + var overrides = GetOverridePolicy(version, policyOverrides); + + var keepAll = overrides?.KeepAllVersionsNewerThanDays ?? globalPolicy.KeepAllVersionsNewerThanDays!; + var keepLatest = overrides?.KeepLatestVersionPerDayForDays ?? globalPolicy.KeepLatestVersionPerDayForDays; + var preventCleanup = overrides?.PreventCleanup ?? false; + + if (preventCleanup) + { + continue; + } + + if (age.TotalDays <= keepAll) + { + continue; + } + + if (age.TotalDays > keepLatest) + { + + yield return version; + continue; + } + + theRest.Add(version); + } + + var grouped = theRest.GroupBy(x => new + { + x.ContentId, + x.VersionDate.Date + }); + + foreach (var group in grouped) + { + yield return group.OrderByDescending(x => x.VersionId).First(); + } + } + } + + private ContentVersionCleanupPolicySettings GetOverridePolicy( + HistoricContentVersionMeta version, + IDictionary overrides) + { + _ = overrides.TryGetValue(version.ContentTypeId, out var value); + + return value; + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 696de73fd01c..818891b376a8 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -130,8 +130,8 @@ - - + + @@ -163,6 +163,7 @@ + @@ -179,8 +180,11 @@ + + + @@ -193,7 +197,10 @@ + + + diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config index d802cfc7adb3..707887bc8b07 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config @@ -57,6 +57,8 @@ jpg,png,gif + + diff --git a/src/Umbraco.Tests/Persistence/Repositories/DocumentVersionRepository_Tests_Integration.cs b/src/Umbraco.Tests/Persistence/Repositories/DocumentVersionRepository_Tests_Integration.cs new file mode 100644 index 000000000000..9c012bf083d9 --- /dev/null +++ b/src/Umbraco.Tests/Persistence/Repositories/DocumentVersionRepository_Tests_Integration.cs @@ -0,0 +1,125 @@ +using System.Diagnostics; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.Persistence.Repositories.Implement; +using Umbraco.Core.Scoping; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Entities; +using Umbraco.Tests.Testing; + +namespace Umbraco.Tests.Persistence.Repositories +{ + /// + /// v9 -> Tests.Integration + /// + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + public class DocumentVersionRepository_Tests_Integration : TestWithDatabaseBase + { + [Test] + public void GetDocumentVersionsEligibleForCleanup_Always_ExcludesActiveVersions() + { + var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); + ServiceContext.ContentTypeService.Save(contentType); + + var content = MockedContent.CreateSimpleContent(contentType); + + ServiceContext.ContentService.SaveAndPublish(content); + // At this point content has 2 versions, a draft version and a published version. + + ServiceContext.ContentService.SaveAndPublish(content); + // At this point content has 3 versions, a historic version, a draft version and a published version. + + var scopeProvider = TestObjects.GetScopeProvider(Logger); + using (scopeProvider.CreateScope()) + { + var sut = new DocumentVersionRepository((IScopeAccessor)scopeProvider); + var results = sut.GetDocumentVersionsEligibleForCleanup(); + + Assert.Multiple(() => + { + Assert.AreEqual(1, results.Count); + Assert.AreEqual(1, results.First().VersionId); + }); + } + } + + [Test] + public void GetDocumentVersionsEligibleForCleanup_Always_ExcludesPinnedVersions() + { + var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); + ServiceContext.ContentTypeService.Save(contentType); + + var content = MockedContent.CreateSimpleContent(contentType); + + ServiceContext.ContentService.SaveAndPublish(content); + // At this point content has 2 versions, a draft version and a published version. + ServiceContext.ContentService.SaveAndPublish(content); + ServiceContext.ContentService.SaveAndPublish(content); + ServiceContext.ContentService.SaveAndPublish(content); + // At this point content has 5 versions, 3 historic versions, a draft version and a published version. + + var allVersions = ServiceContext.ContentService.GetVersions(content.Id); + Debug.Assert(allVersions.Count() == 5); // Sanity check + + var scopeProvider = TestObjects.GetScopeProvider(Logger); + using (var scope = scopeProvider.CreateScope()) + { + scope.Database.Update("set preventCleanup = 1 where id in (1,3)"); + + var sut = new DocumentVersionRepository((IScopeAccessor)scopeProvider); + var results = sut.GetDocumentVersionsEligibleForCleanup(); + + Assert.Multiple(() => + { + Assert.AreEqual(1, results.Count); + + // We pinned 1 & 3 + // 4 is current + // 5 is published + // So all that is left is 2 + Assert.AreEqual(2, results.First().VersionId); + }); + } + } + + [Test] + public void DeleteVersions_Always_DeletesSpecifiedVersions() + { + var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); + ServiceContext.ContentTypeService.Save(contentType); + + var content = MockedContent.CreateSimpleContent(contentType); + + ServiceContext.ContentService.SaveAndPublish(content); + ServiceContext.ContentService.SaveAndPublish(content); + ServiceContext.ContentService.SaveAndPublish(content); + ServiceContext.ContentService.SaveAndPublish(content); + + var scopeProvider = TestObjects.GetScopeProvider(Logger); + using (var scope = scopeProvider.CreateScope()) + { + var query = scope.SqlContext.Sql(); + + query.Select() + .From(); + + var sut = new DocumentVersionRepository((IScopeAccessor)scopeProvider); + sut.DeleteVersions(new []{1,2,3}); + + var after = scope.Database.Fetch(query); + + Assert.Multiple(() => + { + Assert.AreEqual(2, after.Count); + Assert.True(after.All(x => x.Id > 3)); + }); + } + } + } +} diff --git a/src/Umbraco.Tests/Scheduling/ContentVersionCleanup_Tests_UnitTests.cs b/src/Umbraco.Tests/Scheduling/ContentVersionCleanup_Tests_UnitTests.cs new file mode 100644 index 000000000000..82c5c0077ecc --- /dev/null +++ b/src/Umbraco.Tests/Scheduling/ContentVersionCleanup_Tests_UnitTests.cs @@ -0,0 +1,127 @@ +using System; +using AutoFixture.NUnit3; +using Moq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Services; +using Umbraco.Core.Sync; +using Umbraco.Tests.Testing; +using Umbraco.Web.Scheduling; + +namespace Umbraco.Tests.Scheduling +{ + [TestFixture] + class ContentVersionCleanup_Tests_UnitTests + { + [Test, AutoMoqData] + public void ContentVersionCleanup_WhenNotEnabled_DoesNotCleanupWillRepeat( + [Frozen] Mock settings, + [Frozen] Mock state, + [Frozen] Mock cleanupService, + ContentVersionCleanup sut) + { + settings.Setup(x => x.EnableCleanup).Returns(false); + + state.Setup(x => x.Level).Returns(RuntimeLevel.Run); + state.Setup(x => x.IsMainDom).Returns(true); + state.Setup(x => x.ServerRole).Returns(ServerRole.Master); + + var result = sut.PerformRun(); + + Assert.Multiple(() => + { + Assert.False(result); + cleanupService.Verify(x => x.PerformContentVersionCleanup(It.IsAny()), Times.Never); + }); + } + + [Test, AutoMoqData] + public void ContentVersionCleanup_RuntimeLevelNotRun_DoesNotCleanupWillRepeat( + [Frozen] Mock settings, + [Frozen] Mock state, + [Frozen] Mock cleanupService, + ContentVersionCleanup sut) + { + settings.Setup(x => x.EnableCleanup).Returns(true); + + state.Setup(x => x.Level).Returns(RuntimeLevel.Unknown); + state.Setup(x => x.IsMainDom).Returns(true); + state.Setup(x => x.ServerRole).Returns(ServerRole.Master); + + var result = sut.PerformRun(); + + Assert.Multiple(() => + { + Assert.True(result); + cleanupService.Verify(x => x.PerformContentVersionCleanup(It.IsAny()), Times.Never); + }); + } + + [Test, AutoMoqData] + public void ContentVersionCleanup_ServerRoleUnknown_DoesNotCleanupWillRepeat( + [Frozen] Mock settings, + [Frozen] Mock state, + [Frozen] Mock cleanupService, + ContentVersionCleanup sut) + { + settings.Setup(x => x.EnableCleanup).Returns(true); + + state.Setup(x => x.Level).Returns(RuntimeLevel.Run); + state.Setup(x => x.IsMainDom).Returns(true); + state.Setup(x => x.ServerRole).Returns(ServerRole.Unknown); + + var result = sut.PerformRun(); + + Assert.Multiple(() => + { + Assert.True(result); + cleanupService.Verify(x => x.PerformContentVersionCleanup(It.IsAny()), Times.Never); + }); + } + + [Test, AutoMoqData] + public void ContentVersionCleanup_NotMainDom_DoesNotCleanupWillNotRepeat( + [Frozen] Mock settings, + [Frozen] Mock state, + [Frozen] Mock cleanupService, + ContentVersionCleanup sut) + { + settings.Setup(x => x.EnableCleanup).Returns(true); + + state.Setup(x => x.Level).Returns(RuntimeLevel.Run); + state.Setup(x => x.IsMainDom).Returns(false); + state.Setup(x => x.ServerRole).Returns(ServerRole.Master); + + var result = sut.PerformRun(); + + Assert.Multiple(() => + { + Assert.False(result); + cleanupService.Verify(x => x.PerformContentVersionCleanup(It.IsAny()), Times.Never); + }); + } + + [Test, AutoMoqData] + public void ContentVersionCleanup_Enabled_DelegatesToCleanupService( + [Frozen] Mock settings, + [Frozen] Mock state, + [Frozen] Mock cleanupService, + ContentVersionCleanup sut) + { + settings.Setup(x => x.EnableCleanup).Returns(true); + + state.Setup(x => x.Level).Returns(RuntimeLevel.Run); + state.Setup(x => x.IsMainDom).Returns(true); + state.Setup(x => x.ServerRole).Returns(ServerRole.Master); + + var result = sut.PerformRun(); + + Assert.Multiple(() => + { + Assert.True(result); + cleanupService.Verify(x => x.PerformContentVersionCleanup(It.IsAny()), Times.Once); + }); + } + } +} diff --git a/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_Integration.cs b/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_Integration.cs new file mode 100644 index 000000000000..3e6fe63e13f1 --- /dev/null +++ b/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_Integration.cs @@ -0,0 +1,106 @@ +using System; +using System.Data; +using System.Diagnostics; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.Services; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Entities; +using Umbraco.Tests.Testing; + +namespace Umbraco.Tests.Services +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + public class ContentVersionCleanupService_Tests_Integration : TestWithDatabaseBase + { + /// + /// This is covered by the unit tests, but nice to know it deletes on infra. + /// And proves implementation is compatible with SQL CE. + /// + [Test] + public void PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExceptActive() + { + // For reference currently has + // 5000 Documents + // With 200K Versions + // With 11M Property data + + var contentTypeA = MockedContentTypes.CreateSimpleContentType("contentTypeA", "contentTypeA"); + ServiceContext.FileService.SaveTemplate(contentTypeA.DefaultTemplate); + ServiceContext.ContentTypeService.Save(contentTypeA); + + var content = MockedContent.CreateSimpleContent(contentTypeA); + ServiceContext.ContentService.SaveAndPublish(content, raiseEvents: false); + + for (var i = 0; i < 10; i++) + { + ServiceContext.ContentService.SaveAndPublish(content, raiseEvents: false); + } + + var before = GetReport(); + + Debug.Assert(before.ContentVersions == 12); // 10 historic + current draft + current published + Debug.Assert(before.PropertyData == 12 * 3); // CreateSimpleContentType = 3 props + + // Kill all historic + InsertCleanupPolicy(contentTypeA, 0, 0); + + ((IContentVersionCleanupService)ServiceContext.ContentService).PerformContentVersionCleanup(DateTime.Now.AddHours(1)); + + var after = GetReport(); + + Assert.Multiple(() => + { + Assert.AreEqual(2, after.ContentVersions); // current draft, current published + Assert.AreEqual(2, after.DocumentVersions); + Assert.AreEqual(6, after.PropertyData); // CreateSimpleContentType = 3 props + }); + } + + private Report GetReport() + { + var scopeProvider = TestObjects.GetScopeProvider(Logger); + using (var scope = scopeProvider.CreateScope(autoComplete: true)) + { + // SQL CE is fun! + var contentVersions = scope.Database.Single(@"select count(1) from umbracoContentVersion"); + var documentVersions = scope.Database.Single(@"select count(1) from umbracoDocumentVersion"); + var propertyData = scope.Database.Single(@"select count(1) from umbracoPropertyData"); + + return new Report + { + ContentVersions = contentVersions, + DocumentVersions = documentVersions, + PropertyData = propertyData + }; + } + } + + private void InsertCleanupPolicy(IContentType contentType, int daysToKeepAll, int daysToRollupAll, bool preventCleanup = false) + { + var scopeProvider = TestObjects.GetScopeProvider(Logger); + using (var scope = scopeProvider.CreateScope(autoComplete: true)) + { + var entity = new ContentVersionCleanupPolicyDto + { + ContentTypeId = contentType.Id, + KeepAllVersionsNewerThanDays = daysToKeepAll, + KeepLatestVersionPerDayForDays = daysToRollupAll, + PreventCleanup = preventCleanup, + Updated = DateTime.Today + }; + + scope.Database.Insert(entity); + } + } + + class Report + { + public int ContentVersions { get; set; } + public int DocumentVersions { get; set; } + public int PropertyData { get; set; } + } + } +} diff --git a/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_UnitTests.cs b/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_UnitTests.cs new file mode 100644 index 000000000000..f72b61b4ce21 --- /dev/null +++ b/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_UnitTests.cs @@ -0,0 +1,264 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using AutoFixture.NUnit3; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Composing; +using Umbraco.Core.Events; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Scoping; +using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; +using Umbraco.Tests.Testing; + +namespace Umbraco.Tests.Services +{ + /// + /// v9 -> Tests.UnitTests + /// Sut here is ContentService, but in v9 should be a new class + /// + [TestFixture] + public class ContentVersionCleanupService_Tests_UnitTests + { + [SetUp] + public void Setup() + { + Current.Reset(); + } + + /// + /// For v9 this just needs a rewrite, no static events, no service location etc + /// + [Test, AutoMoqData] + public void PerformContentVersionCleanup_Always_RespectsDeleteRevisionsCancellation( + [Frozen] Mock factory, + [Frozen] Mock scope, + Mock documentVersionRepository, + List someHistoricVersions, + DateTime aDateTime, + ContentService sut) + { + factory.Setup(x => x.GetInstance(typeof(IDocumentVersionRepository))) + .Returns(documentVersionRepository.Object); + + factory.Setup(x => x.GetInstance(typeof(IContentVersionCleanupPolicy))) + .Returns(new EchoingCleanupPolicyStub()); + + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(someHistoricVersions); + + scope.Setup(x => x.Events).Returns(new PassThroughEventDispatcher()); + + // Wire up service locator + Current.Factory = factory.Object; + + void OnDeletingVersions(IContentService sender, DeleteRevisionsEventArgs args) => args.Cancel = true; + + ContentService.DeletingVersions += OnDeletingVersions; + + // # Act + var report = sut.PerformContentVersionCleanup(aDateTime); + + ContentService.DeletingVersions -= OnDeletingVersions; + + Assert.AreEqual(0, report.Count); + } + + /// + /// For v9 this just needs a rewrite, no static events, no service location etc + /// + [Test, AutoMoqData] + public void PerformContentVersionCleanup_Always_FiresDeletedVersionsForEachDeletedVersion( + [Frozen] Mock factory, + [Frozen] Mock scope, + Mock documentVersionRepository, + List someHistoricVersions, + DateTime aDateTime, + ContentService sut) + { + factory.Setup(x => x.GetInstance(typeof(IDocumentVersionRepository))) + .Returns(documentVersionRepository.Object); + + factory.Setup(x => x.GetInstance(typeof(IContentVersionCleanupPolicy))) + .Returns(new EchoingCleanupPolicyStub()); + + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(someHistoricVersions); + + scope.Setup(x => x.Events).Returns(new PassThroughEventDispatcher()); + + // Wire up service locator + Current.Factory = factory.Object; + + // v9 can Mock + Verify + var deletedAccordingToEvents = 0; + void OnDeletedVersions(IContentService sender, DeleteRevisionsEventArgs args) => deletedAccordingToEvents++; + + ContentService.DeletedVersions += OnDeletedVersions; + + // # Act + sut.PerformContentVersionCleanup(aDateTime); + + ContentService.DeletedVersions -= OnDeletedVersions; + + Assert.Multiple(() => + { + Assert.Greater(deletedAccordingToEvents, 0); + Assert.AreEqual(someHistoricVersions.Count, deletedAccordingToEvents); + }); + } + + /// + /// For v9 this just needs a rewrite, no static events, no service location etc + /// + [Test, AutoMoqData] + public void PerformContentVersionCleanup_Always_ReturnsReportOfDeletedItems( + [Frozen] Mock factory, + [Frozen] Mock scope, + Mock documentVersionRepository, + List someHistoricVersions, + DateTime aDateTime, + ContentService sut) + { + factory.Setup(x => x.GetInstance(typeof(IDocumentVersionRepository))) + .Returns(documentVersionRepository.Object); + + factory.Setup(x => x.GetInstance(typeof(IContentVersionCleanupPolicy))) + .Returns(new EchoingCleanupPolicyStub()); + + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(someHistoricVersions); + + scope.Setup(x => x.Events).Returns(new PassThroughEventDispatcher()); + + // Wire up service locator + Current.Factory = factory.Object; + + // # Act + var report = sut.PerformContentVersionCleanup(aDateTime); + + Assert.Multiple(() => + { + Assert.Greater(report.Count, 0); + Assert.AreEqual(someHistoricVersions.Count, report.Count); + }); + } + + /// + /// For v9 this just needs a rewrite, no static events, no service location etc + /// + [Test, AutoMoqData] + public void PerformContentVersionCleanup_Always_AdheresToCleanupPolicy( + [Frozen] Mock factory, + [Frozen] Mock scope, + Mock documentVersionRepository, + Mock cleanupPolicy, + List someHistoricVersions, + DateTime aDateTime, + ContentService sut) + { + factory.Setup(x => x.GetInstance(typeof(IDocumentVersionRepository))) + .Returns(documentVersionRepository.Object); + + factory.Setup(x => x.GetInstance(typeof(IContentVersionCleanupPolicy))) + .Returns(cleanupPolicy.Object); + + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(someHistoricVersions); + + scope.Setup(x => x.Events).Returns(new PassThroughEventDispatcher()); + + cleanupPolicy.Setup(x => x.Apply(It.IsAny(), It.IsAny>())) + .Returns>((_, items) => items.Take(1)); + + // Wire up service locator + Current.Factory = factory.Object; + + // # Act + var report = sut.PerformContentVersionCleanup(aDateTime); + + Debug.Assert(someHistoricVersions.Count > 1); + + Assert.Multiple(() => + { + cleanupPolicy.Verify(x => x.Apply(aDateTime, someHistoricVersions), Times.Once); + Assert.AreEqual(someHistoricVersions.First(), report.Single()); + }); + } + + /// + /// For v9 this just needs a rewrite, no static events, no service location etc + /// + [Test, AutoMoqData] + public void PerformContentVersionCleanup_HasVersionsToDelete_CallsDeleteOnRepositoryWithFilteredSet( + [Frozen] Mock factory, + [Frozen] Mock scope, + Mock documentVersionRepository, + Mock cleanupPolicy, + List someHistoricVersions, + DateTime aDateTime, + ContentService sut) + { + factory.Setup(x => x.GetInstance(typeof(IDocumentVersionRepository))) + .Returns(documentVersionRepository.Object); + + factory.Setup(x => x.GetInstance(typeof(IContentVersionCleanupPolicy))) + .Returns(cleanupPolicy.Object); + + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(someHistoricVersions); + + scope.Setup(x => x.Events).Returns(new PassThroughEventDispatcher()); + + var filteredSet = someHistoricVersions.Take(1); + + cleanupPolicy.Setup(x => x.Apply(It.IsAny(), It.IsAny>())) + .Returns>((_, items) => filteredSet); + + // Wire up service locator + Current.Factory = factory.Object; + + // # Act + var report = sut.PerformContentVersionCleanup(aDateTime); + + Debug.Assert(someHistoricVersions.Any()); + + var expectedId = filteredSet.First().VersionId; + + documentVersionRepository.Verify(x => x.DeleteVersions(It.Is>(y => y.Single() == expectedId)), Times.Once); + } + + class EchoingCleanupPolicyStub : IContentVersionCleanupPolicy + { + /// + /// What goes in, must come out + /// + public EchoingCleanupPolicyStub() { } + + /* Note: Could just wire up a mock but its quite wordy. + * + * cleanupPolicy.Setup(x => x.Apply(It.IsAny(), It.IsAny>())) + * .Returns>((date, items) => items); + */ + public IEnumerable Apply( + DateTime asAtDate, + IEnumerable items + ) => items; + } + + /// + /// NPoco < 5 requires a parameter-less constructor but plays nice with get-only properties. + /// Moq won't play nice with get-only properties, but doesn't require a parameter-less constructor. + /// + /// Inheritance solves this so that we get values for test data without a specimen builder + /// + public class TestHistoricContentVersionMeta : HistoricContentVersionMeta + { + public TestHistoricContentVersionMeta(int contentId, int contentTypeId, int versionId, DateTime versionDate) + : base(contentId, contentTypeId, versionId, versionDate) { } + } + } +} diff --git a/src/Umbraco.Tests/Services/DefaultContentVersionCleanupPolicy_Tests_UnitTests.cs b/src/Umbraco.Tests/Services/DefaultContentVersionCleanupPolicy_Tests_UnitTests.cs new file mode 100644 index 000000000000..c20bcc72ef5e --- /dev/null +++ b/src/Umbraco.Tests/Services/DefaultContentVersionCleanupPolicy_Tests_UnitTests.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AutoFixture.NUnit3; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Services.Implement; +using Umbraco.Tests.Testing; + +namespace Umbraco.Tests.Services +{ + [TestFixture] + public class DefaultContentVersionCleanupPolicy_Tests_UnitTests + { + [Test, AutoMoqData] + public void Apply_AllOlderThanKeepSettings_AllVersionsReturned( + [Frozen] Mock documentVersionRepository, + [Frozen] Mock globalSettings, + DefaultContentVersionCleanupPolicy sut) + { + var versionId = 0; + + var historicItems = new List + { + new HistoricContentVersionMeta(versionId: ++versionId, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-1)), + new HistoricContentVersionMeta(versionId: ++versionId, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-1)), + }; + + globalSettings.Setup(x => x.ContentVersionCleanupPolicyGlobalSettings) + .Returns(new TestCleanupSettings(true, 0, 0)); + + documentVersionRepository.Setup(x => x.GetCleanupPolicies()) + .Returns(Array.Empty()); + + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(historicItems); + + var results = sut.Apply(DateTime.Today, historicItems).ToList(); + + Assert.AreEqual(2, results.Count); + } + + [Test, AutoMoqData] + public void Apply_OverlappingKeepSettings_KeepAllVersionsNewerThanDaysTakesPriority( + [Frozen] Mock documentVersionRepository, + [Frozen] Mock globalSettings, + DefaultContentVersionCleanupPolicy sut) + { + var versionId = 0; + + var historicItems = new List + { + new HistoricContentVersionMeta(versionId: ++versionId, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-1)), + new HistoricContentVersionMeta(versionId: ++versionId, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-1)), + }; + + globalSettings.Setup(x => x.ContentVersionCleanupPolicyGlobalSettings) + .Returns(new TestCleanupSettings(true, 2, 2)); + + documentVersionRepository.Setup(x => x.GetCleanupPolicies()) + .Returns(Array.Empty()); + + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(historicItems); + + var results = sut.Apply(DateTime.Today, historicItems).ToList(); + + Assert.AreEqual(0, results.Count); + } + + [Test, AutoMoqData] + public void Apply_WithinInKeepLatestPerDay_ReturnsSinglePerContentPerDay( + [Frozen] Mock documentVersionRepository, + [Frozen] Mock globalSettings, + DefaultContentVersionCleanupPolicy sut) + { + var historicItems = new List + { + new HistoricContentVersionMeta(versionId: 1, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-3)), + new HistoricContentVersionMeta(versionId: 2, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-2)), + new HistoricContentVersionMeta(versionId: 3, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-1)), + + new HistoricContentVersionMeta(versionId: 4, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddDays(-1).AddHours(-3)), + new HistoricContentVersionMeta(versionId: 5, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddDays(-1).AddHours(-2)), + new HistoricContentVersionMeta(versionId: 6, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddDays(-1).AddHours(-1)), + // another content + new HistoricContentVersionMeta(versionId: 7, contentId: 2, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-3)), + new HistoricContentVersionMeta(versionId: 8, contentId: 2, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-2)), + new HistoricContentVersionMeta(versionId: 9, contentId: 2, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-1)), + }; + + globalSettings.Setup(x => x.ContentVersionCleanupPolicyGlobalSettings) + .Returns(new TestCleanupSettings(true, 0, 3)); + + documentVersionRepository.Setup(x => x.GetCleanupPolicies()) + .Returns(Array.Empty()); + + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(historicItems); + + var results = sut.Apply(DateTime.Today, historicItems).ToList(); + + Assert.Multiple(() => + { + Assert.AreEqual(3, results.Count); + Assert.True(results.Exists(x => x.VersionId == 3)); + Assert.True(results.Exists(x => x.VersionId == 6)); + Assert.True(results.Exists(x => x.VersionId == 9)); + }); + } + + [Test, AutoMoqData] + public void Apply_HasOverridePolicy_RespectsPreventCleanup( + [Frozen] Mock documentVersionRepository, + [Frozen] Mock globalSettings, + DefaultContentVersionCleanupPolicy sut) + { + var historicItems = new List + { + new HistoricContentVersionMeta(versionId: 1, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-3)), + new HistoricContentVersionMeta(versionId: 2, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-2)), + new HistoricContentVersionMeta(versionId: 3, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-1)), + // another content & type + new HistoricContentVersionMeta(versionId: 4, contentId: 2, contentTypeId: 2, versionDate: DateTime.Today.AddHours(-3)), + new HistoricContentVersionMeta(versionId: 5, contentId: 2, contentTypeId: 2, versionDate: DateTime.Today.AddHours(-2)), + new HistoricContentVersionMeta(versionId: 6, contentId: 2, contentTypeId: 2, versionDate: DateTime.Today.AddHours(-1)), + }; + + globalSettings.Setup(x => x.ContentVersionCleanupPolicyGlobalSettings) + .Returns(new TestCleanupSettings(true, 0, 0)); + + documentVersionRepository.Setup(x => x.GetCleanupPolicies()) + .Returns(new ContentVersionCleanupPolicySettings[] + { + new ContentVersionCleanupPolicySettings{ ContentTypeId = 2, PreventCleanup = true } + }); + + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(historicItems); + + var results = sut.Apply(DateTime.Today, historicItems).ToList(); + + Assert.True(results.All(x => x.ContentTypeId == 1)); + } + + [Test, AutoMoqData] + public void Apply_HasOverridePolicy_RespectsKeepAll( + [Frozen] Mock documentVersionRepository, + [Frozen] Mock globalSettings, + DefaultContentVersionCleanupPolicy sut) + { + var historicItems = new List + { + new HistoricContentVersionMeta(versionId: 1, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-3)), + new HistoricContentVersionMeta(versionId: 2, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-2)), + new HistoricContentVersionMeta(versionId: 3, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-1)), + // another content & type + new HistoricContentVersionMeta(versionId: 4, contentId: 2, contentTypeId: 2, versionDate: DateTime.Today.AddHours(-3)), + new HistoricContentVersionMeta(versionId: 5, contentId: 2, contentTypeId: 2, versionDate: DateTime.Today.AddHours(-2)), + new HistoricContentVersionMeta(versionId: 6, contentId: 2, contentTypeId: 2, versionDate: DateTime.Today.AddHours(-1)), + }; + + globalSettings.Setup(x => x.ContentVersionCleanupPolicyGlobalSettings) + .Returns(new TestCleanupSettings(true, 0, 0)); + + documentVersionRepository.Setup(x => x.GetCleanupPolicies()) + .Returns(new ContentVersionCleanupPolicySettings[] + { + new ContentVersionCleanupPolicySettings{ ContentTypeId = 2, PreventCleanup = false, KeepAllVersionsNewerThanDays = 3 } + }); + + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(historicItems); + + var results = sut.Apply(DateTime.Today, historicItems).ToList(); + + Assert.True(results.All(x => x.ContentTypeId == 1)); + } + + [Test, AutoMoqData] + public void Apply_HasOverridePolicy_RespectsKeepLatest( + [Frozen] Mock documentVersionRepository, + [Frozen] Mock globalSettings, + DefaultContentVersionCleanupPolicy sut) + { + var historicItems = new List + { + new HistoricContentVersionMeta(versionId: 1, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-3)), + new HistoricContentVersionMeta(versionId: 2, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-2)), + new HistoricContentVersionMeta(versionId: 3, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-1)), + // another content & type + new HistoricContentVersionMeta(versionId: 4, contentId: 2, contentTypeId: 2, versionDate: DateTime.Today.AddHours(-3)), + new HistoricContentVersionMeta(versionId: 5, contentId: 2, contentTypeId: 2, versionDate: DateTime.Today.AddHours(-2)), + new HistoricContentVersionMeta(versionId: 6, contentId: 2, contentTypeId: 2, versionDate: DateTime.Today.AddHours(-1)), + }; + + globalSettings.Setup(x => x.ContentVersionCleanupPolicyGlobalSettings) + .Returns(new TestCleanupSettings(true, 0, 0)); + + documentVersionRepository.Setup(x => x.GetCleanupPolicies()) + .Returns(new ContentVersionCleanupPolicySettings[] + { + new ContentVersionCleanupPolicySettings{ ContentTypeId = 2, PreventCleanup = false, KeepLatestVersionPerDayForDays = 3 } + }); + + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(historicItems); + + var results = sut.Apply(DateTime.Today, historicItems).ToList(); + + Assert.Multiple(() => + { + Assert.AreEqual(3, results.Count(x => x.ContentTypeId == 1)); + Assert.AreEqual(6, results.Single(x => x.ContentTypeId == 2).VersionId); + }); + } + + class TestCleanupSettings : IContentVersionCleanupPolicyGlobalSettings + { + public bool EnableCleanup { get; set; } + public int KeepAllVersionsNewerThanDays { get; set; } + public int KeepLatestVersionPerDayForDays { get; set; } + + public TestCleanupSettings() { } + + public TestCleanupSettings(bool enable, int keepDays, int keepLatestDays) + { + EnableCleanup = enable; + KeepAllVersionsNewerThanDays = keepDays; + KeepLatestVersionPerDayForDays = keepLatestDays; + } + } + } +} diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs index d8902d2d6297..0de9a43ecd8c 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs @@ -19,7 +19,15 @@ public static Content CreateBasicContent(IContentType contentType) public static Content CreateSimpleContent(IContentType contentType) { - var content = new Content("Home", -1, contentType) { Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 }; + var content = new Content("Home", -1, contentType) + { + Level = 1, + SortOrder = 1, + CreatorId = 0, + WriterId = 0, + Key = Guid.NewGuid() + }; + object obj = new { diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index d4bf4d14ec04..123f59fae8ec 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -216,6 +216,7 @@ public static ContentType CreateSimpleContentType(string alias, string name, ICo contentType.SortOrder = 1; contentType.CreatorId = 0; contentType.Trashed = false; + contentType.Key = Guid.NewGuid(); var contentCollection = new PropertyTypeCollection(true); contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext) { Alias = RandomAlias("title", randomizeAliases), Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88, LabelOnTop = true }); diff --git a/src/Umbraco.Tests/Testing/AutoMoqDataAttribute.cs b/src/Umbraco.Tests/Testing/AutoMoqDataAttribute.cs new file mode 100644 index 000000000000..ac03a10053ff --- /dev/null +++ b/src/Umbraco.Tests/Testing/AutoMoqDataAttribute.cs @@ -0,0 +1,13 @@ +using AutoFixture; +using AutoFixture.AutoMoq; +using AutoFixture.NUnit3; + +namespace Umbraco.Tests.Testing +{ + public class AutoMoqDataAttribute : AutoDataAttribute + { + public AutoMoqDataAttribute() + : base(() => new Fixture().Customize(new AutoMoqCustomization{ ConfigureMembers = true })) + { } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index c00c67ca1ce7..bb0dadf60d2d 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -78,6 +78,8 @@ + + @@ -100,10 +102,11 @@ - + - - + + + @@ -150,6 +153,7 @@ + @@ -166,13 +170,17 @@ + + + + @@ -183,6 +191,7 @@ + diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config index f41eb10ce1d3..0e51c63cfaf3 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config @@ -191,7 +191,7 @@ assets/img/application/umbraco_logo_white.svg - + diff --git a/src/Umbraco.Web/Scheduling/ContentVersionCleanup.cs b/src/Umbraco.Web/Scheduling/ContentVersionCleanup.cs new file mode 100644 index 000000000000..4fccbfab0a74 --- /dev/null +++ b/src/Umbraco.Web/Scheduling/ContentVersionCleanup.cs @@ -0,0 +1,79 @@ +using System; +using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Logging; +using Umbraco.Core.Services; +using Umbraco.Core.Sync; + +namespace Umbraco.Web.Scheduling +{ + internal class ContentVersionCleanup : RecurringTaskBase + { + private readonly IRuntimeState _runtimeState; + private readonly IProfilingLogger _logger; + private readonly IContentVersionCleanupPolicyGlobalSettings _settings; + private readonly IContentVersionCleanupService _cleanupService; + + public ContentVersionCleanup( + IBackgroundTaskRunner runner, + long delayMilliseconds, + long periodMilliseconds, + IRuntimeState runtimeState, + IProfilingLogger logger, + IContentVersionCleanupPolicyGlobalSettings settings, + IContentVersionCleanupService cleanupService) + : base(runner, delayMilliseconds, periodMilliseconds) + { + _runtimeState = runtimeState ?? throw new ArgumentNullException(nameof(runtimeState)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _settings = settings ?? throw new ArgumentNullException(nameof(settings)); + _cleanupService = cleanupService ?? throw new ArgumentNullException(nameof(cleanupService)); + } + + public override bool PerformRun() + { + // Globally disabled by feature flag + if (!_settings.EnableCleanup) + { + _logger.Info("ContentVersionCleanup task will not run as it has been globally disabled via configuration."); + return false; + } + + if (_runtimeState.Level != RuntimeLevel.Run) + { + return true; // repeat... + } + + switch (_runtimeState.ServerRole) + { + case ServerRole.Replica: + _logger.Debug("Does not run on replica servers."); + return true; // DO repeat, server role can change + case ServerRole.Unknown: + _logger.Debug("Does not run on servers with unknown role."); + return true; // DO repeat, server role can change + case ServerRole.Single: + case ServerRole.Master: + default: + break; + } + + // Ensure we do not run if not main domain, but do NOT lock it + if (!_runtimeState.IsMainDom) + { + _logger.Debug("Does not run if not MainDom."); + return false; // do NOT repeat, going down + } + + _logger.Info("Starting ContentVersionCleanup task."); + + var report = _cleanupService.PerformContentVersionCleanup(DateTime.Now); + + _logger.Info("Finished ContentVersionCleanup task. Removed {count} item(s).", report.Count); + + return true; + } + + public override bool IsAsync => false; + } +} diff --git a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs index f353a9506e29..a8f4cc1e767d 100644 --- a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs @@ -25,6 +25,7 @@ public sealed class SchedulerComponent : IComponent private readonly IRuntimeState _runtime; private readonly IContentService _contentService; + private readonly IContentVersionCleanupService _cleanupService; private readonly IAuditService _auditService; private readonly IProfilingLogger _logger; private readonly IScopeProvider _scopeProvider; @@ -38,18 +39,26 @@ public sealed class SchedulerComponent : IComponent private BackgroundTaskRunner _scrubberRunner; private BackgroundTaskRunner _fileCleanupRunner; private BackgroundTaskRunner _healthCheckRunner; + private BackgroundTaskRunner _contentVersionCleanupRunner; private bool _started; private object _locker = new object(); private IBackgroundTask[] _tasks; - public SchedulerComponent(IRuntimeState runtime, - IContentService contentService, IAuditService auditService, - HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, - IScopeProvider scopeProvider, IUmbracoContextFactory umbracoContextFactory, IProfilingLogger logger) + public SchedulerComponent( + IRuntimeState runtime, + IContentService contentService, + IContentVersionCleanupService cleanupService, + IAuditService auditService, + HealthCheckCollection healthChecks, + HealthCheckNotificationMethodCollection notifications, + IScopeProvider scopeProvider, + IUmbracoContextFactory umbracoContextFactory, + IProfilingLogger logger) { _runtime = runtime; _contentService = contentService; + _cleanupService = cleanupService; _auditService = auditService; _scopeProvider = scopeProvider; _logger = logger; @@ -68,6 +77,7 @@ public void Initialize() _scrubberRunner = new BackgroundTaskRunner("LogScrubber", _logger); _fileCleanupRunner = new BackgroundTaskRunner("TempFileCleanup", _logger); _healthCheckRunner = new BackgroundTaskRunner("HealthCheckNotifier", _logger); + _contentVersionCleanupRunner = new BackgroundTaskRunner("ContentVersionCleanup", _logger); // we will start the whole process when a successful request is made UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce; @@ -107,6 +117,7 @@ private void RegisterBackgroundTasks() tasks.Add(RegisterScheduledPublishing()); tasks.Add(RegisterLogScrubber(settings)); tasks.Add(RegisterTempFileCleanup()); + tasks.Add(RegisterContentVersionCleanup(settings)); var healthCheckConfig = Current.Configs.HealthChecks(); if (healthCheckConfig.NotificationSettings.Enabled) @@ -180,5 +191,23 @@ private IBackgroundTask RegisterTempFileCleanup() _fileCleanupRunner.TryAdd(task); return task; } + + private IBackgroundTask RegisterContentVersionCleanup(IUmbracoSettingsSection settings) + { + // content version cleanup + // install on all, will only run on non-replica servers. + var task = new ContentVersionCleanup( + _contentVersionCleanupRunner, + DefaultDelayMilliseconds, + OneHourMilliseconds, + _runtime, + _logger, + settings.Content.ContentVersionCleanupPolicyGlobalSettings, + _cleanupService); + + _contentVersionCleanupRunner.TryAdd(task); + + return task; + } } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 5f6ab67a42c3..7d1f50754c0e 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -320,6 +320,7 @@ + From 86f344c484ff1e556ff15f999ea5b6cf85e7798a Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Tue, 26 Oct 2021 16:30:57 +0100 Subject: [PATCH 04/26] Post review feedback Less logging @ info level More re-usable migration --- .../V_8_18_0/AddContentVersionCleanupFeature.cs | 16 +++++++++++----- .../Scheduling/ContentVersionCleanup.cs | 13 +++++++++---- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_18_0/AddContentVersionCleanupFeature.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_18_0/AddContentVersionCleanupFeature.cs index bfa204ec2423..5ec05c1dc9f1 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_18_0/AddContentVersionCleanupFeature.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_18_0/AddContentVersionCleanupFeature.cs @@ -1,5 +1,4 @@ -using System.Linq; -using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.Persistence.Dtos; namespace Umbraco.Core.Migrations.Upgrade.V_8_18_0 { @@ -8,12 +7,19 @@ class AddContentVersionCleanupFeature : MigrationBase public AddContentVersionCleanupFeature(IMigrationContext context) : base(context) { } + /// + /// The conditionals are useful to enable the same migration to be used in multiple + /// migration paths x.x -> 8.18 and x.x -> 9.x + /// public override void Migrate() { - Create.Table().Do(); + var tables = SqlSyntax.GetTablesInSchema(Context.Database); + if (!tables.InvariantContains(ContentVersionCleanupPolicyDto.TableName)) + { + Create.Table().Do(); + } - // What's this about, we worry someone else edited table with same change? - var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList(); + var columns = SqlSyntax.GetColumnsInSchema(Context.Database); AddColumnIfNotExists(columns, "preventCleanup"); } } diff --git a/src/Umbraco.Web/Scheduling/ContentVersionCleanup.cs b/src/Umbraco.Web/Scheduling/ContentVersionCleanup.cs index 4fccbfab0a74..41971a652486 100644 --- a/src/Umbraco.Web/Scheduling/ContentVersionCleanup.cs +++ b/src/Umbraco.Web/Scheduling/ContentVersionCleanup.cs @@ -65,11 +65,16 @@ public override bool PerformRun() return false; // do NOT repeat, going down } - _logger.Info("Starting ContentVersionCleanup task."); + var count = _cleanupService.PerformContentVersionCleanup(DateTime.Now).Count; - var report = _cleanupService.PerformContentVersionCleanup(DateTime.Now); - - _logger.Info("Finished ContentVersionCleanup task. Removed {count} item(s).", report.Count); + if (count > 0) + { + _logger.Info("Deleted {count} ContentVersion(s).", count); + } + else + { + _logger.Debug("Task complete, no items were Deleted."); + } return true; } From d0a0a0a9cbc32b1f5ff241bd71fb90508d0f6247 Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Tue, 26 Oct 2021 18:04:23 +0100 Subject: [PATCH 05/26] Downgrade NUnit and NUnit3TestAdapter --- src/Umbraco.Tests/Umbraco.Tests.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index bb0dadf60d2d..be63380dbc07 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -104,8 +104,8 @@ - - + + From ae20c495908d25660923ae8b8c81dcb2471666ad Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 28 Oct 2021 10:02:04 +0200 Subject: [PATCH 06/26] Do paging in database, when viewing rollbacks --- .../Repositories/Implement/DocumentRepository.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index 753630d186d0..89b34e3f7c18 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -266,9 +266,11 @@ public override IEnumerable GetAllVersionsSlim(int nodeId, int skip, i .OrderByDescending(x => x.Current) .AndByDescending(x => x.VersionDate); - return MapDtosToContent(Database.Fetch(sql), true, + var pageIndex = skip / take; + + return MapDtosToContent(Database.Page(pageIndex+1, take, sql).Items, true, // load bare minimum, need variants though since this is used to rollback with variants - false, false, false, true).Skip(skip).Take(take); + false, false, false, true); } public override IContent GetVersion(int versionId) From b96ec5a3206c419b42dd2def9861f32d4aba6e3e Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 28 Oct 2021 14:14:07 +0200 Subject: [PATCH 07/26] UI+Persistence for history cleanup per doc type --- .../Models/ContentEditing/HistoryCleanup.cs | 16 + src/Umbraco.Core/Models/ContentType.cs | 5 + src/Umbraco.Core/Models/IContentType.cs | 5 + .../Implement/ContentTypeCommonRepository.cs | 33 +- .../Implement/ContentTypeRepository.cs | 14 + src/Umbraco.Core/Umbraco.Core.csproj | 1 + src/Umbraco.Web.UI.Client/package-lock.json | 517 +++++++----------- .../services/umbdataformatter.service.js | 2 +- .../permissions/permissions.controller.js | 14 +- .../views/permissions/permissions.html | 26 + src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 5 + .../Umbraco/config/lang/en_us.xml | 5 + .../Models/ContentEditing/ContentTypeSave.cs | 3 + .../ContentEditing/DocumentTypeDisplay.cs | 2 + .../Models/ContentEditing/HistoryCleanup.cs | 14 + .../Mapping/ContentTypeMapDefinition.cs | 17 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 17 files changed, 367 insertions(+), 313 deletions(-) create mode 100644 src/Umbraco.Core/Models/ContentEditing/HistoryCleanup.cs create mode 100644 src/Umbraco.Web/Models/ContentEditing/HistoryCleanup.cs diff --git a/src/Umbraco.Core/Models/ContentEditing/HistoryCleanup.cs b/src/Umbraco.Core/Models/ContentEditing/HistoryCleanup.cs new file mode 100644 index 000000000000..7ac2fd94f2b4 --- /dev/null +++ b/src/Umbraco.Core/Models/ContentEditing/HistoryCleanup.cs @@ -0,0 +1,16 @@ +using System.Runtime.Serialization; + +namespace Umbraco.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; } +} diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs index 453670253e8f..70bc9eaa4af3 100644 --- a/src/Umbraco.Core/Models/ContentType.cs +++ b/src/Umbraco.Core/Models/ContentType.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using System.Runtime.Serialization; +using Umbraco.Core.Models.ContentEditing; namespace Umbraco.Core.Models { @@ -47,6 +48,8 @@ public ContentType(IContentType parent, string alias) /// public override bool SupportsPublishing => SupportsPublishingConst; + + //Custom comparer for enumerable private static readonly DelegateEqualityComparer> TemplateComparer = new DelegateEqualityComparer>( (templates, enumerable) => templates.UnsortedSequenceEqual(enumerable), @@ -93,6 +96,8 @@ public IEnumerable AllowedTemplates } } + public HistoryCleanup HistoryCleanup { get; set; } + /// /// Determines if AllowedTemplates contains templateId /// diff --git a/src/Umbraco.Core/Models/IContentType.cs b/src/Umbraco.Core/Models/IContentType.cs index 99fffdca7b35..d109f452de3a 100644 --- a/src/Umbraco.Core/Models/IContentType.cs +++ b/src/Umbraco.Core/Models/IContentType.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Umbraco.Core.Models.ContentEditing; namespace Umbraco.Core.Models { @@ -17,6 +18,10 @@ public interface IContentType : IContentTypeComposition ///
IEnumerable AllowedTemplates { get; set; } + /// + /// Gets or Sets the history cleanup configuration + /// + HistoryCleanup HistoryCleanup { get; set; } /// /// Determines if AllowedTemplates contains templateId /// diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs index af3b12f050cb..00246d87ccf6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs @@ -5,6 +5,7 @@ using Umbraco.Core.Cache; using Umbraco.Core.Exceptions; using Umbraco.Core.Models; +using Umbraco.Core.Models.ContentEditing; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Scoping; @@ -109,6 +110,7 @@ private IEnumerable GetAllTypesInternal() MapTemplates(contentTypes); MapComposition(contentTypes); MapGroupsAndProperties(contentTypes); + MapHistoryCleanup(contentTypes); // finalize foreach (var contentType in contentTypes.Values) @@ -119,6 +121,35 @@ private IEnumerable GetAllTypesInternal() return contentTypes.Values; } + private void MapHistoryCleanup(Dictionary contentTypes) + { + // get templates + var sql1 = Sql() + .Select() + .From() + .OrderBy(x => x.ContentTypeId); + + var contentVersionCleanupPolicyDtos = Database.Fetch(sql1); + + var contentVersionCleanupPolicyDictionary = + contentVersionCleanupPolicyDtos.ToDictionary(x => x.ContentTypeId); + foreach (var c in contentTypes.Values) + { + if (!(c is ContentType contentType)) continue; + + var historyCleanup = new HistoryCleanup(); + + if (contentVersionCleanupPolicyDictionary.TryGetValue(contentType.Id, out var versionCleanup)) + { + historyCleanup.PreventCleanup = versionCleanup.PreventCleanup; + historyCleanup.KeepAllVersionsNewerThanDays = versionCleanup.KeepAllVersionsNewerThanDays; + historyCleanup.KeepLatestVersionPerDayForDays = versionCleanup.KeepLatestVersionPerDayForDays; + } + + contentType.HistoryCleanup = historyCleanup; + } + } + private void MapTemplates(Dictionary contentTypes) { // get templates @@ -145,7 +176,7 @@ private void MapTemplates(Dictionary contentTypes) templateDtoIx++; if (!templates.TryGetValue(allowedDto.TemplateNodeId, out var template)) continue; allowedTemplates.Add(template); - + if (allowedDto.IsDefault) defaultTemplateId = template.Id; } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs index 0dcbc72fca6d..88a4199f1999 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs @@ -289,8 +289,22 @@ protected override void PersistUpdatedItem(IContentType entity) PersistUpdatedBaseContentType(entity); PersistTemplates(entity, true); + PersistHistoryCleanup(entity); entity.ResetDirtyProperties(); } + + private void PersistHistoryCleanup(IContentType entity) + { + ContentVersionCleanupPolicyDto dto = new ContentVersionCleanupPolicyDto() + { + ContentTypeId = entity.Id, + Updated = DateTime.Now, + PreventCleanup = entity.HistoryCleanup.PreventCleanup, + KeepAllVersionsNewerThanDays = entity.HistoryCleanup.KeepAllVersionsNewerThanDays, + KeepLatestVersionPerDayForDays = entity.HistoryCleanup.KeepLatestVersionPerDayForDays, + }; + Database.InsertOrUpdate(dto); + } } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 818891b376a8..3bc7aea782d8 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -163,6 +163,7 @@ + diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 61e0299f3ac5..0fa891c29ec1 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -110,7 +110,7 @@ "@babel/helper-annotate-as-pure": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", - "integrity": "sha1-Mj053QtQ4Qx8Bsp9djjmhk2MXDI=", + "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", "dev": true, "requires": { "@babel/types": "^7.0.0" @@ -119,7 +119,7 @@ "@babel/helper-builder-binary-assignment-operator-visitor": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz", - "integrity": "sha1-a2lijf5Ah3mODE7Zjj1Kay+9L18=", + "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==", "dev": true, "requires": { "@babel/helper-explode-assignable-expression": "^7.1.0", @@ -202,7 +202,7 @@ "@babel/helper-explode-assignable-expression": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz", - "integrity": "sha1-U3+hP28WdN90WwwA7I/k6ZaByPY=", + "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", @@ -212,7 +212,7 @@ "@babel/helper-function-name": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha1-oM6wFoX3M1XUNgwSR/WCv6/I/1M=", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.0.0", @@ -223,7 +223,7 @@ "@babel/helper-get-function-arity": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha1-g1ctQyDipGVyY3NBE8QoaLZOScM=", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", "dev": true, "requires": { "@babel/types": "^7.0.0" @@ -250,7 +250,7 @@ "@babel/helper-module-imports": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", - "integrity": "sha1-lggbcRHkhtpNLNlxrRpP4hbMLj0=", + "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", "dev": true, "requires": { "@babel/types": "^7.0.0" @@ -273,7 +273,7 @@ "@babel/helper-optimise-call-expression": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", - "integrity": "sha1-opIMVwKwc8Fd5REGIAqoytIEl9U=", + "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", "dev": true, "requires": { "@babel/types": "^7.0.0" @@ -282,7 +282,7 @@ "@babel/helper-plugin-utils": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha1-u7P77phmHFaQNCN8wDlnupm08lA=", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", "dev": true }, "@babel/helper-regex": { @@ -297,7 +297,7 @@ "@babel/helper-remap-async-to-generator": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", - "integrity": "sha1-Nh2AghtvONp1vT8HheziCojF/n8=", + "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.0.0", @@ -322,7 +322,7 @@ "@babel/helper-simple-access": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", - "integrity": "sha1-Ze65VMjCRb6qToWdphiPOdceWFw=", + "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", "dev": true, "requires": { "@babel/template": "^7.1.0", @@ -1083,7 +1083,7 @@ "accord": { "version": "0.29.0", "resolved": "https://registry.npmjs.org/accord/-/accord-0.29.0.tgz", - "integrity": "sha1-t0HBdtAENcWSnUZt/oz2vukzseQ=", + "integrity": "sha512-3OOR92FTc2p5/EcOzPcXp+Cbo+3C15nV9RXHlOUBCBpHhcB+0frbSNR9ehED/o7sTcyGVtqGJpguToEdlXhD0w==", "dev": true, "requires": { "convert-source-map": "^1.5.0", @@ -1119,7 +1119,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -1130,7 +1130,7 @@ "ace-builds": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.2.tgz", - "integrity": "sha1-avwuQ6e17/3ETYQHQ2EShSVo6A0=" + "integrity": "sha512-M1JtZctO2Zg+1qeGUFZXtYKsyaRptqQtqpVzlj80I0NzGW9MF3um0DBuizIvQlrPYUlTdm+wcOPZpZoerkxQdA==" }, "acorn": { "version": "7.1.0", @@ -1228,7 +1228,7 @@ "angular-animate": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.7.5.tgz", - "integrity": "sha1-H/xsKpze4ieiunnMbNj3HsRNtdw=" + "integrity": "sha512-kU/fHIGf2a4a3bH7E1tzALTHk+QfoUSCK9fEcMFisd6ZWvNDwPzXWAilItqOC3EDiAXPmGHaNc9/aXiD9xrAxQ==" }, "angular-aria": { "version": "1.7.9", @@ -1258,12 +1258,12 @@ "angular-cookies": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-cookies/-/angular-cookies-1.7.5.tgz", - "integrity": "sha1-HFqzwFzcQ/F3e+lQbmRYfLNUNjQ=" + "integrity": "sha512-/8xvvSl/Z9Vwu8ChRm+OQE3vmli8Icwl8uTYkHqD7j7cknJP9kNaf7SgsENlsLVtOqLE/I7TCFYrSx3bmSeNQA==" }, "angular-dynamic-locale": { "version": "0.1.37", "resolved": "https://registry.npmjs.org/angular-dynamic-locale/-/angular-dynamic-locale-0.1.37.tgz", - "integrity": "sha1-fon70uxFvdaryJ82zaiJODjkk1Q=", + "integrity": "sha512-m5Kyk8W8/mOZSqRxuByOwHBjv8labLBAgvl0Z3iQx2xT/tWCqb94imKUPwumudszdPDjxeopwyucQvm8Sw7ogw==", "requires": { "@types/angular": "^1.6.25" } @@ -1271,7 +1271,7 @@ "angular-i18n": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-i18n/-/angular-i18n-1.7.5.tgz", - "integrity": "sha1-Lie2Thl3qMa2sFHFHQF1xtTcglI=" + "integrity": "sha512-52+Jpt8HRJV2bqSbSU6fWkwOvGzj/DxbNpKXxnTuCS9heuJrlm77BS/lhrF4BA8+Uudnh7npr5/yRELobP+8Yw==" }, "angular-local-storage": { "version": "0.7.1", @@ -1281,32 +1281,32 @@ "angular-messages": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-messages/-/angular-messages-1.7.5.tgz", - "integrity": "sha1-fC/XgTFaQ6GYOLEX2gFCqYhFThQ=" + "integrity": "sha512-YDpJpFLyrIgZjE/sIAjgww1y6r3QqXBJbNDI0QjftD37vHXLkwvAOo3A4bxPw8BikyGLcJrFrgf6hRAzntJIWA==" }, "angular-mocks": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.7.5.tgz", - "integrity": "sha1-yLq6WgbtYLk0aXAmtJIWliavOEs=" + "integrity": "sha512-I+Ue2Bkx6R9W5178DYrNvzjIdGh4wKKoCWsgz8dc7ysH4mA70Q3M9v5xRF0RUu7r+2CZj+nDeUecvh2paxcYvg==" }, "angular-route": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-route/-/angular-route-1.7.5.tgz", - "integrity": "sha1-NKNkjEB6FKAw0HXPSFMY4zuiPw4=" + "integrity": "sha512-7KfyEVVOWTI+jTY/j5rUNCIHGRyeCOx7YqZI/Ci3IbDK7GIsy6xH+hS5ai0Xi0sLjzDZ0PUDO4gBn+K0dVtlOg==" }, "angular-sanitize": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.7.5.tgz", - "integrity": "sha1-ddSeFQccqccFgedtIJQPJjcuJNI=" + "integrity": "sha512-wjKCJOIwrkEvfD0keTnKGi6We13gtoCAQIHcdoqyoo3gwvcgNfYymVQIS3+iCGVcjfWz0jHuS3KgB4ysRWsTTA==" }, "angular-touch": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-touch/-/angular-touch-1.7.5.tgz", - "integrity": "sha1-7SYyKmhfApmyPLauqYNMEZQk2kY=" + "integrity": "sha512-XNAZNG0RA1mtdwBJheViCF1H/7wOygp4MLIfs5y1K+rne6AeaYKZcV6EJs9fvgfLKLO6ecm1+3J8hoCkdhhxQw==" }, "angular-ui-sortable": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/angular-ui-sortable/-/angular-ui-sortable-0.19.0.tgz", - "integrity": "sha1-SsQ5H8TU3lcRDbS10xp8GY0xT9A=", + "integrity": "sha512-u/uc981Nzg4XN1bMU9qKleMTSt7F1XjMWnyGw6gxPLIeQeLZm8jWNy7tj8y2r2HmvzXFbQVq2z6rObznFKAekQ==", "requires": { "angular": ">=1.2.x", "jquery": ">=3.1.x", @@ -1369,7 +1369,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -1654,7 +1654,7 @@ "array-sort": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", - "integrity": "sha1-5MBTVkU/VvU1EqfR1hI/LFTAqIo=", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", "dev": true, "requires": { "default-compare": "^1.0.0", @@ -1665,7 +1665,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } @@ -1704,7 +1704,7 @@ "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "dev": true, "requires": { "safer-buffer": "~2.1.0" @@ -1725,7 +1725,7 @@ "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha1-bIw/uCfdQ+45GPJ7gngqt2WKb9k=", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, "async": { @@ -1787,7 +1787,7 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, "autoprefixer": { @@ -1822,7 +1822,7 @@ "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha1-8OAD2cqef1nHpQiUXXsu+aBKVC8=", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, "babel-plugin-dynamic-import-node": { @@ -1928,8 +1928,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true, - "optional": true + "dev": true }, "base64id": { "version": "1.0.0", @@ -2176,7 +2175,6 @@ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", "dev": true, - "optional": true, "requires": { "p-finally": "^1.0.0" } @@ -2218,7 +2216,6 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha1-oWCRFxcQPAdBDO9j71Gzl8Alr5w=", "dev": true, - "optional": true, "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -2228,15 +2225,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2252,7 +2247,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -2262,7 +2256,7 @@ "blob": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha1-1oDu7yX4zZGtUz9bAe7UjmTK9oM=", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", "dev": true }, "bluebird": { @@ -2292,7 +2286,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -2393,7 +2387,6 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", "dev": true, - "optional": true, "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -2419,8 +2412,7 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true, - "optional": true + "dev": true }, "buffer-equal": { "version": "1.0.0", @@ -2437,7 +2429,7 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, "bufferstreams": { @@ -2552,7 +2544,7 @@ }, "callsites": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", "dev": true }, @@ -2585,7 +2577,7 @@ "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha1-Xk2Q4idJYdRikZl99Znj7QCO5MA=", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -2611,7 +2603,6 @@ "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", "integrity": "sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==", "dev": true, - "optional": true, "requires": { "get-proxy": "^2.0.0", "isurl": "^1.0.0-alpha5", @@ -2643,7 +2634,7 @@ "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha1-kAlISfCTfy7twkJdDSip5fDLrZ4=", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, "chart.js": { @@ -2695,7 +2686,7 @@ "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha1-vLJLTzeTTZqnrBe0ra+J58du8us=", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { "micromatch": "^3.1.4", @@ -2756,7 +2747,7 @@ "clean-css": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha1-LUEe92uFabbQyEBo2r6FsKpeXBc=", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", "dev": true, "requires": { "source-map": "~0.6.0" @@ -2765,7 +2756,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } @@ -2802,7 +2793,7 @@ "clipboard": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", - "integrity": "sha1-g22v1mzw/qXXHOXVsL9ulYAJES0=", + "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", "requires": { "good-listener": "^1.2.2", "select": "^1.1.2", @@ -2969,7 +2960,7 @@ "color-string": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha1-ybvF8BtYtUkvPWhXRZy2WQziBMw=", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", "dev": true, "requires": { "color-name": "^1.0.0", @@ -3051,7 +3042,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "dev": true, - "optional": true, "requires": { "graceful-readlink": ">= 1.0.0" } @@ -3083,7 +3073,7 @@ "concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ=", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -3146,7 +3136,6 @@ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", "dev": true, - "optional": true, "requires": { "ini": "^1.3.4", "proto-list": "~1.2.1" @@ -3167,7 +3156,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -3202,7 +3191,6 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", "dev": true, - "optional": true, "requires": { "safe-buffer": "5.1.2" } @@ -3216,7 +3204,7 @@ "convert-source-map": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha1-UbU3qMQ+DwTewZk7/83VBOdYrCA=", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", "dev": true, "requires": { "safe-buffer": "~5.1.1" @@ -3237,7 +3225,7 @@ "copy-props": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", - "integrity": "sha1-k7scrfr9MdpbuKnUtB9HHsOnLf4=", + "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", "dev": true, "requires": { "each-props": "^1.3.0", @@ -3309,7 +3297,7 @@ "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha1-Sl7Hxk364iw6FBJNus3uhG2Ay8Q=", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -3348,7 +3336,7 @@ "css-declaration-sorter": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", - "integrity": "sha1-wZiUD2OnbX42wecQGLABchBUyyI=", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", "dev": true, "requires": { "postcss": "^7.0.1", @@ -3358,7 +3346,7 @@ "css-select": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.2.tgz", - "integrity": "sha1-q0OGzsnh9miFVWSxfDcztDsqXt4=", + "integrity": "sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==", "dev": true, "requires": { "boolbase": "^1.0.0", @@ -3370,7 +3358,7 @@ "css-select-base-adapter": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha1-Oy/0lyzDYquIVhUHqVQIoUMhNdc=", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", "dev": true }, "css-tree": { @@ -3398,7 +3386,7 @@ "cssesc": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", - "integrity": "sha1-OxO9G7HLNuG8taTc0n9UxdyzVwM=", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", "dev": true }, "cssnano": { @@ -3466,7 +3454,7 @@ "cssnano-util-raw-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", - "integrity": "sha1-sm1f1fcqEd/np4RvtMZyYPlr8oI=", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -3475,13 +3463,13 @@ "cssnano-util-same-parent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", - "integrity": "sha1-V0CC+yhZ0ttDOFWDXZqEVuoYu/M=", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", "dev": true }, "csso": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz", - "integrity": "sha1-e564vmFiiXPBsmHhadLwJACOdYs=", + "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==", "dev": true, "requires": { "css-tree": "1.0.0-alpha.29" @@ -3490,7 +3478,7 @@ "css-tree": { "version": "1.0.0-alpha.29", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz", - "integrity": "sha1-P6nU7zFCy9HDAedmTB81K9gvWjk=", + "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==", "dev": true, "requires": { "mdn-data": "~1.1.0", @@ -3646,7 +3634,6 @@ "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz", "integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=", "dev": true, - "optional": true, "requires": { "decompress-tar": "^4.0.0", "decompress-tarbz2": "^4.0.0", @@ -3663,7 +3650,6 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, - "optional": true, "requires": { "pify": "^3.0.0" }, @@ -3672,8 +3658,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true + "dev": true } } } @@ -3684,7 +3669,6 @@ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "dev": true, - "optional": true, "requires": { "mimic-response": "^1.0.0" } @@ -3694,7 +3678,6 @@ "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", "dev": true, - "optional": true, "requires": { "file-type": "^5.2.0", "is-stream": "^1.1.0", @@ -3705,8 +3688,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -3715,7 +3697,6 @@ "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", "dev": true, - "optional": true, "requires": { "decompress-tar": "^4.1.0", "file-type": "^6.1.0", @@ -3728,8 +3709,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", - "dev": true, - "optional": true + "dev": true } } }, @@ -3738,7 +3718,6 @@ "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", "dev": true, - "optional": true, "requires": { "decompress-tar": "^4.1.1", "file-type": "^5.2.0", @@ -3749,8 +3728,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -3759,7 +3737,6 @@ "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", "dev": true, - "optional": true, "requires": { "file-type": "^3.8.0", "get-stream": "^2.2.0", @@ -3771,15 +3748,13 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", - "dev": true, - "optional": true + "dev": true }, "get-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", "dev": true, - "optional": true, "requires": { "object-assign": "^4.0.1", "pinkie-promise": "^2.0.0" @@ -3789,8 +3764,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true + "dev": true } } }, @@ -3803,7 +3777,7 @@ "default-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", - "integrity": "sha1-y2ETGESthNhHiPto/QFoHKd4Gi8=", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", "dev": true, "requires": { "kind-of": "^5.0.2" @@ -3812,7 +3786,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } @@ -3826,7 +3800,7 @@ "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { "object-keys": "^1.0.12" @@ -3882,7 +3856,7 @@ "delegate": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha1-tmtxwxWFIuirV0T3INjKDCr1kWY=" + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" }, "depd": { "version": "1.1.2", @@ -3922,7 +3896,7 @@ "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=" + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" }, "dir-glob": { "version": "3.0.1", @@ -4015,7 +3989,7 @@ "domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha1-Vuo0HoNOBuZ0ivehyyXaZ+qfjCo=", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", "dev": true, "requires": { "dom-serializer": "0", @@ -4025,7 +3999,7 @@ "dot-prop": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha1-HxngwuGqDjJ5fEl5nyg3rGr2nFc=", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "dev": true, "requires": { "is-obj": "^1.0.0" @@ -4072,8 +4046,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -4090,8 +4063,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true, - "optional": true + "dev": true }, "duplexify": { "version": "3.7.1", @@ -4140,7 +4112,7 @@ "each-props": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", - "integrity": "sha1-6kWkFNFt1c+kGbGoFyDVygaJIzM=", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", "dev": true, "requires": { "is-plain-object": "^2.0.1", @@ -4219,7 +4191,7 @@ "engine.io": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", - "integrity": "sha1-tgKBw1SEpw7gNR6g6/+D7IyVIqI=", + "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", "dev": true, "requires": { "accepts": "~1.3.4", @@ -4292,7 +4264,7 @@ "engine.io-parser": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", - "integrity": "sha1-dXq5cPvy37Mse3SwMyFtVznveaY=", + "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", "dev": true, "requires": { "after": "0.8.2", @@ -4333,7 +4305,7 @@ "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "requires": { "is-arrayish": "^0.2.1" @@ -4360,7 +4332,7 @@ "es-to-primitive": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha1-7fckeAM0VujdqO8J4ArZZQcH83c=", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", "dev": true, "requires": { "is-callable": "^1.1.4", @@ -4605,7 +4577,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "esquery": { @@ -4700,7 +4672,6 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, - "optional": true, "requires": { "cross-spawn": "^5.0.1", "get-stream": "^3.0.0", @@ -4716,7 +4687,6 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, - "optional": true, "requires": { "lru-cache": "^4.0.1", "shebang-command": "^1.2.0", @@ -4753,7 +4723,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -4797,7 +4767,7 @@ "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { "is-number": "^2.1.0", @@ -4856,7 +4826,6 @@ "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", "dev": true, - "optional": true, "requires": { "mime-db": "^1.28.0" } @@ -4866,7 +4835,6 @@ "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", "dev": true, - "optional": true, "requires": { "ext-list": "^2.0.0", "sort-keys-length": "^1.0.0" @@ -4875,7 +4843,7 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "extend-shallow": { @@ -4984,7 +4952,7 @@ "fancy-log": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", - "integrity": "sha1-28GRVPVYaQFQojlToK29A1vkX8c=", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", "dev": true, "requires": { "ansi-gray": "^0.1.1", @@ -5101,7 +5069,6 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, - "optional": true, "requires": { "pend": "~1.2.0" } @@ -5140,15 +5107,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", - "dev": true, - "optional": true + "dev": true }, "filenamify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz", "integrity": "sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==", "dev": true, - "optional": true, "requires": { "filename-reserved-regex": "^2.0.0", "strip-outer": "^1.0.0", @@ -5196,7 +5161,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -5436,7 +5401,7 @@ "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -5509,8 +5474,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", - "dev": true, - "optional": true + "dev": true }, "fs-mkdirp-stream": { "version": "1.0.0", @@ -5557,8 +5521,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -5579,14 +5542,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5601,20 +5562,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -5731,8 +5689,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -5744,7 +5701,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5759,7 +5715,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5767,14 +5722,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5793,7 +5746,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5874,8 +5826,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5887,7 +5838,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5973,8 +5923,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -6010,7 +5959,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6030,7 +5978,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6074,14 +6021,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -6100,7 +6045,7 @@ "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, "get-proxy": { @@ -6108,7 +6053,6 @@ "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", "integrity": "sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==", "dev": true, - "optional": true, "requires": { "npm-conf": "^1.1.0" } @@ -6117,15 +6061,13 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true, - "optional": true + "dev": true }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true, - "optional": true + "dev": true }, "get-value": { "version": "2.0.6", @@ -6440,8 +6382,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true, - "optional": true + "dev": true }, "growly": { "version": "1.3.0", @@ -6485,7 +6426,7 @@ "gulp-babel": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/gulp-babel/-/gulp-babel-8.0.0.tgz", - "integrity": "sha1-4NqW9PLsSojdOjAw9HbjirISbYc=", + "integrity": "sha512-oomaIqDXxFkg7lbpBou/gnUkX51/Y/M2ZfSjL2hdqXTAlSWZcgZtd2o0cOH0r/eE8LWD0+Q/PsLsr2DKOoqToQ==", "dev": true, "requires": { "plugin-error": "^1.0.1", @@ -6609,7 +6550,7 @@ "vinyl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", - "integrity": "sha1-2FsH2pbkWNJbL/4Z/s6fLKoT7YY=", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", "dev": true, "requires": { "clone": "^2.1.1", @@ -6655,7 +6596,7 @@ "gulp-less": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/gulp-less/-/gulp-less-4.0.1.tgz", - "integrity": "sha1-NIwzpd3nogfFdxsdgmHRrBAhzu0=", + "integrity": "sha512-hmM2k0FfQp7Ptm3ZaqO2CkMX3hqpiIOn4OHtuSsCeFym63F7oWlEua5v6u1cIjVUKYsVIs9zPg9vbqTEb/udpA==", "dev": true, "requires": { "accord": "^0.29.0", @@ -6700,7 +6641,7 @@ }, "kind-of": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", "dev": true }, @@ -6734,7 +6675,7 @@ "gulp-notify": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/gulp-notify/-/gulp-notify-3.2.0.tgz", - "integrity": "sha1-KugiUAnfiB7vWb5d1aLxM3OHdk4=", + "integrity": "sha512-qEocs1UVoDKKUjfsxJNMNwkRla0PbsyJwsqNNXpzYWsLQ29LhxRMY3wnTGZcc4hMHtalnvah/Dwlwb4NijH/0A==", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -6820,7 +6761,7 @@ "gulp-postcss": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-8.0.0.tgz", - "integrity": "sha1-jTdyzU0nvKVeyMtMjlduO95NxVA=", + "integrity": "sha512-Wtl6vH7a+8IS/fU5W9IbOpcaLqKxd5L1DUOzaPmlnCbX1CrG0aWdwVnC3Spn8th0m8D59YbysV5zPUe1n/GJYg==", "dev": true, "requires": { "fancy-log": "^1.3.2", @@ -6833,7 +6774,7 @@ "gulp-rename": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.4.0.tgz", - "integrity": "sha1-3hxxjnxAla6GH3KW708ySGSCQL0=", + "integrity": "sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==", "dev": true }, "gulp-sort": { @@ -6912,7 +6853,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -6934,7 +6875,7 @@ "gulp-watch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/gulp-watch/-/gulp-watch-5.0.1.tgz", - "integrity": "sha1-g9N4dS9b+0baAj5zwX7R2nBmIV0=", + "integrity": "sha512-HnTSBdzAOFIT4wmXYPDUn783TaYAq9bpaN05vuZNP5eni3z3aRx0NAKbjhhMYtcq76x4R1wf4oORDGdlrEjuog==", "dev": true, "requires": { "ansi-colors": "1.1.0", @@ -7019,7 +6960,7 @@ "vinyl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", - "integrity": "sha1-2FsH2pbkWNJbL/4Z/s6fLKoT7YY=", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", "dev": true, "requires": { "clone": "^2.1.1", @@ -7145,7 +7086,7 @@ "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha1-HvievT5JllV2de7ZiTEQ3DUPoIA=", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "dev": true, "requires": { "ajv": "^6.5.5", @@ -7155,7 +7096,7 @@ "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { "function-bind": "^1.1.1" @@ -7212,8 +7153,7 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", - "dev": true, - "optional": true + "dev": true }, "has-symbols": { "version": "1.0.0", @@ -7226,7 +7166,6 @@ "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", "dev": true, - "optional": true, "requires": { "has-symbol-support-x": "^1.4.1" } @@ -7266,7 +7205,7 @@ "hex-color-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha1-TAb8y0YC/iYCs8k9+C1+fb8aio4=", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", "dev": true }, "homedir-polyfill": { @@ -7299,7 +7238,7 @@ "html-comment-regex": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha1-l9RoiutcgYhqNk+qDK0d2hTUM6c=", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", "dev": true }, "html-encoding-sniffer": { @@ -7416,7 +7355,7 @@ "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -7426,13 +7365,12 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true, - "optional": true + "dev": true }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha1-dQ49tYYgh7RzfrrIIH/9HvJ7Jfw=", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, "image-size": { @@ -7557,7 +7495,6 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, - "optional": true, "requires": { "repeating": "^2.0.0" } @@ -7695,7 +7632,7 @@ "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha1-YQ88ksk1nOHbYW5TgAjSP/NRWOY=", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, "requires": { "loose-envify": "^1.0.0" @@ -7885,7 +7822,6 @@ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7938,8 +7874,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=", - "dev": true, - "optional": true + "dev": true }, "is-negated-glob": { "version": "1.0.0", @@ -7977,15 +7912,13 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", - "dev": true, - "optional": true + "dev": true }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true, - "optional": true + "dev": true }, "is-plain-object": { "version": "2.0.4", @@ -8055,20 +7988,18 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true, - "optional": true + "dev": true }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true, - "optional": true + "dev": true }, "is-svg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", - "integrity": "sha1-kyHb0pwhLlypnE+peUxxS8r6L3U=", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", "dev": true, "requires": { "html-comment-regex": "^1.1.0" @@ -8077,7 +8008,7 @@ "is-symbol": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha1-oFX2rlcZLK7jKeeoYBGLSXqVDzg=", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", "dev": true, "requires": { "has-symbols": "^1.0.0" @@ -8131,7 +8062,7 @@ "isbinaryfile": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha1-XW3vPt6/boyoyunDAYOoBLX4voA=", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", "dev": true, "requires": { "buffer-alloc": "^1.2.0" @@ -8160,7 +8091,6 @@ "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", "dev": true, - "optional": true, "requires": { "has-to-string-tag-x": "^1.2.0", "is-object": "^1.0.1" @@ -8208,7 +8138,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { @@ -8349,7 +8279,7 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha1-gFZNLkg9rPbo7yCWUKZ98/DCg6Q=", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, "json-buffer": { @@ -8362,7 +8292,7 @@ "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha1-u4Z8+zRQ5pEHwTHRxRS6s9yLyqk=", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, "json-schema": { @@ -8587,7 +8517,7 @@ "karma-jasmine": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-2.0.1.tgz", - "integrity": "sha1-JuPjHy+vJy3YDrsOGJiRTMOhl2M=", + "integrity": "sha512-iuC0hmr9b+SNn1DaUD2QEYtUxkS1J+bSJSn7ejdEexs7P8EYvA1CWkEdrDQ+8jVH3AgWlCNwjYsT1chjcNW9lA==", "dev": true, "requires": { "jasmine-core": "^3.3" @@ -8749,7 +8679,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "optional": true } @@ -9052,7 +8982,7 @@ "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha1-ce5R+nvkyuwaY4OffmgtgTLTDK8=", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -9073,8 +9003,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", - "dev": true, - "optional": true + "dev": true }, "lpad-align": { "version": "1.1.2", @@ -9144,8 +9073,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "optional": true + "dev": true }, "map-visit": { "version": "1.0.0", @@ -9315,8 +9243,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "optional": true + "dev": true }, "minimatch": { "version": "3.0.4", @@ -9329,7 +9256,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -9380,7 +9307,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true } @@ -9409,7 +9336,7 @@ "mute-stdout": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", - "integrity": "sha1-rLAwDrTeI6fd7sAU4+lgRLNHIzE=", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", "dev": true }, "mute-stream": { @@ -9428,7 +9355,7 @@ "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -9458,7 +9385,7 @@ }, "next-tick": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, @@ -9470,7 +9397,7 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, "node-notifier": { @@ -9535,7 +9462,7 @@ "normalize-url": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha1-suHE3E98bVd0PfczpPWXjRhlBVk=", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", "dev": true }, "nouislider": { @@ -12651,7 +12578,6 @@ "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", "dev": true, - "optional": true, "requires": { "config-chain": "^1.1.11", "pify": "^3.0.0" @@ -12661,8 +12587,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -12671,7 +12596,6 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, - "optional": true, "requires": { "path-key": "^2.0.0" } @@ -12679,7 +12603,7 @@ "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha1-sr0pXDfj3VijvwcAN2Zjuk2c8Fw=", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", "dev": true, "requires": { "boolbase": "~1.0.0" @@ -12706,7 +12630,7 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", "dev": true }, "object-assign": { @@ -12908,7 +12832,7 @@ "dependencies": { "minimist": { "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "dev": true }, @@ -12999,7 +12923,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -13033,8 +12957,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true, - "optional": true + "dev": true }, "p-is-promise": { "version": "1.1.0", @@ -13071,7 +12994,6 @@ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", "dev": true, - "optional": true, "requires": { "p-finally": "^1.0.0" } @@ -13221,7 +13143,7 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, "path-root": { @@ -13262,8 +13184,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true, - "optional": true + "dev": true }, "performance-now": { "version": "2.1.0", @@ -13279,7 +13200,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, @@ -13301,7 +13222,7 @@ "plugin-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha1-dwFr2JGdCsN3/c3QMiMolTyleBw=", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -13339,7 +13260,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "supports-color": { @@ -13356,7 +13277,7 @@ "postcss-calc": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.1.tgz", - "integrity": "sha1-Ntd7qwI7Dsu5eJ2E3LI8SUEUVDY=", + "integrity": "sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ==", "dev": true, "requires": { "css-unit-converter": "^1.1.1", @@ -13381,7 +13302,7 @@ "postcss-convert-values": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", - "integrity": "sha1-yjgT7U2g+BL51DcDWE5Enr4Ymn8=", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", "dev": true, "requires": { "postcss": "^7.0.0", @@ -13400,7 +13321,7 @@ "postcss-discard-duplicates": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha1-P+EzzTyCKC5VD8myORdqkge3hOs=", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13409,7 +13330,7 @@ "postcss-discard-empty": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha1-yMlR6fc+2UKAGUWERKAq2Qu592U=", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13418,7 +13339,7 @@ "postcss-discard-overridden": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha1-ZSrvipZybwKfXj4AFG7npOdV/1c=", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13476,7 +13397,7 @@ "postcss-minify-font-values": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", - "integrity": "sha1-zUw0TM5HQ0P6xdgiBqssvLiv1aY=", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", "dev": true, "requires": { "postcss": "^7.0.0", @@ -13537,7 +13458,7 @@ "postcss-normalize-charset": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", - "integrity": "sha1-izWt067oOhNrBHHg1ZvlilAoXdQ=", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13603,7 +13524,7 @@ "postcss-normalize-unicode": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", - "integrity": "sha1-hBvUj9zzAZrUuqdJOj02O1KuHPs=", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -13614,7 +13535,7 @@ "postcss-normalize-url": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", - "integrity": "sha1-EOQ3+GvHx+WPe5ZS7YeNqqlfquE=", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", "dev": true, "requires": { "is-absolute-url": "^2.0.0", @@ -13694,7 +13615,7 @@ "postcss-unique-selectors": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", - "integrity": "sha1-lEaRHzKJv9ZMbWgPBzwDsfnuS6w=", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", "dev": true, "requires": { "alphanum-sort": "^1.0.0", @@ -13705,7 +13626,7 @@ "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha1-n/giVH4okyE88cMO+lGsX9G6goE=", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true }, "prelude-ls": { @@ -13735,14 +13656,14 @@ }, "pretty-hrtime": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8=", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, "process-nextick-args": { @@ -13771,8 +13692,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", - "dev": true, - "optional": true + "dev": true }, "prr": { "version": "1.0.1", @@ -13817,7 +13737,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, "q": { @@ -13862,7 +13782,7 @@ "randomatic": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha1-t3bvxZN1mE42xTey9RofCv8Noe0=", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", "dev": true, "requires": { "is-number": "^4.0.0", @@ -13873,7 +13793,7 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", "dev": true } } @@ -13932,7 +13852,7 @@ "readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha1-DodiKjMlqjPokihcr4tOhGUppSU=", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -14001,7 +13921,7 @@ "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha1-SoVuxLVuQHfFV1icroXnpMiGmhE=", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", "dev": true }, "regenerate-unicode-properties": { @@ -14044,7 +13964,7 @@ "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha1-jRnTHPYySCtYkEn4KB+T28uk0H8=", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, "regexpu-core": { @@ -14114,7 +14034,7 @@ "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha1-eC4NglwMWjuzlzH4Tv7mt0Lmsc4=", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", "dev": true }, "repeat-string": { @@ -14128,7 +14048,6 @@ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, - "optional": true, "requires": { "is-finite": "^1.0.0" } @@ -14153,7 +14072,7 @@ "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha1-nC/KT301tZLv5Xx/ClXoEFIST+8=", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "dev": true, "optional": true, "requires": { @@ -14393,7 +14312,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -14415,7 +14334,7 @@ }, "kind-of": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", "dev": true }, @@ -14490,7 +14409,6 @@ "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", "dev": true, - "optional": true, "requires": { "commander": "~2.8.1" } @@ -14585,7 +14503,7 @@ "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha1-1rkYHBpI05cyTISHHvvPxz/AZUs=", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "dev": true }, "signal-exit": { @@ -14597,7 +14515,7 @@ "signalr": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/signalr/-/signalr-2.4.0.tgz", - "integrity": "sha1-kq8AjmtSetSzbpT7s0DhNQh6YNI=", + "integrity": "sha512-GPJHb3pcNk3IUui5/WG8lMuarEn+Vpc8wEvJ60w0KQ43W9FHnJcuNcF8dkZePr81eBslzicsRdyEunKNF7KjZQ==", "requires": { "jquery": ">=1.6.4" } @@ -14614,7 +14532,7 @@ "is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha1-RXSirlb3qyBolvtDHq7tBm/fjwM=", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "dev": true } } @@ -14663,7 +14581,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -14769,7 +14687,7 @@ "socket.io": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", - "integrity": "sha1-oGnF/qvuPmshSnW0DOBlLhz7mYA=", + "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", "dev": true, "requires": { "debug": "~3.1.0", @@ -14783,7 +14701,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -14806,7 +14724,7 @@ "socket.io-client": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha1-3LOBA0NqtFeN2wJmOK4vIbYjZx8=", + "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", "dev": true, "requires": { "backo2": "1.0.2", @@ -14834,7 +14752,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -14893,7 +14811,6 @@ "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", "dev": true, - "optional": true, "requires": { "is-plain-obj": "^1.0.0" } @@ -14903,7 +14820,6 @@ "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=", "dev": true, - "optional": true, "requires": { "sort-keys": "^1.0.0" } @@ -14917,7 +14833,7 @@ "source-map-resolve": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha1-cuLMNAlVQ+Q7LGKyxMENSpBU8lk=", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, "requires": { "atob": "^2.1.1", @@ -14936,7 +14852,7 @@ "sparkles": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", - "integrity": "sha1-AI22XtzmxQ7sDF4ijhlFBh3QQ3w=", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", "dev": true }, "spdx-correct": { @@ -14952,7 +14868,7 @@ "spdx-exceptions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha1-LqRQrudPKom/uUUZwH/Nb0EyKXc=", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", "dev": true }, "spdx-expression-parse": { @@ -15012,7 +14928,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "optional": true, @@ -15123,7 +15039,7 @@ "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { "ms": "^2.1.1" @@ -15234,7 +15150,6 @@ "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", "dev": true, - "optional": true, "requires": { "is-natural-number": "^4.0.1" } @@ -15243,8 +15158,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true, - "optional": true + "dev": true }, "strip-indent": { "version": "1.0.1", @@ -15267,7 +15181,6 @@ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", "dev": true, - "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15299,7 +15212,7 @@ "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -15391,9 +15304,8 @@ "tar-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", - "integrity": "sha1-jqVdqzeXIlPZqa+Q/c1VmuQ1xVU=", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", "dev": true, - "optional": true, "requires": { "bl": "^1.0.0", "buffer-alloc": "^1.2.0", @@ -15408,15 +15320,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -15430,9 +15340,8 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -15443,15 +15352,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", - "dev": true, - "optional": true + "dev": true }, "tempfile": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", "integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=", "dev": true, - "optional": true, "requires": { "temp-dir": "^1.0.0", "uuid": "^3.0.1" @@ -15478,7 +15385,7 @@ "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha1-AcHjnrMdB8t9A6lqcIIyYLIxMs0=", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { "readable-stream": "~2.3.6", @@ -15546,8 +15453,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true, - "optional": true + "dev": true }, "timers-ext": { "version": "0.1.7", @@ -15604,8 +15510,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", - "dev": true, - "optional": true + "dev": true }, "to-fast-properties": { "version": "2.0.0", @@ -15673,7 +15578,7 @@ "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha1-U/Nto/R3g7CSWvoG/587FlKA94E=", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "dev": true, "requires": { "psl": "^1.1.24", @@ -15709,7 +15614,6 @@ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", "dev": true, - "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15846,7 +15750,6 @@ "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", "dev": true, - "optional": true, "requires": { "buffer": "^5.2.1", "through": "^2.3.8" @@ -15889,13 +15792,13 @@ "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha1-JhmADEyCWADv3YNDr33Zkzy+KBg=", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", "dev": true }, "unicode-match-property-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha1-jtKjJWmWG86SJ9Cc0/+7j+1fAgw=", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", "dev": true, "requires": { "unicode-canonical-property-names-ecmascript": "^1.0.4", @@ -16047,13 +15950,12 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", - "dev": true, - "optional": true + "dev": true }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, "useragent": { @@ -16112,7 +16014,7 @@ "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { "spdx-correct": "^3.0.0", @@ -16401,7 +16303,7 @@ "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -16432,7 +16334,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -16544,7 +16446,6 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, - "optional": true, "requires": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js index 7270ae5bbf06..16e59803a1fa 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -63,7 +63,7 @@ var saveModel = _.pick(displayModel, 'compositeContentTypes', 'isContainer', 'allowAsRoot', 'allowedTemplates', 'allowedContentTypes', 'alias', 'description', 'thumbnail', 'name', 'id', 'icon', 'trashed', - 'key', 'parentId', 'alias', 'path', 'allowCultureVariant', 'allowSegmentVariant', 'isElement'); + 'key', 'parentId', 'alias', 'path', 'allowCultureVariant', 'allowSegmentVariant', 'isElement','historyCleanup'); saveModel.allowedTemplates = _.map(displayModel.allowedTemplates, function (t) { return t.alias; }); saveModel.defaultTemplate = displayModel.defaultTemplate ? displayModel.defaultTemplate.alias : null; diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js index da293a6820d9..dc2520020124 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js @@ -27,6 +27,7 @@ vm.toggleAllowSegmentVariants = toggleAllowSegmentVariants; vm.canToggleIsElement = false; vm.toggleIsElement = toggleIsElement; + vm.toggleHistoryCleanupPreventCleanup = toggleHistoryCleanupPreventCleanup; /* ---------- INIT ---------- */ @@ -55,6 +56,11 @@ } else { vm.canToggleIsElement = true; } + + if(!$scope.model.historyCleanup){ + $scope.model.historyCleanup = {}; + } + } function addChild($event) { @@ -62,7 +68,7 @@ var editor = { multiPicker: true, filterCssClass: 'not-allowed not-published', - filter: item => + filter: item => !vm.contentTypes.some(x => x.udi == item.udi) || vm.selectedChildren.some(x => x.udi === item.udi), submit: model => { model.selection.forEach(item => @@ -73,7 +79,7 @@ editorService.close(); }, - close: () => editorService.close() + close: () => editorService.close() }; editorService.contentTypePicker(editor); @@ -111,6 +117,10 @@ $scope.model.isElement = $scope.model.isElement ? false : true; } + function toggleHistoryCleanupPreventCleanup() { + $scope.model.historyCleanup.preventCleanup = $scope.model.historyCleanup.preventCleanup ? false : true; + } + } angular.module('umbraco').controller('Umbraco.Editors.DocumentType.PermissionsController', PermissionsController); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html index 99b874190384..83f3b43dca92 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html @@ -92,6 +92,32 @@
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + + +
+ +
+ diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 35e55f0c80dd..ebfa792c0b50 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1719,6 +1719,11 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Label above (full-width) You are removing the child node Removing a child node will limit the editors options to create different content types beneath a node. + History cleanup + Allow override the global settings for when history versions are removed. + Keep all versions newer than days + Keep latest version per day for days + Prevent cleanup Add language diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index fa8cfad670e9..9c147b8db32f 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1750,6 +1750,11 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Drag properties here to place directly on the tab You are removing the child node Removing a child node will limit the editors options to create different content types beneath a node. + History cleanup + Allow override the global settings for when history versions are removed. + Keep all versions newer than days + Keep latest version per day for days + Prevent cleanup Add language diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs index c210d4fc161e..eb92acbebea9 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs @@ -35,6 +35,9 @@ protected ContentTypeSave() [DataMember(Name = "allowedContentTypes")] public IEnumerable AllowedContentTypes { get; set; } + [DataMember(Name = "historyCleanup")] + public HistoryCleanupViewModel HistoryCleanup { get; set; } + /// /// Custom validation /// diff --git a/src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs index 8aaa5c8af109..27c8602b3df0 100644 --- a/src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/DocumentTypeDisplay.cs @@ -31,5 +31,7 @@ public DocumentTypeDisplay() [DataMember(Name = "apps")] public IEnumerable ContentApps { get; set; } + [DataMember(Name = "historyCleanup")] + public HistoryCleanupViewModel HistoryCleanup { get; set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/HistoryCleanup.cs b/src/Umbraco.Web/Models/ContentEditing/HistoryCleanup.cs new file mode 100644 index 000000000000..c56b02d755f1 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/HistoryCleanup.cs @@ -0,0 +1,14 @@ +using System.Runtime.Serialization; +using Umbraco.Core.Models.ContentEditing; + +namespace Umbraco.Web.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;} +} diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs index dcf8f802908b..647aa0d1297a 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Mapping; using Umbraco.Core.Models; @@ -9,6 +10,7 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Core.Services; using Umbraco.Core.Exceptions; +using Umbraco.Core.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { @@ -25,10 +27,11 @@ internal class ContentTypeMapDefinition : IMapDefinition private readonly IMediaTypeService _mediaTypeService; private readonly IMemberTypeService _memberTypeService; private readonly ILogger _logger; + private readonly IUmbracoSettingsSection _umbracoSettingsSection; public ContentTypeMapDefinition(CommonMapper commonMapper, PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, IFileService fileService, IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, - ILogger logger) + ILogger logger, IUmbracoSettingsSection umbracoSettingsSection) { _commonMapper = commonMapper; _propertyEditors = propertyEditors; @@ -38,6 +41,7 @@ public ContentTypeMapDefinition(CommonMapper commonMapper, PropertyEditorCollect _mediaTypeService = mediaTypeService; _memberTypeService = memberTypeService; _logger = logger; + _umbracoSettingsSection = umbracoSettingsSection; } public void DefineMaps(UmbracoMapper mapper) @@ -84,6 +88,8 @@ private void Map(DocumentTypeSave source, IContentType target, MapperContext con MapSaveToTypeBase(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) @@ -122,6 +128,15 @@ private void Map(IContentType source, DocumentTypeDisplay target, MapperContext { MapTypeToDisplayBase(source, target); + target.HistoryCleanup = new HistoryCleanupViewModel() + { + PreventCleanup = source.HistoryCleanup.PreventCleanup, + KeepAllVersionsNewerThanDays = source.HistoryCleanup.KeepAllVersionsNewerThanDays, + KeepLatestVersionPerDayForDays = source.HistoryCleanup.KeepLatestVersionPerDayForDays, + GlobalKeepAllVersionsNewerThanDays = _umbracoSettingsSection.Content.ContentVersionCleanupPolicyGlobalSettings.KeepAllVersionsNewerThanDays, + GlobalKeepLatestVersionPerDayForDays = _umbracoSettingsSection.Content.ContentVersionCleanupPolicyGlobalSettings.KeepLatestVersionPerDayForDays, + }; + target.AllowCultureVariant = source.VariesByCulture(); target.AllowSegmentVariant = source.VariesBySegment(); target.ContentApps = _commonMapper.GetContentApps(source); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 7d1f50754c0e..64d81cff13bd 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -256,6 +256,7 @@ + From 698e6b1533b843e4e717b8fe9c55dbdc5dff7be9 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 29 Oct 2021 10:55:47 +0200 Subject: [PATCH 08/26] Renamed interface to prepare for adding GetPagedRollbackVersions --- .../Composing/CompositionExtensions/Services.cs | 2 +- ...sionCleanupService.cs => IContentVersionService.cs} | 2 +- src/Umbraco.Core/Services/Implement/ContentService.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 2 +- .../ContentVersionCleanup_Tests_UnitTests.cs | 10 +++++----- .../ContentVersionCleanupService_Tests_Integration.cs | 2 +- src/Umbraco.Web/Scheduling/ContentVersionCleanup.cs | 8 ++++---- src/Umbraco.Web/Scheduling/SchedulerComponent.cs | 8 ++++---- 8 files changed, 18 insertions(+), 18 deletions(-) rename src/Umbraco.Core/Services/{IContentVersionCleanupService.cs => IContentVersionService.cs} (87%) diff --git a/src/Umbraco.Core/Composing/CompositionExtensions/Services.cs b/src/Umbraco.Core/Composing/CompositionExtensions/Services.cs index 56a9b2f8aede..4f9a9532123e 100644 --- a/src/Umbraco.Core/Composing/CompositionExtensions/Services.cs +++ b/src/Umbraco.Core/Composing/CompositionExtensions/Services.cs @@ -34,7 +34,7 @@ public static Composition ComposeServices(this Composition composition) composition.RegisterUnique(); composition.RegisterUnique(factory => factory.GetInstance()); - composition.RegisterUnique(factory => factory.GetInstance()); + composition.RegisterUnique(factory => factory.GetInstance()); composition.RegisterUnique(); composition.RegisterUnique(); diff --git a/src/Umbraco.Core/Services/IContentVersionCleanupService.cs b/src/Umbraco.Core/Services/IContentVersionService.cs similarity index 87% rename from src/Umbraco.Core/Services/IContentVersionCleanupService.cs rename to src/Umbraco.Core/Services/IContentVersionService.cs index 8b7e826f55f4..8c6cf0bd914d 100644 --- a/src/Umbraco.Core/Services/IContentVersionCleanupService.cs +++ b/src/Umbraco.Core/Services/IContentVersionService.cs @@ -4,7 +4,7 @@ namespace Umbraco.Core.Services { - public interface IContentVersionCleanupService + public interface IContentVersionService { /// /// Removes historic content versions according to a policy. diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 5f6782e0b477..ec381923a557 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Services.Implement /// /// Implements the content service. /// - public class ContentService : RepositoryService, IContentService, IContentVersionCleanupService + public class ContentService : RepositoryService, IContentService, IContentVersionService { private readonly IDocumentRepository _documentRepository; private readonly IEntityRepository _entityRepository; diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 3bc7aea782d8..cf5ffc9d0ca7 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -199,7 +199,7 @@ - + diff --git a/src/Umbraco.Tests/Scheduling/ContentVersionCleanup_Tests_UnitTests.cs b/src/Umbraco.Tests/Scheduling/ContentVersionCleanup_Tests_UnitTests.cs index 82c5c0077ecc..e185f2524c90 100644 --- a/src/Umbraco.Tests/Scheduling/ContentVersionCleanup_Tests_UnitTests.cs +++ b/src/Umbraco.Tests/Scheduling/ContentVersionCleanup_Tests_UnitTests.cs @@ -18,7 +18,7 @@ class ContentVersionCleanup_Tests_UnitTests public void ContentVersionCleanup_WhenNotEnabled_DoesNotCleanupWillRepeat( [Frozen] Mock settings, [Frozen] Mock state, - [Frozen] Mock cleanupService, + [Frozen] Mock cleanupService, ContentVersionCleanup sut) { settings.Setup(x => x.EnableCleanup).Returns(false); @@ -40,7 +40,7 @@ public void ContentVersionCleanup_WhenNotEnabled_DoesNotCleanupWillRepeat( public void ContentVersionCleanup_RuntimeLevelNotRun_DoesNotCleanupWillRepeat( [Frozen] Mock settings, [Frozen] Mock state, - [Frozen] Mock cleanupService, + [Frozen] Mock cleanupService, ContentVersionCleanup sut) { settings.Setup(x => x.EnableCleanup).Returns(true); @@ -62,7 +62,7 @@ public void ContentVersionCleanup_RuntimeLevelNotRun_DoesNotCleanupWillRepeat( public void ContentVersionCleanup_ServerRoleUnknown_DoesNotCleanupWillRepeat( [Frozen] Mock settings, [Frozen] Mock state, - [Frozen] Mock cleanupService, + [Frozen] Mock cleanupService, ContentVersionCleanup sut) { settings.Setup(x => x.EnableCleanup).Returns(true); @@ -84,7 +84,7 @@ public void ContentVersionCleanup_ServerRoleUnknown_DoesNotCleanupWillRepeat( public void ContentVersionCleanup_NotMainDom_DoesNotCleanupWillNotRepeat( [Frozen] Mock settings, [Frozen] Mock state, - [Frozen] Mock cleanupService, + [Frozen] Mock cleanupService, ContentVersionCleanup sut) { settings.Setup(x => x.EnableCleanup).Returns(true); @@ -106,7 +106,7 @@ public void ContentVersionCleanup_NotMainDom_DoesNotCleanupWillNotRepeat( public void ContentVersionCleanup_Enabled_DelegatesToCleanupService( [Frozen] Mock settings, [Frozen] Mock state, - [Frozen] Mock cleanupService, + [Frozen] Mock cleanupService, ContentVersionCleanup sut) { settings.Setup(x => x.EnableCleanup).Returns(true); diff --git a/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_Integration.cs b/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_Integration.cs index 3e6fe63e13f1..ed177d35c31b 100644 --- a/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_Integration.cs +++ b/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_Integration.cs @@ -47,7 +47,7 @@ public void PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExce // Kill all historic InsertCleanupPolicy(contentTypeA, 0, 0); - ((IContentVersionCleanupService)ServiceContext.ContentService).PerformContentVersionCleanup(DateTime.Now.AddHours(1)); + ((IContentVersionService)ServiceContext.ContentService).PerformContentVersionCleanup(DateTime.Now.AddHours(1)); var after = GetReport(); diff --git a/src/Umbraco.Web/Scheduling/ContentVersionCleanup.cs b/src/Umbraco.Web/Scheduling/ContentVersionCleanup.cs index 41971a652486..9a75b9be3cc0 100644 --- a/src/Umbraco.Web/Scheduling/ContentVersionCleanup.cs +++ b/src/Umbraco.Web/Scheduling/ContentVersionCleanup.cs @@ -12,7 +12,7 @@ internal class ContentVersionCleanup : RecurringTaskBase private readonly IRuntimeState _runtimeState; private readonly IProfilingLogger _logger; private readonly IContentVersionCleanupPolicyGlobalSettings _settings; - private readonly IContentVersionCleanupService _cleanupService; + private readonly IContentVersionService _service; public ContentVersionCleanup( IBackgroundTaskRunner runner, @@ -21,13 +21,13 @@ public ContentVersionCleanup( IRuntimeState runtimeState, IProfilingLogger logger, IContentVersionCleanupPolicyGlobalSettings settings, - IContentVersionCleanupService cleanupService) + IContentVersionService service) : base(runner, delayMilliseconds, periodMilliseconds) { _runtimeState = runtimeState ?? throw new ArgumentNullException(nameof(runtimeState)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _settings = settings ?? throw new ArgumentNullException(nameof(settings)); - _cleanupService = cleanupService ?? throw new ArgumentNullException(nameof(cleanupService)); + _service = service ?? throw new ArgumentNullException(nameof(service)); } public override bool PerformRun() @@ -65,7 +65,7 @@ public override bool PerformRun() return false; // do NOT repeat, going down } - var count = _cleanupService.PerformContentVersionCleanup(DateTime.Now).Count; + var count = _service.PerformContentVersionCleanup(DateTime.Now).Count; if (count > 0) { diff --git a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs index a8f4cc1e767d..02204667b388 100644 --- a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs @@ -25,7 +25,7 @@ public sealed class SchedulerComponent : IComponent private readonly IRuntimeState _runtime; private readonly IContentService _contentService; - private readonly IContentVersionCleanupService _cleanupService; + private readonly IContentVersionService _service; private readonly IAuditService _auditService; private readonly IProfilingLogger _logger; private readonly IScopeProvider _scopeProvider; @@ -48,7 +48,7 @@ public sealed class SchedulerComponent : IComponent public SchedulerComponent( IRuntimeState runtime, IContentService contentService, - IContentVersionCleanupService cleanupService, + IContentVersionService service, IAuditService auditService, HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, @@ -58,7 +58,7 @@ public SchedulerComponent( { _runtime = runtime; _contentService = contentService; - _cleanupService = cleanupService; + _service = service; _auditService = auditService; _scopeProvider = scopeProvider; _logger = logger; @@ -203,7 +203,7 @@ private IBackgroundTask RegisterContentVersionCleanup(IUmbracoSettingsSection se _runtime, _logger, settings.Content.ContentVersionCleanupPolicyGlobalSettings, - _cleanupService); + _service); _contentVersionCleanupRunner.TryAdd(task); From b0ff096a8a8b7ba8699ae179b7c68d6f1e082ff1 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 29 Oct 2021 13:58:50 +0200 Subject: [PATCH 09/26] Fixed Tests --- src/Umbraco.Core/Models/ContentType.cs | 2 ++ .../Repositories/ContentTypeRepositoryTest.cs | 3 +- .../ContentTypeServiceVariantsTests.cs | 30 ++++++++++--------- .../Entities/MockedContentTypes.cs | 5 +++- .../Models/ContentEditing/HistoryCleanup.cs | 22 +++++++++----- .../Mapping/ContentTypeMapDefinition.cs | 1 + 6 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs index 70bc9eaa4af3..ac8b90630656 100644 --- a/src/Umbraco.Core/Models/ContentType.cs +++ b/src/Umbraco.Core/Models/ContentType.cs @@ -27,6 +27,7 @@ public class ContentType : ContentTypeCompositionBase, IContentType public ContentType(int parentId) : base(parentId) { _allowedTemplates = new List(); + HistoryCleanup = new HistoryCleanup(); } @@ -40,6 +41,7 @@ public ContentType(IContentType parent, string alias) : base(parent, alias) { _allowedTemplates = new List(); + HistoryCleanup = new HistoryCleanup(); } /// diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index 6b35a79eb28d..5a45239f14a5 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -304,7 +304,7 @@ public void Can_Perform_Add_On_ContentTypeRepository() Assert.AreNotEqual(propertyType.Key, Guid.Empty); } - TestHelper.AssertPropertyValuesAreEqual(fetched, contentType, "yyyy-MM-dd HH:mm:ss", ignoreProperties: new [] { "DefaultTemplate", "AllowedTemplates", "UpdateDate" }); + TestHelper.AssertPropertyValuesAreEqual(fetched, contentType, "yyyy-MM-dd HH:mm:ss", ignoreProperties: new [] { "DefaultTemplate", "AllowedTemplates", "UpdateDate", "HistoryCleanup" }); } } @@ -415,6 +415,7 @@ private DocumentTypeSave MapToContentTypeSave(DocumentTypeDisplay display) //Alias = display.Alias, Path = display.Path, //AdditionalData = display.AdditionalData, + HistoryCleanup = display.HistoryCleanup, // ContentTypeBasic Alias = display.Alias, diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index a3735c0ac336..f4e8bcac9248 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models; +using Umbraco.Core.Models.ContentEditing; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; @@ -49,7 +50,7 @@ protected override IPublishedSnapshotService CreatePublishedSnapshotService() var runtimeStateMock = new Mock(); runtimeStateMock.Setup(x => x.Level).Returns(() => RuntimeLevel.Run); - var contentTypeFactory = Factory.GetInstance(); + var contentTypeFactory = Factory.GetInstance(); var documentRepository = Factory.GetInstance(); var mediaRepository = Mock.Of(); var memberRepository = Mock.Of(); @@ -128,8 +129,8 @@ private string GetJson(int id) [TestCase(ContentVariation.CultureAndSegment, ContentVariation.CultureAndSegment, false)] public void Change_Content_Type_Variation_Clears_Redirects(ContentVariation startingContentTypeVariation, ContentVariation changedContentTypeVariation, bool shouldUrlRedirectsBeCleared) { - var contentType = MockedContentTypes.CreateBasicContentType(); - contentType.Variations = startingContentTypeVariation; + var contentType = MockedContentTypes.CreateBasicContentType(); + contentType.Variations = startingContentTypeVariation; ServiceContext.ContentTypeService.Save(contentType); var contentType2 = MockedContentTypes.CreateBasicContentType("test"); ServiceContext.ContentTypeService.Save(contentType2); @@ -408,7 +409,7 @@ public void Change_Property_Type_From_Invariant_Variant(ContentVariation invaria Assert.AreEqual("hello world", doc.GetValue("title")); Assert.IsTrue(doc.IsCultureEdited("en-US")); //invariant prop changes show up on default lang Assert.IsTrue(doc.Edited); - + //change the property type to be variant contentType.PropertyTypes.First().Variations = variant; ServiceContext.ContentTypeService.Save(contentType); @@ -436,7 +437,7 @@ public void Change_Property_Type_From_Variant_Invariant(ContentVariation variant { //create content type with a property type that varies by culture var contentType = MockedContentTypes.CreateBasicContentType(); - // content type supports all variations + // content type supports all variations contentType.Variations = ContentVariation.Culture | ContentVariation.Segment; var properties = CreatePropertyCollection(("title", variant)); contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); @@ -473,7 +474,7 @@ public void Change_Property_Type_From_Variant_Invariant_On_A_Composition(Content { //create content type with a property type that varies by culture var contentType = MockedContentTypes.CreateBasicContentType(); - // content type supports all variations + // content type supports all variations contentType.Variations = ContentVariation.Culture | ContentVariation.Segment; var properties = CreatePropertyCollection(("title", variant)); contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" }); @@ -880,14 +881,14 @@ public void Change_Property_Variations_From_Variant_To_Invariant_And_Ensure_Edit // switch property type to Invariant contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = invariant; ServiceContext.ContentTypeService.Save(contentType); //This is going to have to re-normalize the "Edited" flag - + document = ServiceContext.ContentService.GetById(document.Id); Assert.IsTrue(document.IsCultureEdited("en")); //This will remain true because there is now a pending change for the invariant property data which is flagged under the default lang Assert.IsFalse(document.IsCultureEdited("fr")); //This will be false because nothing has changed for this culture and the property no longer reflects variant changes Assert.IsTrue(document.Edited); //update the invariant value and publish - document.SetValue("value1", "v1inv"); + document.SetValue("value1", "v1inv"); ServiceContext.ContentService.SaveAndPublish(document); document = ServiceContext.ContentService.GetById(document.Id); @@ -907,7 +908,7 @@ public void Change_Property_Variations_From_Variant_To_Invariant_And_Ensure_Edit // switch property back to Culture contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = variant; ServiceContext.ContentTypeService.Save(contentType); - + document = ServiceContext.ContentService.GetById(document.Id); Assert.AreEqual("v1inv", document.GetValue("value1", "en")); //The invariant property value gets copied over to the default language Assert.AreEqual("v1inv", document.GetValue("value1", "en", published: true)); @@ -971,9 +972,9 @@ public void Change_Property_Variations_From_Invariant_To_Variant_And_Ensure_Edit Assert.AreEqual("doc1en", document.GetCultureName("en")); Assert.AreEqual("doc1fr", document.GetCultureName("fr")); Assert.AreEqual("v1en", document.GetValue("value1")); - Assert.AreEqual("v1en-init", document.GetValue("value1", published: true)); + Assert.AreEqual("v1en-init", document.GetValue("value1", published: true)); Assert.IsTrue(document.IsCultureEdited("en")); //This is true because the invariant property reflects changes on the default lang - Assert.IsFalse(document.IsCultureEdited("fr")); + Assert.IsFalse(document.IsCultureEdited("fr")); Assert.IsTrue(document.Edited); // switch property type to Culture @@ -981,7 +982,7 @@ public void Change_Property_Variations_From_Invariant_To_Variant_And_Ensure_Edit ServiceContext.ContentTypeService.Save(contentType); //This is going to have to re-normalize the "Edited" flag document = ServiceContext.ContentService.GetById(document.Id); - Assert.IsTrue(document.IsCultureEdited("en")); //Remains true + Assert.IsTrue(document.IsCultureEdited("en")); //Remains true Assert.IsFalse(document.IsCultureEdited("fr")); //False because no french property has ever been edited Assert.IsTrue(document.Edited); @@ -1011,7 +1012,7 @@ public void Change_Property_Variations_From_Invariant_To_Variant_And_Ensure_Edit Assert.IsNull(document.GetValue("value1", "fr")); //The values are there but the business logic returns null Assert.IsNull(document.GetValue("value1", "fr", published: true)); //The values are there but the business logic returns null Assert.IsFalse(document.IsCultureEdited("en")); //The variant published AND edited values are copied over to the invariant - Assert.IsFalse(document.IsCultureEdited("fr")); + Assert.IsFalse(document.IsCultureEdited("fr")); Assert.IsFalse(document.Edited); } @@ -1263,7 +1264,8 @@ private void CreateFrenchAndEnglishLangs() { Alias = alias, Name = alias, - Variations = variance + Variations = variance, + HistoryCleanup = new HistoryCleanup() }; private PropertyTypeCollection CreatePropertyCollection(params (string alias, ContentVariation variance)[] props) diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index 123f59fae8ec..557676cd11f6 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -2,6 +2,7 @@ using System.Linq; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Core.Models.ContentEditing; namespace Umbraco.Tests.TestHelpers.Entities { @@ -19,6 +20,7 @@ public static ContentType CreateBasicContentType(string alias = "basePage", stri contentType.SortOrder = 1; contentType.CreatorId = 0; contentType.Trashed = false; + contentType.HistoryCleanup = new HistoryCleanup(); //ensure that nothing is marked as dirty contentType.ResetDirtyProperties(false); @@ -121,7 +123,8 @@ public static ContentType CreateSeoContentType() Thumbnail = "doc.png", SortOrder = 1, CreatorId = 0, - Trashed = false + Trashed = false, + HistoryCleanup = new HistoryCleanup() }; var metaCollection = new PropertyTypeCollection(true); diff --git a/src/Umbraco.Web/Models/ContentEditing/HistoryCleanup.cs b/src/Umbraco.Web/Models/ContentEditing/HistoryCleanup.cs index c56b02d755f1..7580ee826aee 100644 --- a/src/Umbraco.Web/Models/ContentEditing/HistoryCleanup.cs +++ b/src/Umbraco.Web/Models/ContentEditing/HistoryCleanup.cs @@ -1,14 +1,20 @@ using System.Runtime.Serialization; using Umbraco.Core.Models.ContentEditing; -namespace Umbraco.Web.Models.ContentEditing; - -[DataContract(Name = "historyCleanup", Namespace = "")] -public class HistoryCleanupViewModel : HistoryCleanup +namespace Umbraco.Web.Models.ContentEditing { - [DataMember(Name = "globalKeepAllVersionsNewerThanDays")] - public int? GlobalKeepAllVersionsNewerThanDays { get;set; } + [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; } + } - [DataMember(Name = "globalKeepLatestVersionPerDayForDays")] - public int? GlobalKeepLatestVersionPerDayForDays { get; set;} } + diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs index 647aa0d1297a..45c6bdfde3a5 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs @@ -135,6 +135,7 @@ private void Map(IContentType source, DocumentTypeDisplay target, MapperContext KeepLatestVersionPerDayForDays = source.HistoryCleanup.KeepLatestVersionPerDayForDays, GlobalKeepAllVersionsNewerThanDays = _umbracoSettingsSection.Content.ContentVersionCleanupPolicyGlobalSettings.KeepAllVersionsNewerThanDays, GlobalKeepLatestVersionPerDayForDays = _umbracoSettingsSection.Content.ContentVersionCleanupPolicyGlobalSettings.KeepLatestVersionPerDayForDays, + GlobalEnableCleanup = _umbracoSettingsSection.Content.ContentVersionCleanupPolicyGlobalSettings.EnableCleanup, }; target.AllowCultureVariant = source.VariesByCulture(); From 82a64ca589b82c9616c2997186ec69d754efdd52 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 29 Oct 2021 14:51:38 +0200 Subject: [PATCH 10/26] Add message if prevent cleanup is globally disabled. --- .../views/documenttypes/views/permissions/permissions.html | 6 ++++++ src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 1 + src/Umbraco.Web/Models/ContentEditing/HistoryCleanup.cs | 2 +- 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html index 83f3b43dca92..ac7c7844793d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html @@ -102,6 +102,12 @@
+

+ +
+

+
+ diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index ebfa792c0b50..e063354b1c3b 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1724,6 +1724,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Keep all versions newer than days Keep latest version per day for days Prevent cleanup + NOTE! The cleanup of historically content versions are disabled globally. These settings will not take effect before it is enabled. Add language diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 9c147b8db32f..4e289a4da409 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1755,6 +1755,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Keep all versions newer than days Keep latest version per day for days Prevent cleanup + NOTE! The cleanup of historically content versions are disabled globally. These settings will not take effect before it is enabled. Add language diff --git a/src/Umbraco.Web/Models/ContentEditing/HistoryCleanup.cs b/src/Umbraco.Web/Models/ContentEditing/HistoryCleanup.cs index 7580ee826aee..3060324d5480 100644 --- a/src/Umbraco.Web/Models/ContentEditing/HistoryCleanup.cs +++ b/src/Umbraco.Web/Models/ContentEditing/HistoryCleanup.cs @@ -12,7 +12,7 @@ public class HistoryCleanupViewModel : HistoryCleanup [DataMember(Name = "globalKeepLatestVersionPerDayForDays")] public int? GlobalKeepLatestVersionPerDayForDays { get; set;} - [DataMember(Name = "GlobalEnableCleanup")] + [DataMember(Name = "globalEnableCleanup")] public bool GlobalEnableCleanup { get; set; } } From c3279f71c1d81eaa4b6ca6d49c44eed69ec2bcf2 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 2 Nov 2021 09:18:03 +0100 Subject: [PATCH 11/26] Negated bool check.. --- .../src/views/documenttypes/views/permissions/permissions.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html index ac7c7844793d..67a6a4a65909 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html @@ -104,7 +104,7 @@

-
+

From b7d5fd4b268ed9a2d3b87a26037c4fe2417e6e8c Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 2 Nov 2021 09:18:15 +0100 Subject: [PATCH 12/26] Cleanup and fixed tests --- src/Umbraco.Core/Models/ContentType.cs | 2 +- .../ContentVersionCleanupPolicySettings.cs | 2 +- src/Umbraco.Core/Models/IContentType.cs | 18 +++-- .../Mapping/ContentTypeMapDefinition.cs | 34 ++++++--- .../IDocumentVersionRepository.cs | 4 +- .../Services/IContentVersionCleanupPolicy.cs | 3 +- .../Services/IContentVersionService.cs | 1 - .../UmbracoBuilder.Repositories.cs | 1 + .../UmbracoBuilder.Services.cs | 3 +- .../HostedServices/ContentVersionCleanup.cs | 11 +-- .../Migrations/Upgrade/UmbracoPlan.cs | 2 +- .../AddContentVersionCleanupFeature.cs | 2 +- .../Implement/ContentTypeRepository.cs | 20 +++-- .../Implement/DocumentVersionRepository.cs | 3 +- .../Implement/ContentVersionService.cs | 2 - .../DefaultContentVersionCleanupPolicy.cs | 7 +- .../DocumentVersionRepositoryTest.cs | 21 +++-- .../ContentVersionCleanupServiceTest.cs | 9 ++- .../Scheduling/ContentVersionCleanupTest.cs | 53 ++++++++++--- .../DefaultContentVersionCleanupPolicyTest.cs | 76 ++++++++++++++----- 20 files changed, 183 insertions(+), 91 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs index f4ff7bccf785..a2357e21ef49 100644 --- a/src/Umbraco.Core/Models/ContentType.cs +++ b/src/Umbraco.Core/Models/ContentType.cs @@ -13,7 +13,7 @@ namespace Umbraco.Cms.Core.Models ///
[Serializable] [DataContract(IsReference = true)] - public class ContentType : ContentTypeCompositionBase, IContentType + public class ContentType : ContentTypeCompositionBase, IContentTypeWithHistoryCleanup { public const bool SupportsPublishingConst = true; diff --git a/src/Umbraco.Core/Models/ContentVersionCleanupPolicySettings.cs b/src/Umbraco.Core/Models/ContentVersionCleanupPolicySettings.cs index 5e3f319e6a73..b23e6c95fae5 100644 --- a/src/Umbraco.Core/Models/ContentVersionCleanupPolicySettings.cs +++ b/src/Umbraco.Core/Models/ContentVersionCleanupPolicySettings.cs @@ -1,6 +1,6 @@ using System; -namespace Umbraco.Core.Models +namespace Umbraco.Cms.Core.Models { public class ContentVersionCleanupPolicySettings { diff --git a/src/Umbraco.Core/Models/IContentType.cs b/src/Umbraco.Core/Models/IContentType.cs index cf4a347112d1..4025239a3fde 100644 --- a/src/Umbraco.Core/Models/IContentType.cs +++ b/src/Umbraco.Core/Models/IContentType.cs @@ -1,8 +1,19 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Models.ContentEditing; namespace Umbraco.Cms.Core.Models { + [Obsolete("This will be merged into IContentType in Umbraco 10")] + public interface IContentTypeWithHistoryCleanup : IContentType + { + /// + /// Gets or Sets the history cleanup configuration + /// + HistoryCleanup HistoryCleanup { get; set; } + } + + /// /// Defines a ContentType, which Content is based on /// @@ -23,11 +34,6 @@ public interface IContentType : IContentTypeComposition ///
IEnumerable AllowedTemplates { get; set; } - /// - /// Gets or Sets the history cleanup configuration - /// - HistoryCleanup HistoryCleanup { get; set; } - /// /// Determines if AllowedTemplates contains templateId /// diff --git a/src/Umbraco.Core/Models/Mapping/ContentTypeMapDefinition.cs b/src/Umbraco.Core/Models/Mapping/ContentTypeMapDefinition.cs index 3281701c84d3..debaa976c510 100644 --- a/src/Umbraco.Core/Models/Mapping/ContentTypeMapDefinition.cs +++ b/src/Umbraco.Core/Models/Mapping/ContentTypeMapDefinition.cs @@ -45,9 +45,10 @@ public ContentTypeMapDefinition(CommonMapper commonMapper, PropertyEditorCollect IMemberTypeService memberTypeService, ILoggerFactory loggerFactory, IShortStringHelper shortStringHelper, IOptions globalSettings, IHostingEnvironment hostingEnvironment) - : this(commonMapper, propertyEditors, dataTypeService, fileService, contentTypeService, mediaTypeService, memberTypeService, loggerFactory, shortStringHelper, globalSettings, hostingEnvironment, StaticServiceProvider.Instance.GetRequiredService>()) + : this(commonMapper, propertyEditors, dataTypeService, fileService, contentTypeService, mediaTypeService, + memberTypeService, loggerFactory, shortStringHelper, globalSettings, hostingEnvironment, + StaticServiceProvider.Instance.GetRequiredService>()) { - } public ContentTypeMapDefinition(CommonMapper commonMapper, PropertyEditorCollection propertyEditors, @@ -130,7 +131,10 @@ private void Map(DocumentTypeSave source, IContentType target, MapperContext con MapSaveToTypeBase(source, target, context); MapComposition(source, target, alias => _contentTypeService.Get(alias)); - target.HistoryCleanup = source.HistoryCleanup; + if (target is IContentTypeWithHistoryCleanup targetWithHistoryCleanup) + { + targetWithHistoryCleanup.HistoryCleanup = source.HistoryCleanup; + } target.AllowedTemplates = source.AllowedTemplates .Where(x => x != null) @@ -177,15 +181,23 @@ private void Map(IContentType source, DocumentTypeDisplay target, MapperContext { MapTypeToDisplayBase(source, target); - target.HistoryCleanup = new HistoryCleanupViewModel + if (source is IContentTypeWithHistoryCleanup sourceWithHistoryCleanup) { - PreventCleanup = source.HistoryCleanup.PreventCleanup, - KeepAllVersionsNewerThanDays = source.HistoryCleanup.KeepAllVersionsNewerThanDays, - KeepLatestVersionPerDayForDays = source.HistoryCleanup.KeepLatestVersionPerDayForDays, - GlobalKeepAllVersionsNewerThanDays = _contentSettings.ContentVersionCleanupPolicy.KeepAllVersionsNewerThanDays, - GlobalKeepLatestVersionPerDayForDays = _contentSettings.ContentVersionCleanupPolicy.KeepLatestVersionPerDayForDays, - GlobalEnableCleanup = _contentSettings.ContentVersionCleanupPolicy.EnableCleanup - }; + target.HistoryCleanup = new HistoryCleanupViewModel + { + PreventCleanup = sourceWithHistoryCleanup.HistoryCleanup.PreventCleanup, + KeepAllVersionsNewerThanDays = + sourceWithHistoryCleanup.HistoryCleanup.KeepAllVersionsNewerThanDays, + KeepLatestVersionPerDayForDays = + sourceWithHistoryCleanup.HistoryCleanup.KeepLatestVersionPerDayForDays, + GlobalKeepAllVersionsNewerThanDays = + _contentSettings.ContentVersionCleanupPolicy.KeepAllVersionsNewerThanDays, + GlobalKeepLatestVersionPerDayForDays = + _contentSettings.ContentVersionCleanupPolicy.KeepLatestVersionPerDayForDays, + GlobalEnableCleanup = _contentSettings.ContentVersionCleanupPolicy.EnableCleanup + }; + } + target.AllowCultureVariant = source.VariesByCulture(); target.AllowSegmentVariant = source.VariesBySegment(); diff --git a/src/Umbraco.Core/Persistence/Repositories/IDocumentVersionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDocumentVersionRepository.cs index bcdf4f8aebeb..8175ba0d99a3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IDocumentVersionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IDocumentVersionRepository.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Persistence; -using Umbraco.Core.Models; -namespace Umbraco.Core.Persistence.Repositories +namespace Umbraco.Cms.Core.Persistence.Repositories { public interface IDocumentVersionRepository : IRepository { diff --git a/src/Umbraco.Core/Services/IContentVersionCleanupPolicy.cs b/src/Umbraco.Core/Services/IContentVersionCleanupPolicy.cs index 43718c801ffd..d2f70206b087 100644 --- a/src/Umbraco.Core/Services/IContentVersionCleanupPolicy.cs +++ b/src/Umbraco.Core/Services/IContentVersionCleanupPolicy.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; using Umbraco.Cms.Core.Models; -using Umbraco.Core.Models; -namespace Umbraco.Core.Services +namespace Umbraco.Cms.Core.Services { /// /// Used to filter historic content versions for cleanup. diff --git a/src/Umbraco.Core/Services/IContentVersionService.cs b/src/Umbraco.Core/Services/IContentVersionService.cs index 656fd899348d..882618780fe6 100644 --- a/src/Umbraco.Core/Services/IContentVersionService.cs +++ b/src/Umbraco.Core/Services/IContentVersionService.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using Umbraco.Cms.Core.Models; -using Umbraco.Core.Models; namespace Umbraco.Cms.Core.Services { diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Repositories.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Repositories.cs index 360e7859068c..d3ebb28f9c53 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Repositories.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Repositories.cs @@ -25,6 +25,7 @@ internal static IUmbracoBuilder AddRepositories(this IUmbracoBuilder builder) builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); + builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs index c7a66373f7bf..661ed9329292 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs @@ -6,7 +6,6 @@ using Microsoft.Extensions.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Hosting; @@ -16,6 +15,7 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; using Umbraco.Cms.Infrastructure.Packaging; +using Umbraco.Cms.Infrastructure.Services.Implement; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.DependencyInjection @@ -44,6 +44,7 @@ internal static IUmbracoBuilder AddServices(this IUmbracoBuilder builder) builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); + builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); diff --git a/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs b/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs index 6c97f7c80fd8..f486275d7625 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs @@ -7,7 +7,6 @@ using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Sync; -using Umbraco.Core.Services; namespace Umbraco.Cms.Infrastructure.HostedServices { @@ -18,10 +17,10 @@ public class ContentVersionCleanup : RecurringHostedServiceBase { private readonly IRuntimeState _runtimeState; private readonly ILogger _logger; + private readonly IOptionsMonitor _settingsMonitor; private readonly IContentVersionService _service; private readonly IMainDom _mainDom; private readonly IServerRoleAccessor _serverRoleAccessor; - private ContentVersionCleanupPolicySettings _settings; /// /// Initializes a new instance of the class. @@ -29,7 +28,7 @@ public class ContentVersionCleanup : RecurringHostedServiceBase public ContentVersionCleanup( IRuntimeState runtimeState, ILogger logger, - IOptionsMonitor settings, + IOptionsMonitor settingsMonitor, IContentVersionService service, IMainDom mainDom, IServerRoleAccessor serverRoleAccessor) @@ -37,19 +36,17 @@ public ContentVersionCleanup( { _runtimeState = runtimeState; _logger = logger; - _settings = settings.CurrentValue.ContentVersionCleanupPolicy; + _settingsMonitor = settingsMonitor; _service = service; _mainDom = mainDom; _serverRoleAccessor = serverRoleAccessor; - - settings.OnChange(x => _settings = x.ContentVersionCleanupPolicy); } /// public override Task PerformExecuteAsync(object state) { // Globally disabled by feature flag - if (!_settings.EnableCleanup) + if (!_settingsMonitor.CurrentValue.ContentVersionCleanupPolicy.EnableCleanup) { _logger.LogInformation("ContentVersionCleanup task will not run as it has been globally disabled via configuration"); return Task.CompletedTask; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index 2e33ab044442..76a67fd33078 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -13,7 +13,7 @@ using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_7_0; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_9_0; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; -using Umbraco.Core.Migrations.Upgrade.V_9_1_0; +using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_1_0; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_1_0/AddContentVersionCleanupFeature.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_1_0/AddContentVersionCleanupFeature.cs index b9729e53f034..aa0d4472e813 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_1_0/AddContentVersionCleanupFeature.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_1_0/AddContentVersionCleanupFeature.cs @@ -2,7 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Extensions; -namespace Umbraco.Core.Migrations.Upgrade.V_9_1_0 +namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_1_0 { class AddContentVersionCleanupFeature : MigrationBase { diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs index 9a1ea90f6012..9fa9207f53f0 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs @@ -296,15 +296,19 @@ protected override void PersistUpdatedItem(IContentType entity) private void PersistHistoryCleanup(IContentType entity) { - ContentVersionCleanupPolicyDto dto = new ContentVersionCleanupPolicyDto() + if (entity is IContentTypeWithHistoryCleanup entityWithHistoryCleanup) { - ContentTypeId = entity.Id, - Updated = DateTime.Now, - PreventCleanup = entity.HistoryCleanup.PreventCleanup, - KeepAllVersionsNewerThanDays = entity.HistoryCleanup.KeepAllVersionsNewerThanDays, - KeepLatestVersionPerDayForDays = entity.HistoryCleanup.KeepLatestVersionPerDayForDays, - }; - Database.InsertOrUpdate(dto); + ContentVersionCleanupPolicyDto dto = new ContentVersionCleanupPolicyDto() + { + ContentTypeId = entity.Id, + Updated = DateTime.Now, + PreventCleanup = entityWithHistoryCleanup.HistoryCleanup.PreventCleanup, + KeepAllVersionsNewerThanDays = entityWithHistoryCleanup.HistoryCleanup.KeepAllVersionsNewerThanDays, + KeepLatestVersionPerDayForDays = entityWithHistoryCleanup.HistoryCleanup.KeepLatestVersionPerDayForDays, + }; + Database.InsertOrUpdate(dto); + } + } } } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentVersionRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentVersionRepository.cs index 1067637f401c..5040ee5d7a70 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentVersionRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentVersionRepository.cs @@ -3,10 +3,9 @@ using System.Linq; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Repositories; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentVersionService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentVersionService.cs index c8ce9ce707a7..40c12a1fe53c 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentVersionService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentVersionService.cs @@ -7,8 +7,6 @@ using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Services; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement diff --git a/src/Umbraco.Infrastructure/Services/Implement/DefaultContentVersionCleanupPolicy.cs b/src/Umbraco.Infrastructure/Services/Implement/DefaultContentVersionCleanupPolicy.cs index 755d30306ba2..383554c34c68 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/DefaultContentVersionCleanupPolicy.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/DefaultContentVersionCleanupPolicy.cs @@ -4,11 +4,10 @@ using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Services; -using ContentVersionCleanupPolicySettings = Umbraco.Core.Models.ContentVersionCleanupPolicySettings; +using Umbraco.Cms.Core.Services; +using ContentVersionCleanupPolicySettings = Umbraco.Cms.Core.Models.ContentVersionCleanupPolicySettings; namespace Umbraco.Cms.Infrastructure.Services.Implement { diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentVersionRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentVersionRepositoryTest.cs index 6ac6fd406069..1649aa2fc6bd 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentVersionRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentVersionRepositoryTest.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using System.Linq; using NUnit.Framework; +using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; @@ -25,8 +26,11 @@ public class DocumentVersionRepositoryTest : UmbracoIntegrationTest [Test] public void GetDocumentVersionsEligibleForCleanup_Always_ExcludesActiveVersions() { - var contentType = ContentTypeBuilder.CreateSimpleContentType("umbTextpage", "Textpage"); - FileService.SaveTemplate(contentType.DefaultTemplate); + Template template = TemplateBuilder.CreateTextPageTemplate(); + FileService.SaveTemplate(template); + + var contentType = ContentTypeBuilder.CreateSimpleContentType("umbTextpage", "Textpage", defaultTemplateId: template.Id); + ContentTypeService.Save(contentType); ContentTypeService.Save(contentType); var content = ContentBuilder.CreateSimpleContent(contentType); @@ -53,8 +57,11 @@ public void GetDocumentVersionsEligibleForCleanup_Always_ExcludesActiveVersions( [Test] public void GetDocumentVersionsEligibleForCleanup_Always_ExcludesPinnedVersions() { - var contentType = ContentTypeBuilder.CreateSimpleContentType("umbTextpage", "Textpage"); - FileService.SaveTemplate(contentType.DefaultTemplate); + Template template = TemplateBuilder.CreateTextPageTemplate(); + FileService.SaveTemplate(template); + + var contentType = ContentTypeBuilder.CreateSimpleContentType("umbTextpage", "Textpage", defaultTemplateId: template.Id); + ContentTypeService.Save(contentType); ContentTypeService.Save(contentType); var content = ContentBuilder.CreateSimpleContent(contentType); @@ -92,8 +99,10 @@ public void GetDocumentVersionsEligibleForCleanup_Always_ExcludesPinnedVersions( [Test] public void DeleteVersions_Always_DeletesSpecifiedVersions() { - var contentType = ContentTypeBuilder.CreateSimpleContentType("umbTextpage", "Textpage"); - FileService.SaveTemplate(contentType.DefaultTemplate); + Template template = TemplateBuilder.CreateTextPageTemplate(); + FileService.SaveTemplate(template); + + var contentType = ContentTypeBuilder.CreateSimpleContentType("umbTextpage", "Textpage", defaultTemplateId: template.Id); ContentTypeService.Save(contentType); var content = ContentBuilder.CreateSimpleContent(contentType); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentVersionCleanupServiceTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentVersionCleanupServiceTest.cs index 1a03482fb6c0..0f7ec616f7bd 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentVersionCleanupServiceTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentVersionCleanupServiceTest.cs @@ -20,6 +20,7 @@ internal class ContentVersionCleanupServiceTest : UmbracoIntegrationTest public IContentTypeService ContentTypeService => GetRequiredService(); public IContentService ContentService => GetRequiredService(); + public IContentVersionService ContentVersionService => GetRequiredService(); /// /// This is covered by the unit tests, but nice to know it deletes on infra. @@ -33,8 +34,10 @@ public void PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExce // With 200K Versions // With 11M Property data - ContentType contentTypeA = ContentTypeBuilder.CreateSimpleContentType("contentTypeA", "contentTypeA"); - FileService.SaveTemplate(contentTypeA.DefaultTemplate); + Template template = TemplateBuilder.CreateTextPageTemplate(); + FileService.SaveTemplate(template); + + ContentType contentTypeA = ContentTypeBuilder.CreateSimpleContentType("contentTypeA", "contentTypeA", defaultTemplateId: template.Id); ContentTypeService.Save(contentTypeA); Content content = ContentBuilder.CreateSimpleContent(contentTypeA); @@ -53,7 +56,7 @@ public void PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExce // Kill all historic InsertCleanupPolicy(contentTypeA, 0, 0); - ((IContentVersionService)ContentService).PerformContentVersionCleanup(DateTime.Now.AddHours(1)); + ContentVersionService.PerformContentVersionCleanup(DateTime.Now.AddHours(1)); Report after = GetReport(); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Scheduling/ContentVersionCleanupTest.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Scheduling/ContentVersionCleanupTest.cs index 040c5059b072..ad06bc2683af 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Scheduling/ContentVersionCleanupTest.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Scheduling/ContentVersionCleanupTest.cs @@ -19,15 +19,20 @@ class ContentVersionCleanupTest { [Test, AutoMoqData] public async Task ContentVersionCleanup_WhenNotEnabled_DoesNotCleanupWillRepeat( - [Frozen] Mock> settings, + [Frozen] Mock> settings, [Frozen] Mock mainDom, [Frozen] Mock serverRoleAccessor, [Frozen] Mock runtimeState, [Frozen] Mock cleanupService, ContentVersionCleanup sut) { - settings.Setup(x => x.Value.ContentVersionCleanupPolicy.EnableCleanup).Returns(false); - + settings.Setup(x => x.CurrentValue).Returns(new ContentSettings() + { + ContentVersionCleanupPolicy = new ContentVersionCleanupPolicySettings() + { + EnableCleanup = false + } + }); runtimeState.Setup(x => x.Level).Returns(RuntimeLevel.Run); mainDom.Setup(x => x.IsMainDom).Returns(true); serverRoleAccessor.Setup(x => x.CurrentServerRole).Returns(ServerRole.SchedulingPublisher); @@ -39,14 +44,20 @@ public async Task ContentVersionCleanup_WhenNotEnabled_DoesNotCleanupWillRepeat( [Test, AutoMoqData] public async Task ContentVersionCleanup_RuntimeLevelNotRun_DoesNotCleanupWillRepeat( - [Frozen] Mock> settings, + [Frozen] Mock> settings, [Frozen] Mock mainDom, [Frozen] Mock serverRoleAccessor, [Frozen] Mock runtimeState, [Frozen] Mock cleanupService, ContentVersionCleanup sut) { - settings.Setup(x => x.Value.ContentVersionCleanupPolicy.EnableCleanup).Returns(true); + settings.Setup(x => x.CurrentValue).Returns(new ContentSettings() + { + ContentVersionCleanupPolicy = new ContentVersionCleanupPolicySettings() + { + EnableCleanup = true + } + }); runtimeState.Setup(x => x.Level).Returns(RuntimeLevel.Unknown); mainDom.Setup(x => x.IsMainDom).Returns(true); serverRoleAccessor.Setup(x => x.CurrentServerRole).Returns(ServerRole.SchedulingPublisher); @@ -58,14 +69,20 @@ public async Task ContentVersionCleanup_RuntimeLevelNotRun_DoesNotCleanupWillRep [Test, AutoMoqData] public async Task ContentVersionCleanup_ServerRoleUnknown_DoesNotCleanupWillRepeat( - [Frozen] Mock> settings, + [Frozen] Mock> settings, [Frozen] Mock mainDom, [Frozen] Mock serverRoleAccessor, [Frozen] Mock runtimeState, [Frozen] Mock cleanupService, ContentVersionCleanup sut) { - settings.Setup(x => x.Value.ContentVersionCleanupPolicy.EnableCleanup).Returns(true); + settings.Setup(x => x.CurrentValue).Returns(new ContentSettings() + { + ContentVersionCleanupPolicy = new ContentVersionCleanupPolicySettings() + { + EnableCleanup = true + } + }); runtimeState.Setup(x => x.Level).Returns(RuntimeLevel.Run); mainDom.Setup(x => x.IsMainDom).Returns(true); serverRoleAccessor.Setup(x => x.CurrentServerRole).Returns(ServerRole.Unknown); @@ -77,14 +94,21 @@ public async Task ContentVersionCleanup_ServerRoleUnknown_DoesNotCleanupWillRep [Test, AutoMoqData] public async Task ContentVersionCleanup_NotMainDom_DoesNotCleanupWillNotRepeat( - [Frozen] Mock> settings, + [Frozen] Mock> settings, [Frozen] Mock mainDom, [Frozen] Mock serverRoleAccessor, [Frozen] Mock runtimeState, [Frozen] Mock cleanupService, ContentVersionCleanup sut) { - settings.Setup(x => x.Value.ContentVersionCleanupPolicy.EnableCleanup).Returns(true); + settings.Setup(x => x.CurrentValue).Returns(new ContentSettings() + { + ContentVersionCleanupPolicy = new ContentVersionCleanupPolicySettings() + { + EnableCleanup = true + } + }); + runtimeState.Setup(x => x.Level).Returns(RuntimeLevel.Run); mainDom.Setup(x => x.IsMainDom).Returns(false); serverRoleAccessor.Setup(x => x.CurrentServerRole).Returns(ServerRole.SchedulingPublisher); @@ -96,14 +120,21 @@ public async Task ContentVersionCleanup_NotMainDom_DoesNotCleanupWillNotRepeat( [Test, AutoMoqData] public async Task ContentVersionCleanup_Enabled_DelegatesToCleanupService( - [Frozen] Mock> settings, + [Frozen] Mock> settings, [Frozen] Mock mainDom, [Frozen] Mock serverRoleAccessor, [Frozen] Mock runtimeState, [Frozen] Mock cleanupService, ContentVersionCleanup sut) { - settings.Setup(x => x.Value.ContentVersionCleanupPolicy.EnableCleanup).Returns(true); + settings.Setup(x => x.CurrentValue).Returns(new ContentSettings() + { + ContentVersionCleanupPolicy = new ContentVersionCleanupPolicySettings() + { + EnableCleanup = true + } + }); + runtimeState.Setup(x => x.Level).Returns(RuntimeLevel.Run); mainDom.Setup(x => x.IsMainDom).Returns(true); serverRoleAccessor.Setup(x => x.CurrentServerRole).Returns(ServerRole.SchedulingPublisher); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DefaultContentVersionCleanupPolicyTest.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DefaultContentVersionCleanupPolicyTest.cs index a51c77c03a3b..800bcd65b798 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DefaultContentVersionCleanupPolicyTest.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DefaultContentVersionCleanupPolicyTest.cs @@ -7,10 +7,10 @@ using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Infrastructure.Services.Implement; using Umbraco.Cms.Tests.UnitTests.AutoFixture; -using Umbraco.Core.Persistence.Repositories; -using ContentVersionCleanupPolicySettings = Umbraco.Core.Models.ContentVersionCleanupPolicySettings; +using ContentVersionCleanupPolicySettings = Umbraco.Cms.Core.Models.ContentVersionCleanupPolicySettings; namespace Umbraco.Tests.Services { @@ -31,9 +31,15 @@ public void Apply_AllOlderThanKeepSettings_AllVersionsReturned( new HistoricContentVersionMeta(versionId: ++versionId, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-1)), }; - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.EnableCleanup).Returns(true); - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.KeepAllVersionsNewerThanDays).Returns(0); - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.KeepLatestVersionPerDayForDays).Returns(0); + contentSettings.Setup(x => x.Value).Returns(new ContentSettings() + { + ContentVersionCleanupPolicy = new Cms.Core.Configuration.Models.ContentVersionCleanupPolicySettings() + { + EnableCleanup = true, + KeepAllVersionsNewerThanDays = 0, + KeepLatestVersionPerDayForDays = 0 + } + }); documentVersionRepository.Setup(x => x.GetCleanupPolicies()) .Returns(Array.Empty()); @@ -60,9 +66,15 @@ public void Apply_OverlappingKeepSettings_KeepAllVersionsNewerThanDaysTakesPrior new HistoricContentVersionMeta(versionId: ++versionId, contentId: 1, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-1)), }; - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.EnableCleanup).Returns(true); - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.KeepAllVersionsNewerThanDays).Returns(2); - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.KeepLatestVersionPerDayForDays).Returns(2); + contentSettings.Setup(x => x.Value).Returns(new ContentSettings() + { + ContentVersionCleanupPolicy = new Cms.Core.Configuration.Models.ContentVersionCleanupPolicySettings() + { + EnableCleanup = true, + KeepAllVersionsNewerThanDays = 2, + KeepLatestVersionPerDayForDays = 2 + } + }); documentVersionRepository.Setup(x => x.GetCleanupPolicies()) .Returns(Array.Empty()); @@ -96,9 +108,15 @@ public void Apply_WithinInKeepLatestPerDay_ReturnsSinglePerContentPerDay( new HistoricContentVersionMeta(versionId: 9, contentId: 2, contentTypeId: 1, versionDate: DateTime.Today.AddHours(-1)), }; - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.EnableCleanup).Returns(true); - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.KeepAllVersionsNewerThanDays).Returns(0); - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.KeepLatestVersionPerDayForDays).Returns(3); + contentSettings.Setup(x => x.Value).Returns(new ContentSettings() + { + ContentVersionCleanupPolicy = new Cms.Core.Configuration.Models.ContentVersionCleanupPolicySettings() + { + EnableCleanup = true, + KeepAllVersionsNewerThanDays = 0, + KeepLatestVersionPerDayForDays = 3 + } + }); documentVersionRepository.Setup(x => x.GetCleanupPolicies()) .Returns(Array.Empty()); @@ -134,9 +152,15 @@ public void Apply_HasOverridePolicy_RespectsPreventCleanup( new HistoricContentVersionMeta(versionId: 6, contentId: 2, contentTypeId: 2, versionDate: DateTime.Today.AddHours(-1)), }; - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.EnableCleanup).Returns(true); - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.KeepAllVersionsNewerThanDays).Returns(0); - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.KeepLatestVersionPerDayForDays).Returns(0); + contentSettings.Setup(x => x.Value).Returns(new ContentSettings() + { + ContentVersionCleanupPolicy = new Cms.Core.Configuration.Models.ContentVersionCleanupPolicySettings() + { + EnableCleanup = true, + KeepAllVersionsNewerThanDays = 0, + KeepLatestVersionPerDayForDays = 0 + } + }); documentVersionRepository.Setup(x => x.GetCleanupPolicies()) .Returns(new ContentVersionCleanupPolicySettings[] @@ -169,9 +193,15 @@ public void Apply_HasOverridePolicy_RespectsKeepAll( new HistoricContentVersionMeta(versionId: 6, contentId: 2, contentTypeId: 2, versionDate: DateTime.Today.AddHours(-1)), }; - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.EnableCleanup).Returns(true); - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.KeepAllVersionsNewerThanDays).Returns(0); - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.KeepLatestVersionPerDayForDays).Returns(0); + contentSettings.Setup(x => x.Value).Returns(new ContentSettings() + { + ContentVersionCleanupPolicy = new Cms.Core.Configuration.Models.ContentVersionCleanupPolicySettings() + { + EnableCleanup = true, + KeepAllVersionsNewerThanDays = 0, + KeepLatestVersionPerDayForDays = 0 + } + }); documentVersionRepository.Setup(x => x.GetCleanupPolicies()) .Returns(new ContentVersionCleanupPolicySettings[] @@ -204,9 +234,15 @@ public void Apply_HasOverridePolicy_RespectsKeepLatest( new HistoricContentVersionMeta(versionId: 6, contentId: 2, contentTypeId: 2, versionDate: DateTime.Today.AddHours(-1)), }; - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.EnableCleanup).Returns(true); - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.KeepAllVersionsNewerThanDays).Returns(0); - contentSettings.Setup(x => x.Value.ContentVersionCleanupPolicy.KeepLatestVersionPerDayForDays).Returns(0); + contentSettings.Setup(x => x.Value).Returns(new ContentSettings() + { + ContentVersionCleanupPolicy = new Cms.Core.Configuration.Models.ContentVersionCleanupPolicySettings() + { + EnableCleanup = true, + KeepAllVersionsNewerThanDays = 0, + KeepLatestVersionPerDayForDays = 0 + } + }); documentVersionRepository.Setup(x => x.GetCleanupPolicies()) .Returns(new ContentVersionCleanupPolicySettings[] From c7e9e94996c5be8eaf0d5fe82cbf9193e24aed33 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 2 Nov 2021 09:25:21 +0100 Subject: [PATCH 13/26] cleanup --- .../src/views/documenttypes/views/permissions/permissions.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html index 67a6a4a65909..feb1ca0a35d0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html @@ -102,8 +102,6 @@
-

-

From b15f86c1c262f6ff37f19ff0cae2487fb0241d94 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 2 Nov 2021 09:45:20 +0100 Subject: [PATCH 14/26] Fixed issue with history cleanup not persisted on first save --- .../Persistence/Repositories/Implement/ContentTypeRepository.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs index 88a4199f1999..9b3bfecd4c07 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs @@ -236,6 +236,7 @@ protected override void PersistNewItem(IContentType entity) PersistNewBaseContentType(entity); PersistTemplates(entity, false); + PersistHistoryCleanup(entity); entity.ResetDirtyProperties(); } From 08c283656ed68907e041cc0eed699bfa0040e8c4 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 2 Nov 2021 10:17:36 +0100 Subject: [PATCH 15/26] aligned background job timings with v8 version --- .../HostedServices/ContentVersionCleanup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs b/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs index f486275d7625..5f3aba5f3f3d 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ContentVersionCleanup.cs @@ -32,7 +32,7 @@ public ContentVersionCleanup( IContentVersionService service, IMainDom mainDom, IServerRoleAccessor serverRoleAccessor) - : base(TimeSpan.FromDays(1), TimeSpan.FromMinutes(1)) + : base(TimeSpan.FromHours(1), TimeSpan.FromMinutes(3)) { _runtimeState = runtimeState; _logger = logger; From 0c3c88609e8326ff21bb8a1a53e2ca7af5905461 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 2 Nov 2021 10:19:41 +0100 Subject: [PATCH 16/26] New projects starts with history cleanup enabled by default --- build/templates/UmbracoProject/appsettings.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/templates/UmbracoProject/appsettings.json b/build/templates/UmbracoProject/appsettings.json index feb6b07d9531..d282abc86f1a 100644 --- a/build/templates/UmbracoProject/appsettings.json +++ b/build/templates/UmbracoProject/appsettings.json @@ -29,6 +29,11 @@ //#endif "Hosting": { "Debug": false + }, + "Content": { + "ContentVersionCleanupPolicy": { + "EnableCleanup": true + } } } } From ab06d21639ae5da3c157f257e8a254d2c34f7c46 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 2 Nov 2021 10:42:51 +0100 Subject: [PATCH 17/26] Fix namespace syntax --- .../Models/ContentEditing/HistoryCleanup.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentEditing/HistoryCleanup.cs b/src/Umbraco.Core/Models/ContentEditing/HistoryCleanup.cs index 7ac2fd94f2b4..23993e4395c9 100644 --- a/src/Umbraco.Core/Models/ContentEditing/HistoryCleanup.cs +++ b/src/Umbraco.Core/Models/ContentEditing/HistoryCleanup.cs @@ -1,16 +1,18 @@ using System.Runtime.Serialization; -namespace Umbraco.Core.Models.ContentEditing; - -[DataContract(Name = "historyCleanup", Namespace = "")] -public class HistoryCleanup +namespace Umbraco.Core.Models.ContentEditing { - [DataMember(Name = "preventCleanup")] - public bool PreventCleanup { get; set; } + [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 = "keepAllVersionsNewerThanDays")] + public int? KeepAllVersionsNewerThanDays { get;set; } - [DataMember(Name = "keepLatestVersionPerDayForDays")] - public int? KeepLatestVersionPerDayForDays { get;set; } + [DataMember(Name = "keepLatestVersionPerDayForDays")] + public int? KeepLatestVersionPerDayForDays { get;set; } + } } + From 456b8a8a7c7a07a09557f6d83f4cfab9818f9ef5 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 2 Nov 2021 10:58:16 +0100 Subject: [PATCH 18/26] Fixed test now that save of content types creates a cleanup policy --- .../ContentVersionCleanupService_Tests_Integration.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_Integration.cs b/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_Integration.cs index ed177d35c31b..4b795cf5a63e 100644 --- a/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_Integration.cs +++ b/src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_Integration.cs @@ -28,6 +28,11 @@ public void PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExce // With 11M Property data var contentTypeA = MockedContentTypes.CreateSimpleContentType("contentTypeA", "contentTypeA"); + // Kill all historic + contentTypeA.HistoryCleanup.PreventCleanup = false; + contentTypeA.HistoryCleanup.KeepAllVersionsNewerThanDays = 0; + contentTypeA.HistoryCleanup.KeepLatestVersionPerDayForDays = 0; + ServiceContext.FileService.SaveTemplate(contentTypeA.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentTypeA); @@ -44,8 +49,7 @@ public void PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExce Debug.Assert(before.ContentVersions == 12); // 10 historic + current draft + current published Debug.Assert(before.PropertyData == 12 * 3); // CreateSimpleContentType = 3 props - // Kill all historic - InsertCleanupPolicy(contentTypeA, 0, 0); + ((IContentVersionService)ServiceContext.ContentService).PerformContentVersionCleanup(DateTime.Now.AddHours(1)); From a1f93f84e241e82d23b1a145fbfee945cc61777e Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 2 Nov 2021 11:59:34 +0100 Subject: [PATCH 19/26] changed default values --- .../Models/ContentVersionCleanupPolicySettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Configuration/Models/ContentVersionCleanupPolicySettings.cs b/src/Umbraco.Core/Configuration/Models/ContentVersionCleanupPolicySettings.cs index 02724197017e..bd460058eb3b 100644 --- a/src/Umbraco.Core/Configuration/Models/ContentVersionCleanupPolicySettings.cs +++ b/src/Umbraco.Core/Configuration/Models/ContentVersionCleanupPolicySettings.cs @@ -8,8 +8,8 @@ namespace Umbraco.Cms.Core.Configuration.Models public class ContentVersionCleanupPolicySettings { private const bool StaticEnableCleanup = false; - private const int StaticKeepAllVersionsNewerThanDays = 2; - private const int StaticKeepLatestVersionPerDayForDays = 30; + private const int StaticKeepAllVersionsNewerThanDays = 7; + private const int StaticKeepLatestVersionPerDayForDays = 90; /// /// Gets or sets a value indicating whether or not the cleanup job should be executed. From 89aa7f87bd080f40563d330f276e3c8e6db6e077 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 2 Nov 2021 13:29:47 +0100 Subject: [PATCH 20/26] Updated cypress + testhelpers --- .../package-lock.json | 25 ++++++++++++------- .../Umbraco.Tests.AcceptanceTest/package.json | 4 +-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json index 2eb6cfa471fc..94502a9ea054 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json @@ -199,9 +199,9 @@ "dev": true }, "async": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.1.tgz", - "integrity": "sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", + "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==", "dev": true }, "asynckit": { @@ -454,9 +454,9 @@ "dev": true }, "cypress": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-8.4.1.tgz", - "integrity": "sha512-itJXq0Vx3sXCUrDyBi2IUrkxVu/gTTp1VhjB5tzGgkeCR8Ae+/T8WV63rsZ7fS8Tpq7LPPXiyoM/sEdOX7cR6A==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-8.7.0.tgz", + "integrity": "sha512-b1bMC3VQydC6sXzBMFnSqcvwc9dTZMgcaOzT0vpSD+Gq1yFc+72JDWi55sfUK5eIeNLAtWOGy1NNb6UlhMvB+Q==", "dev": true, "requires": { "@cypress/request": "^2.88.6", @@ -493,6 +493,7 @@ "minimist": "^1.2.5", "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", + "proxy-from-env": "1.0.0", "ramda": "~0.27.1", "request-progress": "^3.0.0", "supports-color": "^8.1.1", @@ -1321,6 +1322,12 @@ } } }, + "proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", + "dev": true + }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -1615,9 +1622,9 @@ "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==" }, "umbraco-cypress-testhelpers": { - "version": "1.0.0-beta-58", - "resolved": "https://registry.npmjs.org/umbraco-cypress-testhelpers/-/umbraco-cypress-testhelpers-1.0.0-beta-58.tgz", - "integrity": "sha512-qbkqGo+g4FzQTstYQXPYDjJyVRH+guNZt1WZXQ8v64dTucN/Ku/noowMA9y6q0mLOZ8+SR9xmMySHfa7E48Vfw==", + "version": "1.0.0-beta-59", + "resolved": "https://registry.npmjs.org/umbraco-cypress-testhelpers/-/umbraco-cypress-testhelpers-1.0.0-beta-59.tgz", + "integrity": "sha512-eaxzCJOisT8eNX0h2WvEpRVoCcpTK/dIMvXz3n8r1J3mjG1+pBFXLPDMJDyb6xPbu5LqXRoERbe2ka1rgH3QWQ==", "dev": true, "requires": { "camelize": "^1.0.0", diff --git a/tests/Umbraco.Tests.AcceptanceTest/package.json b/tests/Umbraco.Tests.AcceptanceTest/package.json index fd401c72c0cd..3466d837ce84 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package.json @@ -9,11 +9,11 @@ }, "devDependencies": { "cross-env": "^7.0.2", - "cypress": "8.4.1", + "cypress": "^8.7.0", "del": "^6.0.0", "ncp": "^2.0.0", "prompt": "^1.2.0", - "umbraco-cypress-testhelpers": "^1.0.0-beta-58" + "umbraco-cypress-testhelpers": "^1.0.0-beta-59" }, "dependencies": { "typescript": "^3.9.2" From 8e49b916924390a79ef6e1d621c2da1050d9afeb Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 2 Nov 2021 14:07:56 +0100 Subject: [PATCH 21/26] Updated cyoress + testhelpers --- src/Umbraco.Tests.AcceptanceTest/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/package.json b/src/Umbraco.Tests.AcceptanceTest/package.json index 05c8c5111430..101f525e7b39 100644 --- a/src/Umbraco.Tests.AcceptanceTest/package.json +++ b/src/Umbraco.Tests.AcceptanceTest/package.json @@ -7,10 +7,10 @@ }, "devDependencies": { "cross-env": "^7.0.2", - "cypress": "^6.8.0", + "cypress": "^8.7.0", "ncp": "^2.0.0", "prompt": "^1.0.0", - "umbraco-cypress-testhelpers": "^1.0.0-beta-57" + "umbraco-cypress-testhelpers": "^1.0.0-beta-60" }, "dependencies": { "typescript": "^3.9.2" From ef34fe5a8afd7650bc908315968f204163cdd8c5 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 2 Nov 2021 14:25:42 +0100 Subject: [PATCH 22/26] Missing lock file --- tests/Umbraco.Tests.AcceptanceTest/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json index 94502a9ea054..6b605bfbb9a0 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json @@ -1622,9 +1622,9 @@ "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==" }, "umbraco-cypress-testhelpers": { - "version": "1.0.0-beta-59", - "resolved": "https://registry.npmjs.org/umbraco-cypress-testhelpers/-/umbraco-cypress-testhelpers-1.0.0-beta-59.tgz", - "integrity": "sha512-eaxzCJOisT8eNX0h2WvEpRVoCcpTK/dIMvXz3n8r1J3mjG1+pBFXLPDMJDyb6xPbu5LqXRoERbe2ka1rgH3QWQ==", + "version": "1.0.0-beta-60", + "resolved": "https://registry.npmjs.org/umbraco-cypress-testhelpers/-/umbraco-cypress-testhelpers-1.0.0-beta-60.tgz", + "integrity": "sha512-VEe6r7G9nBwtATxAZPFOY4utCpsmKV8oK5FbPV4TrEyMH08hmAYmEq+w84Bq1Q85khG01ivyMCR/8sYUEHTWIg==", "dev": true, "requires": { "camelize": "^1.0.0", From 4882837d602c19090be01a8287b6f5b536acb174 Mon Sep 17 00:00:00 2001 From: Berg Date: Tue, 2 Nov 2021 16:18:49 +0100 Subject: [PATCH 23/26] Fixed test --- .../Services/ContentVersionCleanupServiceTest.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentVersionCleanupServiceTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentVersionCleanupServiceTest.cs index 0f7ec616f7bd..9434a2bdb396 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentVersionCleanupServiceTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentVersionCleanupServiceTest.cs @@ -38,6 +38,12 @@ public void PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExce FileService.SaveTemplate(template); ContentType contentTypeA = ContentTypeBuilder.CreateSimpleContentType("contentTypeA", "contentTypeA", defaultTemplateId: template.Id); + // Kill all historic + contentTypeA.HistoryCleanup.PreventCleanup = false; + contentTypeA.HistoryCleanup.KeepAllVersionsNewerThanDays = 0; + contentTypeA.HistoryCleanup.KeepLatestVersionPerDayForDays = 0; + + ContentTypeService.Save(contentTypeA); Content content = ContentBuilder.CreateSimpleContent(contentTypeA); @@ -53,9 +59,6 @@ public void PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExce Debug.Assert(before.ContentVersions == 12); // 10 historic + current draft + current published Debug.Assert(before.PropertyData == 12 * 3); // CreateSimpleContentType = 3 props - // Kill all historic - InsertCleanupPolicy(contentTypeA, 0, 0); - ContentVersionService.PerformContentVersionCleanup(DateTime.Now.AddHours(1)); Report after = GetReport(); From a25183a063d04806c7fd9c6bf05b3bdd9027a655 Mon Sep 17 00:00:00 2001 From: Berg Date: Tue, 2 Nov 2021 16:22:35 +0100 Subject: [PATCH 24/26] Committing missing package-lock.json --- .../package-lock.json | 2331 ++++++++++++++++- 1 file changed, 2330 insertions(+), 1 deletion(-) diff --git a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json index 6b605bfbb9a0..67ef5f16fe0d 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json @@ -1,7 +1,2336 @@ { "name": "acceptancetest", + "lockfileVersion": 2, "requires": true, - "lockfileVersion": 1, + "packages": { + "": { + "name": "acceptancetest", + "hasInstallScript": true, + "dependencies": { + "typescript": "^3.9.2" + }, + "devDependencies": { + "cross-env": "^7.0.2", + "cypress": "^8.7.0", + "del": "^6.0.0", + "ncp": "^2.0.0", + "prompt": "^1.2.0", + "umbraco-cypress-testhelpers": "^1.0.0-beta-60" + } + }, + "node_modules/@cypress/request": { + "version": "2.88.6", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.6.tgz", + "integrity": "sha512-z0UxBE/+qaESAHY9p9sM2h8Y4XqtsbDCt0/DPOrqA/RZgKi4PkxdpXyK4wCCnSk1xHqWHZZAE+gV6aDAR6+caQ==", + "dev": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@cypress/xvfb": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", + "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", + "dev": true, + "dependencies": { + "debug": "^3.1.0", + "lodash.once": "^4.1.1" + } + }, + "node_modules/@cypress/xvfb/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/node": { + "version": "14.17.32", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.32.tgz", + "integrity": "sha512-JcII3D5/OapPGx+eJ+Ik1SQGyt6WvuqdRfh9jUwL6/iHGjmyOriBDciBUu7lEIBTL2ijxwrR70WUnw5AEDmFvQ==", + "dev": true + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz", + "integrity": "sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A==", + "dev": true + }, + "node_modules/@types/sizzle": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", + "dev": true + }, + "node_modules/@types/yauzl": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", + "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aggregate-error/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", + "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/blob-util": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", + "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", + "dev": true + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/cachedir": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=", + "dev": true + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-more-types": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ci-info": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", + "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==", + "dev": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", + "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "colors": "^1.1.2" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/cypress": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-8.7.0.tgz", + "integrity": "sha512-b1bMC3VQydC6sXzBMFnSqcvwc9dTZMgcaOzT0vpSD+Gq1yFc+72JDWi55sfUK5eIeNLAtWOGy1NNb6UlhMvB+Q==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@cypress/request": "^2.88.6", + "@cypress/xvfb": "^1.2.4", + "@types/node": "^14.14.31", + "@types/sinonjs__fake-timers": "^6.0.2", + "@types/sizzle": "^2.3.2", + "arch": "^2.2.0", + "blob-util": "^2.0.2", + "bluebird": "^3.7.2", + "cachedir": "^2.3.0", + "chalk": "^4.1.0", + "check-more-types": "^2.24.0", + "cli-cursor": "^3.1.0", + "cli-table3": "~0.6.0", + "commander": "^5.1.0", + "common-tags": "^1.8.0", + "dayjs": "^1.10.4", + "debug": "^4.3.2", + "enquirer": "^2.3.6", + "eventemitter2": "^6.4.3", + "execa": "4.1.0", + "executable": "^4.1.1", + "extract-zip": "2.0.1", + "figures": "^3.2.0", + "fs-extra": "^9.1.0", + "getos": "^3.2.1", + "is-ci": "^3.0.0", + "is-installed-globally": "~0.4.0", + "lazy-ass": "^1.6.0", + "listr2": "^3.8.3", + "lodash": "^4.17.21", + "log-symbols": "^4.0.0", + "minimist": "^1.2.5", + "ospath": "^1.2.2", + "pretty-bytes": "^5.6.0", + "proxy-from-env": "1.0.0", + "ramda": "~0.27.1", + "request-progress": "^3.0.0", + "supports-color": "^8.1.1", + "tmp": "~0.2.1", + "untildify": "^4.0.0", + "url": "^0.11.0", + "yauzl": "^2.10.0" + }, + "bin": { + "cypress": "bin/cypress" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dayjs": { + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz", + "integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "dev": true, + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eventemitter2": { + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.5.tgz", + "integrity": "sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw==", + "dev": true + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "dependencies": { + "pify": "^2.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", + "dev": true, + "engines": { + "node": "> 0.1.90" + } + }, + "node_modules/faker": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz", + "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/getos": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", + "dev": true, + "dependencies": { + "async": "^3.2.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "dev": true, + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", + "dev": true, + "engines": { + "node": "> 0.8" + } + }, + "node_modules/listr2": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.13.1.tgz", + "integrity": "sha512-pk4YBDA2cxtpM8iLHbz6oEsfZieJKHf6Pt19NlKaHZZVpqHyVs/Wqr7RfBBCeAFCJchGO7WQHVkUPZTvJMHk8w==", + "dev": true, + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rxjs": "^6.6.7", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.50.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz", + "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.33", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", + "dev": true, + "dependencies": { + "mime-db": "1.50.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "dev": true, + "bin": { + "ncp": "bin/ncp" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ospath": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", + "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=", + "dev": true + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prompt": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.2.0.tgz", + "integrity": "sha512-iGerYRpRUg5ZyC+FJ/25G5PUKuWAGRjW1uOlhX7Pi3O5YygdK6R+KEaBjRbHSkU5vfS5PZCltSPZdDtUYwRCZA==", + "dev": true, + "dependencies": { + "async": "~0.9.0", + "colors": "^1.1.2", + "read": "1.0.x", + "revalidator": "0.1.x", + "winston": "2.x" + }, + "engines": { + "node": ">= 0.6.6" + } + }, + "node_modules/prompt/node_modules/async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + }, + "node_modules/proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", + "dev": true + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ramda": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", + "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", + "dev": true + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "dev": true, + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request-progress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", + "dev": true, + "dependencies": { + "throttleit": "^1.0.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/revalidator": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", + "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/throttleit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/umbraco-cypress-testhelpers": { + "version": "1.0.0-beta-60", + "resolved": "https://registry.npmjs.org/umbraco-cypress-testhelpers/-/umbraco-cypress-testhelpers-1.0.0-beta-60.tgz", + "integrity": "sha512-VEe6r7G9nBwtATxAZPFOY4utCpsmKV8oK5FbPV4TrEyMH08hmAYmEq+w84Bq1Q85khG01ivyMCR/8sYUEHTWIg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "camelize": "^1.0.0", + "faker": "^4.1.0" + }, + "peerDependencies": { + "cross-env": "^7.0.2", + "cypress": "^8.7.0", + "ncp": "^2.0.0" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.5.tgz", + "integrity": "sha512-TWoamHt5yYvsMarGlGEQE59SbJHqGsZV8/lwC+iCcGeAe0vUaOh+Lv6SYM17ouzC/a/LB1/hz/7sxFBtlu1l4A==", + "dev": true, + "dependencies": { + "async": "~1.0.0", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "stack-trace": "0.0.x" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/winston/node_modules/async": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", + "dev": true + }, + "node_modules/winston/node_modules/colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + }, "dependencies": { "@cypress/request": { "version": "2.88.6", From 236bf881434cb30eb2f43c3ea511cfff0839f4c6 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Wed, 3 Nov 2021 10:23:50 +0100 Subject: [PATCH 25/26] Cleanup --- .../ContentEditing/DocumentTypeDisplay.cs | 11 +++++++---- .../Models/ContentEditing/HistoryCleanup.cs | 3 ++- .../ContentEditing/HistoryCleanupViewModel.cs | 11 ++++++----- src/Umbraco.Core/Models/ContentType.cs | 10 ++++------ .../ContentVersionCleanupPolicySettings.cs | 6 +++++- src/Umbraco.Core/Models/IContentType.cs | 3 +-- .../Dtos/ContentVersionCleanupPolicyDto.cs | 7 +++---- .../Implement/ContentTypeRepository.cs | 4 ++-- .../Services/Implement/ContentService.cs | 19 ++++++++++--------- .../services/umbdataformatter.service.js | 2 +- .../ContentVersionCleanupServiceTest.cs | 5 +++-- .../Scheduling/ContentVersionCleanupTest.cs | 1 - .../DefaultContentVersionCleanupPolicyTest.cs | 1 - .../Controllers/MemberControllerUnitTests.cs | 3 +-- 14 files changed, 45 insertions(+), 41 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentEditing/DocumentTypeDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/DocumentTypeDisplay.cs index 1fa0895f61e1..3bb52c39b724 100644 --- a/src/Umbraco.Core/Models/ContentEditing/DocumentTypeDisplay.cs +++ b/src/Umbraco.Core/Models/ContentEditing/DocumentTypeDisplay.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Runtime.Serialization; namespace Umbraco.Cms.Core.Models.ContentEditing @@ -16,7 +16,8 @@ public DocumentTypeDisplay() => [DataMember(Name = "allowedTemplates")] public IEnumerable 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; } @@ -24,8 +25,10 @@ public DocumentTypeDisplay() => [DataMember(Name = "allowSegmentVariant")] public bool AllowSegmentVariant { get; set; } - [DataMember(Name = "apps")] public IEnumerable ContentApps { get; set; } + [DataMember(Name = "apps")] + public IEnumerable ContentApps { get; set; } - [DataMember(Name = "historyCleanup")] public HistoryCleanupViewModel HistoryCleanup { get; set; } + [DataMember(Name = "historyCleanup")] + public HistoryCleanupViewModel HistoryCleanup { get; set; } } } diff --git a/src/Umbraco.Core/Models/ContentEditing/HistoryCleanup.cs b/src/Umbraco.Core/Models/ContentEditing/HistoryCleanup.cs index 5e7b34b0bf0c..b7bfb328087f 100644 --- a/src/Umbraco.Core/Models/ContentEditing/HistoryCleanup.cs +++ b/src/Umbraco.Core/Models/ContentEditing/HistoryCleanup.cs @@ -5,7 +5,8 @@ namespace Umbraco.Cms.Core.Models.ContentEditing [DataContract(Name = "historyCleanup", Namespace = "")] public class HistoryCleanup { - [DataMember(Name = "preventCleanup")] public bool PreventCleanup { get; set; } + [DataMember(Name = "preventCleanup")] + public bool PreventCleanup { get; set; } [DataMember(Name = "keepAllVersionsNewerThanDays")] public int? KeepAllVersionsNewerThanDays { get; set; } diff --git a/src/Umbraco.Core/Models/ContentEditing/HistoryCleanupViewModel.cs b/src/Umbraco.Core/Models/ContentEditing/HistoryCleanupViewModel.cs index 94423a80606e..303ff4eda375 100644 --- a/src/Umbraco.Core/Models/ContentEditing/HistoryCleanupViewModel.cs +++ b/src/Umbraco.Core/Models/ContentEditing/HistoryCleanupViewModel.cs @@ -5,13 +5,14 @@ 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; } + + [DataMember(Name = "globalKeepAllVersionsNewerThanDays")] + public int? GlobalKeepAllVersionsNewerThanDays { get; set; } + + [DataMember(Name = "globalKeepLatestVersionPerDayForDays")] + public int? GlobalKeepLatestVersionPerDayForDays { get; set; } } } diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs index a2357e21ef49..6ff94f57f363 100644 --- a/src/Umbraco.Core/Models/ContentType.cs +++ b/src/Umbraco.Core/Models/ContentType.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; @@ -17,9 +17,8 @@ public class ContentType : ContentTypeCompositionBase, IContentTypeWithHistoryCl { public const bool SupportsPublishingConst = true; - - //Custom comparer for enumerable - private static readonly DelegateEqualityComparer> TemplateComparer = new( + // Custom comparer for enumerable + private static readonly DelegateEqualityComparer> TemplateComparer = new ( (templates, enumerable) => templates.UnsortedSequenceEqual(enumerable), templates => templates.GetHashCode()); @@ -88,8 +87,7 @@ public IEnumerable AllowedTemplates 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) { diff --git a/src/Umbraco.Core/Models/ContentVersionCleanupPolicySettings.cs b/src/Umbraco.Core/Models/ContentVersionCleanupPolicySettings.cs index b23e6c95fae5..5fa0e9895822 100644 --- a/src/Umbraco.Core/Models/ContentVersionCleanupPolicySettings.cs +++ b/src/Umbraco.Core/Models/ContentVersionCleanupPolicySettings.cs @@ -5,9 +5,13 @@ namespace Umbraco.Cms.Core.Models public class ContentVersionCleanupPolicySettings { public int ContentTypeId { get; set; } + + public bool PreventCleanup { get; set; } + public int? KeepAllVersionsNewerThanDays { get; set; } + public int? KeepLatestVersionPerDayForDays { get; set; } - public bool PreventCleanup { get; set; } + public DateTime Updated { get; set; } } } diff --git a/src/Umbraco.Core/Models/IContentType.cs b/src/Umbraco.Core/Models/IContentType.cs index 4025239a3fde..a01e6128878b 100644 --- a/src/Umbraco.Core/Models/IContentType.cs +++ b/src/Umbraco.Core/Models/IContentType.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Umbraco.Cms.Core.Models.ContentEditing; @@ -13,7 +13,6 @@ public interface IContentTypeWithHistoryCleanup : IContentType HistoryCleanup HistoryCleanup { get; set; } } - /// /// Defines a ContentType, which Content is based on /// diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs index f0350d9f1bee..4b2faa166f5b 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs @@ -4,7 +4,6 @@ using Umbraco.Cms.Core; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; - namespace Umbraco.Cms.Infrastructure.Persistence.Dtos { [TableName(TableName)] @@ -18,6 +17,9 @@ internal class ContentVersionCleanupPolicyDto [ForeignKey(typeof(ContentTypeDto), Column = "nodeId", OnDelete = Rule.Cascade)] public int ContentTypeId { get; set; } + [Column("preventCleanup")] + public bool PreventCleanup { get; set; } + [Column("keepAllVersionsNewerThanDays")] [NullSetting(NullSetting = NullSettings.Null)] public int? KeepAllVersionsNewerThanDays { get; set; } @@ -26,9 +28,6 @@ internal class ContentVersionCleanupPolicyDto [NullSetting(NullSetting = NullSettings.Null)] public int? KeepLatestVersionPerDayForDays { get; set; } - [Column("preventCleanup")] - public bool PreventCleanup { get; set; } - [Column("updated")] public DateTime Updated { get; set; } } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs index 0df72cd2f6ef..6ab97c971f8b 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; @@ -305,7 +305,7 @@ private void PersistHistoryCleanup(IContentType entity) Updated = DateTime.Now, PreventCleanup = entityWithHistoryCleanup.HistoryCleanup.PreventCleanup, KeepAllVersionsNewerThanDays = entityWithHistoryCleanup.HistoryCleanup.KeepAllVersionsNewerThanDays, - KeepLatestVersionPerDayForDays = entityWithHistoryCleanup.HistoryCleanup.KeepLatestVersionPerDayForDays, + KeepLatestVersionPerDayForDays = entityWithHistoryCleanup.HistoryCleanup.KeepLatestVersionPerDayForDays }; Database.InsertOrUpdate(dto); } diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index f8669a77b46e..04b93bdd9f51 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -74,19 +74,19 @@ public OperationResult Rollback(int id, int versionId, string culture = "*", { EventMessages evtMsgs = EventMessagesFactory.Get(); - //Get the current copy of the node + // Get the current copy of the node IContent content = GetById(id); - //Get the version + // Get the version IContent version = GetVersion(versionId); - //Good ole null checks + // Good old null checks if (content == null || version == null || content.Trashed) { return new OperationResult(OperationResultType.FailedCannot, evtMsgs); } - //Store the result of doing the save of content for the rollback + // Store the result of doing the save of content for the rollback OperationResult rollbackSaveResult; using (IScope scope = ScopeProvider.CreateScope()) @@ -98,16 +98,16 @@ public OperationResult Rollback(int id, int versionId, string culture = "*", return OperationResult.Cancel(evtMsgs); } - //Copy the changes from the version + // Copy the changes from the version content.CopyFrom(version, culture); - //Save the content for the rollback + // Save the content for the rollback rollbackSaveResult = Save(content, userId); - //Depending on the save result - is what we log & audit along with what we return + // Depending on the save result - is what we log & audit along with what we return if (rollbackSaveResult.Success == false) { - //Log the error/warning + // Log the error/warning _logger.LogError( "User '{UserId}' was unable to rollback content '{ContentId}' to version '{VersionId}'", userId, id, versionId); @@ -117,7 +117,7 @@ public OperationResult Rollback(int id, int versionId, string culture = "*", scope.Notifications.Publish( new ContentRolledBackNotification(content, evtMsgs).WithStateFrom(rollingBackNotification)); - //Logging & Audit message + // Logging & Audit message _logger.LogInformation("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId); Audit(AuditType.RollBack, userId, id, @@ -1799,6 +1799,7 @@ private void PerformScheduledPublishingRelease(DateTime date, List GetRequiredService(); public IContentService ContentService => GetRequiredService(); + public IContentVersionService ContentVersionService => GetRequiredService(); /// @@ -29,7 +30,7 @@ internal class ContentVersionCleanupServiceTest : UmbracoIntegrationTest [Test] public void PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExceptActive() { - // For reference currently has + // For reference, Our currently has // 5000 Documents // With 200K Versions // With 11M Property data @@ -38,12 +39,12 @@ public void PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExce FileService.SaveTemplate(template); ContentType contentTypeA = ContentTypeBuilder.CreateSimpleContentType("contentTypeA", "contentTypeA", defaultTemplateId: template.Id); + // Kill all historic contentTypeA.HistoryCleanup.PreventCleanup = false; contentTypeA.HistoryCleanup.KeepAllVersionsNewerThanDays = 0; contentTypeA.HistoryCleanup.KeepLatestVersionPerDayForDays = 0; - ContentTypeService.Save(contentTypeA); Content content = ContentBuilder.CreateSimpleContent(contentTypeA); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Scheduling/ContentVersionCleanupTest.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Scheduling/ContentVersionCleanupTest.cs index ad06bc2683af..430cd184c155 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Scheduling/ContentVersionCleanupTest.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Scheduling/ContentVersionCleanupTest.cs @@ -139,7 +139,6 @@ public async Task ContentVersionCleanup_Enabled_DelegatesToCleanupService( mainDom.Setup(x => x.IsMainDom).Returns(true); serverRoleAccessor.Setup(x => x.CurrentServerRole).Returns(ServerRole.SchedulingPublisher); - await sut.PerformExecuteAsync(null); cleanupService.Verify(x => x.PerformContentVersionCleanup(It.IsAny()), Times.Once); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DefaultContentVersionCleanupPolicyTest.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DefaultContentVersionCleanupPolicyTest.cs index 800bcd65b798..db258c5fa150 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DefaultContentVersionCleanupPolicyTest.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DefaultContentVersionCleanupPolicyTest.cs @@ -261,6 +261,5 @@ public void Apply_HasOverridePolicy_RespectsKeepLatest( Assert.AreEqual(6, results.Single(x => x.ContentTypeId == 2).VersionId); }); } - } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs index b70b6ae65847..d1ce62242e6b 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs @@ -503,8 +503,7 @@ private MemberController CreateSut( mockShortStringHelper, globalSettings, new Mock().Object, - new Mock>().Object - ) + new Mock>().Object) }); var scopeProvider = Mock.Of(x => x.CreateScope( It.IsAny(), From 53a83abf1a14cf68e9c2fc14e73951a54921b566 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Wed, 3 Nov 2021 11:25:38 +0100 Subject: [PATCH 26/26] Fix lang files --- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 2 +- src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 84b66bc6a967..141cdc48fbd2 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -1829,7 +1829,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Keep all versions newer than days Keep latest version per day for days Prevent cleanup - NOTE! The cleanup of historically content versions are disabled globally. These settings will not take effect before it is enabled. + NOTE! The cleanup of historically content versions is disabled globally. These settings will not take effect before it is enabled. Add language diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index 639021d82df1..c9764c0d3ccd 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -1886,7 +1886,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Keep all versions newer than days Keep latest version per day for days Prevent cleanup - NOTE! The cleanup of historically content versions are disabled globally. These settings will not take effect before it is enabled. + NOTE! The cleanup of historically content versions is disabled globally. These settings will not take effect before it is enabled. Add language