diff --git a/Directory.Packages.props b/Directory.Packages.props
index fa6d14e980..23acaa35f2 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -12,24 +12,24 @@
-
-
+
+
-
-
-
-
+
+
+
+
-
+
-
-
+
+
@@ -47,7 +47,7 @@
-
+
@@ -75,7 +75,7 @@
-
+
@@ -87,5 +87,7 @@
+
+
-
\ No newline at end of file
+
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 523d6f4982..10cc87c013 100644
--- a/src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj
+++ b/src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj
@@ -14,6 +14,9 @@
+
+
+
diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj
index 7e6fc6153d..d663ef0197 100644
--- a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj
+++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj
@@ -8,6 +8,9 @@
+
+
+
diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj b/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj
index 75e2a6fe60..9aef183cf6 100644
--- a/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj
+++ b/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj
@@ -8,6 +8,9 @@
+
+
+
diff --git a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj
index ff487ca66a..e186d9789d 100644
--- a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj
+++ b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj
@@ -35,9 +35,14 @@
-
-
-
+
+
+
+
+
+
+
+
diff --git a/src/Umbraco.Cms.Targets/Umbraco.Cms.Targets.csproj b/src/Umbraco.Cms.Targets/Umbraco.Cms.Targets.csproj
index b83a582ade..a9a7604e7c 100644
--- a/src/Umbraco.Cms.Targets/Umbraco.Cms.Targets.csproj
+++ b/src/Umbraco.Cms.Targets/Umbraco.Cms.Targets.csproj
@@ -43,7 +43,7 @@
-
+
diff --git a/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs
index 6caa7593ab..92e3678e7d 100644
--- a/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs
+++ b/src/Umbraco.Core/PropertyEditors/Validators/RequiredValidator.cs
@@ -7,7 +7,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.Validators;
///
/// A validator that validates that the value is not null or empty (if it is a string)
///
-public sealed class RequiredValidator : IValueRequiredValidator, IValueValidator
+public class RequiredValidator : IValueRequiredValidator, IValueValidator
{
[Obsolete($"Use the constructor that does not accept {nameof(ILocalizedTextService)}. Will be removed in V15.")]
public RequiredValidator(ILocalizedTextService textService)
@@ -24,7 +24,7 @@ public sealed class RequiredValidator : IValueRequiredValidator, IValueValidator
ValidateRequired(value, valueType);
///
- public IEnumerable ValidateRequired(object? value, string? valueType)
+ public virtual IEnumerable ValidateRequired(object? value, string? valueType)
{
if (value == null)
{
diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
index 6201691d9f..6d99898cd6 100644
--- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
+++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
@@ -26,6 +26,7 @@ using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Packaging;
using Umbraco.Cms.Core.PropertyEditors;
+using Umbraco.Cms.Core.PropertyEditors.Validators;
using Umbraco.Cms.Core.PropertyEditors.ValueConverters;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Core.Routing;
@@ -237,6 +238,8 @@ public static partial class UmbracoBuilderExtensions
builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+
return builder;
}
diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_13_3_0/AlignUpgradedDatabase.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_13_3_0/AlignUpgradedDatabase.cs
index 84171e8717..6ee48ce0e7 100644
--- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_13_3_0/AlignUpgradedDatabase.cs
+++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_13_3_0/AlignUpgradedDatabase.cs
@@ -120,23 +120,48 @@ public class AlignUpgradedDatabase : MigrationBase
// We need to do this to ensure we don't try to rename the constraint if it doesn't exist.
const string tableName = "umbracoContentVersion";
const string columnName = "VersionDate";
+ const string newColumnName = "versionDate";
+ const string expectedConstraintName = "DF_umbracoContentVersion_versionDate";
+
ColumnInfo? versionDateColumn = columns
.FirstOrDefault(x => x is { TableName: tableName, ColumnName: columnName });
- if (versionDateColumn is null)
+ // we only want to rename the column if necessary
+ if (versionDateColumn is not null)
{
// The column was not found I.E. the column is correctly named
- return;
+ RenameColumn(tableName, columnName, newColumnName, columns);
}
- RenameColumn(tableName, columnName, "versionDate", columns);
-
// Renames the default constraint for the column,
// apparently the content version table used to be prefixed with cms and not umbraco
// We don't have a fluid way to rename the default constraint so we have to use raw SQL
// This should be okay though since we are only running this migration on SQL Server
+ Sql constraintNameQuery = Database.SqlContext.Sql(@$"
+SELECT obj_Constraint.NAME AS 'constraintName'
+ FROM sys.objects obj_table
+ JOIN sys.objects obj_Constraint
+ ON obj_table.object_id = obj_Constraint.parent_object_id
+ JOIN sys.sysconstraints constraints
+ ON constraints.constid = obj_Constraint.object_id
+ JOIN sys.columns columns
+ ON columns.object_id = obj_table.object_id
+ AND columns.column_id = constraints.colid
+ WHERE obj_table.NAME = '{tableName}'
+ AND columns.NAME = '{newColumnName}'
+ AND obj_Constraint.type = 'D'
+");
+ var currentConstraintName = Database.ExecuteScalar(constraintNameQuery);
+
+
+ // only rename the constraint if necessary
+ if (currentConstraintName == expectedConstraintName)
+ {
+ return;
+ }
+
Sql renameConstraintQuery = Database.SqlContext.Sql(
- "EXEC sp_rename N'DF_cmsContentVersion_VersionDate', N'DF_umbracoContentVersion_versionDate', N'OBJECT'");
+ $"EXEC sp_rename N'{currentConstraintName}', N'{expectedConstraintName}', N'OBJECT'");
Database.Execute(renameConstraintQuery);
}
diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs
index 025bdd887c..ef442b768f 100644
--- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs
+++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs
@@ -2,6 +2,7 @@
// See LICENSE for more details.
using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Cache.PropertyEditors;
@@ -10,6 +11,7 @@ using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Blocks;
using Umbraco.Cms.Core.Models.Editors;
+using Umbraco.Cms.Core.PropertyEditors.Validators;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
@@ -66,14 +68,17 @@ public class RichTextPropertyEditor : DataEditor
internal class RichTextPropertyValueEditor : BlockValuePropertyValueEditorBase
{
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
+ private readonly ILocalizedTextService _localizedTextService;
private readonly IHtmlSanitizer _htmlSanitizer;
private readonly HtmlImageSourceParser _imageSourceParser;
private readonly HtmlLocalLinkParser _localLinkParser;
private readonly RichTextEditorPastedImages _pastedImages;
private readonly IJsonSerializer _jsonSerializer;
private readonly IBlockEditorElementTypeCache _elementTypeCache;
+ private readonly IRichTextRequiredValidator _richTextRequiredValidator;
private readonly ILogger _logger;
+ [Obsolete("Use non-obsolete constructor. This is schedules for removal in v16.")]
public RichTextPropertyValueEditor(
DataEditorAttribute attribute,
PropertyEditorCollection propertyEditors,
@@ -91,20 +96,64 @@ public class RichTextPropertyEditor : DataEditor
IBlockEditorElementTypeCache elementTypeCache,
IPropertyValidationService propertyValidationService,
DataValueReferenceFactoryCollection dataValueReferenceFactoryCollection)
+ : this(
+ attribute,
+ propertyEditors,
+ dataTypeReadCache,
+ logger,
+ backOfficeSecurityAccessor,
+ localizedTextService,
+ shortStringHelper,
+ imageSourceParser,
+ localLinkParser,
+ pastedImages,
+ jsonSerializer,
+ ioHelper,
+ htmlSanitizer,
+ elementTypeCache,
+ propertyValidationService,
+ dataValueReferenceFactoryCollection,
+ StaticServiceProvider.Instance.GetRequiredService())
+ {
+
+ }
+
+ public RichTextPropertyValueEditor(
+ DataEditorAttribute attribute,
+ PropertyEditorCollection propertyEditors,
+ IDataTypeConfigurationCache dataTypeReadCache,
+ ILogger logger,
+ IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
+ ILocalizedTextService localizedTextService,
+ IShortStringHelper shortStringHelper,
+ HtmlImageSourceParser imageSourceParser,
+ HtmlLocalLinkParser localLinkParser,
+ RichTextEditorPastedImages pastedImages,
+ IJsonSerializer jsonSerializer,
+ IIOHelper ioHelper,
+ IHtmlSanitizer htmlSanitizer,
+ IBlockEditorElementTypeCache elementTypeCache,
+ IPropertyValidationService propertyValidationService,
+ DataValueReferenceFactoryCollection dataValueReferenceFactoryCollection,
+ IRichTextRequiredValidator richTextRequiredValidator)
: base(attribute, propertyEditors, dataTypeReadCache, localizedTextService, logger, shortStringHelper, jsonSerializer, ioHelper, dataValueReferenceFactoryCollection)
{
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
+ _localizedTextService = localizedTextService;
_imageSourceParser = imageSourceParser;
_localLinkParser = localLinkParser;
_pastedImages = pastedImages;
_htmlSanitizer = htmlSanitizer;
_elementTypeCache = elementTypeCache;
+ _richTextRequiredValidator = richTextRequiredValidator;
_jsonSerializer = jsonSerializer;
_logger = logger;
Validators.Add(new RichTextEditorBlockValidator(propertyValidationService, CreateBlockEditorValues(), elementTypeCache, jsonSerializer, logger));
}
+ public override IValueRequiredValidator RequiredValidator => _richTextRequiredValidator;
+
///
public override object? ConfigurationObject
{
diff --git a/src/Umbraco.Infrastructure/PropertyEditors/Validators/IRichTextRequiredValidator.cs b/src/Umbraco.Infrastructure/PropertyEditors/Validators/IRichTextRequiredValidator.cs
new file mode 100644
index 0000000000..7358e92f38
--- /dev/null
+++ b/src/Umbraco.Infrastructure/PropertyEditors/Validators/IRichTextRequiredValidator.cs
@@ -0,0 +1,7 @@
+using Umbraco.Cms.Core.PropertyEditors;
+
+namespace Umbraco.Cms.Core.PropertyEditors.Validators;
+
+internal interface IRichTextRequiredValidator : IValueRequiredValidator
+{
+}
diff --git a/src/Umbraco.Infrastructure/PropertyEditors/Validators/RichTextRequiredValidator.cs b/src/Umbraco.Infrastructure/PropertyEditors/Validators/RichTextRequiredValidator.cs
new file mode 100644
index 0000000000..b239f0bda5
--- /dev/null
+++ b/src/Umbraco.Infrastructure/PropertyEditors/Validators/RichTextRequiredValidator.cs
@@ -0,0 +1,30 @@
+using System.ComponentModel.DataAnnotations;
+using Microsoft.Extensions.Logging;
+using Umbraco.Cms.Core.Serialization;
+using Umbraco.Cms.Core.Services;
+
+namespace Umbraco.Cms.Core.PropertyEditors.Validators;
+
+internal class RichTextRequiredValidator : RequiredValidator, IRichTextRequiredValidator
+{
+ private readonly IJsonSerializer _jsonSerializer;
+ private readonly ILogger _logger;
+
+ public RichTextRequiredValidator(ILocalizedTextService textService, IJsonSerializer jsonSerializer, ILogger logger) : base(textService)
+ {
+ _jsonSerializer = jsonSerializer;
+ _logger = logger;
+ }
+
+ public override IEnumerable ValidateRequired(object? value, string? valueType) => base.ValidateRequired(GetValue(value), valueType);
+
+ private object? GetValue(object? value)
+ {
+ if(RichTextPropertyEditorHelper.TryParseRichTextEditorValue(value, _jsonSerializer, _logger, out RichTextEditorValue? richTextEditorValue))
+ {
+ return richTextEditorValue?.Markup;
+ }
+
+ return value;
+ }
+}
diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
index 4fa0721a68..ba92e96fc0 100644
--- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
+++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
@@ -21,6 +21,8 @@
+
+
diff --git a/src/Umbraco.Web.UI.Client b/src/Umbraco.Web.UI.Client
index a2fc54b77e..56091cad98 160000
--- a/src/Umbraco.Web.UI.Client
+++ b/src/Umbraco.Web.UI.Client
@@ -1 +1 @@
-Subproject commit a2fc54b77e99de28a0669ab628ecfd7983df7ad8
+Subproject commit 56091cad981b6f51f73c4906ed674bd43c660d5d
diff --git a/tests/Directory.Packages.props b/tests/Directory.Packages.props
index e01362ca11..f5eef701cb 100644
--- a/tests/Directory.Packages.props
+++ b/tests/Directory.Packages.props
@@ -4,8 +4,8 @@
-
-
+
+
@@ -22,4 +22,4 @@
-
\ No newline at end of file
+
diff --git a/umbraco.sln b/umbraco.sln
index 59015768a8..5e26e18d8c 100644
--- a/umbraco.sln
+++ b/umbraco.sln
@@ -184,7 +184,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Persistence.EFC
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Persistence.EFCore.SqlServer", "src\Umbraco.Cms.Persistence.EFCore.SqlServer\Umbraco.Cms.Persistence.EFCore.SqlServer.csproj", "{9276C3F0-0DC9-46C9-BF32-9EE79D92AE02}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.AcceptanceTest.UmbracoProject", "tests\Umbraco.Tests.AcceptanceTest.UmbracoProject\Umbraco.Tests.AcceptanceTest.UmbracoProject.csproj", "{A13FF0A0-69FA-468A-9F79-565401D5C341}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Tests.AcceptanceTest.UmbracoProject", "tests\Umbraco.Tests.AcceptanceTest.UmbracoProject\Umbraco.Tests.AcceptanceTest.UmbracoProject.csproj", "{A13FF0A0-69FA-468A-9F79-565401D5C341}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Cms.Api.Management", "src\Umbraco.Cms.Api.Management\Umbraco.Cms.Api.Management.csproj", "{B4929148-3BD9-4589-829D-7C31FFCFF6D7}"
EndProject
@@ -272,11 +272,8 @@ Global
{79E4293D-C92C-4649-AEC8-F1EFD95BDEB1}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU
{79E4293D-C92C-4649-AEC8-F1EFD95BDEB1}.SkipTests|Any CPU.Build.0 = Debug|Any CPU
{2A5027D9-F71D-4957-929E-F7A56AA1B95A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2A5027D9-F71D-4957-929E-F7A56AA1B95A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A5027D9-F71D-4957-929E-F7A56AA1B95A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2A5027D9-F71D-4957-929E-F7A56AA1B95A}.Release|Any CPU.Build.0 = Release|Any CPU
{2A5027D9-F71D-4957-929E-F7A56AA1B95A}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU
- {2A5027D9-F71D-4957-929E-F7A56AA1B95A}.SkipTests|Any CPU.Build.0 = Debug|Any CPU
{32F6A309-EC1E-4CDB-BA80-C804CF680BEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32F6A309-EC1E-4CDB-BA80-C804CF680BEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32F6A309-EC1E-4CDB-BA80-C804CF680BEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -356,9 +353,7 @@ Global
{9276C3F0-0DC9-46C9-BF32-9EE79D92AE02}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU
{9276C3F0-0DC9-46C9-BF32-9EE79D92AE02}.SkipTests|Any CPU.Build.0 = Debug|Any CPU
{A13FF0A0-69FA-468A-9F79-565401D5C341}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A13FF0A0-69FA-468A-9F79-565401D5C341}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A13FF0A0-69FA-468A-9F79-565401D5C341}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A13FF0A0-69FA-468A-9F79-565401D5C341}.Release|Any CPU.Build.0 = Release|Any CPU
{A13FF0A0-69FA-468A-9F79-565401D5C341}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU
{A13FF0A0-69FA-468A-9F79-565401D5C341}.SkipTests|Any CPU.Build.0 = Debug|Any CPU
{B4929148-3BD9-4589-829D-7C31FFCFF6D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU