From 5fc12ec36ed14cd578990888ed0970d3d26486bf Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 5 Aug 2025 13:19:25 +0200 Subject: [PATCH] Support persistence of unrestricted selections from the check box list (#19856) * Use unrestricted text field when creating data types based on the CheckboxList property editor. Initialize default checkbox list data type with the unrestricted text field for storage on new installs. Migrate existing data type and property data. * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Correctly use constant. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Umbraco.Core/Models/ValueStorageType.cs | 4 +- .../PropertyEditors/ValueTypes.cs | 4 +- .../Migrations/Install/DatabaseDataCreator.cs | 2 +- .../Migrations/Upgrade/UmbracoPlan.cs | 1 + ...ateCheckboxListDataTypesAndPropertyData.cs | 50 +++++++++++++++++++ .../CheckBoxListPropertyEditor.cs | 1 + 6 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_0_0/MigrateCheckboxListDataTypesAndPropertyData.cs diff --git a/src/Umbraco.Core/Models/ValueStorageType.cs b/src/Umbraco.Core/Models/ValueStorageType.cs index 975369f993..e7e4f8b354 100644 --- a/src/Umbraco.Core/Models/ValueStorageType.cs +++ b/src/Umbraco.Core/Models/ValueStorageType.cs @@ -14,13 +14,13 @@ public enum ValueStorageType // changing the casing of values. /// - /// Store property value as NText. + /// Store property value as NVarchar(max). /// [EnumMember] Ntext, /// - /// Store property value as NVarChar. + /// Store property value as NVarChar(512). /// [EnumMember] Nvarchar, diff --git a/src/Umbraco.Core/PropertyEditors/ValueTypes.cs b/src/Umbraco.Core/PropertyEditors/ValueTypes.cs index ac6e6a9bb8..dc1e636c7f 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueTypes.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueTypes.cs @@ -45,7 +45,7 @@ public static class ValueTypes public const string Json = "JSON"; // NText /// - /// Text value (maps to text database type). + /// Text value (maps to nvarchar(max) database type). /// public const string Text = "TEXT"; // NText @@ -55,7 +55,7 @@ public static class ValueTypes public const string Time = "TIME"; // Date /// - /// Text value (maps to varchar database type). + /// Text value (maps to nvarchar(512) database type). /// public const string String = "STRING"; // NVarchar diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs index e99ce46587..7fed5a6afb 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs @@ -2060,7 +2060,7 @@ internal sealed class DatabaseDataCreator NodeId = -43, EditorAlias = Constants.PropertyEditors.Aliases.CheckBoxList, EditorUiAlias = "Umb.PropertyEditorUi.CheckBoxList", - DbType = "Nvarchar", + DbType = "Ntext", }); } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index b02c56fea6..a62ce1d8a1 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -126,5 +126,6 @@ public class UmbracoPlan : MigrationPlan // To 17.0.0 To("{17D5F6CA-CEB8-462A-AF86-4B9C3BF91CF1}"); + To("{EB1E50B7-CD5E-4B6B-B307-36237DD2C506}"); } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_0_0/MigrateCheckboxListDataTypesAndPropertyData.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_0_0/MigrateCheckboxListDataTypesAndPropertyData.cs new file mode 100644 index 0000000000..35873b9501 --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_0_0/MigrateCheckboxListDataTypesAndPropertyData.cs @@ -0,0 +1,50 @@ +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_17_0_0; + +/// +/// Migrates data types based on the Umbraco.CheckBoxList property editor to store data in the text column without length restriction. +/// Also migrates the data for properties this property editor from the length restricted field (varcharValue - nvarchar(512)), to the +/// one without a restriction (textValue - nvarchar(max)). +/// +public class MigrateCheckboxListDataTypesAndPropertyData : AsyncMigrationBase +{ + private readonly IDataTypeService _dataTypeService; + + /// + /// Initializes a new instance of the class. + /// + public MigrateCheckboxListDataTypesAndPropertyData(IMigrationContext context, IDataTypeService dataTypeService) + : base(context) => _dataTypeService = dataTypeService; + + /// + protected override async Task MigrateAsync() + { + // Update the definition of the datatypes. + IEnumerable dataTypes = await _dataTypeService.GetByEditorAliasAsync(Constants.PropertyEditors.Aliases.CheckBoxList); + foreach (IDataType dataType in dataTypes) + { + dataType.DatabaseType = ValueStorageType.Ntext; + await _dataTypeService.UpdateAsync(dataType, Constants.Security.SuperUserKey); + } + + // Copy from varcharValue to textValue and set varcharValue to null for all property data stored using data types based on + // the Umbraco.CheckBoxList property editor. + string sql = $@" +UPDATE umbracoPropertyData +SET textValue = varcharValue, varcharValue = NULL +WHERE propertyTypeId IN ( + SELECT id + FROM cmsPropertyType + WHERE dataTypeId IN ( + SELECT nodeId + FROM umbracoDataType + WHERE propertyEditorAlias = '{Constants.PropertyEditors.Aliases.CheckBoxList}' + ) +) +AND varcharValue IS NOT NULL"; + await Database.ExecuteAsync(sql); + } +} diff --git a/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs index cd06118041..9f9606e3f0 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs @@ -12,6 +12,7 @@ namespace Umbraco.Cms.Core.PropertyEditors; /// [DataEditor( Constants.PropertyEditors.Aliases.CheckBoxList, + ValueType = ValueTypes.Text, // We use the Text value type to ensure we don't run out of storage space in the database field with large lists with multiple values selected. ValueEditorIsReusable = true)] public class CheckBoxListPropertyEditor : DataEditor {