From 3e7aa299a3416414047a1d2f9f099bf4eab713dd Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 21 Oct 2021 09:51:05 +0200 Subject: [PATCH 01/57] https://github.com/umbraco/Umbraco-CMS/issues/11427 Ensure there can't be duplicate culture codes in languages dropdown --- .../Controllers/LanguageController.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs index 310b1142c08d..a4001ce79ff4 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs @@ -9,8 +9,6 @@ using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Web.BackOffice.Extensions; -using Umbraco.Cms.Web.Common.ActionsResults; using Umbraco.Cms.Web.Common.Attributes; using Umbraco.Cms.Web.Common.Authorization; using Umbraco.Extensions; @@ -23,7 +21,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// Backoffice controller supporting the dashboard for language administration. /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] - //[PrefixlessBodyModelValidator] public class LanguageController : UmbracoAuthorizedJsonController { private readonly ILocalizationService _localizationService; @@ -51,10 +48,12 @@ public IDictionary GetAllCultures() // (see notes in Language class about culture info names) // TODO: Fix this requirement, see https://github.com/umbraco/Umbraco-CMS/issues/3623 return CultureInfo.GetCultures(CultureTypes.AllCultures) - .Where(x => !x.Name.IsNullOrWhiteSpace()) - .Select(x => new CultureInfo(x.Name)) // important! - .OrderBy(x => x.EnglishName) - .ToDictionary(x => x.Name, x => x.EnglishName); + .Select(x=>x.Name) + .Distinct() + .Where(x => !x.IsNullOrWhiteSpace()) + .Select(x => new CultureInfo(x)) // important! + .OrderBy(x => x.EnglishName) + .ToDictionary(x => x.Name, x => x.EnglishName); } /// From d865448177f7cc2f70860afe58cad98a974f3904 Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Fri, 15 Oct 2021 15:45:21 +0100 Subject: [PATCH 02/57] 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 03/57] 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 04/57] 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 05/57] 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 06/57] 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 4876108f712efb24a6545c56ad742714948321f1 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle Date: Wed, 27 Oct 2021 09:49:05 +0200 Subject: [PATCH 07/57] change button to user editUser function --- .../src/views/common/overlays/user/user.controller.js | 6 ++++++ .../src/views/common/overlays/user/user.html | 7 +++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js index 28dcedd67705..a98eacd70246 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js @@ -182,6 +182,12 @@ angular.module("umbraco") $scope.changePasswordModel.value.confirm = ""; } + $scope.editUser = function() { + $location + .path('/users/users/user/' + $scope.user.id); + $scope.model.close(); + } + dashboardResource.getDashboard("user-dialog").then(function (dashboard) { $scope.dashboard = dashboard; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html index 8f2627795f7c..c722424e3182 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html @@ -11,9 +11,8 @@
Un-link your {{login.caption}} account - + From ae20c495908d25660923ae8b8c81dcb2471666ad Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 28 Oct 2021 10:02:04 +0200 Subject: [PATCH 08/57] 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 09/57] 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 10/57] 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 11/57] 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 12/57] 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 c3625aed872e46092b3766df19b9b6fca2a7553a Mon Sep 17 00:00:00 2001 From: Jeavon Leopold Date: Thu, 28 Oct 2021 14:57:26 +0100 Subject: [PATCH 13/57] Fix the basehttpheader so that it's checking the root of the domain instead of /umbraco --- .../Checks/Security/BaseHttpHeaderCheck.cs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs index fea674e12327..1c5aa308ba99 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs @@ -14,8 +14,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security { public abstract class BaseHttpHeaderCheck : HealthCheck { - protected IRuntimeState Runtime { get; } - protected ILocalizedTextService TextService { get; } + private readonly ILocalizedTextService _textService; + private readonly IRuntimeState _runtime; private const string SetHeaderInConfigAction = "setHeaderInConfig"; @@ -24,14 +24,14 @@ public abstract class BaseHttpHeaderCheck : HealthCheck private readonly string _localizedTextPrefix; private readonly bool _metaTagOptionAvailable; + protected BaseHttpHeaderCheck( IRuntimeState runtime, ILocalizedTextService textService, string header, string value, string localizedTextPrefix, bool metaTagOptionAvailable) { - Runtime = runtime; - TextService = textService ?? throw new ArgumentNullException(nameof(textService)); - + _runtime = runtime; + _textService = textService ?? throw new ArgumentNullException(nameof(textService)); _header = header; _value = value; _localizedTextPrefix = localizedTextPrefix; @@ -70,7 +70,8 @@ protected HealthCheckStatus CheckForHeader() var success = false; // Access the site home page and check for the click-jack protection header or meta tag - var url = Runtime.ApplicationUrl; + var url = _runtime.ApplicationUrl.GetLeftPart(UriPartial.Authority); + var request = WebRequest.Create(url); request.Method = "GET"; try @@ -84,15 +85,16 @@ protected HealthCheckStatus CheckForHeader() if (success == false && _metaTagOptionAvailable) { success = DoMetaTagsContainKeyForHeader(response); + } message = success - ? TextService.Localize($"healthcheck", $"{_localizedTextPrefix}CheckHeaderFound") - : TextService.Localize($"healthcheck", $"{_localizedTextPrefix}CheckHeaderNotFound"); + ? _textService.Localize($"healthcheck", $"{_localizedTextPrefix}CheckHeaderFound") + : _textService.Localize($"healthcheck", $"{_localizedTextPrefix}CheckHeaderNotFound"); } catch (Exception ex) { - message = TextService.Localize("healthcheck", "healthCheckInvalidUrl", new[] { url.ToString(), ex.Message }); + message = _textService.Localize("healthcheck", "healthCheckInvalidUrl", new[] { url.ToString(), ex.Message }); } var actions = new List(); @@ -100,8 +102,8 @@ protected HealthCheckStatus CheckForHeader() { actions.Add(new HealthCheckAction(SetHeaderInConfigAction, Id) { - Name = TextService.Localize("healthcheck", "setHeaderInConfig"), - Description = TextService.Localize($"healthcheck", $"{_localizedTextPrefix}SetHeaderInConfigDescription") + Name = _textService.Localize("healthcheck", "setHeaderInConfig"), + Description = _textService.Localize($"healthcheck", $"{_localizedTextPrefix}SetHeaderInConfigDescription") }); } @@ -149,14 +151,14 @@ private HealthCheckStatus SetHeaderInConfig() if (success) { return - new HealthCheckStatus(TextService.Localize("healthcheck", _localizedTextPrefix + "SetHeaderInConfigSuccess")) + new HealthCheckStatus(_textService.Localize("healthcheck", _localizedTextPrefix + "SetHeaderInConfigSuccess")) { ResultType = StatusResultType.Success }; } return - new HealthCheckStatus(TextService.Localize("healthcheck", "setHeaderInConfigError", new [] { errorMessage })) + new HealthCheckStatus(_textService.Localize("healthcheck", "setHeaderInConfigError", new [] { errorMessage })) { ResultType = StatusResultType.Error }; From 56e05988f42b7e71aa91bae1313beff029f7fd17 Mon Sep 17 00:00:00 2001 From: Martin Bentancour Date: Fri, 29 Oct 2021 16:00:52 +0200 Subject: [PATCH 14/57] Validate fileName to prevent path traversal --- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + .../Umbraco/config/lang/en_us.xml | 1 + .../Editors/ContentTypeController.cs | 68 +++++++++++-------- 3 files changed, 41 insertions(+), 29 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index bf89c758aae4..2d1e7d9109f4 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -323,6 +323,7 @@ or click here to choose files You can drag files here to upload Cannot upload this file, it does not have an approved file type + Cannot upload this file, it does not have a valid file name Max file size is Media root Failed to move media 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 79c113f99dbc..62c4b4f739f0 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -328,6 +328,7 @@ or click here to choose files You can drag files here to upload. Cannot upload this file, it does not have an approved file type + Cannot upload this file, it does not have a valid file name Max file size is Media root Failed to move media diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index c878d58e6cc3..62787fbedb94 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -576,43 +576,53 @@ public async Task Upload() var fileName = file.Headers.ContentDisposition.FileName.Trim(Constants.CharArrays.DoubleQuote); var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower(); - var destFileName = root + "\\" + fileName; - try - { - // due to a bug before 8.7.0 we didn't delete temp files, so we need to make sure to delete before - // moving else you get errors and the upload fails without a message in the UI (there's a JS error) - if(System.IO.File.Exists(destFileName)) - System.IO.File.Delete(destFileName); - - // renaming the file because MultipartFormDataStreamProvider has created a random fileName instead of using the name from the - // content-disposition for more than 6 years now. Creating a CustomMultipartDataStreamProvider deriving from MultipartFormDataStreamProvider - // seems like a cleaner option, but I'm not sure where to put it and renaming only takes one line of code. - System.IO.File.Move(result.FileData[0].LocalFileName, destFileName); - } - catch (Exception ex) + var destFileName = Path.Combine(root, fileName); + if (Path.GetFullPath(destFileName).StartsWith(Path.GetFullPath(root))) { - Logger.Error(ex, "Error uploading udt file to App_Data: {File}", destFileName); - } - - if (ext.InvariantEquals("udt")) - { - model.TempFileName = Path.Combine(root, fileName); + try + { + // due to a bug before 8.7.0 we didn't delete temp files, so we need to make sure to delete before + // moving else you get errors and the upload fails without a message in the UI (there's a JS error) + if(System.IO.File.Exists(destFileName)) + System.IO.File.Delete(destFileName); + + // renaming the file because MultipartFormDataStreamProvider has created a random fileName instead of using the name from the + // content-disposition for more than 6 years now. Creating a CustomMultipartDataStreamProvider deriving from MultipartFormDataStreamProvider + // seems like a cleaner option, but I'm not sure where to put it and renaming only takes one line of code. + System.IO.File.Move(result.FileData[0].LocalFileName, destFileName); + } + catch (Exception ex) + { + Logger.Error(ex, "Error uploading udt file to App_Data: {File}", destFileName); + } - var xd = new XmlDocument + if (ext.InvariantEquals("udt")) { - XmlResolver = null - }; - xd.Load(model.TempFileName); + model.TempFileName = destFileName; + + var xd = new XmlDocument + { + XmlResolver = null + }; + xd.Load(model.TempFileName); - model.Alias = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Alias")?.FirstChild.Value; - model.Name = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Name")?.FirstChild.Value; + model.Alias = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Alias")?.FirstChild.Value; + model.Name = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Name")?.FirstChild.Value; + } + else + { + model.Notifications.Add(new Notification( + Services.TextService.Localize("speechBubbles", "operationFailedHeader"), + Services.TextService.Localize("media", "disallowedFileType"), + NotificationStyle.Warning)); + } } else { model.Notifications.Add(new Notification( - Services.TextService.Localize("speechBubbles", "operationFailedHeader"), - Services.TextService.Localize("media", "disallowedFileType"), - NotificationStyle.Warning)); + Services.TextService.Localize("speechBubbles", "operationFailedHeader"), + Services.TextService.Localize("media", "invalidFileName"), + NotificationStyle.Warning)); } return model; From a20915664dc35cbc915e06bd881ac0384329fb79 Mon Sep 17 00:00:00 2001 From: Ibrahim Muhammad Nada Date: Fri, 29 Oct 2021 19:06:00 +0300 Subject: [PATCH 15/57] GitHub issue#11299 fix v8 (#11493) * adding validation * adding localization keys * applying cleaning up notes * fix spaces * revert ex.xml * spaces again * keep it DRY * chenge hasTabsAndFirstRender to hasTabsOrFirstRender Co-authored-by: inada --- .../nestedcontent.doctypepicker.html | 9 +++++++-- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 19 +++++++++---------- .../Umbraco/config/lang/en_us.xml | 1 + 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html index fa146f12f04b..f429c04f1d31 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html @@ -22,7 +22,7 @@ - {{ph = placeholder(config);""}} + {{ph = placeholder(config);hasTabsOrFirstRender = (elemTypeTabs[config.ncAlias].length || config.ncAlias=='');""}}
-
+
@@ -90,7 +90,7 @@ ng-show="item.isFolder" ng-class="{'-locked': item.selected || !item.file || !item.thumbnail}" ng-click="clickItemName(item, $event, $index)"> - + {{item.name}}
@@ -101,4 +101,3 @@
- diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 35d7b8862c08..0a2edb727311 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -806,6 +806,7 @@ Avatar for Header system field + Last Updated Blue 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 2003536def7a..16002c80f596 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -815,6 +815,7 @@ Avatar for Header system field + Last Updated Blue From 7e55c469e3ca9501036b185efd83295a61a2409c Mon Sep 17 00:00:00 2001 From: Anders Bjerner Date: Sat, 30 Oct 2021 13:33:09 +0200 Subject: [PATCH 17/57] Added extra selector for styling elements in the content --- src/Umbraco.Web.UI.Docs/umb-docs.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Docs/umb-docs.css b/src/Umbraco.Web.UI.Docs/umb-docs.css index 850e0d4aa45b..80d1bbbd2a0d 100644 --- a/src/Umbraco.Web.UI.Docs/umb-docs.css +++ b/src/Umbraco.Web.UI.Docs/umb-docs.css @@ -34,7 +34,8 @@ a:hover { color: rgba(0, 0, 0, .8); } -.content p code { +.content p code, +.content li code { font-size: 85%; font-family: inherit; background-color: #f7f7f9; From 152956bb1ac22b62e8115ebf780db3bce211b4fa Mon Sep 17 00:00:00 2001 From: Anders Bjerner Date: Sat, 30 Oct 2021 16:00:49 +0200 Subject: [PATCH 18/57] Added ngdocs documentation for overlay.service.js --- .../src/common/services/overlay.service.js | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js index 8a965f2c78d9..113b26d74cbb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js @@ -12,6 +12,58 @@ var currentOverlay = null; + /** + * @ngdoc method + * @name umbraco.services.overlayService#open + * @methodOf umbraco.services.overlayService + * + * @description + * Opens a new overlay. + * + * @param {object} overlay The rendering options for the overlay. + * @param {string=} overlay.view The URL to the view. Defaults to `views/common/overlays/default/default.html` if nothing is specified. + * @param {string=} overlay.position The alias of the position of the overlay. Defaults to `center`. + * + * Custom positions can be added by adding a CSS rule for the the underlying CSS rule. Eg. for the position `center`, the corresponding `umb-overlay-center` CSS rule is defined as: + * + *
+         * .umb-overlay.umb-overlay-center {
+         *     position: absolute;
+         *     width: 600px;
+         *     height: auto;
+         *     top: 50%;
+         *     left: 50%;
+         *     transform: translate(-50%,-50%);
+         *     border-radius: 3px;
+         * }
+         * 
+ * @param {string=} overlay.size Sets an alias for the size of the overlay to be opened. If set to `small` (default), an `umb-overlay--small` class name will be appended the the class list of the main overlay element in the DOM. + * + * Umbraco does not support any more sizes by default, but if you wish to introduce a `medium` size, you could do so by adding a CSS rule simlar to: + * + *
+         * .umb-overlay-center.umb-overlay--medium {
+         *     width: 800px;
+         * }
+         * 
+ * @param {booean=} overlay.disableBackdropClick A boolean value indicating whether the click event on the backdrop should be disabled. + * @param {string=} overlay.title The overall title of the overlay. The title will be omitted if not specified. + * @param {string=} overlay.subtitle The sub title of the overlay. The sub title will be omitted if not specified. + * @param {object=} overlay.itemDetails An item that will replace the header of the overlay. + * @param {string=} overlay.itemDetails.icon The icon of the item - eg. `icon-book`. + * @param {string=} overlay.itemDetails.title The title of the item. + * @param {string=} overlay.itemDetails.description Sets the description of the item. * + * @param {string=} overlay.submitButtonLabel The label of the submit button. To support localized values, it's recommended to use the `submitButtonLabelKey` instead. + * @param {string=} overlay.submitButtonLabelKey The key to be used for the submit button label. Defaults to `general_submit` if not specified. + * @param {string=} overlay.submitButtonState The state of the submit button. Possible values are inherited from the [umbButton directive](#/api/umbraco.directives.directive:umbButton) and are `init`, `busy", `success`, `error`. + * @param {string=} overlay.submitButtonStyle The styling of the submit button. Possible values are inherited from the [umbButton directive](#/api/umbraco.directives.directive:umbButton) and are `primary`, `info`, `success`, `warning`, `danger`, `inverse`, `link` and `block`. Defaults to `success` if not specified specified. + * @param {string=} overlay.hideSubmitButton A boolean value indicating whether the submit button should be hidden. Default is `false`. + * @param {string=} overlay.disableSubmitButton A boolean value indicating whether the submit button should be disabled, preventing the user from submitting the overlay. Default is `false`. + * @param {string=} overlay.closeButtonLabel The label of the close button. To support localized values, it's recommended to use the `closeButtonLabelKey` instead. + * @param {string=} overlay.closeButtonLabelKey The key to be used for the close button label. Defaults to `general_close` if not specified. + * @param {string=} overlay.submit A callback function that is invoked when the user submits the overlay. + * @param {string=} overlay.close A callback function that is invoked when the user closes the overlay. + */ function open(newOverlay) { // prevent two open overlays at the same time @@ -49,6 +101,14 @@ eventsService.emit("appState.overlay", overlay); } + /** + * @ngdoc method + * @name umbraco.services.overlayService#close + * @methodOf umbraco.services.overlayService + * + * @description + * Closes the current overlay. + */ function close() { focusLockService.removeInertAttribute(); @@ -61,6 +121,16 @@ eventsService.emit("appState.overlay", null); } + /** + * @ngdoc method + * @name umbraco.services.overlayService#ysod + * @methodOf umbraco.services.overlayService + * + * @description + * Opens a new overlay with an error message. + * + * @param {object} error The error to be shown. + */ function ysod(error) { const overlay = { view: "views/common/overlays/ysod/ysod.html", @@ -72,6 +142,36 @@ open(overlay); } + /** + * @ngdoc method + * @name umbraco.services.overlayService#confirm + * @methodOf umbraco.services.overlayService + * + * @description + * Opens a new overlay prompting the user to confirm the overlay. + * + * @param {object} overlay The options for the overlay. + * @param {string=} overlay.confirmType The type of the confirm dialog, which helps define standard styling and labels of the overlay. Supported values are `delete` and `remove`. + * @param {string=} overlay.closeButtonLabelKey The key to be used for the cancel button label. Defaults to `general_cancel` if not specified. + * @param {string=} overlay.view The URL to the view. Defaults to `views/common/overlays/confirm/confirm.html` if nothing is specified. + * @param {string=} overlay.confirmMessageStyle The styling of the confirm message. If `overlay.confirmType` is `delete`, the fallback value is `danger` - otherwise a message style isn't explicitly specified. + * @param {string=} overlay.submitButtonStyle The styling of the confirm button. Possible values are inherited from the [umbButton directive](#/api/umbraco.directives.directive:umbButton) and are `primary`, `info`, `success`, `warning`, `danger`, `inverse`, `link` and `block`. + * + * If not specified, the fallback value depends on the value specified for the `overlay.confirmType` parameter: + * + * - `delete`: fallback key is `danger` + * - `remove`: fallback key is `primary` + * - anything else: no fallback AKA default button style + * @param {string=} overlay.submitButtonLabelKey The key to be used for the confirm button label. + * + * If not specified, the fallback value depends on the value specified for the `overlay.confirmType` parameter: + * + * - `delete`: fallback key is `actions_delete` + * - `remove`: fallback key is `actions_remove` + * - anything else: fallback is `general_confirm` + * @param {function=} overlay.close A callback function that is invoked when the user closes the overlay. + * @param {function=} overlay.submit A callback function that is invoked when the user confirms the overlay. + */ function confirm(overlay) { if (!overlay.closeButtonLabelKey) overlay.closeButtonLabelKey = "general_cancel"; @@ -99,11 +199,45 @@ open(overlay); } + /** + * @ngdoc method + * @name umbraco.services.overlayService#confirmDelete + * @methodOf umbraco.services.overlayService + * + * @description + * Opens a new overlay prompting the user to confirm the overlay. The overlay will have styling and labels useful for when the user needs to confirm a delete action. + * + * @param {object} overlay The options for the overlay. + * @param {string=} overlay.closeButtonLabelKey The key to be used for the cancel button label. Defaults to `general_cancel` if not specified. + * @param {string=} overlay.view The URL to the view. Defaults to `views/common/overlays/confirm/confirm.html` if nothing is specified. + * @param {string=} overlay.confirmMessageStyle The styling of the confirm message. Defaults to `delete` if not specified specified. + * @param {string=} overlay.submitButtonStyle The styling of the confirm button. Possible values are inherited from the [umbButton directive](#/api/umbraco.directives.directive:umbButton) and are `primary`, `info`, `success`, `warning`, `danger`, `inverse`, `link` and `block`. Defaults to `danger` if not specified specified. + * @param {string=} overlay.submitButtonLabelKey The key to be used for the confirm button label. Defaults to `actions_delete` if not specified. + * @param {function=} overlay.close A callback function that is invoked when the user closes the overlay. + * @param {function=} overlay.submit A callback function that is invoked when the user confirms the overlay. + */ function confirmDelete(overlay) { overlay.confirmType = "delete"; confirm(overlay); } + /** + * @ngdoc method + * @name umbraco.services.overlayService#confirmRemove + * @methodOf umbraco.services.overlayService + * + * @description + * Opens a new overlay prompting the user to confirm the overlay. The overlay will have styling and labels useful for when the user needs to confirm a remove action. + * + * @param {object} overlay The options for the overlay. + * @param {string=} overlay.closeButtonLabelKey The key to be used for the cancel button label. Defaults to `general_cancel` if not specified. + * @param {string=} overlay.view The URL to the view. Defaults to `views/common/overlays/confirm/confirm.html` if nothing is specified. + * @param {string=} overlay.confirmMessageStyle The styling of the confirm message - eg. `danger`. + * @param {string=} overlay.submitButtonStyle The styling of the confirm button. Possible values are inherited from the [umbButton directive](#/api/umbraco.directives.directive:umbButton) and are `primary`, `info`, `success`, `warning`, `danger`, `inverse`, `link` and `block`. Defaults to `primary` if not specified specified. + * @param {string=} overlay.submitButtonLabelKey The key to be used for the confirm button label. Defaults to `actions_remove` if not specified. + * @param {function=} overlay.close A callback function that is invoked when the user closes the overlay. + * @param {function=} overlay.submit A callback function that is invoked when the user confirms the overlay. + */ function confirmRemove(overlay) { overlay.confirmType = "remove"; confirm(overlay); From 1553f02b18585c3946f9b146156042f5e1ab80a4 Mon Sep 17 00:00:00 2001 From: Claus Date: Mon, 1 Nov 2021 15:50:54 +0100 Subject: [PATCH 19/57] adding unit test for validating language xml files. (#7174) * adding unit test for validating language xml files. * locating the language files by traversing instead of hardcoded path. * debugging null refs. * debugging * Updated method of detecting language folder for XML tests, that should work correctly on the build server. * Path amends to support running test on build server. * Path amends to support running test on build server (2). Co-authored-by: Andy Butland --- .../Configurations/LanguageXmlTests.cs | 51 +++++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + 2 files changed, 52 insertions(+) create mode 100644 src/Umbraco.Tests/Configurations/LanguageXmlTests.cs diff --git a/src/Umbraco.Tests/Configurations/LanguageXmlTests.cs b/src/Umbraco.Tests/Configurations/LanguageXmlTests.cs new file mode 100644 index 000000000000..19f8c96f2593 --- /dev/null +++ b/src/Umbraco.Tests/Configurations/LanguageXmlTests.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml; +using NUnit.Framework; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Configurations +{ + [TestFixture] + public class LanguageXmlTests + { + [Test] + public void Can_Load_Language_Xml_Files() + { + var languageDirectory = GetLanguageDirectory(); + var readFilesCount = 0; + var xmlDocument = new XmlDocument(); + foreach (var languageFile in languageDirectory.EnumerateFiles("*.xml")) + { + // Load will throw an exception if the XML isn't valid. + xmlDocument.Load(languageFile.FullName); + readFilesCount++; + } + + // Ensure that at least one file was read. + Assert.AreNotEqual(0, readFilesCount); + } + + private static DirectoryInfo GetLanguageDirectory() + { + var testDirectoryPathParts = Path.GetDirectoryName(TestContext.CurrentContext.TestDirectory) + .Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); + var solutionDirectoryPathParts = testDirectoryPathParts + .Take(Array.IndexOf(testDirectoryPathParts, "src") + 1); + var languageFolderPathParts = new List(solutionDirectoryPathParts); + var additionalPathParts = new[] { "Umbraco.Web.UI", "Umbraco", "config", "lang" }; + languageFolderPathParts.AddRange(additionalPathParts); + + // Hack for build-server - when this path is generated in that envrionment it's missing the "src" folder. + // Not sure why, but if it's missing we'll add it in the right place. + if (!languageFolderPathParts.Contains("src")) + { + languageFolderPathParts.Insert(languageFolderPathParts.Count - additionalPathParts.Length, "src"); + } + + return new DirectoryInfo(string.Join(Path.DirectorySeparatorChar.ToString(), languageFolderPathParts)); + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index c00c67ca1ce7..c77ba6d188c4 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -120,6 +120,7 @@ + From bd23a5fd2d61a7e4f80dfdcfb7c47fd7cf504ea3 Mon Sep 17 00:00:00 2001 From: Callum Whyte Date: Fri, 29 Oct 2021 19:12:32 +0100 Subject: [PATCH 20/57] Making UmbracoTreeSearcherFields virtual for easier overriding --- .../Search/UmbracoTreeSearcherFields.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcherFields.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcherFields.cs index 3025f869b4c0..55becb08a05a 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcherFields.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcherFields.cs @@ -24,28 +24,28 @@ public UmbracoTreeSearcherFields(ILocalizationService localizationService) } /// - public IEnumerable GetBackOfficeFields() => _backOfficeFields; + public virtual IEnumerable GetBackOfficeFields() => _backOfficeFields; /// - public IEnumerable GetBackOfficeMembersFields() => _backOfficeMembersFields; + public virtual IEnumerable GetBackOfficeMembersFields() => _backOfficeMembersFields; /// - public IEnumerable GetBackOfficeMediaFields() => _backOfficeMediaFields; + public virtual IEnumerable GetBackOfficeMediaFields() => _backOfficeMediaFields; /// - public IEnumerable GetBackOfficeDocumentFields() => Enumerable.Empty(); + public virtual IEnumerable GetBackOfficeDocumentFields() => Enumerable.Empty(); /// - public ISet GetBackOfficeFieldsToLoad() => _backOfficeFieldsToLoad; + public virtual ISet GetBackOfficeFieldsToLoad() => _backOfficeFieldsToLoad; /// - public ISet GetBackOfficeMembersFieldsToLoad() => _backOfficeMembersFieldsToLoad; + public virtual ISet GetBackOfficeMembersFieldsToLoad() => _backOfficeMembersFieldsToLoad; /// - public ISet GetBackOfficeMediaFieldsToLoad() => _backOfficeMediaFieldsToLoad; + public virtual ISet GetBackOfficeMediaFieldsToLoad() => _backOfficeMediaFieldsToLoad; /// - public ISet GetBackOfficeDocumentFieldsToLoad() + public virtual ISet GetBackOfficeDocumentFieldsToLoad() { var fields = _backOfficeDocumentFieldsToLoad; From c3279f71c1d81eaa4b6ca6d49c44eed69ec2bcf2 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 2 Nov 2021 09:18:03 +0100 Subject: [PATCH 21/57] 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 22/57] 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 23/57] 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 24/57] 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 25/57] 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 26/57] 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 27/57] 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 28/57] 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 29/57] 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 f68dba7bcb16308af17c5385b8e586165e44b578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 2 Nov 2021 13:21:44 +0100 Subject: [PATCH 30/57] Additional optional sanitization of scripting in TinyMCE (#10653) --- .../Configuration/GlobalSettings.cs | 21 ++++++ .../Configuration/IGlobalSettings.cs | 5 ++ src/Umbraco.Core/Constants-AppSettings.cs | 7 +- .../src/common/services/tinymce.service.js | 67 +++++++++++++++++++ src/Umbraco.Web.UI/web.Template.config | 1 + .../Editors/BackOfficeServerVariables.cs | 1 + 6 files changed, 101 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Configuration/GlobalSettings.cs b/src/Umbraco.Core/Configuration/GlobalSettings.cs index c844abe75e49..41e8f633c99b 100644 --- a/src/Umbraco.Core/Configuration/GlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/GlobalSettings.cs @@ -395,6 +395,27 @@ public bool UseHttps } } + /// + /// Returns true if TinyMCE scripting sanitization should be applied + /// + /// + /// The default value is false + /// + public bool SanitizeTinyMce + { + get + { + try + { + return bool.Parse(ConfigurationManager.AppSettings[Constants.AppSettings.SanitizeTinyMce]); + } + catch + { + return false; + } + } + } + /// /// An int value representing the time in milliseconds to lock the database for a write operation /// diff --git a/src/Umbraco.Core/Configuration/IGlobalSettings.cs b/src/Umbraco.Core/Configuration/IGlobalSettings.cs index 483829f85ff3..2ebab722f001 100644 --- a/src/Umbraco.Core/Configuration/IGlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/IGlobalSettings.cs @@ -77,5 +77,10 @@ public interface IGlobalSettings /// Gets the write lock timeout. /// int SqlWriteLockTimeOut { get; } + + /// + /// Returns true if TinyMCE scripting sanitization should be applied + /// + bool SanitizeTinyMce { get; } } } diff --git a/src/Umbraco.Core/Constants-AppSettings.cs b/src/Umbraco.Core/Constants-AppSettings.cs index 99ea26b4d698..de7799c1655c 100644 --- a/src/Umbraco.Core/Constants-AppSettings.cs +++ b/src/Umbraco.Core/Constants-AppSettings.cs @@ -109,7 +109,7 @@ public static class AppSettings /// A true or false indicating whether umbraco should force a secure (https) connection to the backoffice. ///
public const string UseHttps = "Umbraco.Core.UseHttps"; - + /// /// A true/false value indicating whether the content dashboard should be visible for all user groups. /// @@ -155,6 +155,11 @@ public static class Debug /// An int value representing the time in milliseconds to lock the database for a write operation ///
public const string SqlWriteLockTimeOut = "Umbraco.Core.SqlWriteLockTimeOut"; + + /// + /// Returns true if TinyMCE scripting sanitization should be applied + /// + public const string SanitizeTinyMce = "Umbraco.Web.SanitizeTinyMce"; } } } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index 070504d93232..0e176155af1e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -1497,6 +1497,19 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s }); } + + if(Umbraco.Sys.ServerVariables.umbracoSettings.sanitizeTinyMce === true){ + /** prevent injecting arbitrary JavaScript execution in on-attributes. */ + const allNodes = Array.prototype.slice.call(args.editor.dom.doc.getElementsByTagName("*")); + allNodes.forEach(node => { + for (var i = 0; i < node.attributes.length; i++) { + if(node.attributes[i].name.indexOf("on") === 0) { + node.removeAttribute(node.attributes[i].name) + } + } + }); + } + }); args.editor.on('init', function (e) { @@ -1508,6 +1521,60 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s //enable browser based spell checking args.editor.getBody().setAttribute('spellcheck', true); + + /** Setup sanitization for preventing injecting arbitrary JavaScript execution in attributes: + * https://github.com/advisories/GHSA-w7jx-j77m-wp65 + * https://github.com/advisories/GHSA-5vm8-hhgr-jcjp + */ + const uriAttributesToSanitize = ['src', 'href', 'data', 'background', 'action', 'formaction', 'poster', 'xlink:href']; + const parseUri = function() { + // Encapsulated JS logic. + const safeSvgDataUrlElements = [ 'img', 'video' ]; + const scriptUriRegExp = /((java|vb)script|mhtml):/i; + const trimRegExp = /[\s\u0000-\u001F]+/g; + const isInvalidUri = (uri, tagName) => { + if (/^data:image\//i.test(uri)) { + return safeSvgDataUrlElements.indexOf(tagName) !== -1 && /^data:image\/svg\+xml/i.test(uri); + } else { + return /^data:/i.test(uri); + } + }; + + return function parseUri(uri, tagName) { + uri = uri.replace(trimRegExp, ''); + try { + // Might throw malformed URI sequence + uri = decodeURIComponent(uri); + } catch (ex) { + // Fallback to non UTF-8 decoder + uri = unescape(uri); + } + + if (scriptUriRegExp.test(uri)) { + return; + } + + if (isInvalidUri(uri, tagName)) { + return; + } + + return uri; + } + }(); + + if(Umbraco.Sys.ServerVariables.umbracoSettings.sanitizeTinyMce === true){ + args.editor.serializer.addAttributeFilter(uriAttributesToSanitize, function (nodes) { + nodes.forEach(function(node) { + node.attributes.forEach(function(attr) { + const attrName = attr.name.toLowerCase(); + if(uriAttributesToSanitize.indexOf(attrName) !== -1) { + attr.value = parseUri(attr.value, node.name); + } + }); + }); + }); + } + //start watching the value startWatch(); }); diff --git a/src/Umbraco.Web.UI/web.Template.config b/src/Umbraco.Web.UI/web.Template.config index e61c6585ad43..f19ab5d3b638 100644 --- a/src/Umbraco.Web.UI/web.Template.config +++ b/src/Umbraco.Web.UI/web.Template.config @@ -51,6 +51,7 @@ + - + all diff --git a/src/Umbraco.PublishedCache.NuCache/ContentStore.cs b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs index 42aec80e51a4..240e6c8861a6 100644 --- a/src/Umbraco.PublishedCache.NuCache/ContentStore.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs @@ -528,7 +528,7 @@ private bool BuildKit(ContentNodeKit kit, out LinkedNode parent) parent = GetParentLink(kit.Node, null); if (parent == null) { - _logger.LogWarning("Skip item id={kit.Node.Id}, could not find parent id={kit.Node.ParentContentId}.", kit.Node.Id, kit.Node.ParentContentId); + _logger.LogWarning("Skip item id={kitNodeId}, could not find parent id={kitNodeParentContentId}.", kit.Node.Id, kit.Node.ParentContentId); return false; } @@ -537,21 +537,21 @@ private bool BuildKit(ContentNodeKit kit, out LinkedNode parent) // because the data sort operation is by path. if (parent.Value == null) { - _logger.LogWarning("Skip item id={kit.Node.Id}, no Data assigned for linked node with path {kit.Node.Path} and parent id {kit.Node.ParentContentId}. This can indicate data corruption for the Path value for node {kit.Node.Id}. See the Health Check dashboard in Settings to resolve data integrity issues.", kit.Node.Id, kit.Node.ParentContentId); + _logger.LogWarning("Skip item id={kitNodeId}, no Data assigned for linked node with path {kitNodePath} and parent id {kitNodeParentContentId}. This can indicate data corruption for the Path value for node {kitNodeId}. See the Health Check dashboard in Settings to resolve data integrity issues.", kit.Node.Id, kit.Node.Path, kit.Node.ParentContentId, kit.Node.Id); return false; } // make sure the kit is valid if (kit.DraftData == null && kit.PublishedData == null) { - _logger.LogWarning("Skip item id={kit.Node.Id}, both draft and published data are null.", kit.Node.Id); + _logger.LogWarning("Skip item id={kitNodeId}, both draft and published data are null.", kit.Node.Id); return false; } // unknown = bad if (_contentTypesById.TryGetValue(kit.ContentTypeId, out var link) == false || link.Value == null) { - _logger.LogWarning("Skip item id={kit.Node.Id}, could not find content type id={kit.ContentTypeId}.", kit.Node.Id, kit.ContentTypeId); + _logger.LogWarning("Skip item id={kitNodeId}, could not find content type id={kitContentTypeId}.", kit.Node.Id, kit.ContentTypeId); return false; } @@ -727,7 +727,7 @@ public bool SetAllFastSortedLocked(IEnumerable kits, bool fromDb previousNode = null; // there is no previous sibling } - _logger.LogDebug("Set {thisNode.Id} with parent {thisNode.ParentContentId}", thisNode.Id, thisNode.ParentContentId); + _logger.LogDebug("Set {thisNodeId} with parent {thisNodeParentContentId}", thisNode.Id, thisNode.ParentContentId); SetValueLocked(_contentNodes, thisNode.Id, thisNode); // if we are initializing from the database source ensure the local db is updated @@ -784,7 +784,7 @@ public bool SetAllLocked(IEnumerable kits) ok = false; continue; // skip that one } - _logger.LogDebug("Set {kit.Node.Id} with parent {kit.Node.ParentContentId}", kit.Node.Id, kit.Node.ParentContentId); + _logger.LogDebug("Set {kitNodeId} with parent {kitNodeParentContentId}", kit.Node.Id, kit.Node.ParentContentId); SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); if (_localDb != null) RegisterChange(kit.Node.Id, kit); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts new file mode 100644 index 000000000000..123109816482 --- /dev/null +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts @@ -0,0 +1,38 @@ +/// +context('Languages', () => { + + beforeEach(() => { + cy.umbracoLogin(Cypress.env('username'), Cypress.env('password'), false); + }); + + it('Deletes language', () => { + // Setup + const language1 = 'da'; + const language2 = 'en-GB'; + cy.umbracoEnsureLanguageNotExists(language1); + cy.umbracoEnsureLanguageNotExists(language2); + cy.umbracoCreateLanguage(language1, true, '1'); + cy.umbracoCreateLanguage(language2, true, '1'); + cy.umbracoSection('settings'); + + // Enter language tree and select the language we just created + cy.umbracoTreeItem('settings', ['Languages']).click(); + + // Assert there are 3 languages + cy.get('tbody > tr').should('have.length', 3); + + // Delete the Danish language + cy.get('tr').contains('Danish').parents('tr').within(() => { + cy.get('umb-button[label-key="general_delete"]').click() + }); + cy.umbracoButtonByLabelKey('contentTypeEditor_yesDelete').click(); + + // Assert there is only 2 language + cy.get('tbody > tr').should('have.length', 3); + + // Cleanup + cy.umbracoEnsureLanguageNotExists(language1); + cy.umbracoEnsureLanguageNotExists(language2); + }); + +}); \ No newline at end of file diff --git a/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs b/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs index de3125ad6416..008582b6b3b8 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs @@ -91,12 +91,10 @@ public ActionResult GetSearchResults(string searcherName, string return SearchResults.Empty(); } - var pagedResults = results.Skip(pageIndex * pageSize); - return new SearchResults { TotalRecords = results.TotalItemCount, - Results = pagedResults.Select(x => new SearchResult + Results = results.Select(x => new SearchResult { Id = x.Id, Score = x.Score, diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index d54bd7a093aa..14a90805864e 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -858,7 +858,7 @@ public async Task PostAddFile([FromForm] string path, [FromForm] } } - return Ok(); + return Ok(tempFiles); } private IMedia FindInChildren(int mediaId, string nameToFind, string contentTypeAlias) diff --git a/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs b/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs index 0a800693f80c..23c955219aee 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs @@ -3,6 +3,7 @@ using System.Linq; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Mapping; @@ -12,6 +13,7 @@ using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Web.Common.Attributes; using Umbraco.Cms.Web.Common.Authorization; +using Umbraco.Cms.Web.Common.DependencyInjection; using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Web.BackOffice.Controllers @@ -24,15 +26,28 @@ public class TemplateController : BackOfficeNotificationsController private readonly IFileService _fileService; private readonly IUmbracoMapper _umbracoMapper; private readonly IShortStringHelper _shortStringHelper; + private readonly IDefaultViewContentProvider _defaultViewContentProvider; + [ActivatorUtilitiesConstructor] public TemplateController( IFileService fileService, IUmbracoMapper umbracoMapper, - IShortStringHelper shortStringHelper) + IShortStringHelper shortStringHelper, + IDefaultViewContentProvider defaultViewContentProvider) { _fileService = fileService ?? throw new ArgumentNullException(nameof(fileService)); _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper)); _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); + _defaultViewContentProvider = defaultViewContentProvider ?? throw new ArgumentNullException(nameof(defaultViewContentProvider)); + } + + [Obsolete("Use ctor will all params")] + public TemplateController( + IFileService fileService, + IUmbracoMapper umbracoMapper, + IShortStringHelper shortStringHelper) + : this(fileService, umbracoMapper, shortStringHelper, StaticServiceProvider.Instance.GetRequiredService()) + { } /// @@ -136,10 +151,10 @@ public TemplateDisplay GetScaffold(int id) } } - var content = ViewHelper.GetDefaultFileContent( layoutPageAlias: dt.MasterTemplateAlias ); + var content = _defaultViewContentProvider.GetDefaultFileContent( layoutPageAlias: dt.MasterTemplateAlias ); var scaffold = _umbracoMapper.Map(dt); - scaffold.Content = content + "\r\n\r\n@* the fun starts here *@\r\n\r\n"; + scaffold.Content = content; return scaffold; } diff --git a/src/Umbraco.Web.BackOffice/PropertyEditors/TagsDataController.cs b/src/Umbraco.Web.BackOffice/PropertyEditors/TagsDataController.cs index 17d015abc803..7f02de479448 100644 --- a/src/Umbraco.Web.BackOffice/PropertyEditors/TagsDataController.cs +++ b/src/Umbraco.Web.BackOffice/PropertyEditors/TagsDataController.cs @@ -1,10 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Web.BackOffice.Controllers; using Umbraco.Cms.Web.Common.Attributes; +using Umbraco.Cms.Web.Common.Filters; using Umbraco.Extensions; using Constants = Umbraco.Cms.Core.Constants; @@ -33,6 +34,8 @@ public TagsDataController(ITagQuery tagQuery) /// /// /// + /// + [AllowHttpJsonConfigration] public IEnumerable GetTags(string tagGroup, string culture, string query = null) { if (culture == string.Empty) culture = null; diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs index 1bddb12cdeb3..8bf84357031c 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs @@ -68,7 +68,7 @@ AppCaches appCaches /// /// /// - public ActionResult GetTreeNode([FromRoute] string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings) + public ActionResult GetTreeNode([FromRoute] string id, [ModelBinder(typeof(HttpQueryStringModelBinder))] FormCollection queryStrings) { int asInt; Guid asGuid = Guid.Empty; @@ -325,7 +325,8 @@ private IEnumerable GetChildrenFromEntityService(int entityId) /// protected bool HasPathAccess(IUmbracoEntity entity, FormCollection queryStrings) { - if (entity == null) return false; + if (entity == null) + return false; return RecycleBinId == Constants.System.RecycleBinContent ? _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.HasContentPathAccess(entity, _entityService, _appCaches) : _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.HasMediaPathAccess(entity, _entityService, _appCaches); @@ -469,13 +470,13 @@ protected sealed override ActionResult GetMenuForNode(string // only add empty recycle bin if the current user is allowed to delete by default if (deleteAllowed) { - menu.Items.Add(new MenuItem("emptyrecyclebin", LocalizedTextService) - { - Icon = "trash", - OpensDialog = true - }); - menu.Items.Add(new RefreshNode(LocalizedTextService, true)); - } + menu.Items.Add(new MenuItem("emptyRecycleBin", LocalizedTextService) + { + Icon = "trash", + OpensDialog = true + }); + menu.Items.Add(new RefreshNode(LocalizedTextService, true)); + } return menu; } @@ -608,7 +609,8 @@ internal IEntitySlim GetEntityFromId(string id) /// internal bool IgnoreUserStartNodes(FormCollection queryStrings) { - if (_ignoreUserStartNodes.HasValue) return _ignoreUserStartNodes.Value; + if (_ignoreUserStartNodes.HasValue) + return _ignoreUserStartNodes.Value; var dataTypeKey = queryStrings.GetValue(TreeQueryStringParameters.DataTypeKey); _ignoreUserStartNodes = dataTypeKey.HasValue && _dataTypeService.IsDataTypeIgnoringUserStartNodes(dataTypeKey.Value); diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs index c4112cc77e39..ecc5b78a51e9 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs @@ -76,7 +76,8 @@ protected override ActionResult GetTreeNodes(string id, Form })); //if the request is for folders only then just return - if (queryStrings["foldersonly"].ToString().IsNullOrWhiteSpace() == false && queryStrings["foldersonly"] == "1") return nodes; + if (queryStrings["foldersonly"].ToString().IsNullOrWhiteSpace() == false && queryStrings["foldersonly"] == "1") + return nodes; var children = _entityService.GetChildren(intId, UmbracoObjectTypes.DocumentType).ToArray(); var contentTypes = _contentTypeService.GetAll(children.Select(c => c.Id).ToArray()).ToDictionary(c => c.Id); @@ -117,7 +118,7 @@ protected override ActionResult GetMenuForNode(string id, Fo // root actions menu.Items.Add(LocalizedTextService, opensDialog: true); - menu.Items.Add(new MenuItem("importdocumenttype", LocalizedTextService) + menu.Items.Add(new MenuItem("importDocumentType", LocalizedTextService) { Icon = "page-up", SeparatorBefore = true, diff --git a/src/Umbraco.Web.BackOffice/Trees/TreeCollectionBuilder.cs b/src/Umbraco.Web.BackOffice/Trees/TreeCollectionBuilder.cs index 37a857f78d14..d8d8afe13ada 100644 --- a/src/Umbraco.Web.BackOffice/Trees/TreeCollectionBuilder.cs +++ b/src/Umbraco.Web.BackOffice/Trees/TreeCollectionBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Trees; @@ -56,5 +57,16 @@ public void AddTreeControllers(IEnumerable controllerTypes) foreach (var controllerType in controllerTypes) AddTreeController(controllerType); } + + public void RemoveTreeController() => RemoveTreeController(typeof(T)); + + public void RemoveTreeController(Type type) + { + var tree = _trees.FirstOrDefault(it => it.TreeControllerType == type); + if (tree != null) + { + _trees.Remove(tree); + } + } } } diff --git a/src/Umbraco.Web.Common/ActionsResults/PublishedContentNotFoundResult.cs b/src/Umbraco.Web.Common/ActionsResults/PublishedContentNotFoundResult.cs index 2ef9a5e4b34f..0eb30bcd0377 100644 --- a/src/Umbraco.Web.Common/ActionsResults/PublishedContentNotFoundResult.cs +++ b/src/Umbraco.Web.Common/ActionsResults/PublishedContentNotFoundResult.cs @@ -44,18 +44,14 @@ public async Task ExecuteResultAsync(ActionContext context) reason = "No template exists to render the document at URL '{0}'."; } - await response.WriteAsync("

Page not found

"); - await response.WriteAsync("

"); - await response.WriteAsync(string.Format(reason, WebUtility.HtmlEncode(_umbracoContext.OriginalRequestUrl.PathAndQuery))); - await response.WriteAsync("

"); - if (string.IsNullOrWhiteSpace(_message) == false) + var viewResult = new ViewResult { - await response.WriteAsync("

" + _message + "

"); - } + ViewName = "~/umbraco/UmbracoWebsite/NotFound.cshtml" + }; + context.HttpContext.Items.Add("reason", string.Format(reason, WebUtility.HtmlEncode(_umbracoContext.OriginalRequestUrl.PathAndQuery))); + context.HttpContext.Items.Add("message", _message); - await response.WriteAsync("

This page can be replaced with a custom 404. Check the documentation for Custom 404 Error Pages.

"); - await response.WriteAsync("

This page is intentionally left ugly ;-)

"); - await response.WriteAsync(""); + await viewResult.ExecuteResultAsync(context); } } } diff --git a/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeHandler.cs b/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeHandler.cs index 0a4981d6c6e8..283accb08506 100644 --- a/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeHandler.cs +++ b/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeHandler.cs @@ -47,6 +47,13 @@ protected override Task HandleRequirementAsync(AuthorizationHandlerContext conte break; } + case Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext authorizationFilterContext: + { + IEndpointFeature endpointFeature = authorizationFilterContext.HttpContext.Features.Get(); + endpoint = endpointFeature.Endpoint; + break; + } + case Endpoint resourceEndpoint: { endpoint = resourceEndpoint; diff --git a/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs b/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs index 22ddc1551189..c59e7b112625 100644 --- a/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs @@ -32,6 +32,15 @@ public static string GetCropUrl( UrlMode urlMode = UrlMode.Default) => mediaItem.GetCropUrl(cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider, urlMode); + /// + /// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias in the MediaWithCrops content item) on the MediaWithCrops item. + /// + /// The MediaWithCrops item. + /// The crop alias e.g. thumbnail. + /// The url mode. + /// + /// The URL of the cropped image. + /// public static string GetCropUrl(this MediaWithCrops mediaWithCrops, string cropAlias, UrlMode urlMode = UrlMode.Default) => ImageCropperTemplateCoreExtensions.GetCropUrl(mediaWithCrops, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider, urlMode); @@ -69,6 +78,16 @@ public static string GetCropUrl( UrlMode urlMode = UrlMode.Default) => mediaItem.GetCropUrl(propertyAlias, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider, urlMode); + /// + /// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper JSON data on the MediaWithCrops content item. + /// + /// The MediaWithCrops item. + /// The property alias of the property containing the JSON data e.g. umbracoFile. + /// The crop alias e.g. thumbnail. + /// The url mode. + /// + /// The URL of the cropped image. + /// public static string GetCropUrl(this MediaWithCrops mediaWithCrops, string propertyAlias, string cropAlias, UrlMode urlMode = UrlMode.Default) => ImageCropperTemplateCoreExtensions.GetCropUrl(mediaWithCrops, propertyAlias, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider, urlMode); @@ -126,6 +145,60 @@ public static string GetCropUrl( urlMode ); + /// + /// Gets the underlying image processing service URL from the MediaWithCrops item. + /// + /// The MediaWithCrops item. + /// The width of the output image. + /// The height of the output image. + /// Property alias of the property containing the JSON data. + /// The crop alias. + /// Quality percentage of the output image. + /// The image crop mode. + /// The image crop anchor. + /// Use focal point, to generate an output image using the focal point instead of the predefined crop. + /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters. + /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. + /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: + /// + /// The url mode. + /// + /// The URL of the cropped image. + /// + public static string GetCropUrl( + this MediaWithCrops mediaWithCrops, + int? width = null, + int? height = null, + string propertyAlias = Cms.Core.Constants.Conventions.Media.File, + string cropAlias = null, + int? quality = null, + ImageCropMode? imageCropMode = null, + ImageCropAnchor? imageCropAnchor = null, + bool preferFocalPoint = false, + bool useCropDimensions = false, + bool cacheBuster = true, + string furtherOptions = null, + UrlMode urlMode = UrlMode.Default) + => mediaWithCrops.GetCropUrl( + ImageUrlGenerator, + PublishedValueFallback, + PublishedUrlProvider, + width, + height, + propertyAlias, + cropAlias, + quality, + imageCropMode, + imageCropAnchor, + preferFocalPoint, + useCropDimensions, + cacheBuster, + furtherOptions, + urlMode + ); + /// /// Gets the underlying image processing service URL from the image path. /// diff --git a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs index a3e96ebebb47..f335a81ae78a 100644 --- a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs @@ -32,6 +32,18 @@ public static string GetCropUrl( IPublishedUrlProvider publishedUrlProvider, UrlMode urlMode = UrlMode.Default) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true, urlMode: urlMode); + /// + /// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias in the MediaWithCrops content item) on the MediaWithCrops item. + /// + /// The MediaWithCrops item. + /// The crop alias e.g. thumbnail. + /// The image URL generator. + /// The published value fallback. + /// The published URL provider. + /// The url mode. + /// + /// The URL of the cropped image. + /// public static string GetCropUrl( this MediaWithCrops mediaWithCrops, string cropAlias, @@ -84,6 +96,19 @@ public static string GetCropUrl( IPublishedUrlProvider publishedUrlProvider, UrlMode urlMode = UrlMode.Default) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true, urlMode: urlMode); + /// + /// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper JSON data on the MediaWithCrops content item. + /// + /// The MediaWithCrops item. + /// The property alias of the property containing the JSON data e.g. umbracoFile. + /// The crop alias e.g. thumbnail. + /// The image URL generator. + /// The published value fallback. + /// The published URL provider. + /// The url mode. + /// + /// The URL of the cropped image. + /// public static string GetCropUrl(this MediaWithCrops mediaWithCrops, IPublishedValueFallback publishedValueFallback, IPublishedUrlProvider publishedUrlProvider, @@ -135,6 +160,31 @@ public static string GetCropUrl( string furtherOptions = null, UrlMode urlMode = UrlMode.Default) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, null, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, urlMode); + /// + /// Gets the underlying image processing service URL from the MediaWithCrops item. + /// + /// The MediaWithCrops item. + /// The image URL generator. + /// The published value fallback. + /// The published URL provider. + /// The width of the output image. + /// The height of the output image. + /// Property alias of the property containing the JSON data. + /// The crop alias. + /// Quality percentage of the output image. + /// The image crop mode. + /// The image crop anchor. + /// Use focal point, to generate an output image using the focal point instead of the predefined crop. + /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters. + /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. + /// These are any query string parameters (formatted as query strings) that ImageProcessor supports. For example: + /// + /// The url mode. + /// + /// The URL of the cropped image. + /// public static string GetCropUrl( this MediaWithCrops mediaWithCrops, IImageUrlGenerator imageUrlGenerator, diff --git a/src/Umbraco.Web.Common/Filters/AllowHttpJsonConfigrationAttribute.cs b/src/Umbraco.Web.Common/Filters/AllowHttpJsonConfigrationAttribute.cs new file mode 100644 index 000000000000..31fddc65f107 --- /dev/null +++ b/src/Umbraco.Web.Common/Filters/AllowHttpJsonConfigrationAttribute.cs @@ -0,0 +1,42 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using Umbraco.Cms.Web.Common.Formatters; + +namespace Umbraco.Cms.Web.Common.Filters +{ + public class AllowHttpJsonConfigrationAttribute : TypeFilterAttribute + { + /// + /// This filter overwrites AngularJsonOnlyConfigurationAttribute and get the api back to its defualt behavior + /// + public AllowHttpJsonConfigrationAttribute() : base(typeof(AllowJsonXHRConfigrationFilter)) + { + Order = 2; // this value must be more than the AngularJsonOnlyConfigurationAttribute on order to overwrtie it + } + + private class AllowJsonXHRConfigrationFilter : IResultFilter + { + public void OnResultExecuted(ResultExecutedContext context) + { + } + + public void OnResultExecuting(ResultExecutingContext context) + { + if (context.Result is ObjectResult objectResult) + { + objectResult.Formatters.RemoveType(); + } + } + } + } +} diff --git a/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs b/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs index 90a48c401768..ea8408b21200 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs @@ -26,15 +26,17 @@ internal class ModelsBuilderNotificationHandler : private readonly ModelsBuilderSettings _config; private readonly IShortStringHelper _shortStringHelper; private readonly IModelsBuilderDashboardProvider _modelsBuilderDashboardProvider; + private readonly IDefaultViewContentProvider _defaultViewContentProvider; public ModelsBuilderNotificationHandler( IOptions config, IShortStringHelper shortStringHelper, - IModelsBuilderDashboardProvider modelsBuilderDashboardProvider) + IModelsBuilderDashboardProvider modelsBuilderDashboardProvider, IDefaultViewContentProvider defaultViewContentProvider) { _config = config.Value; _shortStringHelper = shortStringHelper; _modelsBuilderDashboardProvider = modelsBuilderDashboardProvider; + _defaultViewContentProvider = defaultViewContentProvider; } /// @@ -123,7 +125,7 @@ public void Handle(TemplateSavingNotification notification) // we do not support configuring this at the moment, so just let Umbraco use its default value // var modelNamespaceAlias = ...; - var markup = ViewHelper.GetDefaultFileContent( + var markup = _defaultViewContentProvider.GetDefaultFileContent( modelClassName: className, modelNamespace: modelNamespace/*, modelNamespaceAlias: modelNamespaceAlias*/); diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js index 6bc785552872..3d2c77f2b4eb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js @@ -28,7 +28,7 @@ angular.module('umbraco.mocks'). { separator: true, name: "Reload", cssclass: "refresh", alias: "users", metaData: {} }, - { separator: true, name: "Empty Recycle Bin", cssclass: "trash", alias: "emptyrecyclebin", metaData: {} } + { separator: true, name: "Empty Recycle Bin", cssclass: "trash", alias: "emptyRecycleBin", metaData: {} } ]; var result = { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index 6c6237263f49..e6eb430201bb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -1278,11 +1278,22 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s function insertLink() { if (anchorElm) { editor.dom.setAttribs(anchorElm, createElemAttributes()); - editor.selection.select(anchorElm); editor.execCommand('mceEndTyping'); } else { - editor.execCommand('mceInsertLink', false, createElemAttributes()); + var selectedContent = editor.selection.getContent(); + // If there is no selected content, we can't insert a link + // as TinyMCE needs selected content for this, so instead we + // create a new dom element and insert it, using the chosen + // link name as the content. + if (selectedContent !== "") { + editor.execCommand('mceInsertLink', false, createElemAttributes()); + } else { + // Using the target url as a fallback, as href might be confusing with a local link + var linkContent = typeof target.name !== "undefined" && target.name !== "" ? target.name : target.url + var domElement = editor.dom.createHTML("a", createElemAttributes(), linkContent); + editor.execCommand('mceInsertContent', false, domElement); + } } } diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/user.html b/src/Umbraco.Web.UI.Client/src/installer/steps/user.html index 4dd8afd51205..e314a16319b0 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/steps/user.html +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/user.html @@ -1,4 +1,4 @@ -
+

Install Umbraco

Enter your name, email and password to install Umbraco with its default settings, alternatively you can customize your installation

@@ -59,7 +59,7 @@

Install Umbraco

- +
diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 040ec42c0c37..fd699b79d0a9 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -607,6 +607,7 @@ box-sizing: border-box; line-height: 0; contain: content; + position: relative; .checkeredBackground(); &:focus, &:focus-within { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card.less b/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card.less index bfd72c021699..d5823651022d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card.less +++ b/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card.less @@ -58,7 +58,7 @@ umb-block-card { padding-bottom: 10/16*100%; background-color: @gray-12; - background-size: cover; + background-size: contain; background-position: 50% 50%; background-repeat: no-repeat; diff --git a/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.list.controller.js b/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.list.controller.js index e55dfd44a106..669a0d518336 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.list.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.list.controller.js @@ -1,4 +1,4 @@ -/** +/** * @ngdoc controller * @name Umbraco.Editors.Dictionary.ListController * @function @@ -6,7 +6,7 @@ * @description * The controller for listting dictionary items */ -function DictionaryListController($scope, $location, dictionaryResource, localizationService, appState) { +function DictionaryListController($scope, $location, dictionaryResource, localizationService, appState, navigationService) { var vm = this; vm.title = "Dictionary overview"; vm.loading = false; @@ -31,7 +31,17 @@ function DictionaryListController($scope, $location, dictionaryResource, localiz $location.path("/" + currentSection + "/dictionary/edit/" + id); } + function createNewItem() { + var rootNode = appState.getTreeState("currentRootNode").root; + //We need to load the menu first before we can access the menu actions. + navigationService.showMenu({ node: rootNode }).then(function () { + const action = appState.getMenuState("menuActions").find(item => item.alias === "create"); + navigationService.executeMenuAction(action, rootNode, appState.getSectionState("currentSection")); + }); + } + vm.clickItem = clickItem; + vm.createNewItem = createNewItem; function onInit() { localizationService.localize("dictionaryItem_overviewTitle").then(function (value) { diff --git a/src/Umbraco.Web.UI.Client/src/views/dictionary/list.html b/src/Umbraco.Web.UI.Client/src/views/dictionary/list.html index 928aba0607d6..14c7bb4c5cb5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dictionary/list.html +++ b/src/Umbraco.Web.UI.Client/src/views/dictionary/list.html @@ -1,4 +1,4 @@ -
+
@@ -13,43 +13,55 @@ - - - - - There are no dictionary items. - - - - - - - - - - - - - - - - - - - -
Name{{column.displayName}}
- - - -
+ + + + + + + + + + + + + + There are no dictionary items. + + + + + + + + + + + + + + + + + + + +
Name{{column.displayName}}
+ + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html index f56a6c8656ad..0ab66c964efb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html @@ -27,7 +27,7 @@
- +
@@ -36,32 +36,37 @@ + placeholder="Enter XPath query">
  • - +

    - Use Xpath query to set a root node on the tree, either based on a search from the root of the content tree, or by using a context-aware placeholder. + Use an XPath query to set a root node on the tree, either based on a search from the root of the content tree, or by using a context-aware placeholder.

    - Placeholders finds the nearest published ID and runs its query from there, so for instance: - -

    $parent/newsArticle
    - - Will try to get the parent if available, but will then fall back to the nearest ancestor and query for all news articles there. + A placeholder finds the nearest published ID and runs its query from there, so for instance: +

    + +
    $parent/newsArticle
    + +

    + Will try to get the parent if available, but will then fall back to the nearest ancestor and query for all news article children there.

    Available placeholders:
    - $current: current page or closest found ancestor
    - $parent: parent page or closest found ancestor
    - $root: root of the content tree
    - $site: Ancestor node at level 1
    + $current: Current page or closest found ancestor
    + $parent: Parent page or closest found ancestor
    + $root: Root of the content tree
    + $site: Ancestor node at level 1
    +

    +

    + Note: The placeholder can only be used at the beginning of the query.

  • diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 76e6759a475e..c66ff1a46171 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -380,7 +380,7 @@ function listViewController($scope, $interpolate, $routeParams, $injector, $time }); } - $scope.delete = function () { + $scope.delete = function (numberOfItems, totalItems) { const dialog = { view: "views/propertyeditors/listview/overlays/delete.html", @@ -394,7 +394,9 @@ function listViewController($scope, $interpolate, $routeParams, $injector, $time }, close: function () { overlayService.close(); - } + }, + numberOfItems: numberOfItems, + totalItems: totalItems }; localizationService.localize("general_delete").then(value => { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index 92ad56b045a7..7fad01fe6c2c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -190,7 +190,7 @@ button-style="white" label-key="actions_delete" icon="icon-trash" - action="delete()" + action="delete(selection.length, listViewResultSet.items.length)" disabled="actionInProgress" size="xs" add-ellipsis="true"> diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/delete.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/delete.html index 1c99aa594e42..74b3cce7e52a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/delete.html @@ -1,7 +1,7 @@

    - ? + ?

    diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index bae39162f470..622766b4b15e 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -43,6 +43,7 @@ + diff --git a/src/Umbraco.Web.UI/Views/Partials/blocklist/default.cshtml b/src/Umbraco.Web.UI/Views/Partials/blocklist/default.cshtml index fffd5e58bb60..d5944b93c3a8 100644 --- a/src/Umbraco.Web.UI/Views/Partials/blocklist/default.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/blocklist/default.cshtml @@ -8,6 +8,6 @@ if (block?.ContentUdi == null) { continue; } var data = block.Content; - @await Html.PartialAsync("BlockList/Components/" + data.ContentType.Alias, block) + @await Html.PartialAsync("blocklist/Components/" + data.ContentType.Alias, block) }
    diff --git a/src/Umbraco.Web.UI/umbraco/UmbracoWebsite/NotFound.cshtml b/src/Umbraco.Web.UI/umbraco/UmbracoWebsite/NotFound.cshtml new file mode 100644 index 000000000000..62a149f2ab9d --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/UmbracoWebsite/NotFound.cshtml @@ -0,0 +1,84 @@ +@using Microsoft.Extensions.Options +@using Umbraco.Cms.Core.Configuration.Models +@using Umbraco.Cms.Core.Hosting +@using Umbraco.Cms.Core.Routing +@using Umbraco.Extensions +@inject IHostingEnvironment hostingEnvironment +@inject IOptions globalSettings +@{ + var backOfficePath = globalSettings.Value.GetBackOfficePath(hostingEnvironment); +} + + + + + + + + Page Not Found + + + + + + +
    +
    +
    +

    Page Not Found

    + @if (hostingEnvironment.IsDebugMode) + { + + var reason = (string)Context.Items["reason"]; + var message = (string)Context.Items["message"]; + + if (!reason.IsNullOrWhiteSpace()) + { +

    @reason

    + } + if (!message.IsNullOrWhiteSpace()) + { +

    @message

    + } + +
    + +
    +
    +

    This page can be replaced

    +

    + Custom error handling might make your site look more on-brand and minimize the impact of errors on user experience - for example, a custom 404 with some helpful links (or a search function) could bring some value to the site. +

    + + Implementing custom error pages → +
    + +
    +

    Be a part of the community

    +

    The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we're sure that you can get your answers from the community.

    + + our.Umbraco → +
    +
    + + } +
    +
    + +
    + + + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/cs.xml b/src/Umbraco.Web.UI/umbraco/config/lang/cs.xml index 148ebf23532b..af701cd5e392 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/cs.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/cs.xml @@ -16,10 +16,10 @@ Vytvořit skupinu Odstranit Deaktivovat - Vyprázdnit koš + Vyprázdnit koš Aktivovat Exportovat typ dokumentu - Importovat typ dokumentu + Importovat typ dokumentu Importovat balíček Editovat na stránce Odhlásit @@ -765,21 +765,12 @@ Instalátor se nemůže připojit k databázi. - Nelze uložit soubor web.config. Modifikujte, prosím, připojovací řetězec manuálně. Vyše databáze byla nalezena a je identifikována jako Nastavení databáze instalovat, abyste nainstalovali Umbraco %0% databázi ]]> následující pro pokračování.]]> - Databáze nenalezena! Zkontrolujte, prosím, že informace v "připojovacím řetězci" souboru "web.config" jsou správné.

    -

    Pro pokračování otevřete, prosím, soubor "web.config" (za pužití Visual Studia nebo Vašeho oblíbeného tedtového editoru), přejděte na jeho konec, přidejte připojovací řetězec pro Vaši databázi v klíčí nazvaném "umbracoDbDSN" a soubor uložte.

    -

    - Klikněte na tlačítko zopakovat, až budete hotovi.
    - Další informace o editování souboru web.config zde.

    ]]>
    - - Pokud je to nezbytné, kontaktujte vašeho poskytovatele hostingu. - Jestliže instalujete na místní počítač nebo server, budete potřebovat informace od Vašeho systémového administrátora.]]> Stiskněte tlačítko povýšit pro povýšení Vaší databáze na Umbraco %0%

    @@ -794,7 +785,6 @@ Heslo výchozího uživatele bylo úspěšně změněno od doby instalace!

    Netřeba nic dalšího dělat. Klikněte na Následující pro pokračování.]]> Heslo je změněno! Mějte skvělý start, sledujte naše uváděcí videa - Kliknutím na tlačítko následující (nebo modifikováním umbracoConfigurationStatus v souboru web.config) přijímáte licenci tohoto software tak, jak je uvedena v poli níže. Upozorňujeme, že tato distribuce Umbraca se skládá ze dvou různých licencí, open source MIT licence pro framework a Umbraco freeware licence, která pokrývá UI. Není nainstalováno. Dotčené soubory a složky Další informace o nastavování oprávnění pro Umbraco zde @@ -854,7 +844,6 @@ Další pomoc a informace Abyste získali pomoc od naší oceňované komunity, projděte si dokumentaci, nebo si pusťte některá videa zdarma o tom, jak vytvořit jednoduchý web, jak používat balíčky a rychlý úvod do terminologie umbraca]]> Umbraco %0% je nainstalováno a připraveno k použití - soubor /web.config a upravit klíč AppSetting umbracoConfigurationStatus dole na hodnotu '%0%'.]]> ihned začít kliknutím na tlačítko "Spustit Umbraco" níže.
    Jestliže je pro Vás Umbraco nové, spoustu zdrojů naleznete na naších stránkách "začínáme".]]>
    Spustit Umbraco @@ -1938,8 +1927,6 @@ Platnost certifikátu SSL vašeho webu vyprší za %0% dní. Chyba při pingování adresy URL %0% - '%1%' Aktuálně prohlížíte web pomocí schématu HTTPS. - AppSetting 'Umbraco.Core.UseHttps' je v souboru web.config nastaven na 'false'. Jakmile vstoupíte na tento web pomocí schématu HTTPS, mělo by být nastaveno na 'true'. - AppSetting 'Umbraco.Core.UseHttps' je v souboru web.config nastaven na '%0%', vaše cookies %1% jsou označeny jako zabezpečené. Režim kompilace ladění je zakázán. Režim ladění je aktuálně povolen. Před spuštěním webu se doporučuje toto nastavení deaktivovat. @@ -1962,10 +1949,7 @@ --> %0%.]]> Nebyly nalezeny žádné hlavičky odhalující informace o technologii webových stránek. - V souboru Web.config nelze najít system.net/mailsettings. - V části system.net/mailsettings v souboru web.config není hostitel nakonfigurován. Nastavení SMTP jsou správně nakonfigurována a služba funguje jak má. - Server SMTP konfigurovaný s hostitelem '%0%' a portem '%1%' nelze nalézt. Zkontrolujte prosím, zda jsou nastavení SMTP v souboru Web.config a v sekci system.net/mailsettings správná. %0%.]]> %0%.]]>

    Výsledky plánovaných kontrol Umbraco Health Checks provedených na %0% v %1% jsou následující:

    %2%]]>
    diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/cy.xml b/src/Umbraco.Web.UI/umbraco/config/lang/cy.xml index 0a414d23df04..ed078653ab31 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/cy.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/cy.xml @@ -929,7 +929,6 @@ Ni all y gosodydd gysylltu â'r gronfa ddata. - Methwyd achub y ffeil web.config. Ceisiwch newid y llinyn gyswllt yn uniongyrchol. Canfwyd eich cronfa ddata ac mae'n cael ei adnabod fel Ffurfwedd gronfa ddata @@ -970,7 +969,6 @@ Mae cyfrinair y defnyddiwr Diofyn wedi'i newid yn llwyddiannus ers y gosodiad!

    Does dim angen unrhyw weithredoedd pellach. Cliciwch Nesaf i barhau.]]> Mae'r cyfrinair wedi'i newid! Cewch gychwyn gwych, gwyliwch ein fideos rhaglith - Wrth glicio'r botwm nesaf (neu newid y umbracoConfigurationStatus yn web.config), rydych yn derbyn y trwydded ar gyfer y meddalwedd yma fel y nodir yn y blwch isod. Sylwch fod y dosbarthiad Umbraco yma yn cynnwys 2 drwydded gwahanol, y trwydded cod agored MIT ar gyfer y fframwaith ac y trwydded Umbraco rhadwedd sy'n ymdrin â'r Rhyngwyneb Defnyddiwr. Heb osod eto. Ffeiliau a ffolderi wedi'u effeithio Mwy o wybodaeth am osod hawliau ar gyfer Umbraco yma @@ -1053,10 +1051,6 @@ Rydych wedi gosod Runway, felly beth am weld sut mae eich gwefan newydd yn edryc Cewch gymorth o'n cymuned gwobrwyol, porwch drwy ein dogfennaeth neu gwyliwch fideos yn rhad ac am ddim ar sut i adeiladu gwefan syml, sut i ddefnyddio pecynnau a chanllaw cyflym i dermeg Umbraco]]> Mae Umbraco wedi'i osod %0% ac mae'n barod i'w ddefnyddio - - /web.config a diweddaru'r allwedd AppSetting UmbracoConfigurationStatus yng ngwaelod y gwerth o '%0%'.]]> - yn syth wrth glicio ar y botwm "Cychwyn Umbraco" isod.
    Os ydych yn newydd i Umbraco, gallwch ddarganfod digonedd o adnoddau ar ein tudalennau cychwyn allan.]]> @@ -2407,8 +2401,6 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang Mae tystysgrif SSL eich gwefan am derfynu mewn %0% diwrnod. Gwall yn pingio'r URL %0% - '%1%' Rydych yn bresennol %0% yn gweld y wefan yn defnyddio'r cynllun HTTPS. - Mae'r appSetting 'umbracoUseSSL' wedi'i osod at 'false' yn eich ffeil web.config. Unwaith rydych yn ymweld â'r safle gan ddefnyddio'r cynllun HTTPS, dylai hynny gael ei osod i 'true'. - Mae'r appSetting 'umbracoUseSSL' wedi'i osod at '%0%' yn eich ffeil web.config, mae eich cwcis %1% marcio yn ddiogel. Ni ellir diweddaru'r gosodiad 'umbracoUseSSL' yn eich ffeil web.config. Gwall: %0% Galluogi HTTPS @@ -2459,10 +2451,7 @@ Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwang --> %0%.]]> Dim peniadau sy'n datgelu gwynodaeth am dechnoleg eich gwefan wedi'u canfod. - Ni ellir darganfod system.net/mailsettings yn y ffeil Web.config. - Yn yr adran system.net/mailsettings o'r ffeil Web.config, nid yw'r "host" wedi ffurfweddu. Gosodiadau SMTP wedi ffurfweddu'n gywir ac mae'r gwasanaeth yn gweithio fel y disgwylir. - Ni ellir cysylltu â gweinydd SMTP sydd wedi ffurfweddu gyda "host" '%0%' a phorth '%1%'. Gwiriwch fod y gosodiadau SMTP yn y ffeil Web.config, system.net/mailsettings yn gywir. %0%.]]> %0%.]]>

    Canlyniadau'r gwiriad Statws Iechyd Umbraco ar amserlen rhedwyd ar %0% am %1% fel y ganlyn:

    %2%]]>
    diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index e1ea44225e4e..7e29e6b9784f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -18,10 +18,10 @@ Slet Deaktivér Edit settings - Tøm papirkurv + Tøm papirkurv Aktivér Eksportér dokumenttype - Importér dokumenttype + Importér dokumenttype Importér pakke Redigér i Canvas Log af diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/de.xml b/src/Umbraco.Web.UI/umbraco/config/lang/de.xml index cc9b047bc462..8f2ba350d0a6 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/de.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/de.xml @@ -16,10 +16,10 @@ Neue Gruppe Entfernen Deaktivieren - Papierkorb leeren + Papierkorb leeren Aktivieren Dokumenttyp exportieren - Dokumenttyp importieren + Dokumenttyp importieren Paket importieren 'Canvas'-Modus starten Abmelden @@ -765,7 +765,6 @@ Mit dieser Datenbank kann leider keine Verbindung hergestellt werden. - Die "web.config"-Datei konnte nicht angepasst werden (Zugriffsrechte?). Bitte passen Sie die Verbindungszeichenfolge manuell an. Die Datenbank ist erreichbar und wurde identifiziert als Datenbank @@ -774,13 +773,6 @@ ]]> Die Datenbank wurde für Umbraco %0% konfiguriert. Klicken Sie auf <strong>weiter</strong>, um fortzufahren. - - Die angegebene Datenbank ist leider nicht erreichbar. Bitte prüfen Sie die Verbindungszeichenfolge ("Connection String") in der "web.config"-Datei.

    -

    Um fortzufahren, passen Sie bitte die "web.config"-Datei mit einem beliebigen Text-Editor an. Scrollen Sie dazu nach unten, fügen Sie die Verbindungszeichenfolge für die zuverbindende Datenbank als Eintrag "UmbracoDbDSN" hinzu und speichern Sie die Datei.

    -

    Klicken Sie nach erfolgter Anpassung auf Wiederholen.
    Wenn Sie weitere technische Informationen benötigen, besuchen Sie The Umbraco documentation wik.

    - ]]> -
    Um diesen Schritt abzuschließen, müssen Sie die notwendigen Informationen zur Datenbankverbindung angeben.<br />Bitte kontaktieren Sie Ihren Provider bzw. Server-Administrator für weitere Informationen. <strong>Das Kennwort des Standard-Benutzers wurde seit der Installation verändert.</strong></p><p>Es sind keine weiteren Aktionen notwendig. Klicken Sie auf <b>Weiter</b> um fortzufahren. Das Kennwort wurde geändert! Schauen Sie sich die Einführungsvideos für einen schnellen und einfachen Start an. - Mit der Installation stimmen Sie der angezeigten Lizenz für diese Software zu. Bitte beachten Sie, dass diese Umbraco-Distribution aus zwei Lizenzen besteht. Einer freien Open-Source MIT-Lizenz für das Framework und der Umbraco-Freeware-Lizenz für die Verwaltungsoberfläche. Noch nicht installiert. Betroffene Verzeichnisse und Dateien Weitere Informationen zum Thema "Dateiberechtigungen" für Umbraco @@ -854,7 +845,6 @@ <h3>Zur neuen Seite</h3>Sie haben Runway installiert, schauen Sie sich doch mal auf Ihrer Website um. <h3>Weitere Hilfe und Informationen</h3>Hilfe von unserer preisgekrönten Community, Dokumentation und kostenfreie Videos, wie Sie eine einfache Website erstellen, ein Packages nutzen und eine schnelle Einführung in alle Umbraco-Begriffe Umbraco %0% wurde installiert und kann verwendet werden - Um die Installation abzuschließen, müssen Sie die <strong>"web.config"-Datei</strong> von Hand anpassen und den AppSetting-Schlüssel <strong>UmbracoConfigurationStatus</strong> auf den Wert <strong>'%0%'</strong> ändern. Sie können <strong>sofort starten</strong>, in dem Sie auf "Umbraco starten" klicken. <h3>Umbraco starten</h3>Um Ihre Website zu verwalten, öffnen Sie einfach den Administrationsbereich und beginnen Sie damit, Inhalte hinzuzufügen sowie Vorlagen und Stylesheets zu bearbeiten oder neue Funktionen einzurichten Verbindung zur Datenbank fehlgeschlagen. @@ -2004,8 +1994,6 @@ Ihr Website-Zertifikat (SSL) wird in %0% Tagen ablaufen. Fehler beim PINGen der URL %0% - '%1%' Sie betrachten diese Website %0% unter Verwendung des HTTPS-Schemas. - Der Schlüssel 'Umbraco.Core.UseHttps' im Abschnitt 'appSettings' der 'web.config'-Datei ist auf 'false' gesetzt. Sobald Sie diese Site über HTTPS nutzen, sollte dieser auf 'true' gestellt werden. - Der Schlüssel 'Umbraco.Core.UseHttps' im Abschnitt 'appSettings' der 'web.config'-Datei ist auf '%0%' gesetzt, Cookies sind %1% als sicher markiert. 'Debug' Kompilierungsmodus ist abgeschaltet. 'Debug' Kompilierungsmodus ist gegenwertig eingeschaltet. Es ist empfehlenswert diesen vor Live-Gang abzuschalten. Modo Debug en compilación está desactivado. Modo Debug en compilación está activado. Se recomienda desactivarlo antes de publicar el sitio. @@ -1528,10 +1522,7 @@ --> %0%.]]> No se ha encontrado ninguna cabecera que revele información sobre la tecnología del sitio. - No se encontró system.net/mailsettings en Web.config. - En la sección system.net/mailsettings section de web.config, el host no está configurado. Los valores SMTP están configurados correctamente y el servicio opera con normalidad. - El servidor SMTP configurado con host '%0%' y puerto '%1%' no se pudo alcanzar. Por favor revisa que la configuración en la sección system.net/mailsettings del archivo Web.config es correcta. %0%.]]> %0%.]]>

    Los resultados de los Chequeos de Salud de Umbraco programados para ejecutarse el %0% a las %1% son:

    %2%]]>
    diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml index 1caf58cf6034..786104e6e57d 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml @@ -16,10 +16,10 @@ Créer un groupe Supprimer Désactiver - Vider la corbeille + Vider la corbeille Activer Exporter le type de document - Importer un type de document + Importer un type de document Importer un package Editer dans Canvas Déconnexion @@ -388,6 +388,7 @@ Nom Fermer cette fenêtre Êtes-vous certain(e) de vouloir supprimer + %0% des %1% éléments]]> Êtes-vous certain(e) de vouloir désactiver Êtes-vous certain(e)? Êtes-vous certain(e)? @@ -773,19 +774,12 @@ Le programme d'installation ne parvient pas à se connecter à la base de données. - Impossible de sauvegarder le fichier web.config. Veuillez modifier la "connection string" manuellement. Votre base de données a été détectée et est identifiée comme étant Configuration de la base de données installer pour installer la base de données Umbraco %0% ]]> Suivant pour poursuivre.]]> - Base de données non trouvée ! Veuillez vérifier les informations de la "connection string" dans le fichier web.config.

    -

    Pour poursuivre, veuillez éditer le fichier "web.config" (avec Visual Studio ou votre éditeur de texte favori), scroller jusqu'en bas, ajouter le "connection string" pour votre base de données dans la ligne avec la clé "umbracoDbDSN" et sauvegarder le fichier.

    -

    - Cliquez sur le bouton Réessayer lorsque cela est fait. -
    - Plus d'informations sur l'édition du fichier web.config ici.

    ]]>
    Veuillez contacter votre fournisseur de services internet si nécessaire. Si vous installez Umbraco sur un ordinateur ou un serveur local, vous aurez peut-être besoin de consulter votre administrateur système.]]> @@ -804,7 +798,6 @@ Le mot de passe par défaut a été modifié avec succès depuis l'installation!

    Aucune autre action n'est requise. Cliquez sur Suivant pour poursuivre.]]> Le mot de passe a été modifié ! Pour bien commencer, regardez nos vidéos d'introduction - En cliquant sur le bouton "Suivant" (ou en modifiant umbracoConfigurationStatus dans le fichier web.config), vous acceptez la licence de ce logiciel telle que spécifiée dans le champ ci-dessous. Veuillez noter que cette distribution Umbraco consiste en deux licences différentes, la licence open source MIT pour le framework et la licence Umbraco freeware qui couvre l'UI. Pas encore installé. Fichiers et dossiers concernés Plus d'informations sur la configuration des permissions @@ -867,8 +860,6 @@ Vous avez installé Runway, alors pourquoi ne pas jeter un oeil au look de votre Aide et informations complémentaires Obtenez de l'aide de notre communauté "award winning", parcourez la documentation ou regardez quelques vidéos gratuites sur la manière de construire un site simple, d'utiliser les packages ainsi qu'un guide rapide sur la terminologie Umbraco]]> Umbraco %0% est installé et prêt à être utilisé - fichier /web.config et mettre à jour le paramètre AppSetting umbracoConfigurationStatus situé en bas à la valeur '%0%'.]]> démarrer instantanément en cliquant sur le bouton "Lancer Umbraco" ci-dessous.
    Si vous débutez avec Umbraco, vous pouvez trouver une foule de ressources dans nos pages "Getting Started".]]>
    Lancer Umbraco @@ -1971,8 +1962,6 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à Le certificat SSL de votre site web va expirer dans %0% jours. Erreur en essayant de contacter l'URL %0% - '%1%' Vous êtes actuellement %0% à voir le site via le schéma HTTPS. - La valeur appSetting 'Umbraco.Core.UseHttps' est fixée à 'false' dans votre fichier web.config. Une fois que vous donnerez accès à ce site en utilisant le schéma HTTPS, cette valeur devra être mise à 'true'. - La valeur appSetting 'Umbraco.Core.UseHttps' est fixée à '%0%' dans votre fichier web.config, vos cookies sont %1% marqués comme étant sécurisés. Le mode de compilation Debug est désactivé. Le mode de compilation Debug est actuellement activé. Il est recommandé de désactiver ce paramètre avant la mise en ligne. @@ -1998,10 +1987,7 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à --> %0%.]]> Aucun header révélant des informations à propos de la technologie du site web n'a été trouvé. - La section system.net/mailsettings n'a pas pu être trouvée dans le fichier Web.config. - Dans la section system.net/mailsettings du fichier Web.config, le "host" n'est pas configuré. La configuration SMTP est correcte et le service fonctionne comme prévu. - Le serveur SMTP configuré avec le host '%0%' et le port '%1%' n'a pas pu être contacté. Veuillez vérifier et vous assurer que la configuration SMTP est correcte dans la section system.net/mailsettings du fichier Web.config. %0%.]]> %0%.]]>

    Les résultats de l'exécution du Umbraco Health Checks planifiée le %0% à %1% sont les suivants :

    %2%]]>
    diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/he.xml b/src/Umbraco.Web.UI/umbraco/config/lang/he.xml index ee03ca6fc9ea..9ee8bbf014ec 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/he.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/he.xml @@ -13,9 +13,9 @@ צור חבילה מחק נטרל - רוקן סל מיחזור + רוקן סל מיחזור ייצא סוג קובץ - ייבא סוג מסמך + ייבא סוג מסמך ייבא חבילה ערוך במצב "קנבס" יציאה @@ -346,19 +346,12 @@ ההתקנה לא מצליחה להתחבר לבסיס הנתונים. - אין אפשרות לשמור את הקובץ Web.config file. הגדר את ה- connection string באופן ידני. בסיס הנתונים שלך נמצא והוא מזוהה כ הגדרת בסיס נתונים install button to install the Umbraco %0% database ]]> Next to proceed.]]> - Database not found! Please check that the information in the "connection string" of the “web.config” file is correct.

    -

    To proceed, please edit the "web.config" file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named "UmbracoDbDSN" and save the file.

    -

    - Click the retry button when - done.
    - More information on editing web.config here.

    ]]>
    Please contact your ISP if necessary. If you're installing on a local machine or server you might need information from your system administrator.]]> @@ -377,7 +370,6 @@ The Default user's password has been successfully changed since the installation!

    No further actions needs to be taken. Click Next to proceed.]]> הסיסמה שונתה! התחל מכאן, צפה בסרטוני ההדרכה עבור אומברקו - על ידי לחיצה על 'הבא', הנך מאשר את פרטי התקנון כפי שמפורט בתיבת הטקטס למטה. שים לב, הפצה זו של אומברקו כוללת שני גירסאות שונות של רשיון,קוד פתוח ברשיון MIT עבור ה- framework ורשיון Umbraco freeware המכסה את ה- UI. לא הותקן עדיין. קבצים ותיקיות המושפעים מידע נוסף אודות התקנה ורשאות עבור אומרקו ניתן לקרוא כאן @@ -440,8 +432,6 @@ You installed Runway, so why not see how your new website looks.]]> Further help and information Get help from our award winning community, browse the documentation or watch some free videos on how to build a simple site, how to use packages and a quick guide to the Umbraco terminology]]> אומברקו %0% מותקנת ומוכנה לשימוש - /web.config file and update the AppSetting key UmbracoConfigurationStatus in the bottom to the value of '%0%'.]]> started instantly by clicking the "Launch Umbraco" button below.
    If you are new to Umbraco, you can find plenty of resources on our getting started pages.]]>
    Launch Umbraco diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/it.xml b/src/Umbraco.Web.UI/umbraco/config/lang/it.xml index ffc1e8272b8d..a0d89bff2df4 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/it.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/it.xml @@ -917,8 +917,6 @@ - - @@ -928,14 +926,6 @@ Avanti per proseguire.]]> - - Database non trovato! Perfavore, controlla che le informazioni della stringa di connessione nel file "web.config" siano corrette.

    -

    Per procedere, modifica il file "web.config" (utilizzando Visual Studio o l'editor di testo che preferisci), scorri in basso, aggiungi la stringa di connessione per il database nel parametro chiamato "umbracoDbDSN" e salva il file.

    -

    - Clicca il tasto riprova quando hai - finito.
    - Maggiori dettagli per la modifica del file web.config qui.

    ]]> -
    Se è necessario contatta il tuo ISP per reperire le informazioni necessarie. @@ -964,8 +954,6 @@ La password dell'utente di default è stata modificata con successo

    Non è necessario eseguire altre operazioni. Clicca il tasto Avanti per continuare.]]> - - @@ -1053,10 +1041,6 @@ Hai installato Runway, quindi perché non dare uno sguardo al vostro nuovo sito Fatti aiutare dalla nostra community, consulta la documentazione o guarda alcuni video gratuiti su come costruire un semplice sito web, come usare i pacchetti e una guida rapida alla terminologia Umbraco]]> - - /web.config e aggiornare la chiave AppSetting UmbracoConfigurationStatus impostando il valore '%0%'.]]> - iniziare immediatamente cliccando sul bottone "Avvia Umbraco".
    Se sei nuovo su Umbraco, si possono trovare un sacco di risorse sulle nostre pagine Getting Started.]]> diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml index 9775668df9ef..4b98adad2659 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml @@ -14,9 +14,9 @@ パッケージの作成 削除 無効 - ごみ箱を空にする + ごみ箱を空にする ドキュメントタイプの書出 - ドキュメントタイプの読込 + ドキュメントタイプの読込 パッケージの読み込み ライブ編集 ログアウト @@ -461,19 +461,12 @@ インストーラーはデータベースに接続できませんでした。 - web.configファイルを保存できませんでした。接続文字列を手作業で編集してください。 データベースが見つかりました。識別子: データベースの設定 インストールボタンを押すと Umbraco %0% のデータベースへインストールします ]]> 次へを押して続行してください。]]> - データベースを見つけられません!"web.config"ファイルの中の"接続文字列"を確認してください。

    -

    続行するには"web.config"ファイルを編集(Visual Studioないし使い慣れたテキストエディタで)し、下の方にスクロールし、"umbracoDbDSN"という名前のキーでデータベースの接続文字列を追加して保存します。

    -

    - 再施行ボタンをクリックして - 続けます。
    - より詳細にはこちらの web.config を編集します。

    ]]>
    必要ならISPに連絡するなどしてみてください。 もしローカルのパソコンないしサーバーへインストールするのなら、システム管理者に情報を確認してください。]]> @@ -492,7 +485,6 @@ インストール後にデフォルトユーザーのパスワードが変更されています!

    これ以上のアクションは必要ありません。次へをクリックして続行してください。]]> パスワードは変更されました! 始めに、ビデオによる解説を見ましょう - 次へボタンをクリック(またはweb.configのumbracoConfigurationStatusを編集)すると、あなたはここに示されるこのソフトウェアのライセンスを承諾したと見做されます。注意として、UmbracoはMITライセンスをフレームワークへ、フリーウェアライセンスをUIへ、それぞれ異なる2つのライセンスを採用しています。 まだインストールは完了していません。 影響するファイルとフォルダ Umbracoに必要なアクセス権の設定についての詳細はこちらをどうぞ @@ -555,8 +547,6 @@ Runwayをインストールして作られた新しいウェブサイトがど 追加の情報と手助け 我々の認めるコミュニティから手助けを得られるでしょう。どうしたら簡単なサイトを構築できるか、どうしたらパッケージを使えるかについてのビデオや文書、またUmbracoの用語のクイックガイドも見る事ができます。]]> Umbraco %0% のインストールは完了、準備が整いました - /web.config fileを手作業で編集し、'%0%'の下にあるumbracoConfigurationStatusキーを設定してください。]]> 今すぐ開始できます。
    もしUmbracoの初心者なら、 私たちの初心者向けのたくさんの情報を参考にしてください。]]>
    Umbracoの開始 diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml index 1bf5d052ddd4..792dd6700c2f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml @@ -13,9 +13,9 @@ 패키지 새로 만들기 삭제 비활성 - 휴지통 비우기 + 휴지통 비우기 추출 문서 유형 - 등록 문서 유형 + 등록 문서 유형 패키지 등록 캔버스 내용 편집 종료 @@ -346,16 +346,10 @@ 인스톨러가 데이터베이스에 연결할 수 없습니다. - web.config를 저장할 수 없습니다.connection string을 수동으로 수정하세요. 데이터베이스가 확인되었으며 정보는 데이터베이스 설정 설치 버튼을 누르면 Umbraco %0% 데이터베이스가 설치됩니다.]]> 다음을 누르세요.]]> - 데이터베이스를 찾을 수 없습니다. “web.config”파일의 "connection string"이 바르게 설정되었는지 확인하세요.

    -

    "web.config" 파일에 맨아래에 ,키네임을 "UmbracoDbDSN"로 하여 사용하시는 데이터베이스의 connection string 정보를 입력하시고 파일을 저장하세요.

    -

    - 완료 후재시도버튼을 누르세요.
    - web.config의 더많은 정보는 여기에 있습니다.

    ]]>
    필요하시다면 사용하시는 ISP쪽에 문의하시기 바랍니다.. 로컬 머신이나 서버에 설치되어 있다면 해당 시스템 관리자에게 문의하시기 바랍니다.]]> @@ -367,7 +361,6 @@ 설치후 기본사용자의 암호가 성공적으로 변경되었습니다!

    더 이상 과정이 필요없으시면 다음을 눌러주세요.]]> 비밀번호가 변경되었습니다! 편리한 시작을 위해, 소개 Video를 시청하세요 - 다음버튼을 누르시면 (또는Web.config에 UmbracoConfigurationStatus를 수정하시면), 여러분은 아래에 명시된 소프트웨어 라이센스를 수락합니다. Umbraco 배포는 2가지 다른 라이센스로 구성되어 있습니다. 프레임워크에는 오픈소스 MIT라이센스가 UI에는 Umbraco 프리웨어 라이센스가 적용됩니다. 아직 설치되지 않았습니다. 영향받는 파일과 폴더 Umbraco권한관리을 위해 더정보가 필요하시면 여기를 누르세요 @@ -427,7 +420,6 @@ 고급 도움말과 정보 우수 커뮤니티에서 도음을 받으세요. 간단한 사이트제작이나 패키지 사용법, Umbraco기술의 퀵가이드를 제공하는 문서를 보시거나 무료 비디오를 시청하세요.]]> Umbraco %0% 가 설치되어 사용준비가 되었습니다. - /web.config file을 수동으로 편집해야 합니다. AppSetting 키의 UmbracoConfigurationStatus'%0%'의 값으로 설정하세요.]]> Umbraco 와 첫만남이시면
    아래의 "Umbraco 접속하기" 버튼을 클릭하여 즉시 시작하실 수 있습니다. 시작페이지에서 풍부한 리소소를 제공받을 수 있습니다.]]>
    Umbraco 실행 diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nb.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nb.xml index 9f518fa319ef..1c4796918973 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/nb.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/nb.xml @@ -14,9 +14,9 @@ Opprett pakke Slett Deaktiver - Tøm papirkurv + Tøm papirkurv Eksporter dokumenttype - Importer dokumenttype + Importer dokumenttype Importer pakke Rediger i Canvas Logg av @@ -411,12 +411,10 @@ Installasjonsprogrammet kan ikke koble til databasen - Kunne ikke lagre Web.Config-filen. Vennligst endre databasens tilkoblingsstreng manuelt. Din database er funnet og identifisert som Databasekonfigurasjon installer-knappen for å installere Umbraco %0% databasen]]> Neste for å fortsette.]]> - Databasen ble ikke funnet! Vennligst sjekk at informasjonen i "connection string" i "web.config"-filen er korrekt.

    For å fortsette, vennligst rediger "web.config"-filen (bruk Visual Studio eller din favoritteditor), rull ned til bunnen, og legg til tilkoblingsstrengen for din database i nøkkelen "umbracoDbDSN" og lagre filen.

    Klikk prøv på nytt når du er ferdig.
    Mer informasjon om redigering av web.config her.

    ]]>
    Vennligst kontakt din ISP om nødvendig. Hvis du installerer på en lokal maskin eller server, må du kanskje skaffe informasjonen fra din systemadministrator.]]> Trykk på knappen oppgrader for å oppgradere databasen din til Umbraco %0%

    Ikke vær urolig - intet innhold vil bli slettet og alt vil fortsette å virke etterpå!

    ]]>
    Trykk Neste for å fortsette.]]> @@ -426,7 +424,6 @@ Passordet til standardbrukeren har blitt forandret etter installasjonen!

    Ingen videre handling er nødvendig. Klikk Neste for å fortsette.]]> Passordet er blitt endret! Få en god start med våre introduksjonsvideoer - Ved å klikke på Neste-knappen (eller endre UmbracoConfigurationStatus i Web.config), godtar du lisensen for denne programvaren som angitt i boksen nedenfor. Legg merke til at denne Umbraco distribusjon består av to ulike lisenser, åpen kilde MIT lisens for rammen og Umbraco frivareverktøy lisens som dekker brukergrensesnittet. Ikke installert. Berørte filer og mapper Mer informasjon om å sette opp rettigheter for Umbraco her @@ -460,7 +457,6 @@ Se ditt nye nettsted Du har installert Runway, hvorfor ikke se hvordan ditt nettsted ser ut.]]> Mer hjelp og info Få hjelp fra vårt prisbelønte samfunn, bla gjennom dokumentasjonen eller se noen gratis videoer på hvordan man bygger et enkelt nettsted, hvordan bruke pakker og en rask guide til Umbraco terminologi]]> Umbraco %0% er installert og klar til bruk - web.config filen, og oppdatere AppSetting-nøkkelen UmbracoConfigurationStatus til verdien '%0%']]> starte øyeblikkelig ved å klikke på "Start Umbraco" knappen nedenfor.
    Hvis du er ny på Umbraco, kan du finne mange ressurser på våre komme-i-gang sider.]]>
    Start Umbraco For å administrere din webside, åpne Umbraco og begynn å legge til innhold, oppdatere maler og stilark eller utvide funksjonaliteten]]> Tilkobling til databasen mislyktes. diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml index 3ef84f2354d2..cd675f7056e1 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml @@ -18,10 +18,10 @@ Verwijderen Uitschakelen Instellingen wijzigen - Prullenbak leegmaken + Prullenbak leegmaken Inschakelen Documenttype exporteren - Documenttype importeren + Documenttype importeren Package importeren Aanpassen in Canvas Afsluiten @@ -867,17 +867,12 @@ De installer kan geen connectie met de database maken. - De web.config kon niet worden opgeslagen. Gelieve de connectiestring handmatig - aan te passen. - Je database is gevonden en is geïdentificeerd als Database configuratie installeren om de Umbraco %0% database te installeren]]> Volgende om door te gaan.]]> - - De database kon niet gevonden worden! Gelieve na te kijken of de informatie in de "connection string" van het "web.config" bestand correct is.

    Om door te gaan, gelieve het "web.config" bestand aan te passen (met behulp van Visual Studio of je favoriete tekstverwerker), scroll in het bestand naar beneden, voeg de connection string voor je database toe in de key met naam "umbracoDbDSN" en sla het bestand op.

    Klik op de knop opnieuw proberen als je hiermee klaar bent.
    Meer informatie over het aanpassen van de web.config vind je hier.

    ]]>
    Gelieve contact op te nemen met je ISP indien nodig. Wanneer je installeert op een lokale computer of server, dan heb je waarschijnlijk informatie nodig van je systeembeheerder.]]> @@ -894,9 +889,6 @@ Het wachtwoord van de default gebruiker is sinds installatie met succes veranderd.

    Geen verdere actie noodzakelijk. Klik Volgende om verder te gaan.]]> Het wachtwoord is veranderd! Neem een jumpstart en bekijk onze introductie videos - Nog niet geïnstalleerd. Betreffende bestanden en mappen Meer informatie over het instellen van machtigingen voor Umbraco @@ -951,8 +943,6 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Meer hulp en informatie Vind hulp in onze bekroonde community, blader door de documentatie of bekijk enkele gratis videos over het bouwen van een eenvoudige site, het gebruiken van packages en een overzicht van Umbraco terminologie]]> Umbraco %0% is geïnstalleerd en klaar voor gebruik. - - /web.config bestand aanpassen, en de Appsetting key UmbracoConfigurationStatus onder in het bestand veranderen naar '%0%'.]]> meteen beginnen door de "Launch Umbraco" knop hieronder te klikken.
    Als je een beginnende Umbraco gebruiker bent, dan kun je you can find veel informatie op onze "getting started" pagina's vinden.]]>
    @@ -2106,12 +2096,6 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Het SSL certificaat van de website zal vervallen binnen %0% dagen. Fout bij pingen van URL %0% - '%1%' De site wordt momenteel %0% bekeken via HTTPS. - De appSetting 'Umbraco.Core.UseHttps' in web.config staat op - 'false'. Indien HTTPS gebruikt wordt moet deze op 'true' staan. - - De appSetting 'Umbraco.Core.UseHttps' in web.config is ingesteld op - '%0%'. Cookies zijn %1% ingesteld als secure. - Debug compilatie mode staat uit. Debug compilatie mode staat momenteel aan. Wij raden aan deze @@ -2150,15 +2134,8 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Er zijn geen headers gevonden die informatie vrijgeven over de gebruikte website technologie! - In de Web.config werd system.net/mailsettings niet gevonden - In de Web.config sectie system.net/mailsettings is de host niet - geconfigureerd. - SMTP instellingen zijn correct ingesteld en werken zoals verwacht. - De SMTP server geconfigureerd met host '%0%' en poort '%1%' kon niet - gevonden worden. Controleer of de SMTP instellingen in Web.config file system.net/mailsettings correct zijn. - %0%.]]> diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml index 77e01ae766b3..dfbc324df603 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml @@ -15,10 +15,10 @@ Stwórz grupę Usuń Deaktywuj - Opróżnij kosz + Opróżnij kosz Aktywuj Eksportuj typ dokumentu - Importuj typ dokumentu + Importuj typ dokumentu Importuj zbiór Edytuj na stronie Wyjście @@ -576,19 +576,12 @@ Instalator nie mógł połączyć się z bazą danych. - Nie udało się zapisać pliku web.config. Zmodyfikuj parametry połączenia ręcznie. Twoja baza danych została znaleziona i zidentyfikowana jako Konfiguracja bazy danych instaluj, aby zainstalować bazę danych Umbraco %0% ]]> Dalej, aby kontynuować.]]> - Nie odnaleziono bazy danych! Sprawdź, czy informacje w sekcji "connection string" w pliku "web.config" są prawidłowe.

    -

    Aby kontynuować, dokonaj edycji pliku "web.config" (używając Visual Studio lub dowolnego edytora tekstu), przemieść kursor na koniec pliku, dodaj parametry połączenia do Twojej bazy danych w kluczu o nazwie "umbracoDbDSN" i zapisz plik.

    -

    - Kliknij ponów próbę kiedy - skończysz.
    - Tu znajdziesz więcej informacji na temat edycji pliku "web.config".

    ]]>
    Skontaktuj się z Twoim dostawą usług internetowych jeśli zajdzie taka potrzeba. W przypadku instalacji na lokalnej maszynie lub serwerze możesz potrzebować pomocy administratora.]]> @@ -607,7 +600,6 @@ Naciśnij przycisk instaluj, aby zainstalować bazę danych Umb Hasło domyślnego użytkownika zostało zmienione od czasu instalacji!

    Żadne dodatkowe czynności nie są konieczne. Naciśnij Dalej, aby kontynuować.]]> Hasło zostało zmienione! Aby szybko wejść w świat Umbraco, obejrzyj nasze filmy wprowadzające - Klikając przycisk dalej (lub modyfikując klucz UmbracoConfigurationStatus w pliku web.config), akceptujesz licencję na niniejsze oprogramowanie zgodnie ze specyfikacją w poniższym polu. Zauważ, że ta dystrybucja Umbraco składa się z dwoch licencji - licencja MIT typu open source dla kodu oraz licencja "Umbraco freeware", która dotyczy interfejsu użytkownika. Nie zainstalowane. Zmienione pliki i foldery Więcej informacji na temat ustalania pozwoleń dla Umbraco znajdziesz tutaj @@ -670,8 +662,6 @@ Naciśnij przycisk instaluj, aby zainstalować bazę danych Umb Dalsza pomoc i informacje Zaczerpnij pomocy z naszej nagrodzonej społeczności, przeglądaj dokumentację lub obejrzyj niektóre darmowe filmy o tym, jak budować proste strony, jak używać pakietów i szybki przewodnik po terminologii Umbraco]]> Umbraco %0% zostało zainstalowane i jest gotowe do użycia - plik web.config i zaktualizować klucz AppSetting o nazwie UmbracoConfigurationStatus na dole do wartości '%0%'.]]> rozpocząć natychmiast klikając przycisk "Uruchom Umbraco" poniżej.
    Jeżeli jesteś nowy dla Umbraco znajdziesz mnóstwo materiałów na naszych stronach "jak rozpocząć".]]>
    Uruchom Umbraco @@ -1345,8 +1335,6 @@ Naciśnij przycisk instaluj, aby zainstalować bazę danych Umb Certyfikat SSL Twojej strony wygaśnie za %0% dni. Błąd pingowania adresu URL %0% - '%1%' Oglądasz %0% stronę używając HTTPS. - appSetting 'Umbraco.Core.UseHttps' został ustawiony na 'false' w Twoim pliku web.config. Po uzyskaniu dostępu do strony, używając HTTPS, powinieneś go ustawić na 'true'. - appSetting 'Umbraco.Core.UseHttps' został ustawiony na '%0%' w Twoim pliku web.config, Twoje ciasteczka są %1% ustawione jako bezpieczne. Tryb kompilacji debugowania jest wyłączony. Tryb kompilacji debugowania jest obecnie włączony. Zaleca się wyłączenie tego ustawienia przed wypuszczeniem strony na produkcję. @@ -1363,10 +1351,7 @@ Naciśnij przycisk instaluj, aby zainstalować bazę danych Umb --> %0%.]]> Nie znaleziono żadnych nagłówków, ujawniających informacji o technologii strony. - Nie znaleziono system.net/mailsettings w pliku Web.config. - Host nie jest skonfigurowany w sekcji system.net/mailsettings pliku Web.config. Ustawienia SMTP są skonfigurowane poprawnie i serwis działa według oczekiwań. - Nie można połączyć się z serwerem SMTP skonfigurowanym z hostem '%0%' i portem '%1%'. Proszę sprawdzić ponownie, czy ustawienia system.net/mailsettings w pliku Web.config są poprawne. %0%.]]> %0%.]]> diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml b/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml index ad6db137ba18..542b03abc1c7 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml @@ -13,9 +13,9 @@ Criar Pacote Remover Desabilitar - Esvaziar Lixeira + Esvaziar Lixeira Exportar Tipo de Documento - Importar Tipo de Documento + Importar Tipo de Documento Importar Pacote Editar na Tela Sair @@ -344,15 +344,10 @@ O instalador não pôde conectar-se ao banco de dados. - Não foi possível salvar o arquivo web.config. Favor modificar a linha de conexão manualmente. Seu banco de dados foi encontrado e identificado como Configuração do Banco de Dados instalar para instalar o banco de dados do Umbraco %0%]]> Próximo para prosseguir.]]> - Banco de dados não encontrado! Favor checar se a informação no "connection string" do "web.config" esteja correta.

    -

    Para prosseguir, favor editar o arquivo "web.config" (usando Visual Studio ou seu editor de texto favorito), role até embaixo, adicione a connection string para seu banco de dados com a chave de nome "UmbracoDbDSN" e salve o arquivo

    -

    Clique o botão tentar novamente quando terminar.
    - Mais informações em como editar o web.config aqui.

    ]]>
    Favor contatar seu provedor de internet ou hospedagem web se necessário. Se você estiver instalando em uma máquina ou servidor local é possível que você precise dessas informações por um administrador de sistema.]]> Pressione o botão atualizar para atualizar seu banco de dados para Umbraco %0%

    @@ -367,7 +362,6 @@ A senha do usuário padrão foi alterada com sucesso desde a instalação!

    Nenhuma ação posterior é necessária. Clique Próximo para prosseguir.]]> Senha foi alterada! Comece com o pé direito, assista nossos vídeos introdutórios - Ao clicar no próximo botão (ou modificando o UmbracoConfigurationStatus no web.config), você aceita a licença deste software cmo especificado na caixa abaixo. Note que esta distribuição de Umbraco consiste em duas licenças diferentes, a licença aberta MIT para a framework e a licença de software livre (freeware) Umbraco que cobre o UI. Nenhum instalado ainda. Pastas e arquivos afetados Mais informações em como configurar permissões para Umbraco aqui @@ -421,7 +415,6 @@ Você instalou Runway, então por que não ver como é seu novo website.]]>Ajuda adicional e informações Consiga ajuda de nossa comunidade ganhadora de prêmios, navegue a documentação e assista alguns vídeos grátis sobre como construir um site simples, como usar pacotes e um guia prático sobre a terminologia Umbraco]]> Umbraco %0% está instalado e pronto para uso - web.config e atualizar a chave AppSettings UmbracoConfigurationStatus no final para '%0%'.]]> iniciar instantâneamente clicando em "Lançar Umbraco" abaixo.
    Se você é novo com Umbraco você pode encontrar vários recursos em nossa página para iniciantes.]]>
    Lançar Umbraco Para gerenciar seu website, simplesmente abra a área administrativa do Umbraco para começar adicionando conteúdo, atualizando modelos e stylesheets e adicionando nova funcionalidade]]> diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml index 6d5a6c154303..9c1d9e12fb2d 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml @@ -17,11 +17,11 @@ Значение по умолчанию Удалить Отключить - Очистить корзину + Очистить корзину Включить Экспорт Экспортировать - Импортировать + Импортировать Импортировать пакет Править на месте Выйти @@ -427,7 +427,6 @@ Ваши данные сохранены, но для того, чтобы опубликовать этот документ, Вы должны сначала исправить следующие ошибки: - Текущий провайдер ролей пользователей не поддерживает изменение пароля (необходимо свойству EnablePasswordRetrieval в файле web.config присвоить значение true) %0% уже существует Обнаружены следующие ошибки: Обнаружены следующие ошибки: @@ -661,8 +660,6 @@ Сертификат Вашего веб-сайта отмечен как проверенный. Ошибка проверки сертификата: '%0%' Сейчас Вы %0% просматриваете сайт, используя протокол HTTPS. - Параметр 'Umbraco.Core.UseHttps' в секции 'appSetting' установлен в 'false' в файле web.config. Если Вам необходим доступ к сайту по протоколу HTTPS, нужно установить данный параметр в 'true'. - Параметр 'Umbraco.Core.UseHttps' в секции 'appSetting' в файле установлен в '%0%', значения cookies %1% маркированы как безопасные. Режим компиляции с отладкой выключен. Режим компиляции с отладкой сейчас включен. Рекомендуется выключить перед размещением сайта в сети. @@ -688,10 +685,7 @@ --> %0%.]]> Заголовки, позволяющие выяснить базовую технологию сайта, не обнаружены. - В файле Web.config, не обнаружено параметров работы с отправкой электронной почты (секция 'system.net/mailsettings'). - В файле Web.config в секции 'system.net/mailsettings' не обнаружены настройки почтового хоста. Параметры отправки электронной почты (SMTP) настроены корректно, сервис работает как ожидается. - Сервер SMTP сконфигурирован на использование хоста '%0%' на порту '%1%', который в настоящее время недоступен. Пожалуйста, убедитесь, что настройки SMTP в файле Web.config в секции 'system.net/mailsettings' верны. %0%.]]> %0%.]]>

    Зафиксированы следующие результаты автоматической проверки состояния Umbraco по расписанию, запущенной на %0% в %1%:

    %2%]]>
    @@ -705,18 +699,12 @@ Программа установки не может установить подключение к базе данных. - Невозможно сохранить изменения в файл web.config. Пожалуйста, вручную измените настройки строки подключения к базе данных. База данных обнаружена и идентифицирована как Конфигурация базы данных Установить чтобы установить базу данных Umbraco %0% ]]> Далее для продолжения.]]> - База данных не найдена! Пожалуйста, проверьте настройки строки подключения ("connection string") в файле конфигурации "web.config"

    -

    Для настройки откройте файл "web.config" с помощью любого текстового редактора и добавьте нужную информацию в строку подключения (параметр "UmbracoDbDSN"), - затем сохраните файл.

    -

    Нажмите кнопку "Повторить" когда все будет готово
    - Более подробно о внесении изменений в файл "web.config" рассказано здесь.

    ]]>
    Пожалуйста, свяжитесь с Вашим хостинг-провайдером, если есть необходимость, а если устанавливаете на локальную рабочую станцию или сервер, то получите информацию у Вашего системного администратора.]]> @@ -734,7 +722,6 @@ Пароль пользователя по-умолчанию успешно изменен в процессе установки!

    Нет надобности в каких-либо дальнейших действиях. Нажмите кнопку Далее для продолжения.]]> Пароль изменен! Для начального обзора возможностей системы рекомендуем посмотреть ознакомительные видеоматериалы - Далее (или модифицируя вручную ключ "UmbracoConfigurationStatus" в файле "web.config"), Вы принимаете лицензионное соглашение для данного программного обеспечения, расположенное ниже. Пожалуйста, обратите внимание, что установочный пакет Umbraco отвечает двум различным типам лицензий: лицензии MIT на программные продукты с открытым исходным кодом в части ядра системы и свободной лицензии Umbraco в части пользовательского интерфейса.]]> Система не установлена. Затронутые файлы и папки Более подробно об установке разрешений для Umbraco рассказано здесь @@ -797,8 +784,6 @@ Дальнейшее изучение и помощь Получайте помощь от нашего замечательного сообщества пользователей, изучайте документацию или просматривайте наши свободно распространяемые видео-материалы о том, как создать собственный несложный сайт, как использовать расширения и пакеты, а также краткое руководство по терминологии Umbraco.]]> Система Umbraco %0% установлена и готова к работе - web.config и изменить значение ключа UmbracoConfigurationStatus в секции AppSetting, установив его равным '%0%'.]]> прямо сейчас, воспользовавшись ссылкой "Начать работу с Umbraco".
    Если Вы новичок в мире Umbraco, Вы сможете найти много полезных ссылок на ресурсы на странице "Начало работы".]]>
    Начните работу с Umbraco diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml index 9a00dcdb197b..e0e2235ae98d 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml @@ -20,9 +20,9 @@ Standardvärde Ta bort Avaktivera - Töm papperskorgen + Töm papperskorgen Exportera dokumenttyp - Importera dokumenttyp + Importera dokumenttyp Importera paket Redigera i Canvas Logga ut @@ -483,12 +483,10 @@ Installationsprogrammet kan inte ansluta till databasen. - Kunde inte spara filen web.config. Vänligen ändra databasanslutnings-inställningarna manuellt. Din databas har lokaliserats och är identifierad som Databaskonfiguration installera]]> Nästa för att fortsätta.]]> - Databasen kunde inte hittas! Kontrollera att informationen i databasanslutnings-inställningarna i filen "web.config" är rätt.

    För att fortsätta måste du redigera filen "web.config" (du kan använda Visual Studio eller din favorit text-redigerare), bläddra till slutet, lägg till databasanslutnings-inställningarna för din databas i fältet som heter "umbracoDbDSN" och spara filen.

    Klicka på Försök igen knappen när du är klar.
    Mer information om att redigera web.config hittar du här.

    ]]>
    Eventuellt kan du behöva kontakta ditt webb-hotell. Om du installerar på en lokal maskin eller server kan du få informationen från din systemadministratör.]]> Tryck Uppgradera knappen för att uppgradera din databas till Umbraco %0%

    Du behöver inte vara orolig. Inget innehåll kommer att raderas och efteråt kommer allt att fungera som vanligt!

    ]]>
    Tryck Nästa för att fortsätta.]]> @@ -498,7 +496,6 @@ Standardanvändarens lösenord har ändrats sedan installationen!

    Du behöver inte göra något ytterligare här. Klicka Nästa för att fortsätta.]]> Lösenordet är ändrat! Få en flygande start, kolla på våra introduktionsvideor - Genom att klicka på Nästa knappen (eller redigera UmbracoConfigurationStatus i web.config), accepterar du licensavtalet för den här mjukvaran som det är skrivet i rutan nedan. Observera att den här Umbracodistributionen består av två olika licensavtal, "the open source MIT license" för ramverket och "the Umbraco freeware license" som täcker användargränssnittet. Inte installerad än. Berörda filer och mappar Här hittar du mer information om att sätta rättigheter för Umbraco @@ -533,7 +530,6 @@ Besök din nya webbplats Du installerade Runway, så varför inte se hur din nya webbplats ser ut.]]> Ytterligare hjälp och information Få hjälp från våra prisbelönta community, bläddra i dokumentationen eller titta på några gratis videor om hur man bygger en enkel webbplats, hur du använder paket eller en snabbguide till Umbracos terminologi]]> Umbraco %0% är installerat och klart för användning - /web.config filen och ändra AppSettingsnyckeln UmbracoConfigurationStatus på slutet till %0%]]> börja omedelbart genom att klicka på "Starta Umbraco"-knappen nedan.
    Om du är en ny Umbraco användarekan du hitta massor av resurser på våra kom igång sidor.]]>
    Starta Umbraco För att administrera din webbplats öppnar du bara Umbraco backoffice och börjar lägga till innehåll, uppdatera mallar och stilmallar eller lägga till nya funktioner.]]> Anslutningen till databasen misslyckades. diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/tr.xml b/src/Umbraco.Web.UI/umbraco/config/lang/tr.xml index 41794d5d1133..58c0f7f94bfb 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/tr.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/tr.xml @@ -17,10 +17,10 @@ Sil Devre Dışı Bırak Ayarları düzenle - Geri dönüşüm kutusunu boşalt + Geri dönüşüm kutusunu boşalt Etkinleştir Belge Türünü Dışa Aktar - Belge Türünü İçe Aktar + Belge Türünü İçe Aktar Paketi İçe Aktar Kanvas'ta Düzenle Çıkış @@ -790,7 +790,6 @@ Yükleyici veritabanına bağlanamıyor. - web.config dosyası kaydedilemedi. Lütfen bağlantı dizesini manuel olarak değiştirin. ​​Veritabanınız bulundu ve tanımlandı Veritabanı yapılandırması @@ -799,14 +798,6 @@ ]]> İleri'ye basın.]]> - - ​​ Veritabanı bulunamadı! Lütfen "web.config" dosyasının "bağlantı dizesindeki" bilgilerin doğru olup olmadığını kontrol edin.

    -

    Devam etmek için lütfen "web.config" dosyasını düzenleyin (Visual Studio veya favori metin düzenleyicinizi kullanarak), en alta kaydırın, veritabanınız için "UmbracoDbDSN" adlı anahtara bağlantı dizesini ekleyin ve dosyayı kaydedin.

    -

    - Yeniden dene düğmesini tıklayın. - bitti.
    - Web.config'i düzenleme hakkında daha fazla bilgi burada.

    ]]> -
    Lütfen gerekirse ISS'niz ile iletişime geçin. @@ -831,7 +822,6 @@ Varsayılan kullanıcının şifresi kurulumdan bu yana başarıyla değiştirildi!

    Başka işlem yapılmasına gerek yok. Devam etmek için İleri 'yi tıklayın.]]> Şifre değiştirildi! Harika bir başlangıç ​​yapın, tanıtım videolarımızı izleyin - Sonraki düğmeye tıklayarak (veya web.config'deki umbracoConfigurationStatus'u değiştirerek), bu yazılımın lisansını aşağıdaki kutuda belirtildiği şekilde kabul etmiş olursunuz. Bu Umbraco dağıtımının iki farklı lisanstan oluştuğuna dikkat edin, çerçeve için açık kaynak MIT lisansı ve kullanıcı arayüzünü kapsayan ücretsiz Umbraco lisansı. Henüz yüklenmedi. Etkilenen dosyalar ve klasörler Umbraco için izinlerin ayarlanmasıyla ilgili daha fazla bilgiyi burada bulabilirsiniz @@ -914,10 +904,6 @@ Runway'i kurdunuz, öyleyse neden yeni web sitenizin nasıl göründüğüne bak Ödüllü topluluğumuzdan yardım alın, belgelere göz atın veya basit bir sitenin nasıl oluşturulacağı, paketlerin nasıl kullanılacağı ve Umbraco terminolojisine yönelik hızlı bir kılavuzla ilgili bazı ücretsiz videolar izleyin]]> Umbraco %0% yüklendi ve kullanıma hazır - - /web.config dosyasını manuel olarak düzenleyin ve alttaki AppSetting anahtarını UmbracoConfigurationStatus '%0%' değerine güncelleyin.]]> - anında başlayabilirsiniz .
    Umbraco'da yeniyseniz , başlangıç ​​sayfalarımızda birçok kaynak bulabilirsiniz.]]> @@ -2070,8 +2056,6 @@ Web sitenizi yönetmek için, Umbraco'nun arka ofisini açın ve içerik eklemey Web sitenizin SSL sertifikasının süresi %0% gün içinde doluyor. URL %0% - '%1%' pinglenirken hata oluştu Şu anda HTTPS şemasını kullanarak siteyi %0% görüntülüyorsunuz. - appSetting 'Umbraco.Core.Https' web.config dosyanızda 'false' olarak ayarlandı. Bu siteye HTTPS şemasını kullanarak eriştiğinizde, bu 'doğru' olarak ayarlanmalıdır. - appSetting 'Umbraco.Core.Https' web.config dosyanızda '%0%' olarak ayarlandı, çerezleriniz%1% güvenli olarak işaretlendi. Hata ayıklama derleme modu devre dışı. Hata ayıklama derleme modu şu anda etkin. Yayınlanmadan önce bu ayarı devre dışı bırakmanız önerilir. @@ -2094,10 +2078,7 @@ Web sitenizi yönetmek için, Umbraco'nun arka ofisini açın ve içerik eklemey --> ​​%0%.]]> ​​Web sitesi teknolojisi hakkında bilgi veren hiçbir başlık bulunamadı. - ​​Web.config dosyasında system.net/mailsettings bulunamadı. - Web.config dosyası system.net/mailsettings bölümünde, ana bilgisayar yapılandırılmamış. SMTP ayarları doğru yapılandırıldı ve hizmet beklendiği gibi çalışıyor. - '%0%' ana bilgisayarı ve '%1%' bağlantı noktası ile yapılandırılan SMTP sunucusuna ulaşılamadı. Lütfen Web.config dosyasındaki system.net/mailsettings içindeki SMTP ayarlarının doğruluğunu kontrol edin. %0% olarak ayarlandı]]> %0%.]]>

    %0% tarihinde %1% ile çalıştırılan planlanmış Umbraco Sağlık Kontrollerinin sonuçları aşağıdaki gibidir:

    %2%]]>
    diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml index cf2db35e9cd3..423e5cca36dc 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml @@ -14,9 +14,9 @@ 创建扩展包 删除 禁用 - 清空回收站 + 清空回收站 导出文档类型 - 导入文档类型 + 导入文档类型 导入扩展包 实时编辑模式 退出 @@ -482,19 +482,12 @@ 无法连接到数据库。 - 无法保存web.config文件,请手工修改。 发现数据库 数据库配置 安装进行 %0% 数据库配置 ]]> 下一步继续。]]> - 数据库未找到!请检查数据库连接串设置。

    -

    您可以自行编辑“web.config”文件,键名为 “UmbracoDbDSN”

    -

    - 当自行编辑后,单击重试按钮
    。 - 如何编辑web.config

    - ]]>
    如有必要,请联系您的系统管理员。 如果您是本机安装,请使用管理员账号。]]> @@ -512,7 +505,6 @@ 安装过程中默认用户密码已更改

    点击下一步继续。]]> 密码已更改 作为入门者,从视频教程开始吧! - 点击下一步 (或在Web.config中自行修改UmbracoConfigurationStatus),意味着您接受上述许可协议。 安装失败。 受影响的文件和文件夹 此处查看更多信息 @@ -575,8 +567,6 @@ 更多的帮助信息 从社区获取帮助]]> 系统 %0% 安装完毕 - /web.config file 的 AppSetting 键 - UmbracoConfigurationStatus'%0%'。]]> 立即开始请点“运行系统”
    如果您是新手, 您可以得到相当丰富的学习资源。]]>
    运行系统 管理您的网站, 运行后台添加内容, @@ -1153,8 +1143,6 @@ Certificate validation error: '%0%' Error pinging the URL %0% - '%1%' You are currently %0% viewing the site using the HTTPS scheme. - The appSetting 'Umbraco.Core.UseHttps' is set to 'false' in your web.config file. Once you access this site using the HTTPS scheme, that should be set to 'true'. - The appSetting 'Umbraco.Core.UseHttps' is set to '%0%' in your web.config file, your cookies are %1% marked as secure. Debug compilation mode is disabled. Debug compilation mode is currently enabled. It is recommended to disable this setting before go live. @@ -1174,10 +1162,7 @@ --> %0%.]]> No headers revealing information about the website technology were found. - In the Web.config file, system.net/mailsettings could not be found. - In the Web.config file system.net/mailsettings section, the host is not configured. SMTP settings are configured correctly and the service is operating as expected. - The SMTP server configured with host '%0%' and port '%1%' could not be reached. Please check to ensure the SMTP settings in the Web.config file system.net/mailsettings are correct. %0%.]]> %0%.]]> diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/zh_tw.xml b/src/Umbraco.Web.UI/umbraco/config/lang/zh_tw.xml index 0fee4e6a1838..8d5cf16de28b 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/zh_tw.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/zh_tw.xml @@ -14,9 +14,9 @@ 創建擴展包 刪除 禁用 - 清空回收站 + 清空回收站 匯出文檔類型 - 導入文檔類型 + 導入文檔類型 導入擴展包 即時編輯模式 退出 @@ -475,19 +475,12 @@ 無法連接到資料庫。 - 無法保存web.config檔,請手工修改。 發現資料庫 資料庫配置 安裝 按鈕來安裝Umbraco資料庫 %0% ]]> 下一步繼續。]]> - 沒有找到資料庫!請確認檔案"web.config"中的字串"connection string"是否正確。

    -

    請編輯檔案"web.config" (例如使用Visual Studio或您喜歡的編輯器),移動到檔案底部,並在名稱為"UmbracoDbDSN"的字串中設定資料庫連結資訊,並存檔。

    -

    - 點選重試按鈕當上述步驟完成。
    - - 在此查詢更多編輯web.config的資訊。

    ]]>
    若需要時,請聯繫您的網路公司。如果您在本地機器或伺服器安裝的話,您也許需要聯絡系統管理者。]]> 安裝後預設使用者的密碼已經成功修改!

    不需更多的操作步驟。點選下一步繼續。]]> 密碼已更改 作為入門者,從視頻教程開始吧! - 點擊下一步 (或在Web.config中自行修改UmbracoConfigurationStatus),意味著您接受上述授權合約。 安裝失敗。 受影響的檔和資料夾 此處查看更多資訊 @@ -567,7 +559,6 @@ 更多的幫忙與資訊 從我們獲獎的社群得到幫助,瀏覽文件,或觀看免費影片來瞭解如何輕鬆架設網站,如何使用插件,和瞭解Umbraco項目名稱的快速上手指引。]]> 系統 %0% 安裝完畢 - /web.config 檔案並且更新AppSetting中的字串UmbracoConfigurationStatus 內容為 '%0%'。]]> 快速開始指引。
    如果您是Umbraco的新成員, 您可以在其中找到相當多的資源。]]>
    啟動Umbraco @@ -1134,8 +1125,6 @@ 憑證驗證錯誤:%0% 網址探查錯誤:%0% - '%1%' 您目前使用HTTPS瀏覽本站:%0% - 在您的web.config檔案中,appSetting的Umbraco.Core.UseHttps是設為false。當您開始使用HTTPS時,應將其改為 true。 - 在您的web.config檔案中,appSetting的Umbraco.Core.UseHttps是設為 %0%,您的cookies %0% 標成安全。 偵錯編輯模式關閉。 偵錯編輯模式目前已開啟。上線前建議將其關閉。 @@ -1155,10 +1144,7 @@ --> %0%。]]> 在標頭中沒有找到揭露網站技術的資訊。 - 在 Web.config 檔案中,找不到 system.net/mailsettings。 - 在 Web.config 檔案中的 system.net/mailsettings,沒有設定 host 。 SMTP設定正確,而且服務正常運作。 - SMTP伺服器 %0% : %1% 無法連接。請確認在Web.config 檔案中 system.net/mailsettings 設定正確。 %0%。]]> %0%。]]> diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/DataTypes/dataTypes.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/DataTypes/dataTypes.ts index 9e1e6cc185dd..942d42a9bf77 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/DataTypes/dataTypes.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/DataTypes/dataTypes.ts @@ -2,6 +2,7 @@ import { AliasHelper, ApprovedColorPickerDataTypeBuilder, + TextBoxDataTypeBuilder, } from 'umbraco-cypress-testhelpers'; context('DataTypes', () => { @@ -61,6 +62,48 @@ context('DataTypes', () => { cy.umbracoEnsureTemplateNameNotExists(name); }); + it('Tests Textbox Maxlength', () => { + cy.deleteAllContent(); + const name = 'Textbox Maxlength Test'; + const alias = AliasHelper.toAlias(name); + + cy.umbracoEnsureDocumentTypeNameNotExists(name); + cy.umbracoEnsureDataTypeNameNotExists(name); + + const textBoxDataType = new TextBoxDataTypeBuilder() + .withName(name) + .withMaxChars(10) + .build() + + cy.umbracoCreateDocTypeWithContent(name, alias, textBoxDataType); + + // Act + // Enter content + // Assert no helptext with (max-2) chars & can save + cy.umbracoRefreshContentTree(); + cy.umbracoTreeItem("content", [name]).click(); + cy.get('input[name="textbox"]').type('12345678'); + cy.get('localize[key="textbox_characters_left"]').should('not.exist'); + cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); + cy.umbracoSuccessNotification().should('be.visible'); + cy.get('.property-error').should('not.be.visible'); + + // Add char and assert helptext appears - no publish to save time & has been asserted above & below + cy.get('input[name="textbox"]').type('9'); + cy.get('localize[key="textbox_characters_left"]').contains('characters left').should('exist'); + cy.get('.property-error').should('not.be.visible'); + + // Add char and assert errortext appears and can't save + cy.get('input[name="textbox"]').type('10'); // 1 char over max + cy.get('localize[key="textbox_characters_exceed"]').contains('too many').should('exist'); + cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); + cy.get('.property-error').should('be.visible'); + + // Clean + cy.umbracoEnsureDataTypeNameNotExists(name); + cy.umbracoEnsureDocumentTypeNameNotExists(name); + }) + // it('Tests Checkbox List', () => { // const name = 'CheckBox List'; // const alias = AliasHelper.toAlias(name); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js index be9b93134d01..6add16b4ee68 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js +++ b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js @@ -1,4 +1,4 @@ -context('User Groups', () => { +context('Member Groups', () => { beforeEach(() => { cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/users.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/users.ts index e122b2156432..12a9bee7a20b 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/users.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/users.ts @@ -32,4 +32,109 @@ context('Users', () => { }); -}); + it('Update user', () => { + // Set userdata + const name = "Alice Bobson"; + const email = "alice-bobson@acceptancetest.umbraco"; + const startContentIds = []; + const startMediaIds = []; + const userGroups = ["admin"]; + + var userData = + { + "id": -1, + "parentId": -1, + "name": name, + "username": email, + "culture": "en-US", + "email": email, + "startContentIds": startContentIds, + "startMediaIds": startMediaIds, + "userGroups": userGroups, + "message": "" + }; + + // Ensure user doesn't exist + cy.umbracoEnsureUserEmailNotExists(email); + + // Create user through API + cy.getCookie('UMB-XSRF-TOKEN', { log: false }).then((token) => { + cy.request({ + method: 'POST', + url: '/umbraco/backoffice/umbracoapi/users/PostCreateUser', + followRedirect: true, + headers: { + Accept: 'application/json', + 'X-UMB-XSRF-TOKEN': token.value, + }, + body: userData, + log: false, + }).then((response) => { + return; + }); + }); + + // Go to the user and edit their name + cy.umbracoSection('users'); + cy.get('.umb-user-card__name').contains(name).click(); + cy.get('#headerName').type('{movetoend}son'); + cy.umbracoButtonByLabelKey('buttons_save').click(); + + // assert save succeeds + cy.umbracoSuccessNotification().should('be.visible'); + cy.umbracoEnsureUserEmailNotExists(email); + }) + + it('Delete user', () => { + // Set userdata + const name = "Alice Bobson"; + const email = "alice-bobson@acceptancetest.umbraco"; + const startContentIds = []; + const startMediaIds = []; + const userGroups = ["admin"]; + + var userData = + { + "id": -1, + "parentId": -1, + "name": name, + "username": email, + "culture": "en-US", + "email": email, + "startContentIds": startContentIds, + "startMediaIds": startMediaIds, + "userGroups": userGroups, + "message": "" + }; + + // Ensure user doesn't exist + cy.umbracoEnsureUserEmailNotExists(email); + + // Create user through API + cy.getCookie('UMB-XSRF-TOKEN', { log: false }).then((token) => { + cy.request({ + method: 'POST', + url: '/umbraco/backoffice/umbracoapi/users/PostCreateUser', + followRedirect: true, + headers: { + Accept: 'application/json', + 'X-UMB-XSRF-TOKEN': token.value, + }, + body: userData, + log: false, + }).then((response) => { + return; + }); + }); + + // Go to the user and delete them + cy.umbracoSection('users'); + cy.get('.umb-user-card__name').contains(name).click(); + cy.umbracoButtonByLabelKey("user_deleteUser").click(); + cy.get('umb-button[label="Yes, delete"]').click(); + + // assert deletion succeeds + cy.umbracoSuccessNotification().should('be.visible'); + cy.umbracoEnsureUserEmailNotExists(email); + }) +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/support/commands.js b/tests/Umbraco.Tests.AcceptanceTest/cypress/support/commands.js index 5b0be47114e1..34da26d04e5f 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/support/commands.js +++ b/tests/Umbraco.Tests.AcceptanceTest/cypress/support/commands.js @@ -26,5 +26,73 @@ import {Command} from 'umbraco-cypress-testhelpers'; import {Chainable} from './chainable'; +import { JsonHelper } from 'umbraco-cypress-testhelpers'; new Chainable(); new Command().registerCypressCommands(); + +Cypress.Commands.add('umbracoCreateLanguage', (culture, isMandatory = false, fallbackLanguageId = 1) => { + + var langData = + { + "culture": culture, + "isMandatory": isMandatory, + "fallbackLanguageId": fallbackLanguageId + }; + + // Create language through API + cy.getCookie('UMB-XSRF-TOKEN', { log: false }).then((token) => { + cy.request({ + method: 'POST', + url: '/umbraco/backoffice/umbracoapi/language/SaveLanguage', + followRedirect: true, + headers: { + Accept: 'application/json', + 'X-UMB-XSRF-TOKEN': token.value, + }, + body: langData, + log: false, + }).then((response) => { + return; + }); + }); +}); + +Cypress.Commands.add('umbracoEnsureLanguageNotExists', (culture) => { + cy.getCookie('UMB-XSRF-TOKEN', { log: false }).then((token) => { + console.log('hit commands') + cy.request({ + method: 'GET', + url: '/umbraco/backoffice/umbracoapi/language/GetAllLanguages', + followRedirect: true, + headers: { + Accept: 'application/json', + 'X-UMB-XSRF-TOKEN': token.value, + }, + log: false, + }).then((response) => { + const searchBody = JsonHelper.getBody(response); + if (searchBody.length > 0) { + let languageId = null; + for (const sb of searchBody) { + if (sb.culture === culture) { + languageId = sb.id; + } + } + + if (languageId !== null) { + cy.request({ + method: 'POST', + url: '/umbraco/backoffice/umbracoapi/language/DeleteLanguage?id=' + languageId, + followRedirect: false, + headers: { + ContentType: 'application/json', + 'X-UMB-XSRF-TOKEN': token.value, + }, + }).then((resp) => { + return; + }); + } + } + }); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.Integration.SqlCe/Umbraco.Tests.Integration.SqlCe.csproj b/tests/Umbraco.Tests.Integration.SqlCe/Umbraco.Tests.Integration.SqlCe.csproj index b4631e56dc62..24c20104668f 100644 --- a/tests/Umbraco.Tests.Integration.SqlCe/Umbraco.Tests.Integration.SqlCe.csproj +++ b/tests/Umbraco.Tests.Integration.SqlCe/Umbraco.Tests.Integration.SqlCe.csproj @@ -6,7 +6,7 @@ - + diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs index decd1d7c5c88..9f79414b550f 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; +using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.IO; @@ -73,7 +74,7 @@ public void Maps_Templates_Correctly() IScopeProvider provider = ScopeProvider; using (IScope scope = provider.CreateScope()) { - var templateRepo = new TemplateRepository((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger(), FileSystems, IOHelper, ShortStringHelper); + var templateRepo = new TemplateRepository((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger(), FileSystems, IOHelper, ShortStringHelper, Mock.Of()); ContentTypeRepository repository = ContentTypeRepository; Template[] templates = new[] { diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs index a7e164183935..7207718071fb 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs @@ -114,7 +114,7 @@ private DocumentRepository CreateRepository(IScopeAccessor scopeAccessor, out Co appCaches ??= AppCaches; - templateRepository = new TemplateRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger(), FileSystems, IOHelper, ShortStringHelper); + templateRepository = new TemplateRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger(), FileSystems, IOHelper, ShortStringHelper, Mock.Of()); var tagRepository = new TagRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger()); var commonRepository = new ContentTypeCommonRepository(scopeAccessor, templateRepository, appCaches, ShortStringHelper); var languageRepository = new LanguageRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger(), globalSettings); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs index d137ef8d055b..80eb61d21f91 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs @@ -38,10 +38,11 @@ public class TemplateRepositoryTest : UmbracoIntegrationTest private IHostingEnvironment HostingEnvironment => GetRequiredService(); private FileSystems FileSystems => GetRequiredService(); + private IViewHelper ViewHelper => GetRequiredService(); private ITemplateRepository CreateRepository(IScopeProvider provider) => - new TemplateRepository((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger(), FileSystems, IOHelper, ShortStringHelper); - + new TemplateRepository((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger(), FileSystems, IOHelper, ShortStringHelper, ViewHelper); + [Test] public void Can_Instantiate_Repository() { @@ -90,16 +91,14 @@ public void Can_Perform_Add_View_With_Default_Content() // Act var template = new Template(ShortStringHelper, "test", "test") { - Content = ViewHelper.GetDefaultFileContent() + Content = "mock-content" }; repository.Save(template); // Assert Assert.That(repository.Get("test"), Is.Not.Null); Assert.That(FileSystems.MvcViewsFileSystem.FileExists("test.cshtml"), Is.True); - Assert.AreEqual( - @"@usingUmbraco.Cms.Web.Common.PublishedModels;@inheritsUmbraco.Cms.Web.Common.Views.UmbracoViewPage@{Layout=null;}".StripWhitespace(), - template.Content.StripWhitespace()); + Assert.AreEqual("mock-content", template.Content.StripWhitespace()); } } @@ -144,13 +143,13 @@ public void Can_Perform_Add_Unique_Alias() // Act var template = new Template(ShortStringHelper, "test", "test") { - Content = ViewHelper.GetDefaultFileContent() + Content = "mock-content" }; repository.Save(template); var template2 = new Template(ShortStringHelper, "test", "test") { - Content = ViewHelper.GetDefaultFileContent() + Content = "mock-content" }; repository.Save(template2); @@ -172,13 +171,13 @@ public void Can_Perform_Update_Unique_Alias() // Act var template = new Template(ShortStringHelper, "test", "test") { - Content = ViewHelper.GetDefaultFileContent() + Content = "mock-content" }; repository.Save(template); var template2 = new Template(ShortStringHelper, "test1", "test1") { - Content = ViewHelper.GetDefaultFileContent() + Content = "mock-content" }; repository.Save(template2); @@ -205,7 +204,7 @@ public void Can_Perform_Update_View() // Act var template = new Template(ShortStringHelper, "test", "test") { - Content = ViewHelper.GetDefaultFileContent() + Content = "mock-content" }; repository.Save(template); @@ -216,7 +215,7 @@ public void Can_Perform_Update_View() // Assert Assert.That(FileSystems.MvcViewsFileSystem.FileExists("test.cshtml"), Is.True); - Assert.That(updated.Content, Is.EqualTo(ViewHelper.GetDefaultFileContent() + "")); + Assert.That(updated.Content, Is.EqualTo("mock-content" + "")); } } @@ -232,7 +231,7 @@ public void Can_Perform_Delete_View() var template = new Template(ShortStringHelper, "test", "test") { - Content = ViewHelper.GetDefaultFileContent() + Content = "mock-content" }; repository.Save(template); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index ca6096835aff..36c901773ee9 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -81,7 +81,7 @@ - + diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/ViewHelperTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/DefaultViewContentProviderTests.cs similarity index 71% rename from tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/ViewHelperTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/DefaultViewContentProviderTests.cs index 446659467b6a..d43d88f001ad 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/ViewHelperTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/DefaultViewContentProviderTests.cs @@ -7,12 +7,14 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Templates { [TestFixture] - public class ViewHelperTests + public class DefaultViewContentProviderTests { + private IDefaultViewContentProvider DefaultViewContentProvider => new DefaultViewContentProvider(); + [Test] public void NoOptions() { - var view = ViewHelper.GetDefaultFileContent(); + var view = DefaultViewContentProvider.GetDefaultFileContent(); Assert.AreEqual( FixView(@"@using Umbraco.Cms.Web.Common.PublishedModels; @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage @@ -24,7 +26,7 @@ @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage [Test] public void Layout() { - var view = ViewHelper.GetDefaultFileContent(layoutPageAlias: "Dharznoik"); + var view = DefaultViewContentProvider.GetDefaultFileContent(layoutPageAlias: "Dharznoik"); Assert.AreEqual( FixView(@"@using Umbraco.Cms.Web.Common.PublishedModels; @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage @@ -36,7 +38,7 @@ @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage [Test] public void ClassName() { - var view = ViewHelper.GetDefaultFileContent(modelClassName: "ClassName"); + var view = DefaultViewContentProvider.GetDefaultFileContent(modelClassName: "ClassName"); Assert.AreEqual( FixView(@"@using Umbraco.Cms.Web.Common.PublishedModels; @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage @@ -48,7 +50,7 @@ @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage [Test] public void Namespace() { - var view = ViewHelper.GetDefaultFileContent(modelNamespace: "Models"); + var view = DefaultViewContentProvider.GetDefaultFileContent(modelNamespace: "Models"); Assert.AreEqual( FixView(@"@using Umbraco.Cms.Web.Common.PublishedModels; @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage @@ -60,7 +62,7 @@ @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage [Test] public void ClassNameAndNamespace() { - var view = ViewHelper.GetDefaultFileContent(modelClassName: "ClassName", modelNamespace: "My.Models"); + var view = DefaultViewContentProvider.GetDefaultFileContent(modelClassName: "ClassName", modelNamespace: "My.Models"); Assert.AreEqual( FixView(@"@using Umbraco.Cms.Web.Common.PublishedModels; @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage @@ -73,7 +75,7 @@ @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage [Test] public void ClassNameAndNamespaceAndAlias() { - var view = ViewHelper.GetDefaultFileContent(modelClassName: "ClassName", modelNamespace: "My.Models", modelNamespaceAlias: "MyModels"); + var view = DefaultViewContentProvider.GetDefaultFileContent(modelClassName: "ClassName", modelNamespace: "My.Models", modelNamespaceAlias: "MyModels"); Assert.AreEqual( FixView(@"@using Umbraco.Cms.Web.Common.PublishedModels; @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage @@ -86,7 +88,7 @@ @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage [Test] public void Combined() { - var view = ViewHelper.GetDefaultFileContent(layoutPageAlias: "Dharznoik", modelClassName: "ClassName", modelNamespace: "My.Models", modelNamespaceAlias: "MyModels"); + var view = DefaultViewContentProvider.GetDefaultFileContent(layoutPageAlias: "Dharznoik", modelClassName: "ClassName", modelNamespace: "My.Models", modelNamespaceAlias: "MyModels"); Assert.AreEqual( FixView(@"@using Umbraco.Cms.Web.Common.PublishedModels; @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/BuilderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/BuilderTests.cs index 4dea81facbce..73f38da362b1 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/BuilderTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/BuilderTests.cs @@ -26,6 +26,7 @@ public void GenerateSimpleType() Id = 1, Alias = "type1", ClrName = "Type1", + Name = "type1Name", ParentId = 0, BaseType = null, ItemType = TypeModel.ItemTypes.Content, @@ -34,6 +35,7 @@ public void GenerateSimpleType() { Alias = "prop1", ClrName = "Prop1", + Name = "prop1Name", ModelClrType = typeof(string), }); @@ -67,6 +69,7 @@ public void GenerateSimpleType() namespace Umbraco.Cms.Web.Common.PublishedModels { + ///

    type1Name [PublishedModel(""type1"")] public partial class Type1 : PublishedContentModel { @@ -97,6 +100,9 @@ public Type1(IPublishedContent content, IPublishedValueFallback publishedValueFa // properties + /// + /// prop1Name + /// [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] [global::System.Diagnostics.CodeAnalysis.MaybeNull] [ImplementPropertyType(""prop1"")] @@ -271,6 +277,388 @@ public void GenerateAmbiguous() Assert.IsTrue(gen.Contains(" global::Umbraco.Cms.Core.Exceptions.BootFailedException Prop3")); } + [Test] + public void GenerateInheritedType() + { + var parentType = new TypeModel + { + Id = 1, + Alias = "parentType", + ClrName = "ParentType", + Name = "parentTypeName", + ParentId = 0, + IsParent = true, + BaseType = null, + ItemType = TypeModel.ItemTypes.Content, + }; + parentType.Properties.Add(new PropertyModel + { + Alias = "prop1", + ClrName = "Prop1", + Name = "prop1Name", + ModelClrType = typeof(string), + }); + + var childType = new TypeModel + { + Id = 2, + Alias = "childType", + ClrName = "ChildType", + Name = "childTypeName", + ParentId = 1, + BaseType = parentType, + ItemType = TypeModel.ItemTypes.Content, + }; + + TypeModel[] docTypes = new[] { parentType, childType }; + + var modelsBuilderConfig = new ModelsBuilderSettings(); + var builder = new TextBuilder(modelsBuilderConfig, docTypes); + + var sb = new StringBuilder(); + builder.Generate(sb, builder.GetModelsToGenerate().First()); + var genParent = sb.ToString(); + + SemVersion version = ApiVersion.Current.Version; + + var expectedParent = @"//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Umbraco.ModelsBuilder.Embedded v" + version + @" +// +// Changes to this file will be lost if the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; +using System.Linq.Expressions; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Infrastructure.ModelsBuilder; +using Umbraco.Cms.Core; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Web.Common.PublishedModels +{ + /// parentTypeName + [PublishedModel(""parentType"")] + public partial class ParentType : PublishedContentModel + { + // helpers +#pragma warning disable 0109 // new is redundant + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + public new const string ModelTypeAlias = ""parentType""; + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + public new const PublishedItemType ModelItemType = PublishedItemType.Content; + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [return: global::System.Diagnostics.CodeAnalysis.MaybeNull] + public new static IPublishedContentType GetModelContentType(IPublishedSnapshotAccessor publishedSnapshotAccessor) + => PublishedModelUtility.GetModelContentType(publishedSnapshotAccessor, ModelItemType, ModelTypeAlias); + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [return: global::System.Diagnostics.CodeAnalysis.MaybeNull] + public static IPublishedPropertyType GetModelPropertyType(IPublishedSnapshotAccessor publishedSnapshotAccessor, Expression> selector) + => PublishedModelUtility.GetModelPropertyType(GetModelContentType(publishedSnapshotAccessor), selector); +#pragma warning restore 0109 + + private IPublishedValueFallback _publishedValueFallback; + + // ctor + public ParentType(IPublishedContent content, IPublishedValueFallback publishedValueFallback) + : base(content, publishedValueFallback) + { + _publishedValueFallback = publishedValueFallback; + } + + // properties + + /// + /// prop1Name + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [global::System.Diagnostics.CodeAnalysis.MaybeNull] + [ImplementPropertyType(""prop1"")] + public virtual string Prop1 => this.Value(_publishedValueFallback, ""prop1""); + } +} +"; + Console.WriteLine(genParent); + Assert.AreEqual(expectedParent.ClearLf(), genParent); + + var sb2 = new StringBuilder(); + builder.Generate(sb2, builder.GetModelsToGenerate().Skip(1).First()); + var genChild = sb2.ToString(); + + var expectedChild = @"//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Umbraco.ModelsBuilder.Embedded v" + version + @" +// +// Changes to this file will be lost if the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; +using System.Linq.Expressions; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Infrastructure.ModelsBuilder; +using Umbraco.Cms.Core; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Web.Common.PublishedModels +{ + /// childTypeName + [PublishedModel(""childType"")] + public partial class ChildType : ParentType + { + // helpers +#pragma warning disable 0109 // new is redundant + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + public new const string ModelTypeAlias = ""childType""; + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + public new const PublishedItemType ModelItemType = PublishedItemType.Content; + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [return: global::System.Diagnostics.CodeAnalysis.MaybeNull] + public new static IPublishedContentType GetModelContentType(IPublishedSnapshotAccessor publishedSnapshotAccessor) + => PublishedModelUtility.GetModelContentType(publishedSnapshotAccessor, ModelItemType, ModelTypeAlias); + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [return: global::System.Diagnostics.CodeAnalysis.MaybeNull] + public static IPublishedPropertyType GetModelPropertyType(IPublishedSnapshotAccessor publishedSnapshotAccessor, Expression> selector) + => PublishedModelUtility.GetModelPropertyType(GetModelContentType(publishedSnapshotAccessor), selector); +#pragma warning restore 0109 + + private IPublishedValueFallback _publishedValueFallback; + + // ctor + public ChildType(IPublishedContent content, IPublishedValueFallback publishedValueFallback) + : base(content, publishedValueFallback) + { + _publishedValueFallback = publishedValueFallback; + } + + // properties + } +} +"; + + Console.WriteLine(genChild); + Assert.AreEqual(expectedChild.ClearLf(), genChild); + + } + + [Test] + public void GenerateComposedType() + { + // Umbraco returns nice, pascal-cased names. + var composition1 = new TypeModel + { + Id = 2, + Alias = "composition1", + ClrName = "Composition1", + Name = "composition1Name", + ParentId = 0, + BaseType = null, + ItemType = TypeModel.ItemTypes.Content, + IsMixin = true, + }; + composition1.Properties.Add(new PropertyModel + { + Alias = "compositionProp", + ClrName = "CompositionProp", + Name = "compositionPropName", + ModelClrType = typeof(string), + ClrTypeName = typeof(string).FullName + }); + + var type1 = new TypeModel + { + Id = 1, + Alias = "type1", + ClrName = "Type1", + Name = "type1Name", + ParentId = 0, + BaseType = null, + ItemType = TypeModel.ItemTypes.Content, + }; + type1.Properties.Add(new PropertyModel + { + Alias = "prop1", + ClrName = "Prop1", + Name = "prop1Name", + ModelClrType = typeof(string), + }); + type1.MixinTypes.Add(composition1); + + TypeModel[] types = new[] { type1, composition1 }; + + var modelsBuilderConfig = new ModelsBuilderSettings(); + var builder = new TextBuilder(modelsBuilderConfig, types); + + SemVersion version = ApiVersion.Current.Version; + + var sb = new StringBuilder(); + builder.Generate(sb, builder.GetModelsToGenerate().First()); + var genComposed = sb.ToString(); + + var expectedComposed = @"//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Umbraco.ModelsBuilder.Embedded v" + version + @" +// +// Changes to this file will be lost if the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; +using System.Linq.Expressions; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Infrastructure.ModelsBuilder; +using Umbraco.Cms.Core; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Web.Common.PublishedModels +{ + /// type1Name + [PublishedModel(""type1"")] + public partial class Type1 : PublishedContentModel, IComposition1 + { + // helpers +#pragma warning disable 0109 // new is redundant + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + public new const string ModelTypeAlias = ""type1""; + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + public new const PublishedItemType ModelItemType = PublishedItemType.Content; + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [return: global::System.Diagnostics.CodeAnalysis.MaybeNull] + public new static IPublishedContentType GetModelContentType(IPublishedSnapshotAccessor publishedSnapshotAccessor) + => PublishedModelUtility.GetModelContentType(publishedSnapshotAccessor, ModelItemType, ModelTypeAlias); + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [return: global::System.Diagnostics.CodeAnalysis.MaybeNull] + public static IPublishedPropertyType GetModelPropertyType(IPublishedSnapshotAccessor publishedSnapshotAccessor, Expression> selector) + => PublishedModelUtility.GetModelPropertyType(GetModelContentType(publishedSnapshotAccessor), selector); +#pragma warning restore 0109 + + private IPublishedValueFallback _publishedValueFallback; + + // ctor + public Type1(IPublishedContent content, IPublishedValueFallback publishedValueFallback) + : base(content, publishedValueFallback) + { + _publishedValueFallback = publishedValueFallback; + } + + // properties + + /// + /// prop1Name + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [global::System.Diagnostics.CodeAnalysis.MaybeNull] + [ImplementPropertyType(""prop1"")] + public virtual string Prop1 => this.Value(_publishedValueFallback, ""prop1""); + + /// + /// compositionPropName + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [global::System.Diagnostics.CodeAnalysis.MaybeNull] + [ImplementPropertyType(""compositionProp"")] + public virtual string CompositionProp => global::Umbraco.Cms.Web.Common.PublishedModels.Composition1.GetCompositionProp(this, _publishedValueFallback); + } +} +"; + Console.WriteLine(genComposed); + Assert.AreEqual(expectedComposed.ClearLf(), genComposed); + + var sb2 = new StringBuilder(); + builder.Generate(sb2, builder.GetModelsToGenerate().Skip(1).First()); + var genComposition = sb2.ToString(); + + var expectedComposition = @"//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Umbraco.ModelsBuilder.Embedded v" + version + @" +// +// Changes to this file will be lost if the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; +using System.Linq.Expressions; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Infrastructure.ModelsBuilder; +using Umbraco.Cms.Core; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Web.Common.PublishedModels +{ + // Mixin Content Type with alias ""composition1"" + /// composition1Name + public partial interface IComposition1 : IPublishedContent + { + /// compositionPropName + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [global::System.Diagnostics.CodeAnalysis.MaybeNull] + string CompositionProp { get; } + } + + /// composition1Name + [PublishedModel(""composition1"")] + public partial class Composition1 : PublishedContentModel, IComposition1 + { + // helpers +#pragma warning disable 0109 // new is redundant + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + public new const string ModelTypeAlias = ""composition1""; + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + public new const PublishedItemType ModelItemType = PublishedItemType.Content; + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [return: global::System.Diagnostics.CodeAnalysis.MaybeNull] + public new static IPublishedContentType GetModelContentType(IPublishedSnapshotAccessor publishedSnapshotAccessor) + => PublishedModelUtility.GetModelContentType(publishedSnapshotAccessor, ModelItemType, ModelTypeAlias); + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [return: global::System.Diagnostics.CodeAnalysis.MaybeNull] + public static IPublishedPropertyType GetModelPropertyType(IPublishedSnapshotAccessor publishedSnapshotAccessor, Expression> selector) + => PublishedModelUtility.GetModelPropertyType(GetModelContentType(publishedSnapshotAccessor), selector); +#pragma warning restore 0109 + + private IPublishedValueFallback _publishedValueFallback; + + // ctor + public Composition1(IPublishedContent content, IPublishedValueFallback publishedValueFallback) + : base(content, publishedValueFallback) + { + _publishedValueFallback = publishedValueFallback; + } + + // properties + + /// + /// compositionPropName + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [global::System.Diagnostics.CodeAnalysis.MaybeNull] + [ImplementPropertyType(""compositionProp"")] + public virtual string CompositionProp => GetCompositionProp(this, _publishedValueFallback); + + /// Static getter for compositionPropName + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] + [return: global::System.Diagnostics.CodeAnalysis.MaybeNull] + public static string GetCompositionProp(IComposition1 that, IPublishedValueFallback publishedValueFallback) => that.Value(publishedValueFallback, ""compositionProp""); + } +} +"; + + Console.WriteLine(genComposition); + Assert.AreEqual(expectedComposition.ClearLf(), genComposition); + } + [TestCase("int", typeof(int))] [TestCase("global::System.Collections.Generic.IEnumerable", typeof(IEnumerable))] [TestCase("global::Umbraco.Cms.Tests.UnitTests.Umbraco.ModelsBuilder.Embedded.BuilderTestsClass1", typeof(BuilderTestsClass1))] diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/CollectionBuilders/TreeCollectionBuilderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/CollectionBuilders/TreeCollectionBuilderTests.cs new file mode 100644 index 000000000000..5392d50ebd1f --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/CollectionBuilders/TreeCollectionBuilderTests.cs @@ -0,0 +1,36 @@ +using System.Linq; +using NUnit.Framework; +using Umbraco.Cms.Core.Trees; +using Umbraco.Cms.Web.BackOffice.Trees; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.CollectionBuilders +{ + public class TreeCollectionBuilderTests + { + [Test] + public void Adding_Tree_To_Collection_Builder() + { + var collectionBuilder = new TreeCollectionBuilder(); + var treeDefinition = new Tree(0, "test", "test", "test", "test", TreeUse.Main, typeof(LanguageTreeController), false); + + collectionBuilder.AddTree(treeDefinition); + var collection = collectionBuilder.CreateCollection(null); + + Assert.AreEqual(1, collection.Count); + Assert.AreEqual(treeDefinition, collection.FirstOrDefault()); + } + + [Test] + public void Remove_Tree_From_Collection_Builder() + { + var collectionBuilder = new TreeCollectionBuilder(); + var treeDefinition = new Tree(0, "test", "test", "test", "test", TreeUse.Main, typeof(LanguageTreeController), false); + + collectionBuilder.AddTree(treeDefinition); + collectionBuilder.RemoveTreeController(); + var collection = collectionBuilder.CreateCollection(null); + + Assert.AreEqual(0, collection.Count); + } + } +} From 67604654c13b8c0b5738ba6def63c27c447d5adc Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 3 Nov 2021 14:03:28 +0100 Subject: [PATCH 42/57] Fixed breaking changes --- .../Checks/Security/BaseHttpHeaderCheck.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Umbraco.Core/HealthChecks/Checks/Security/BaseHttpHeaderCheck.cs b/src/Umbraco.Core/HealthChecks/Checks/Security/BaseHttpHeaderCheck.cs index 99deaa2af7f9..b7e5e867c467 100644 --- a/src/Umbraco.Core/HealthChecks/Checks/Security/BaseHttpHeaderCheck.cs +++ b/src/Umbraco.Core/HealthChecks/Checks/Security/BaseHttpHeaderCheck.cs @@ -26,6 +26,20 @@ public abstract class BaseHttpHeaderCheck : HealthCheck private readonly bool _metaTagOptionAvailable; private static HttpClient s_httpClient; + [Obsolete("Use ctor without value.")] + protected BaseHttpHeaderCheck( + IHostingEnvironment hostingEnvironment, + ILocalizedTextService textService, + string header, + string value, + string localizedTextPrefix, + bool metaTagOptionAvailable) :this(hostingEnvironment, textService, header, localizedTextPrefix, metaTagOptionAvailable) + { + + } + + [Obsolete("Save ILocalizedTextService in a field on the super class instead of using this")] + protected ILocalizedTextService LocalizedTextService => _textService; /// /// Initializes a new instance of the class. /// From 439e1b55964dd52871d6bfd84730989d63b75265 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle Date: Wed, 3 Nov 2021 14:15:36 +0100 Subject: [PATCH 43/57] Updated User after merge --- .../src/views/common/overlays/user/user.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html index b926d9e26681..fa857858687c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html @@ -6,14 +6,13 @@
    + type="button"> Date: Wed, 3 Nov 2021 14:29:59 +0100 Subject: [PATCH 44/57] Updated nuget dependencies --- .../UmbracoProject/UmbracoProject.csproj | 14 +- src/JsonSchema/JsonSchema.csproj | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 2 +- .../Umbraco.Examine.Lucene.csproj | 2 +- .../Umbraco.Infrastructure.csproj | 12 +- .../Umbraco.PublishedCache.NuCache.csproj | 4 +- .../Umbraco.Web.Common.csproj | 10 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- .../package-lock.json | 2331 +---------------- .../Umbraco.Tests.Integration.SqlCe.csproj | 6 +- .../Umbraco.Tests.Integration.csproj | 8 +- .../Umbraco.Tests.UnitTests.csproj | 6 +- 12 files changed, 36 insertions(+), 2363 deletions(-) diff --git a/build/templates/UmbracoProject/UmbracoProject.csproj b/build/templates/UmbracoProject/UmbracoProject.csproj index 1b3290927d36..97d1e1b22958 100644 --- a/build/templates/UmbracoProject/UmbracoProject.csproj +++ b/build/templates/UmbracoProject/UmbracoProject.csproj @@ -5,11 +5,6 @@ Umbraco.Cms.Web.UI - - - - - @@ -17,7 +12,14 @@ - + + + + + + + + diff --git a/src/JsonSchema/JsonSchema.csproj b/src/JsonSchema/JsonSchema.csproj index 942bdd970556..84de67b657ce 100644 --- a/src/JsonSchema/JsonSchema.csproj +++ b/src/JsonSchema/JsonSchema.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 56cb1e6d01eb..f36d7a8ee5c0 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj index 118a895073c6..1c12ad5be326 100644 --- a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj +++ b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj @@ -21,7 +21,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 1d2f8bc505f9..a226749dcac0 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -18,9 +18,9 @@ - + - + @@ -28,7 +28,7 @@ - + @@ -41,16 +41,16 @@ - + - + - + all diff --git a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj index 2115eab39820..25aed4b85628 100644 --- a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj +++ b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj @@ -19,8 +19,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index 537df5aab48f..1f03b83bf1cc 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -25,8 +25,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -34,9 +34,9 @@ - - - + + + all diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index bae39162f470..48466f5abc9d 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -66,7 +66,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json index 67ef5f16fe0d..6b605bfbb9a0 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json @@ -1,2336 +1,7 @@ { "name": "acceptancetest", - "lockfileVersion": 2, "requires": true, - "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" - } - } - }, + "lockfileVersion": 1, "dependencies": { "@cypress/request": { "version": "2.88.6", diff --git a/tests/Umbraco.Tests.Integration.SqlCe/Umbraco.Tests.Integration.SqlCe.csproj b/tests/Umbraco.Tests.Integration.SqlCe/Umbraco.Tests.Integration.SqlCe.csproj index b4631e56dc62..256055ce354c 100644 --- a/tests/Umbraco.Tests.Integration.SqlCe/Umbraco.Tests.Integration.SqlCe.csproj +++ b/tests/Umbraco.Tests.Integration.SqlCe/Umbraco.Tests.Integration.SqlCe.csproj @@ -6,10 +6,10 @@ - + - - + + diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index ca6096835aff..c6d77a7627fc 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -10,7 +10,6 @@ - @@ -81,10 +80,11 @@ - + - - + + + diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj index 668741bb87a2..14c1ef689c46 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj @@ -8,7 +8,6 @@ - @@ -20,8 +19,9 @@ - - + + + From eee8bd55651729c6a61d7e126c30b8c99bc4ad58 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 3 Nov 2021 14:42:46 +0100 Subject: [PATCH 45/57] Fixed missing null check in BlockEditorPropertyEditor (#11184) Thanks @lauraneto - reviewed this and your fix solves the issue. #h5yr (cherry picked from commit 23d21062773889916fbe2c14f32158dce282e1f1) # Conflicts: # src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs --- src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs index b2eab4f8ab65..16907edd0bdc 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs @@ -258,7 +258,7 @@ public IEnumerable Validate(object value, string valueType, ob _textService.Localize("validation", "entriesShort", new[] { validationLimit.Min.ToString(), - (validationLimit.Min - blockEditorData.Layout.Count()).ToString() + (validationLimit.Min - (blockEditorData?.Layout.Count() ?? 0)).ToString() }), new[] { "minCount" }); } From f555f0adfe68d06c78ec64b0763359291d0f72e6 Mon Sep 17 00:00:00 2001 From: Jesper Mayntzhusen <79840720+jemayn@users.noreply.github.com> Date: Sun, 10 Oct 2021 18:50:02 +0200 Subject: [PATCH 46/57] add cypress test for deleting a user (#11282) (cherry picked from commit a2f05850c0d9771189861214a6ec35828e359528) # Conflicts: # src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/users.ts --- .../cypress/integration/Users/users.ts | 107 +++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/users.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/users.ts index e122b2156432..12a9bee7a20b 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/users.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/users.ts @@ -32,4 +32,109 @@ context('Users', () => { }); -}); + it('Update user', () => { + // Set userdata + const name = "Alice Bobson"; + const email = "alice-bobson@acceptancetest.umbraco"; + const startContentIds = []; + const startMediaIds = []; + const userGroups = ["admin"]; + + var userData = + { + "id": -1, + "parentId": -1, + "name": name, + "username": email, + "culture": "en-US", + "email": email, + "startContentIds": startContentIds, + "startMediaIds": startMediaIds, + "userGroups": userGroups, + "message": "" + }; + + // Ensure user doesn't exist + cy.umbracoEnsureUserEmailNotExists(email); + + // Create user through API + cy.getCookie('UMB-XSRF-TOKEN', { log: false }).then((token) => { + cy.request({ + method: 'POST', + url: '/umbraco/backoffice/umbracoapi/users/PostCreateUser', + followRedirect: true, + headers: { + Accept: 'application/json', + 'X-UMB-XSRF-TOKEN': token.value, + }, + body: userData, + log: false, + }).then((response) => { + return; + }); + }); + + // Go to the user and edit their name + cy.umbracoSection('users'); + cy.get('.umb-user-card__name').contains(name).click(); + cy.get('#headerName').type('{movetoend}son'); + cy.umbracoButtonByLabelKey('buttons_save').click(); + + // assert save succeeds + cy.umbracoSuccessNotification().should('be.visible'); + cy.umbracoEnsureUserEmailNotExists(email); + }) + + it('Delete user', () => { + // Set userdata + const name = "Alice Bobson"; + const email = "alice-bobson@acceptancetest.umbraco"; + const startContentIds = []; + const startMediaIds = []; + const userGroups = ["admin"]; + + var userData = + { + "id": -1, + "parentId": -1, + "name": name, + "username": email, + "culture": "en-US", + "email": email, + "startContentIds": startContentIds, + "startMediaIds": startMediaIds, + "userGroups": userGroups, + "message": "" + }; + + // Ensure user doesn't exist + cy.umbracoEnsureUserEmailNotExists(email); + + // Create user through API + cy.getCookie('UMB-XSRF-TOKEN', { log: false }).then((token) => { + cy.request({ + method: 'POST', + url: '/umbraco/backoffice/umbracoapi/users/PostCreateUser', + followRedirect: true, + headers: { + Accept: 'application/json', + 'X-UMB-XSRF-TOKEN': token.value, + }, + body: userData, + log: false, + }).then((response) => { + return; + }); + }); + + // Go to the user and delete them + cy.umbracoSection('users'); + cy.get('.umb-user-card__name').contains(name).click(); + cy.umbracoButtonByLabelKey("user_deleteUser").click(); + cy.get('umb-button[label="Yes, delete"]').click(); + + // assert deletion succeeds + cy.umbracoSuccessNotification().should('be.visible'); + cy.umbracoEnsureUserEmailNotExists(email); + }) +}); \ No newline at end of file From 771dfdb48f94ab596b30978d62eb01eef48149f6 Mon Sep 17 00:00:00 2001 From: Jesper Mayntzhusen <79840720+jemayn@users.noreply.github.com> Date: Tue, 5 Oct 2021 02:20:51 +0200 Subject: [PATCH 47/57] Cypress test for textbox max length (#11245) * add test for textbox max length * remove leftover comment Co-authored-by: Jesper (cherry picked from commit f29bda645593e6dc71f877ac1730946cea7afb5c) --- .../integration/DataTypes/dataTypes.ts | 43 +++++++++++++++++++ .../integration/Members/memberGroups.js | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/DataTypes/dataTypes.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/DataTypes/dataTypes.ts index a8c2d8d151c3..b92a7482ec20 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/DataTypes/dataTypes.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/DataTypes/dataTypes.ts @@ -2,6 +2,7 @@ import { AliasHelper, ApprovedColorPickerDataTypeBuilder, + TextBoxDataTypeBuilder, } from 'umbraco-cypress-testhelpers'; context('DataTypes', () => { @@ -62,6 +63,48 @@ context('DataTypes', () => { cy.umbracoEnsureTemplateNameNotExists(name); }); + it('Tests Textbox Maxlength', () => { + cy.deleteAllContent(); + const name = 'Textbox Maxlength Test'; + const alias = AliasHelper.toAlias(name); + + cy.umbracoEnsureDocumentTypeNameNotExists(name); + cy.umbracoEnsureDataTypeNameNotExists(name); + + const textBoxDataType = new TextBoxDataTypeBuilder() + .withName(name) + .withMaxChars(10) + .build() + + cy.umbracoCreateDocTypeWithContent(name, alias, textBoxDataType); + + // Act + // Enter content + // Assert no helptext with (max-2) chars & can save + cy.umbracoRefreshContentTree(); + cy.umbracoTreeItem("content", [name]).click(); + cy.get('input[name="textbox"]').type('12345678'); + cy.get('localize[key="textbox_characters_left"]').should('not.exist'); + cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); + cy.umbracoSuccessNotification().should('be.visible'); + cy.get('.property-error').should('not.be.visible'); + + // Add char and assert helptext appears - no publish to save time & has been asserted above & below + cy.get('input[name="textbox"]').type('9'); + cy.get('localize[key="textbox_characters_left"]').contains('characters left').should('exist'); + cy.get('.property-error').should('not.be.visible'); + + // Add char and assert errortext appears and can't save + cy.get('input[name="textbox"]').type('10'); // 1 char over max + cy.get('localize[key="textbox_characters_exceed"]').contains('too many').should('exist'); + cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); + cy.get('.property-error').should('be.visible'); + + // Clean + cy.umbracoEnsureDataTypeNameNotExists(name); + cy.umbracoEnsureDocumentTypeNameNotExists(name); + }) + // it('Tests Checkbox List', () => { // const name = 'CheckBox List'; // const alias = AliasHelper.toAlias(name); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js index be9b93134d01..6add16b4ee68 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js @@ -1,4 +1,4 @@ -context('User Groups', () => { +context('Member Groups', () => { beforeEach(() => { cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); From ac83a32d34e1590156b46a1694f4b1ae619891bd Mon Sep 17 00:00:00 2001 From: Jesper Mayntzhusen <79840720+jemayn@users.noreply.github.com> Date: Tue, 26 Oct 2021 17:40:37 +0200 Subject: [PATCH 48/57] add delete language test (#11380) (cherry picked from commit ba73a3955807c1cedb5b593cf689170977e1f837) --- .../integration/Languages/languages.ts | 38 +++++++++++ .../cypress/support/commands.js | 68 +++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 src/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts new file mode 100644 index 000000000000..123109816482 --- /dev/null +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts @@ -0,0 +1,38 @@ +/// +context('Languages', () => { + + beforeEach(() => { + cy.umbracoLogin(Cypress.env('username'), Cypress.env('password'), false); + }); + + it('Deletes language', () => { + // Setup + const language1 = 'da'; + const language2 = 'en-GB'; + cy.umbracoEnsureLanguageNotExists(language1); + cy.umbracoEnsureLanguageNotExists(language2); + cy.umbracoCreateLanguage(language1, true, '1'); + cy.umbracoCreateLanguage(language2, true, '1'); + cy.umbracoSection('settings'); + + // Enter language tree and select the language we just created + cy.umbracoTreeItem('settings', ['Languages']).click(); + + // Assert there are 3 languages + cy.get('tbody > tr').should('have.length', 3); + + // Delete the Danish language + cy.get('tr').contains('Danish').parents('tr').within(() => { + cy.get('umb-button[label-key="general_delete"]').click() + }); + cy.umbracoButtonByLabelKey('contentTypeEditor_yesDelete').click(); + + // Assert there is only 2 language + cy.get('tbody > tr').should('have.length', 3); + + // Cleanup + cy.umbracoEnsureLanguageNotExists(language1); + cy.umbracoEnsureLanguageNotExists(language2); + }); + +}); \ No newline at end of file diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/support/commands.js b/src/Umbraco.Tests.AcceptanceTest/cypress/support/commands.js index 5b0be47114e1..34da26d04e5f 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/support/commands.js +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/support/commands.js @@ -26,5 +26,73 @@ import {Command} from 'umbraco-cypress-testhelpers'; import {Chainable} from './chainable'; +import { JsonHelper } from 'umbraco-cypress-testhelpers'; new Chainable(); new Command().registerCypressCommands(); + +Cypress.Commands.add('umbracoCreateLanguage', (culture, isMandatory = false, fallbackLanguageId = 1) => { + + var langData = + { + "culture": culture, + "isMandatory": isMandatory, + "fallbackLanguageId": fallbackLanguageId + }; + + // Create language through API + cy.getCookie('UMB-XSRF-TOKEN', { log: false }).then((token) => { + cy.request({ + method: 'POST', + url: '/umbraco/backoffice/umbracoapi/language/SaveLanguage', + followRedirect: true, + headers: { + Accept: 'application/json', + 'X-UMB-XSRF-TOKEN': token.value, + }, + body: langData, + log: false, + }).then((response) => { + return; + }); + }); +}); + +Cypress.Commands.add('umbracoEnsureLanguageNotExists', (culture) => { + cy.getCookie('UMB-XSRF-TOKEN', { log: false }).then((token) => { + console.log('hit commands') + cy.request({ + method: 'GET', + url: '/umbraco/backoffice/umbracoapi/language/GetAllLanguages', + followRedirect: true, + headers: { + Accept: 'application/json', + 'X-UMB-XSRF-TOKEN': token.value, + }, + log: false, + }).then((response) => { + const searchBody = JsonHelper.getBody(response); + if (searchBody.length > 0) { + let languageId = null; + for (const sb of searchBody) { + if (sb.culture === culture) { + languageId = sb.id; + } + } + + if (languageId !== null) { + cy.request({ + method: 'POST', + url: '/umbraco/backoffice/umbracoapi/language/DeleteLanguage?id=' + languageId, + followRedirect: false, + headers: { + ContentType: 'application/json', + 'X-UMB-XSRF-TOKEN': token.value, + }, + }).then((resp) => { + return; + }); + } + } + }); + }); +}); \ No newline at end of file From 0bd82d33aab8612bfc74a46c1c12bd24d0d390a1 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Tue, 26 Oct 2021 16:03:19 +0200 Subject: [PATCH 49/57] v9: Update cypress to newest version and fix errors (#11486) * fixed flaky macro test and updated cypress version * fixed flaky macro test and updated cypress version * Update package.json * Updated cypress to 8.6 and fixed failing script test * Updated cypress to 8.6 and fixed failing script test * Update package-lock.json * Fixed Scripts and stylesheet tests with proper waits instead of magic numbers * Updated to newest version of cypress and fixed template test * Remove duplicate click * Revert version to 8.4.1 * changed template Co-authored-by: Nikolaj Geisle (cherry picked from commit e9ae56761797e1ea35e492e2918dc11959f1da53) # Conflicts: # src/Umbraco.Tests.AcceptanceTest/package.json # tests/Umbraco.Tests.AcceptanceTest/package-lock.json --- .../cypress/integration/Settings/scripts.ts | 5 + .../integration/Settings/stylesheets.ts | 4 +- .../cypress/integration/Settings/templates.ts | 2 +- src/Umbraco.Tests.AcceptanceTest/package.json | 9 +- .../package-lock.json | 1748 +++++++++++++++++ 5 files changed, 1763 insertions(+), 5 deletions(-) create mode 100644 tests/Umbraco.Tests.AcceptanceTest/package-lock.json diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts index 430f8aa10877..3d7bd9dfca98 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts @@ -24,6 +24,9 @@ context('Scripts', () => { cy.umbracoContextMenuAction("action-create").click(); cy.get('.menu-label').first().click(); // TODO: Fucked we cant use something like cy.umbracoContextMenuAction("action-mediaType").click(); + //We have to wait here till everything is loaded, or worker will throw error + cy.intercept('/umbraco/lib/ace-builds/src-min-noconflict/worker-javascript.js').as('aceWorker'); + cy.wait('@aceWorker'); //Type name cy.umbracoEditorHeaderName(name); @@ -33,6 +36,8 @@ context('Scripts', () => { //Assert cy.umbracoSuccessNotification().should('be.visible'); + + cy.umbracoScriptExists(fileName).should('be.true'); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/stylesheets.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/stylesheets.ts index 2a1286c49931..0ebb733bc1ec 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/stylesheets.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/stylesheets.ts @@ -18,7 +18,9 @@ context('Stylesheets', () => { cy.umbracoContextMenuAction("action-create").click(); cy.get('.menu-label').first().click(); // TODO: Fucked we cant use something like cy.umbracoContextMenuAction("action-mediaType").click(); - + //We have to wait here till everything is loaded, or worker will throw error + cy.intercept('/umbraco/lib/ace-builds/src-min-noconflict/worker-css.js').as('aceWorker'); + cy.wait('@aceWorker'); //Type name cy.umbracoEditorHeaderName(name); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts index 65d03e5a7841..2978d0b0dbe9 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts @@ -136,7 +136,7 @@ context('Templates', () => { // Insert macro cy.umbracoButtonByLabelKey('general_insert').click(); cy.get('.umb-insert-code-box__title').contains('Macro').click(); - cy.get('.umb-card-grid-item').contains(name).click(); + cy.get(`.umb-card-grid-item[title='${name}']`).click('bottom'); // Assert cy.get('.ace_content').contains('@Umbraco.RenderMacro("' + name + '")').should('exist'); diff --git a/src/Umbraco.Tests.AcceptanceTest/package.json b/src/Umbraco.Tests.AcceptanceTest/package.json index 05c8c5111430..fd401c72c0cd 100644 --- a/src/Umbraco.Tests.AcceptanceTest/package.json +++ b/src/Umbraco.Tests.AcceptanceTest/package.json @@ -1,4 +1,6 @@ { + "name": "acceptancetest", + "private": true, "scripts": { "postinstall": "node postinstall.js", "config": "node config.js", @@ -7,10 +9,11 @@ }, "devDependencies": { "cross-env": "^7.0.2", - "cypress": "^6.8.0", + "cypress": "8.4.1", + "del": "^6.0.0", "ncp": "^2.0.0", - "prompt": "^1.0.0", - "umbraco-cypress-testhelpers": "^1.0.0-beta-57" + "prompt": "^1.2.0", + "umbraco-cypress-testhelpers": "^1.0.0-beta-58" }, "dependencies": { "typescript": "^3.9.2" diff --git a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json new file mode 100644 index 000000000000..2eb6cfa471fc --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json @@ -0,0 +1,1748 @@ +{ + "name": "acceptancetest", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@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, + "requires": { + "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" + } + }, + "@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, + "requires": { + "debug": "^3.1.0", + "lodash.once": "^4.1.1" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "@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, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@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 + }, + "@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, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@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 + }, + "@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 + }, + "@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 + }, + "@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, + "requires": { + "@types/node": "*" + } + }, + "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, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "dependencies": { + "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 + } + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "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 + }, + "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, + "requires": { + "type-fest": "^0.21.3" + } + }, + "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 + }, + "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, + "requires": { + "color-convert": "^2.0.1" + } + }, + "arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true + }, + "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 + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "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 + }, + "async": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.1.tgz", + "integrity": "sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "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 + }, + "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 + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "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 + }, + "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, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "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 + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "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, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "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 + }, + "cachedir": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", + "dev": true + }, + "camelize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "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, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "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 + }, + "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 + }, + "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 + }, + "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, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "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, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + } + }, + "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, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, + "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, + "requires": { + "color-name": "~1.1.4" + } + }, + "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 + }, + "colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, + "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, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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, + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "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, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", + "dev": true + }, + "cypress": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-8.4.1.tgz", + "integrity": "sha512-itJXq0Vx3sXCUrDyBi2IUrkxVu/gTTp1VhjB5tzGgkeCR8Ae+/T8WV63rsZ7fS8Tpq7LPPXiyoM/sEdOX7cR6A==", + "dev": true, + "requires": { + "@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", + "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" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "dayjs": { + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz", + "integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig==", + "dev": true + }, + "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, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "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 + } + } + }, + "del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "dev": true, + "requires": { + "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" + } + }, + "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 + }, + "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, + "requires": { + "path-type": "^4.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "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 + }, + "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, + "requires": { + "once": "^1.4.0" + } + }, + "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, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "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 + }, + "eventemitter2": { + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.5.tgz", + "integrity": "sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw==", + "dev": true + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "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" + } + }, + "executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "requires": { + "pify": "^2.2.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "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, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", + "dev": true + }, + "faker": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz", + "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=", + "dev": true + }, + "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 + }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "requires": { + "@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" + } + }, + "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 + }, + "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, + "requires": { + "reusify": "^1.0.4" + } + }, + "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, + "requires": { + "pend": "~1.2.0" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "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, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "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, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "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, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "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, + "requires": { + "pump": "^3.0.0" + } + }, + "getos": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", + "dev": true, + "requires": { + "async": "^3.2.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "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" + } + }, + "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, + "requires": { + "is-glob": "^4.0.1" + } + }, + "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, + "requires": { + "ini": "2.0.0" + } + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "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" + } + }, + "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 + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "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 + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "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 + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true + }, + "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, + "requires": { + "ci-info": "^3.2.0" + } + }, + "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 + }, + "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 + }, + "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, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "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, + "requires": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + } + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "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 + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "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 + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "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 + }, + "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 + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", + "dev": true + }, + "listr2": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.13.1.tgz", + "integrity": "sha512-pk4YBDA2cxtpM8iLHbz6oEsfZieJKHf6Pt19NlKaHZZVpqHyVs/Wqr7RfBBCeAFCJchGO7WQHVkUPZTvJMHk8w==", + "dev": true, + "requires": { + "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" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "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 + }, + "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, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "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, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "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, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "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, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "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 + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "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 + }, + "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, + "requires": { + "mime-db": "1.50.0" + } + }, + "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 + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "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 + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "dev": true + }, + "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, + "requires": { + "path-key": "^3.0.0" + } + }, + "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 + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "ospath": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", + "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=", + "dev": true + }, + "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, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "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 + }, + "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 + }, + "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 + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "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 + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "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 + }, + "prompt": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.2.0.tgz", + "integrity": "sha512-iGerYRpRUg5ZyC+FJ/25G5PUKuWAGRjW1uOlhX7Pi3O5YygdK6R+KEaBjRbHSkU5vfS5PZCltSPZdDtUYwRCZA==", + "dev": true, + "requires": { + "async": "~0.9.0", + "colors": "^1.1.2", + "read": "1.0.x", + "revalidator": "0.1.x", + "winston": "2.x" + }, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + } + } + }, + "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 + }, + "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, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "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 + }, + "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 + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "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 + }, + "ramda": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", + "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", + "dev": true + }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "dev": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "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, + "requires": { + "throttleit": "^1.0.0" + } + }, + "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, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "revalidator": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", + "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "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, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "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 + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "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, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "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 + }, + "signal-exit": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "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, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "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" + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, + "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, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "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, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "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 + }, + "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, + "requires": { + "has-flag": "^4.0.0" + } + }, + "throttleit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "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, + "requires": { + "is-number": "^7.0.0" + } + }, + "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, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "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 + }, + "typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "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==", + "dev": true, + "requires": { + "camelize": "^1.0.0", + "faker": "^4.1.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "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 + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "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, + "requires": { + "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" + }, + "dependencies": { + "async": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", + "dev": true + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true + } + } + }, + "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, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} From a3797aaeefc14dd6cb76b19227af1e39d803b915 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Tue, 26 Oct 2021 15:01:14 +0200 Subject: [PATCH 50/57] v9: Fixing flaky cypress test "Macro in Grid" (#11485) * fixed flaky macro test and updated cypress version * fixed flaky macro test and updated cypress version * Update package.json * Fixed failing tabs test * Amend magic numbers with actual wait * Update tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts * Updated UI to click on button instead of icon * Make macro test click bottom to ensure we're not clicking on icon Co-authored-by: Nikolaj Geisle (cherry picked from commit 34be464cf8987d7afa8586c634b5f0f460552d72) # Conflicts: # tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Tabs/tabs.ts --- .../cypress/integration/Content/content.ts | 3 +- .../macropicker/macropicker.html | 4 +- .../cypress/integration/Tabs/tabs.ts | 552 ++++++++++++++++++ 3 files changed, 556 insertions(+), 3 deletions(-) create mode 100644 tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Tabs/tabs.ts diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts index 25d0b02e1dd6..b367ea124b95 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts @@ -766,7 +766,8 @@ context('Content', () => { // Click macro cy.get(':nth-child(4) > .umb-card-grid-item > :nth-child(1)').click(); // Select the macro - cy.get('.umb-card-grid-item').contains(macroName).click(); + cy.get(`.umb-card-grid-item[title='${macroName}']`).click('bottom'); + // Save and publish cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html index 49d40efffaa3..0e936e52c427 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html @@ -28,12 +28,12 @@

    • - +
    diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Tabs/tabs.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Tabs/tabs.ts new file mode 100644 index 000000000000..196a104518f7 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Tabs/tabs.ts @@ -0,0 +1,552 @@ +/// +import { + DocumentTypeBuilder, + AliasHelper + } from 'umbraco-cypress-testhelpers'; + + const tabsDocTypeName = 'Tabs Test Document'; + const tabsDocTypeAlias = AliasHelper.toAlias(tabsDocTypeName); + + context('Tabs', () => { + + beforeEach(() => { + cy.umbracoLogin(Cypress.env('username'), Cypress.env('password'), false); + }); + + afterEach(() => { + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName) + }); + + function OpenDocTypeFolder(){ + cy.umbracoSection('settings'); + cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); + cy.get('.umb-tree-item__inner > .umb-tree-item__arrow').eq(0).click(); + cy.get('.umb-tree-item__inner > .umb-tree-item__label').contains(tabsDocTypeName).click(); + } + + function CreateDocWithTabAndNavigate(){ + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .done() + .done() + .done() + .build(); + cy.saveDocumentType(tabsDocType); + OpenDocTypeFolder(); + } + + it('Create tab', () => { + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); + cy.deleteAllContent(); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addGroup() + .withName('Tabs1Group') + .addUrlPickerProperty() + .withAlias('picker') + .done() + .done() + .build(); + cy.saveDocumentType(tabsDocType); + OpenDocTypeFolder(); + //Create a tab + cy.get('.umb-group-builder__tabs__add-tab').click(); + cy.get('ng-form.ng-invalid > .umb-group-builder__group-title-input').type('Tab 1'); + //Create a 2nd tab manually + cy.get('.umb-group-builder__tabs__add-tab').click(); + cy.get('ng-form.ng-invalid > .umb-group-builder__group-title-input').type('Tab 2'); + //Create a textstring property + cy.get('[aria-hidden="false"] > .umb-box-content > .umb-group-builder__group-add-property').click(); + cy.get('.editor-label').type('property name'); + cy.get('[data-element="editor-add"]').click(); + + //Search for textstring + cy.get('#datatype-search').type('Textstring'); + + // Choose first item + cy.get('[title="Textstring"]').closest("li").click(); + + // Save property + cy.get('.btn-success').last().click(); + cy.umbracoButtonByLabelKey('buttons_save').click(); + //Assert + cy.umbracoSuccessNotification().should('be.visible'); + cy.get('[title="tab1"]').should('be.visible'); + cy.get('[title="tab2"]').should('be.visible'); + }); + + it('Delete tabs', () => { + CreateDocWithTabAndNavigate(); + //Check if tab is there, else if it wasnt created, this test would always pass + cy.get('[title="aTab 1"]').should('be.visible'); + //Delete a tab + cy.get('.btn-reset > [icon="icon-trash"]').first().click(); + cy.get('.umb-button > .btn').last().click(); + cy.umbracoButtonByLabelKey('buttons_save').click(); + //Assert + cy.get('[title="aTab 1"]').should('not.exist'); + //Clean + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); + }); + + it('Delete property in tab', () => { + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .done() + .addContentPickerProperty() + .withAlias('picker') + .done() + .done() + .done() + .build(); + cy.saveDocumentType(tabsDocType); + OpenDocTypeFolder(); + cy.get('[aria-label="Delete property"]').last().click(); + cy.umbracoButtonByLabelKey('actions_delete').click(); + cy.umbracoButtonByLabelKey('buttons_save').click() + //Assert + cy.umbracoSuccessNotification().should('be.visible'); + cy.get('[title=urlPicker]').should('be.visible'); + cy.get('[title=picker]').should('not.exist'); + }); + + it('Delete group in tab', () => { + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .done() + .done() + .addGroup() + .withName('Content Picker Group') + .addContentPickerProperty() + .withAlias('picker') + .done() + .done() + .done() + .build(); + cy.saveDocumentType(tabsDocType); + OpenDocTypeFolder(); + //Delete group + cy.get('.umb-group-builder__group-remove > [icon="icon-trash"]').eq(1).click(); + cy.umbracoButtonByLabelKey('actions_delete').click(); + cy.umbracoButtonByLabelKey('buttons_save').click() + //Assert + cy.umbracoSuccessNotification().should('be.visible'); + cy.get('[title=picker]').should('be.visible'); + cy.get('[title=urlPicker]').should('not.exist'); + }); + + it('Reorders tab', () => { + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group 1') + .addUrlPickerProperty() + .withLabel('Url picker 1') + .withAlias("urlPicker") + .done() + .done() + .done() + .addTab() + .withName('Tab 2') + .addGroup() + .withName('Tab group 2') + .addUrlPickerProperty() + .withLabel('Url picker 2') + .withAlias("pickerTab 2") + .done() + .done() + .done() + .addTab() + .withName('Tab 3') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withLabel('Url picker 3') + .withAlias('pickerTab3') + .done() + .done() + .done() + .build(); + cy.saveDocumentType(tabsDocType); + OpenDocTypeFolder(); + //Check if there are any tabs + cy.get('ng-form.ng-valid-required > .umb-group-builder__group-title-input').should('be.visible'); + cy.get('[alias="reorder"]').click(); + //Type order in + cy.get('.umb-group-builder__tab-sort-order > .umb-property-editor-tiny').first().type('3'); + cy.get('[alias="reorder"]').click(); + //Assert + cy.get('.umb-group-builder__group-title-input').eq(0).invoke('attr', 'title').should('eq', 'aTab 2') + cy.get('.umb-group-builder__group-title-input').eq(1).invoke('attr', 'title').should('eq', 'aTab 3') + cy.get('.umb-group-builder__group-title-input').eq(2).invoke('attr', 'title').should('eq', 'aTab 1') + }); + + it('Reorders groups in a tab', () => { + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group 1') + .addUrlPickerProperty() + .withLabel('Url picker 1') + .withAlias("urlPicker") + .done() + .done() + .addGroup() + .withName('Tab group 2') + .addUrlPickerProperty() + .withLabel('Url picker 2') + .withAlias('urlPickerTwo') + .done() + .done() + .done() + .build(); + cy.saveDocumentType(tabsDocType); + OpenDocTypeFolder(); + cy.get('[alias="reorder"]').click(); + cy.get('.umb-property-editor-tiny').eq(2).type('1'); + + cy.get('[alias="reorder"]').click(); + cy.umbracoButtonByLabelKey('buttons_save').click(); + //Assert + cy.umbracoSuccessNotification().should('be.visible'); + cy.get('.umb-group-builder__group-title-input').eq(2) + .invoke('attr', 'title').should('eq', 'aTab 1/aTab group 2'); + }); + + it('Reorders properties in a tab', () => { + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withLabel('PickerOne') + .withAlias("urlPicker") + .done() + .addUrlPickerProperty() + .withLabel('PickerTwo') + .withAlias('urlPickerTwo') + .done() + .done() + .done() + .build(); + cy.saveDocumentType(tabsDocType); + OpenDocTypeFolder(); + //Reorder + cy.get('[alias="reorder"]').click(); + cy.get('.umb-group-builder__group-sort-value').first().type('2'); + cy.get('[alias="reorder"]').click(); + cy.umbracoButtonByLabelKey('buttons_save').click(); + //Assert + cy.umbracoSuccessNotification().should('be.visible'); + cy.get('.umb-locked-field__input').last().invoke('attr', 'title').should('eq', 'urlPicker'); + }); + + it('Tab name cannot be empty', () => { + CreateDocWithTabAndNavigate(); + cy.get('.umb-group-builder__group-title-input').first().clear(); + cy.umbracoButtonByLabelKey('buttons_save').click(); + //Assert + cy.umbracoErrorNotification().should('be.visible'); + }); + + it('Two tabs cannot have the same name', () => { + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .done() + .done() + .done() + .build(); + cy.saveDocumentType(tabsDocType); + OpenDocTypeFolder(); + //Create a 2nd tab manually + cy.get('.umb-group-builder__tabs__add-tab').click(); + cy.get('ng-form.ng-invalid > .umb-group-builder__group-title-input').type('Tab 1'); + cy.umbracoButtonByLabelKey('buttons_save').click(); + //Assert + cy.umbracoErrorNotification().should('be.visible'); + }); + + it('Group name cannot be empty', () => { + CreateDocWithTabAndNavigate(); + cy.get('.clearfix > .-placeholder').click(); + cy.umbracoButtonByLabelKey('buttons_save').click(); + //Assert + cy.umbracoErrorNotification().should('be.visible'); + }); + + it('Group name cannot have the same name', () => { + CreateDocWithTabAndNavigate(); + cy.get('.clearfix > .-placeholder').click(); + cy.get('.umb-group-builder__group-title-input').last().type('Tab group'); + cy.umbracoButtonByLabelKey('buttons_save').click(); + //Assert + cy.umbracoErrorNotification().should('be.visible'); + }); + + it('Drag a group into another tab', () => { + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .done() + .done() + .done() + .addTab() + .withName('Tab 2') + .addGroup() + .withName('Tab group tab 2') + .addUrlPickerProperty() + .withAlias('urlPickerTabTwo') + .done() + .done() + .addGroup() + .withName('Tab group 2') + .addUrlPickerProperty() + .withAlias('urlPickerTwo') + .done() + .done() + .done() + .build(); + cy.saveDocumentType(tabsDocType); + OpenDocTypeFolder(); + cy.get('[alias="reorder"]').click(); + cy.get('body') + .then(($body) => { + while($body.find('.umb-group-builder__tabs-overflow--right > .caret').hasClass('active')){ + cy.click(); + } + }); + cy.get('.umb-group-builder__tab').last().click(); + cy.get('.umb-group-builder__group-title-icon').last().trigger('mousedown', { which: 1 }) + cy.get('.umb-group-builder__tab').eq(1).trigger('mousemove', {which: 1, force: true}); + cy.get('.umb-group-builder__tab').eq(1).should('have.class', 'is-active').trigger('mouseup', {force:true}); + cy.umbracoButtonByLabelKey('buttons_save').click(); + //Assert + cy.umbracoSuccessNotification().should('be.visible'); + cy.get('[title="aTab 1/aTab group 2"]').should('be.visible'); + }); + + it('Drag and drop reorders a tab', () => { + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .done() + .done() + .done() + .addTab() + .withName('Tab 2') + .addGroup() + .withName('Tab group tab 2') + .addUrlPickerProperty() + .withAlias('urlPickerTabTwo') + .done() + .done() + .addGroup() + .withName('Tab group 2') + .addUrlPickerProperty() + .withAlias('urlPickerTwo') + .done() + .done() + .done() + .build(); + cy.saveDocumentType(tabsDocType); + OpenDocTypeFolder(); + cy.get('[alias="reorder"]').click(); + //Scroll right so we can see tab 2 + cy.get('body') + .then(($body) => { + while($body.find('.umb-group-builder__tabs-overflow--right > .caret').hasClass('active')){ + cy.click(); + } + }); + cy.get('.umb-group-builder__tab-title-icon').eq(1).trigger('mousedown', { which: 1 }) + cy.get('.umb-group-builder__tab').eq(1).trigger('mousemove', {which: 1, force: true}); + cy.get('.umb-group-builder__tab').eq(1).should('have.class', 'is-active').trigger('mouseup', {force:true}); + cy.get('[alias="reorder"]').click(); + cy.umbracoButtonByLabelKey('buttons_save').click(); + //Assert + cy.umbracoSuccessNotification().should('be.visible'); + cy.get('[title="aTab 2"]').should('be.visible'); + }); + + it('Drags and drops a property in a tab', () => { + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .withLabel('UrlPickerOne') + .done() + .done() + .done() + .addTab() + .withName('Tab 2') + .addGroup() + .withName('Tab group tab 2') + .addUrlPickerProperty() + .withAlias('urlPickerTabTwo') + .withLabel('UrlPickerTabTwo') + .done() + .addUrlPickerProperty() + .withAlias('urlPickerTwo') + .withLabel('UrlPickerTwo') + .done() + .done() + .done() + .build(); + cy.saveDocumentType(tabsDocType); + OpenDocTypeFolder(); + cy.get('[alias="reorder"]').click(); + //Scroll so we are sure we see tab 2 + cy.get('body') + .then(($body) => { + while($body.find('.umb-group-builder__tabs-overflow--right > .caret').hasClass('active')){ + cy.click(); + } + }); + //Navigate to tab 2 + cy.get('.umb-group-builder__tab').last().click(); + cy.get('.umb-group-builder__property-meta > .flex > .icon').eq(1).trigger('mousedown', {which: 1}) + cy.get('.umb-group-builder__tab').eq(1).trigger('mousemove', {which: 1, force: true}); + cy.get('.umb-group-builder__tab').eq(1).should('have.class', 'is-active'); + cy.get('.umb-group-builder__property') + .first().trigger('mousemove', {which: 1, force: true}).trigger('mouseup', {which: 1, force:true}); + cy.get('[alias="reorder"]').click(); + cy.umbracoButtonByLabelKey('buttons_save').click(); + //Assert + cy.umbracoSuccessNotification().should('be.visible'); + cy.get('[title="urlPickerTabTwo"]').should('be.visible'); + }); + + it('Drags and drops a group and converts to tab', () => { + cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .withLabel('UrlPickerOne') + .done() + .done() + .addGroup() + .withName('Tab group 2') + .addUrlPickerProperty() + .withAlias('urlPickerTwo') + .withLabel('UrlPickerTwo') + .done() + .done() + .done() + .addTab() + .withName('Tab 2') + .addGroup() + .withName('Tab group tab 2') + .addUrlPickerProperty() + .withAlias('urlPickerTabTwo') + .withLabel('UrlPickerTabTwo') + .done() + .done() + .done() + .build(); + cy.saveDocumentType(tabsDocType); + OpenDocTypeFolder(); + cy.get('[alias="reorder"]').click(); + cy.get('.umb-group-builder__group-title-icon').eq(1).trigger('mousedown', {which: 1}) + cy.get('.umb-group-builder__convert-dropzone').trigger('mousemove', {which: 1, force: true}); + cy.get('.umb-group-builder__convert-dropzone').trigger('mouseup', {which: 1, force:true}); + cy.umbracoButtonByLabelKey('buttons_save').click(); + //Assert + cy.umbracoSuccessNotification().should('be.visible'); + cy.get('[title="tabGroup"]').should('be.visible'); + }); + }); From 64dc1d4862e21bcdc7a66654708227b33064d65a Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 3 Nov 2021 15:57:41 +0100 Subject: [PATCH 51/57] Gets all of the remaining changes to Cypress tests from v9 dev - all tests pass on v8 --- src/Umbraco.Tests.AcceptanceTest/cypress.json | 6 +++++- .../cypress/integration/Settings/documentTypes.ts | 2 +- .../cypress/integration/Settings/mediaTypes.ts | 2 +- .../cypress/integration/Settings/memberTypes.ts | 2 +- .../integration/{Tour => Tours}/backofficeTour.ts | 0 .../cypress/integration/Users/userGroups.js | 2 +- .../cypress/plugins/index.js | 9 +++++++++ src/Umbraco.Tests.AcceptanceTest/package.json | 4 ++-- 8 files changed, 20 insertions(+), 7 deletions(-) rename src/Umbraco.Tests.AcceptanceTest/cypress/integration/{Tour => Tours}/backofficeTour.ts (100%) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress.json b/src/Umbraco.Tests.AcceptanceTest/cypress.json index 33978211edca..340eede2a03d 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress.json +++ b/src/Umbraco.Tests.AcceptanceTest/cypress.json @@ -7,5 +7,9 @@ "password": "" }, "supportFile": "cypress/support/index.ts", - "videoUploadOnPasses" : false + "videoUploadOnPasses" : false, + "retries": { + "runMode": 5, + "openMode": 1 + } } diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts index 1a86e908528c..fa4c6ec9a3cc 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts @@ -35,7 +35,7 @@ context('Document Types', () => { cy.get('#datatype-search').type('Textstring'); // Choose first item - cy.get('ul.umb-card-grid li [title="Textstring"]').closest("li").click(); + cy.get('ul.umb-card-grid [title="Textstring"]').closest("li").click(); // Save property cy.get('.btn-success').last().click(); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/mediaTypes.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/mediaTypes.ts index 4064c1f41e87..385a70bd5b3b 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/mediaTypes.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/mediaTypes.ts @@ -34,7 +34,7 @@ context('Media Types', () => { cy.get('#datatype-search').type('Textstring'); // Choose first item - cy.get('ul.umb-card-grid li [title="Textstring"]').closest("li").click(); + cy.get('ul.umb-card-grid [title="Textstring"]').closest("li").click(); // Save property cy.get('.btn-success').last().click(); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/memberTypes.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/memberTypes.ts index e84c29c0d8b1..4dcc80432e36 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/memberTypes.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/memberTypes.ts @@ -32,7 +32,7 @@ context('Member Types', () => { cy.get('#datatype-search').type('Textstring'); // Choose first item - cy.get('ul.umb-card-grid li [title="Textstring"]').closest("li").click(); + cy.get('ul.umb-card-grid [title="Textstring"]').closest("li").click(); // Save property cy.get('.btn-success').last().click(); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tour/backofficeTour.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tours/backofficeTour.ts similarity index 100% rename from src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tour/backofficeTour.ts rename to src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tours/backofficeTour.ts diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/userGroups.js b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/userGroups.js index afef0e7701f7..ce2e366f2c66 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/userGroups.js +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/userGroups.js @@ -19,7 +19,7 @@ context('User Groups', () => { // Assign sections cy.get('.umb-box:nth-child(1) .umb-property:nth-child(1) localize').click(); - cy.get('.umb-tree-item span').click({multiple:true}); + cy.get('.umb-tree-item__inner').click({multiple:true, timeout: 10000}); cy.get('.btn-success').last().click(); // Save diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/plugins/index.js b/src/Umbraco.Tests.AcceptanceTest/cypress/plugins/index.js index 59283feec5c2..51b79a1fef46 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/plugins/index.js +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/plugins/index.js @@ -11,6 +11,7 @@ // This function is called when a project is opened or re-opened (e.g. due to // the project's config changing) +const del = require('del') /** * @type {Cypress.PluginConfig} @@ -24,5 +25,13 @@ module.exports = (on, config) => { config.baseUrl = baseUrl; } + on('after:spec', (spec, results) => { + if(results.stats.failures === 0 && results.video) { + // `del()` returns a promise, so it's important to return it to ensure + // deleting the video is finished before moving + return del(results.video) + } + }) + return config; } diff --git a/src/Umbraco.Tests.AcceptanceTest/package.json b/src/Umbraco.Tests.AcceptanceTest/package.json index fd401c72c0cd..3b9b2864e1c9 100644 --- a/src/Umbraco.Tests.AcceptanceTest/package.json +++ b/src/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-60" }, "dependencies": { "typescript": "^3.9.2" From f39909dd90abb0d2598f7111ef4475814a84b257 Mon Sep 17 00:00:00 2001 From: Patrick de Mooij Date: Tue, 5 Oct 2021 22:20:27 +0200 Subject: [PATCH 52/57] 8258: Added create dictionary item button (cherry picked from commit 49132e2b7f64292ac9049b2348ee8e7e4245e794) # Conflicts: # src/Umbraco.Web.UI/Umbraco/config/lang/en.xml # src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml --- .../dictionary/dictionary.list.controller.js | 14 ++- .../src/views/dictionary/list.html | 88 +++++++++++-------- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + .../Umbraco/config/lang/en_us.xml | 1 + 4 files changed, 64 insertions(+), 40 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.list.controller.js b/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.list.controller.js index e55dfd44a106..669a0d518336 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.list.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dictionary/dictionary.list.controller.js @@ -1,4 +1,4 @@ -/** +/** * @ngdoc controller * @name Umbraco.Editors.Dictionary.ListController * @function @@ -6,7 +6,7 @@ * @description * The controller for listting dictionary items */ -function DictionaryListController($scope, $location, dictionaryResource, localizationService, appState) { +function DictionaryListController($scope, $location, dictionaryResource, localizationService, appState, navigationService) { var vm = this; vm.title = "Dictionary overview"; vm.loading = false; @@ -31,7 +31,17 @@ function DictionaryListController($scope, $location, dictionaryResource, localiz $location.path("/" + currentSection + "/dictionary/edit/" + id); } + function createNewItem() { + var rootNode = appState.getTreeState("currentRootNode").root; + //We need to load the menu first before we can access the menu actions. + navigationService.showMenu({ node: rootNode }).then(function () { + const action = appState.getMenuState("menuActions").find(item => item.alias === "create"); + navigationService.executeMenuAction(action, rootNode, appState.getSectionState("currentSection")); + }); + } + vm.clickItem = clickItem; + vm.createNewItem = createNewItem; function onInit() { localizationService.localize("dictionaryItem_overviewTitle").then(function (value) { diff --git a/src/Umbraco.Web.UI.Client/src/views/dictionary/list.html b/src/Umbraco.Web.UI.Client/src/views/dictionary/list.html index 928aba0607d6..14c7bb4c5cb5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dictionary/list.html +++ b/src/Umbraco.Web.UI.Client/src/views/dictionary/list.html @@ -1,4 +1,4 @@ -
    +
    @@ -13,43 +13,55 @@ - - - - - There are no dictionary items. - - - - - - - - - - - - - - - - - - - -
    Name{{column.displayName}}
    - - - -
    + + + + + + + + + + + + + + There are no dictionary items. + + + + + + + + + + + + + + + + + + + +
    Name{{column.displayName}}
    + + + +
    diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 0a2edb727311..a87910a40d5b 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -526,6 +526,7 @@ There are no dictionary items. + Create dictionary item There are no dictionary items. + Create dictionary item Date: Wed, 6 Oct 2021 22:15:28 +0100 Subject: [PATCH 53/57] Link insertion with no selected text TinyMCE requires text to be selected in order to insert links. Added checks for if there is a text selection, and insert HTML content if there is not. Link contents consists of the target name, or the target url if not populated (cherry picked from commit e7f8e692c2397f268e864524b892b65acff3d29a) --- .../src/common/services/tinymce.service.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index 0e176155af1e..ea9ae7238097 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -1273,11 +1273,22 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s function insertLink() { if (anchorElm) { editor.dom.setAttribs(anchorElm, createElemAttributes()); - editor.selection.select(anchorElm); editor.execCommand('mceEndTyping'); } else { - editor.execCommand('mceInsertLink', false, createElemAttributes()); + var selectedContent = editor.selection.getContent(); + // If there is no selected content, we can't insert a link + // as TinyMCE needs selected content for this, so instead we + // create a new dom element and insert it, using the chosen + // link name as the content. + if (selectedContent !== "") { + editor.execCommand('mceInsertLink', false, createElemAttributes()); + } else { + // Using the target url as a fallback, as href might be confusing with a local link + var linkContent = typeof target.name !== "undefined" && target.name !== "" ? target.name : target.url + var domElement = editor.dom.createHTML("a", createElemAttributes(), linkContent); + editor.execCommand('mceInsertContent', false, domElement); + } } } From f1668c6bd7d05c62ef40cbfc985e01bc27cdba3e Mon Sep 17 00:00:00 2001 From: Chriztian Steinmeier Date: Sat, 9 Oct 2021 15:59:31 +0200 Subject: [PATCH 54/57] Add positioning context A fallback for browsers that don't support `contain: content`, to make sure the positioning context is correct (cherry picked from commit 8989c4ad1335e1c13ef1d77dc6714e488c8bce76) --- src/Umbraco.Web.UI.Client/src/less/property-editors.less | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 040ec42c0c37..fd699b79d0a9 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -607,6 +607,7 @@ box-sizing: border-box; line-height: 0; contain: content; + position: relative; .checkeredBackground(); &:focus, &:focus-within { From c7b285796df7e0ca554dfd77279e03e022e199a2 Mon Sep 17 00:00:00 2001 From: Matthew Care Date: Mon, 11 Oct 2021 11:06:10 +0100 Subject: [PATCH 55/57] Update background size Update background size property to `contain` so that the whole image shows (cherry picked from commit 215ef3d7b6203a2bd2898161237046a827f0d36b) --- .../src/views/components/blockcard/umb-block-card.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card.less b/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card.less index bfd72c021699..d5823651022d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card.less +++ b/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card.less @@ -58,7 +58,7 @@ umb-block-card { padding-bottom: 10/16*100%; background-color: @gray-12; - background-size: cover; + background-size: contain; background-position: 50% 50%; background-repeat: no-repeat; From 9a6a7ffa8864161c34915140adbd6de4b83999ac Mon Sep 17 00:00:00 2001 From: Paul Seal Date: Sun, 17 Oct 2021 00:40:47 +0100 Subject: [PATCH 56/57] Added more detailed message when deleting items in the list view (#11375) * Added more detailed message when deleting items in the list view * Update FR language file (cherry picked from commit 14fe7d99a82c32f6510b9684279a84a4c2bbc040) # Conflicts: # src/Umbraco.Web.UI/Umbraco/config/lang/en.xml # src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml --- .../views/propertyeditors/listview/listview.controller.js | 6 ++++-- .../src/views/propertyeditors/listview/listview.html | 2 +- .../src/views/propertyeditors/listview/overlays/delete.html | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 1 + src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml | 1 + 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 76e6759a475e..c66ff1a46171 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -380,7 +380,7 @@ function listViewController($scope, $interpolate, $routeParams, $injector, $time }); } - $scope.delete = function () { + $scope.delete = function (numberOfItems, totalItems) { const dialog = { view: "views/propertyeditors/listview/overlays/delete.html", @@ -394,7 +394,9 @@ function listViewController($scope, $interpolate, $routeParams, $injector, $time }, close: function () { overlayService.close(); - } + }, + numberOfItems: numberOfItems, + totalItems: totalItems }; localizationService.localize("general_delete").then(value => { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index 92ad56b045a7..7fad01fe6c2c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -190,7 +190,7 @@ button-style="white" label-key="actions_delete" icon="icon-trash" - action="delete()" + action="delete(selection.length, listViewResultSet.items.length)" disabled="actionInProgress" size="xs" add-ellipsis="true"> diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/delete.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/delete.html index 1c99aa594e42..74b3cce7e52a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/overlays/delete.html @@ -1,7 +1,7 @@

    - ? + ?

    diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index a87910a40d5b..bf497885a20c 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -437,7 +437,7 @@ Manage hostnames Close this window Are you sure you want to delete - Are you sure you want to delete %0% based on %1% + %0% of %1% items]]> Are you sure you want to disable Are you sure you want to remove %0%]]> 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 9fe4dc528cda..36f0ee34855a 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -443,6 +443,7 @@ Name Close this window Are you sure you want to delete + %0% of %1% items]]> Are you sure you want to disable Are you sure you want to remove %0%]]> diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml index 9ac12607ed13..7be6abc04cfe 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml @@ -403,6 +403,7 @@ Nom Fermer cette fenêtre Êtes-vous certain(e) de vouloir supprimer + %0% des %1% éléments]]> Êtes-vous certain(e) de vouloir désactiver Êtes-vous certain(e)? Êtes-vous certain(e)? From b876771a75f681f09f543c1942a1b6bfc0109743 Mon Sep 17 00:00:00 2001 From: Chriztian Steinmeier Date: Sun, 17 Oct 2021 18:02:59 +0200 Subject: [PATCH 57/57] Minor edits to the XPath query help sheet (#11385) * Clarify the use of placeholders Since the placeholder is only replaced if it's at the beginning of the query, I've added a note about it. Also made the spelling of XPath consistent and fixed a nesting error in the HTML. * Rephrase a couple of sentences (cherry picked from commit e04efe6067ae60b44b1ab985ca871ca28e7b19d6) --- .../src/views/prevalueeditors/treesource.html | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html index f56a6c8656ad..0ab66c964efb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html @@ -27,7 +27,7 @@
    - +
    @@ -36,32 +36,37 @@ + placeholder="Enter XPath query">
    • - +

      - Use Xpath query to set a root node on the tree, either based on a search from the root of the content tree, or by using a context-aware placeholder. + Use an XPath query to set a root node on the tree, either based on a search from the root of the content tree, or by using a context-aware placeholder.

      - Placeholders finds the nearest published ID and runs its query from there, so for instance: - -

      $parent/newsArticle
      - - Will try to get the parent if available, but will then fall back to the nearest ancestor and query for all news articles there. + A placeholder finds the nearest published ID and runs its query from there, so for instance: +

      + +
      $parent/newsArticle
      + +

      + Will try to get the parent if available, but will then fall back to the nearest ancestor and query for all news article children there.

      Available placeholders:
      - $current: current page or closest found ancestor
      - $parent: parent page or closest found ancestor
      - $root: root of the content tree
      - $site: Ancestor node at level 1
      + $current: Current page or closest found ancestor
      + $parent: Parent page or closest found ancestor
      + $root: Root of the content tree
      + $site: Ancestor node at level 1
      +

      +

      + Note: The placeholder can only be used at the beginning of the query.