From 79ae829ac78201435b2230381f6c595dd18c03ae Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Mon, 15 May 2023 10:55:43 +0200 Subject: [PATCH 1/7] Set version to rc1 --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 1af6ab23c5..e22da7303c 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "12.0.0-rc", + "version": "12.0.0-rc1", "assemblyVersion": { "precision": "build" }, From e50a3d13409d93e7d30363f0bf9e32108c1ba2a3 Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Mon, 15 May 2023 12:43:13 +0200 Subject: [PATCH 2/7] Trust Test DB certificate --- build/azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index 310477fa4c..af8cdea3a3 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -292,7 +292,7 @@ stages: Linux: vmImage: 'ubuntu-latest' testDb: SqlServer - connectionString: 'Server=localhost,1433;User Id=sa;Password=$(SA_PASSWORD);' + connectionString: 'Server=localhost,1433;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=true' pool: vmImage: $(vmImage) variables: From 27e5b55e0575a92ae8c6b7a6ea84796b70889168 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 15 May 2023 13:24:19 +0200 Subject: [PATCH 3/7] Added OpenIddict dependencies for future usage (#14247) --- src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj | 2 ++ .../Umbraco.Cms.Persistence.EFCore.csproj | 1 + 2 files changed, 3 insertions(+) diff --git a/src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj b/src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj index fd5e7d4b83..5a2dfa69ef 100644 --- a/src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj +++ b/src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj @@ -10,6 +10,8 @@ + + diff --git a/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj b/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj index af566ab67a..3e6e57983f 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj +++ b/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj @@ -9,6 +9,7 @@ + From 0548266d0fa452ad5e5a4eb4b9b1cc7b09008aee Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Mon, 15 May 2023 16:09:36 +0200 Subject: [PATCH 4/7] Check folder exist before trying to delete (#14249) Co-authored-by: Zeegaan --- .../Migrations/Upgrade/V_12_0_0/ResetCache.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs index ce105bf6b8..a3ec96ad7e 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs @@ -22,6 +22,11 @@ public class ResetCache : MigrationBase private void DeleteAllFilesInFolder(string path) { + if (Directory.Exists(path) == false) + { + return; + } + var directoryInfo = new DirectoryInfo(path); foreach (FileInfo file in directoryInfo.GetFiles()) From c632908a516d87d507d51767353a1ce61f2bd2ef Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 16 May 2023 14:28:30 +0200 Subject: [PATCH 5/7] Re-added the postmigration from the migration context (#14255) * Re-added the postmigration from the migration context * Minor cleanup * Add executed migration contexts to the result object --------- Co-authored-by: Nikolaj --- .../Migrations/ExecutedMigrationPlan.cs | 4 ++ .../Migrations/IMigrationContext.cs | 7 +++ .../Migrations/MigrationContext.cs | 15 +++++++ .../Migrations/MigrationPlanExecutor.cs | 44 +++++++++++++++++-- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Infrastructure/Migrations/ExecutedMigrationPlan.cs b/src/Umbraco.Infrastructure/Migrations/ExecutedMigrationPlan.cs index 065a8e049f..09d726fd6a 100644 --- a/src/Umbraco.Infrastructure/Migrations/ExecutedMigrationPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/ExecutedMigrationPlan.cs @@ -24,6 +24,7 @@ public class ExecutedMigrationPlan FinalState = finalState ?? throw new ArgumentNullException(nameof(finalState)); Successful = successful; CompletedTransitions = completedTransitions; + ExecutedMigrationContexts = Array.Empty(); } public ExecutedMigrationPlan() @@ -59,4 +60,7 @@ public class ExecutedMigrationPlan /// A collection of all the succeeded transition. /// public required IReadOnlyList CompletedTransitions { get; init; } + + [Obsolete("This will be removed in the V13, and replaced with UmbracoPlanExecutedNotification")] + internal IReadOnlyList ExecutedMigrationContexts { get; init; } = Array.Empty(); } diff --git a/src/Umbraco.Infrastructure/Migrations/IMigrationContext.cs b/src/Umbraco.Infrastructure/Migrations/IMigrationContext.cs index 0001b3381d..5e0766755a 100644 --- a/src/Umbraco.Infrastructure/Migrations/IMigrationContext.cs +++ b/src/Umbraco.Infrastructure/Migrations/IMigrationContext.cs @@ -37,4 +37,11 @@ public interface IMigrationContext /// Gets or sets a value indicating whether an expression is being built. /// bool BuildingExpression { get; set; } + + /// + /// Adds a post-migration. + /// + [Obsolete("This will be removed in the V13, and replaced with a RebuildCache flag on the MigrationBase")] + void AddPostMigration() + where TMigration : MigrationBase; } diff --git a/src/Umbraco.Infrastructure/Migrations/MigrationContext.cs b/src/Umbraco.Infrastructure/Migrations/MigrationContext.cs index 4b1900c27c..c07adbec90 100644 --- a/src/Umbraco.Infrastructure/Migrations/MigrationContext.cs +++ b/src/Umbraco.Infrastructure/Migrations/MigrationContext.cs @@ -8,6 +8,8 @@ namespace Umbraco.Cms.Infrastructure.Migrations; /// internal class MigrationContext : IMigrationContext { + private readonly List _postMigrations = new(); + /// /// Initializes a new instance of the class. /// @@ -18,6 +20,10 @@ internal class MigrationContext : IMigrationContext Logger = logger ?? throw new ArgumentNullException(nameof(logger)); } + // this is only internally exposed + [Obsolete("This will be removed in the V13, and replaced with a RebuildCache flag on the MigrationBase")] + internal IReadOnlyList PostMigrations => _postMigrations; + /// public ILogger Logger { get; } @@ -34,4 +40,13 @@ internal class MigrationContext : IMigrationContext /// public bool BuildingExpression { get; set; } + + + /// + [Obsolete("This will be removed in the V13, and replaced with a RebuildCache flag on the MigrationBase, and a UmbracoPlanExecutedNotification.")] + public void AddPostMigration() + where TMigration : MigrationBase => + + // just adding - will be de-duplicated when executing + _postMigrations.Add(typeof(TMigration)); } diff --git a/src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs b/src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs index c271088626..faea87bd68 100644 --- a/src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs +++ b/src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs @@ -99,6 +99,8 @@ public class MigrationPlanExecutor : IMigrationPlanExecutor ExecutedMigrationPlan result = RunMigrationPlan(plan, fromState); + HandlePostMigrations(result); + // If any completed migration requires us to rebuild cache we'll do that. if (_rebuildCache) { @@ -108,6 +110,33 @@ public class MigrationPlanExecutor : IMigrationPlanExecutor return result; } + [Obsolete] + private void HandlePostMigrations(ExecutedMigrationPlan result) + { + // prepare and de-duplicate post-migrations, only keeping the 1st occurence + var executedTypes = new HashSet(); + + foreach (var executedMigrationContext in result.ExecutedMigrationContexts) + { + if (executedMigrationContext is MigrationContext migrationContext) + { + foreach (Type migrationContextPostMigration in migrationContext.PostMigrations) + { + if (executedTypes.Contains(migrationContextPostMigration)) + { + continue; + } + + _logger.LogInformation($"PostMigration: {migrationContextPostMigration.FullName}."); + MigrationBase postMigration = _migrationBuilder.Build(migrationContextPostMigration, executedMigrationContext); + postMigration.Run(); + + executedTypes.Add(migrationContextPostMigration); + } + } + } + } + private ExecutedMigrationPlan RunMigrationPlan(MigrationPlan plan, string fromState) { _logger.LogInformation("Starting '{MigrationName}'...", plan.Name); @@ -122,6 +151,7 @@ public class MigrationPlanExecutor : IMigrationPlanExecutor List completedTransitions = new(); + var executedMigrationContexts = new List(); while (transition is not null) { _logger.LogInformation("Execute {MigrationType}", transition.MigrationType.Name); @@ -130,11 +160,11 @@ public class MigrationPlanExecutor : IMigrationPlanExecutor { if (transition.MigrationType.IsAssignableTo(typeof(UnscopedMigrationBase))) { - RunUnscopedMigration(transition.MigrationType, plan); + executedMigrationContexts.Add(RunUnscopedMigration(transition.MigrationType, plan)); } else { - RunScopedMigration(transition.MigrationType, plan); + executedMigrationContexts.Add(RunScopedMigration(transition.MigrationType, plan)); } } catch (Exception exception) @@ -149,6 +179,7 @@ public class MigrationPlanExecutor : IMigrationPlanExecutor FinalState = transition.SourceState, CompletedTransitions = completedTransitions, Plan = plan, + ExecutedMigrationContexts = executedMigrationContexts }; } @@ -197,18 +228,21 @@ public class MigrationPlanExecutor : IMigrationPlanExecutor FinalState = finalState, CompletedTransitions = completedTransitions, Plan = plan, + ExecutedMigrationContexts = executedMigrationContexts }; } - private void RunUnscopedMigration(Type migrationType, MigrationPlan plan) + private MigrationContext RunUnscopedMigration(Type migrationType, MigrationPlan plan) { using IUmbracoDatabase database = _databaseFactory.CreateDatabase(); var context = new MigrationContext(plan, database, _loggerFactory.CreateLogger()); RunMigration(migrationType, context); + + return context; } - private void RunScopedMigration(Type migrationType, MigrationPlan plan) + private MigrationContext RunScopedMigration(Type migrationType, MigrationPlan plan) { // We want to suppress scope (service, etc...) notifications during a migration plan // execution. This is because if a package that doesn't have their migration plan @@ -225,6 +259,8 @@ public class MigrationPlanExecutor : IMigrationPlanExecutor RunMigration(migrationType, context); scope.Complete(); + + return context; } } From c5e524c9b9a6c935a52a99780700e0ca5ac9f1f7 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 16 May 2023 19:08:38 +0200 Subject: [PATCH 6/7] Wrap RTE content in a dedicated model for future expansion (#14258) --- src/Umbraco.Core/Models/DeliveryApi/RichTextModel.cs | 6 ++++++ .../ValueConverters/RteMacroRenderingValueConverter.cs | 7 +++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Core/Models/DeliveryApi/RichTextModel.cs diff --git a/src/Umbraco.Core/Models/DeliveryApi/RichTextModel.cs b/src/Umbraco.Core/Models/DeliveryApi/RichTextModel.cs new file mode 100644 index 0000000000..2af7570183 --- /dev/null +++ b/src/Umbraco.Core/Models/DeliveryApi/RichTextModel.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Cms.Core.Models.DeliveryApi; + +public class RichTextModel +{ + public required string Markup { get; set; } +} diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index 68d15aa99f..6172fbc629 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -82,13 +82,16 @@ public class RteMacroRenderingValueConverter : SimpleTinyMceValueConverter, IDel public Type GetDeliveryApiPropertyValueType(IPublishedPropertyType propertyType) => _deliveryApiSettings.RichTextOutputAsJson ? typeof(IRichTextElement) - : typeof(string); + : typeof(RichTextModel); public object? ConvertIntermediateToDeliveryApiObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object? inter, bool preview) { if (_deliveryApiSettings.RichTextOutputAsJson is false) { - return Convert(inter, preview) ?? string.Empty; + return new RichTextModel + { + Markup = Convert(inter, preview) ?? string.Empty + }; } var sourceString = inter?.ToString(); From a4202f352aabbf230b300fad389c8ebefe4c2b6c Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 17 May 2023 11:03:27 +0200 Subject: [PATCH 7/7] Ignore macros completely in deliveryapi (#14262) * Hack a fix for MNTP that saves content in configuration instead of document * Ignore macro parsing in delivery api, instead of exploding --- .../ValueConverters/MultiNodeTreePickerValueConverter.cs | 7 +++++++ .../ValueConverters/RteMacroRenderingValueConverter.cs | 9 ++++++--- .../MultiNodeTreePickerValueConverterTests.cs | 6 ++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs index f863c96151..d0ab92223b 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs @@ -204,6 +204,13 @@ public class MultiNodeTreePickerValueConverter : PropertyValueConverterBase, IDe IPublishedSnapshot publishedSnapshot = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot(); var entityType = GetEntityType(propertyType); + + if (entityType == "content") + { + // TODO Why do MNTP config saves "content" and not "document"? + entityType = Constants.UdiEntityType.Document; + } + GuidUdi[] entityTypeUdis = udis.Where(udi => udi.EntityType == entityType).OfType().ToArray(); return entityType switch { diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index 6172fbc629..9d8010ab5f 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -90,7 +90,7 @@ public class RteMacroRenderingValueConverter : SimpleTinyMceValueConverter, IDel { return new RichTextModel { - Markup = Convert(inter, preview) ?? string.Empty + Markup = Convert(inter, preview, false) ?? string.Empty }; } @@ -129,7 +129,7 @@ public class RteMacroRenderingValueConverter : SimpleTinyMceValueConverter, IDel } } - private string? Convert(object? source, bool preview) + private string? Convert(object? source, bool preview, bool handleMacros = true) { if (source == null) { @@ -144,7 +144,10 @@ public class RteMacroRenderingValueConverter : SimpleTinyMceValueConverter, IDel sourceString = _imageSourceParser.EnsureImageSources(sourceString); // ensure string is parsed for macros and macros are executed correctly - sourceString = RenderRteMacros(sourceString, preview); + if (handleMacros) + { + sourceString = RenderRteMacros(sourceString, preview); + } // find and remove the rel attributes used in the Umbraco UI from img tags var doc = new HtmlDocument(); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiNodeTreePickerValueConverterTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiNodeTreePickerValueConverterTests.cs index 80e175bf81..276df9a223 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiNodeTreePickerValueConverterTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiNodeTreePickerValueConverterTests.cs @@ -93,7 +93,9 @@ public class MultiNodeTreePickerValueConverterTests : PropertyValueConverterTest } [Test] - public void MultiNodeTreePickerValueConverter_RendersContentProperties() + [TestCase(Constants.UdiEntityType.Document)] + [TestCase("content")] + public void MultiNodeTreePickerValueConverter_RendersContentProperties(string entityType) { var content = new Mock(); @@ -112,7 +114,7 @@ public class MultiNodeTreePickerValueConverterTests : PropertyValueConverterTest .Setup(pcc => pcc.GetById(key)) .Returns(content.Object); - var publishedDataType = MultiNodePickerPublishedDataType(false, Constants.UdiEntityType.Document); + var publishedDataType = MultiNodePickerPublishedDataType(false, entityType); var publishedPropertyType = new Mock(); publishedPropertyType.SetupGet(p => p.DataType).Returns(publishedDataType);