From 3cf9099f56eff5a7573cce0957378af9ab2ba3de Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 4 Mar 2025 11:52:20 +0100 Subject: [PATCH 1/3] Remove version from models builder generated code header when configured to do so. (#18501) --- .../ModelsBuilder/Building/TextBuilder.cs | 14 ++- .../Building/TextHeaderWriter.cs | 21 +++- .../BuilderTests.cs | 98 ++++++++++++++++++- 3 files changed, 126 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs b/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs index 7484741b58..4e7ee76e16 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs @@ -51,7 +51,15 @@ public class TextBuilder : Builder /// Outputs an "auto-generated" header to a string builder. /// /// The string builder. - public static void WriteHeader(StringBuilder sb) => TextHeaderWriter.WriteHeader(sb); + [Obsolete("Please use the overload taking all parameters. Scheduled for removal in Umbraco 17.")] + public static void WriteHeader(StringBuilder sb) => WriteHeader(sb, true); + + /// + /// Outputs an "auto-generated" header to a string builder. + /// + /// The string builder. + /// Flag indicating whether the tool version number should be included in the output. + public static void WriteHeader(StringBuilder sb, bool includeVersion) => TextHeaderWriter.WriteHeader(sb, includeVersion); /// /// Outputs a generated model to a string builder. @@ -60,7 +68,7 @@ public class TextBuilder : Builder /// The model to generate. public void Generate(StringBuilder sb, TypeModel typeModel) { - WriteHeader(sb); + WriteHeader(sb, Config.IncludeVersionNumberInGeneratedModels); foreach (var t in TypesUsing) { @@ -83,7 +91,7 @@ public class TextBuilder : Builder /// The models to generate. public void Generate(StringBuilder sb, IEnumerable typeModels) { - WriteHeader(sb); + WriteHeader(sb, Config.IncludeVersionNumberInGeneratedModels); foreach (var t in TypesUsing) { diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextHeaderWriter.cs b/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextHeaderWriter.cs index 5a532cbdba..9ab59516d7 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextHeaderWriter.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextHeaderWriter.cs @@ -8,13 +8,30 @@ internal static class TextHeaderWriter /// Outputs an "auto-generated" header to a string builder. /// /// The string builder. - public static void WriteHeader(StringBuilder sb) + [Obsolete("Please use the overload taking all parameters. Scheduled for removal in Umbraco 17.")] + public static void WriteHeader(StringBuilder sb) => WriteHeader(sb, true); + + /// + /// Outputs an "auto-generated" header to a string builder. + /// + /// The string builder. + /// Flag indicating whether the tool version number should be included in the output. + public static void WriteHeader(StringBuilder sb, bool includeVersion) { sb.Append("//------------------------------------------------------------------------------\n"); sb.Append("// \n"); sb.Append("// This code was generated by a tool.\n"); sb.Append("//\n"); - sb.AppendFormat("// Umbraco.ModelsBuilder.Embedded v{0}\n", ApiVersion.Current.Version); + + if (includeVersion) + { + sb.AppendFormat("// Umbraco.ModelsBuilder.Embedded v{0}\n", ApiVersion.Current.Version); + } + else + { + sb.Append("// Umbraco.ModelsBuilder.Embedded\n"); + } + sb.Append("//\n"); sb.Append("// Changes to this file will be lost if the code is regenerated.\n"); sb.Append("// \n"); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/BuilderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/BuilderTests.cs index 3327e6f27d..86838d921d 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/BuilderTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/BuilderTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -using System.Collections.Generic; -using System.Linq; using System.Text; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; @@ -118,6 +116,102 @@ namespace Umbraco.Cms.Web.Common.PublishedModels Assert.AreEqual(expected.ClearLf(), gen); } + [Test] + public void GenerateSimpleType_WithoutVersion() + { + // Umbraco returns nice, pascal-cased names. + 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), + }); + + TypeModel[] types = { type1 }; + + var modelsBuilderConfig = new ModelsBuilderSettings { IncludeVersionNumberInGeneratedModels = false }; + var builder = new TextBuilder(modelsBuilderConfig, types); + + var sb = new StringBuilder(); + builder.Generate(sb, builder.GetModelsToGenerate().First()); + var gen = sb.ToString(); + + var expected = @"//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Umbraco.ModelsBuilder.Embedded +// +// 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 + { + // helpers +#pragma warning disable 0109 // new is redundant + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """")] + public new const string ModelTypeAlias = ""type1""; + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """")] + public new const PublishedItemType ModelItemType = PublishedItemType.Content; + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """")] + [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"", """")] + [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"", """")] + [global::System.Diagnostics.CodeAnalysis.MaybeNull] + [ImplementPropertyType(""prop1"")] + public virtual string Prop1 => this.Value(_publishedValueFallback, ""prop1""); + } +} +"; + Console.WriteLine(gen); + Assert.AreEqual(expected.ClearLf(), gen); + } + [Test] public void GenerateSimpleType_Ambiguous_Issue() { From 396b5ea21107f2456c36414503b1a3a62ca55087 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 4 Mar 2025 12:12:29 +0100 Subject: [PATCH 2/3] Avoids collection was modified issue when flowing identities to the authenticated user's principal. (#18527) --- .../Extensions/HttpContextExtensions.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs b/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs index fd46ef6903..cf90229513 100644 --- a/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs @@ -68,7 +68,13 @@ public static class HttpContextExtensions // Otherwise we can't log in as both a member and a backoffice user // For instance if you've enabled basic auth. ClaimsPrincipal? authenticatedPrincipal = result.Principal; - IEnumerable existingIdentities = httpContext.User.Identities.Where(x => x.IsAuthenticated && x.AuthenticationType != authenticatedPrincipal.Identity.AuthenticationType); + + // Make sure to copy into a list before attempting to update the authenticated principal, so we don't attempt to modify + // the collection while iterating it. + // See: https://github.com/umbraco/Umbraco-CMS/issues/18509 + var existingIdentities = httpContext.User.Identities + .Where(x => x.IsAuthenticated && x.AuthenticationType != authenticatedPrincipal.Identity.AuthenticationType) + .ToList(); authenticatedPrincipal.AddIdentities(existingIdentities); httpContext.User = authenticatedPrincipal; From 1a205d8727e38573d1cdd1f41e2aa94fbede09cc Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 4 Mar 2025 17:27:33 +0100 Subject: [PATCH 3/3] Handle multiline statements in migrations (#18478) * Handle multiline statements in migrations * Fixed failing unit tests and incorrect obsoletion. * Fixed failing integration tests. * Applied suggestion from code review. --------- Co-authored-by: Sven Geusens --- .../Migrations/MigrationExpressionBase.cs | 5 +++++ .../Migrations/MigrationPlanExecutor.cs | 1 - .../SyntaxProvider/SqlServerSyntaxProviderTests.cs | 1 - 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Infrastructure/Migrations/MigrationExpressionBase.cs b/src/Umbraco.Infrastructure/Migrations/MigrationExpressionBase.cs index a2029cbef8..117eb8fe7b 100644 --- a/src/Umbraco.Infrastructure/Migrations/MigrationExpressionBase.cs +++ b/src/Umbraco.Infrastructure/Migrations/MigrationExpressionBase.cs @@ -70,6 +70,11 @@ public abstract class MigrationExpressionBase : IMigrationExpression } else { + if (stmtBuilder.Length > 0) + { + stmtBuilder.Append(Environment.NewLine); + } + stmtBuilder.Append(line); } } diff --git a/src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs b/src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs index 77677b0bfc..441ff674f2 100644 --- a/src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs +++ b/src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs @@ -92,7 +92,6 @@ public class MigrationPlanExecutor : IMigrationPlanExecutor /// Each migration in the plan, may or may not run in a scope depending on the type of plan. /// A plan can complete partially, the changes of each completed migration will be saved. /// - [Obsolete("This will return an ExecutedMigrationPlan in V13")] public ExecutedMigrationPlan ExecutePlan(MigrationPlan plan, string fromState) { plan.Validate(); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SyntaxProvider/SqlServerSyntaxProviderTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SyntaxProvider/SqlServerSyntaxProviderTests.cs index 7e1d1f163f..3d419ff955 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SyntaxProvider/SqlServerSyntaxProviderTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SyntaxProvider/SqlServerSyntaxProviderTests.cs @@ -18,7 +18,6 @@ using Umbraco.Cms.Persistence.SqlServer.Services; using Umbraco.Cms.Tests.Common.TestHelpers; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; -using Umbraco.Extensions; namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.SyntaxProvider;