From 85176d1bf6bf4d6a85c342fdeecba0500593758f Mon Sep 17 00:00:00 2001 From: Lee Kelleher Date: Thu, 27 Mar 2025 15:24:45 +0000 Subject: [PATCH 1/5] Fixes localization culture case-sensitive check (#18849) Fixes #18801. --- .../core/localization/registry/localization.registry.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/localization/registry/localization.registry.ts b/src/Umbraco.Web.UI.Client/src/packages/core/localization/registry/localization.registry.ts index 5fd55b451c..ef76a9af67 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/localization/registry/localization.registry.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/localization/registry/localization.registry.ts @@ -67,7 +67,9 @@ export class UmbLocalizationRegistry { if (!translations.length) return; if (diff.length) { - const filteredTranslations = translations.filter((t) => diff.some((ext) => ext.meta.culture === t.$code)); + const filteredTranslations = translations.filter((t) => + diff.some((ext) => ext.meta.culture.toLowerCase() === t.$code), + ); umbLocalizationManager.registerManyLocalizations(filteredTranslations); } From 26907f202fb123a9425bd3738080fde295e43cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 31 Mar 2025 10:51:45 +0200 Subject: [PATCH 2/5] hotfix: context provider should not destroy instance (#18864) * do not destroy instance * Update src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/libs/context-api/provide/context-provider.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.ts index 10ddd09b63..06abdf2d38 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.ts @@ -103,8 +103,7 @@ export class UmbContextProvider Date: Mon, 31 Mar 2025 13:13:00 +0200 Subject: [PATCH 3/5] V15: Revert "Fix: RTE markup props not up to date issue" (#18879) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "simplifying the use of props (#18430)" This reverts commit 347e898190f84cd79ec67fb6e6cb851d2c6f8875. * do not set value if identical check cherry-picked from c03a8afab54c188c85beb67c86af97289b30723e Co-authored-by: Niels Lyngsø --------- Co-authored-by: Niels Lyngsø --- .../input-tiny-mce/input-tiny-mce.element.ts | 3 +- .../property-editor-ui-tiny-mce.element.ts | 41 ++++++++----------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts index bcd7a805bc..41739b5f9d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts @@ -61,9 +61,8 @@ export class UmbInputTinyMceElement extends UUIFormControlMixin(UmbLitElement, ' } override set value(newValue: FormDataEntryValue | FormData) { - if (newValue === this.value) return; - super.value = newValue; const newContent = typeof newValue === 'string' ? newValue : ''; + super.value = newContent; if (this.#editorRef && this.#editorRef.getContent() != newContent) { this.#editorRef.setContent(newContent); diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts index 5e796a3d85..aa63a33930 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts @@ -1,6 +1,6 @@ import type { UmbInputTinyMceElement } from '../../components/input-tiny-mce/input-tiny-mce.element.js'; import { customElement, html } from '@umbraco-cms/backoffice/external/lit'; -import { UmbPropertyEditorUiRteElementBase, UMB_BLOCK_RTE_DATA_CONTENT_KEY } from '@umbraco-cms/backoffice/rte'; +import { UmbPropertyEditorUiRteElementBase } from '@umbraco-cms/backoffice/rte'; import '../../components/input-tiny-mce/input-tiny-mce.element.js'; @@ -10,37 +10,32 @@ import '../../components/input-tiny-mce/input-tiny-mce.element.js'; @customElement('umb-property-editor-ui-tiny-mce') export class UmbPropertyEditorUITinyMceElement extends UmbPropertyEditorUiRteElementBase { #onChange(event: CustomEvent & { target: UmbInputTinyMceElement }) { - const value = typeof event.target.value === 'string' ? event.target.value : ''; + const markup = typeof event.target.value === 'string' ? event.target.value : ''; // If we don't get any markup clear the property editor value. - if (value === '') { + if (markup === '') { this.value = undefined; this._fireChangeEvent(); return; } - // Clone the DOM, to remove the classes and attributes on the original: - const div = document.createElement('div'); - div.innerHTML = value; - - // Loop through used, to remove the classes on these. - const blockEls = div.querySelectorAll(`umb-rte-block, umb-rte-block-inline`); - blockEls.forEach((blockEl) => { - blockEl.removeAttribute('contenteditable'); - blockEl.removeAttribute('class'); - }); - - const markup = div.innerHTML; - // Remove unused Blocks of Blocks Layout. Leaving only the Blocks that are present in Markup. - //const blockElements = editor.dom.select(`umb-rte-block, umb-rte-block-inline`); - const usedContentKeys = Array.from(blockEls).map((blockElement) => - blockElement.getAttribute(UMB_BLOCK_RTE_DATA_CONTENT_KEY), - ); + const usedContentKeys: string[] = []; - if (super.value) { - super.value = { - ...super.value, + // Regex matching all block elements in the markup, and extracting the content key. It's the same as the one used on the backend. + const regex = new RegExp( + /(?:)?<\/umb-rte-block(?:-inline)?>/gi, + ); + let blockElement: RegExpExecArray | null; + while ((blockElement = regex.exec(markup)) !== null) { + if (blockElement.groups?.key) { + usedContentKeys.push(blockElement.groups.key); + } + } + + if (this.value) { + this.value = { + ...this.value, markup: markup, }; } else { From fc815db80b01527d6e0470dcd11e94d3f7a96001 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 31 Mar 2025 14:48:54 +0200 Subject: [PATCH 4/5] bump version to 15.3.1 --- src/Umbraco.Web.UI.Client/package-lock.json | 4 ++-- src/Umbraco.Web.UI.Client/package.json | 2 +- version.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 4267f3ea8e..435970b711 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1,12 +1,12 @@ { "name": "@umbraco-cms/backoffice", - "version": "15.3.0", + "version": "15.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@umbraco-cms/backoffice", - "version": "15.3.0", + "version": "15.3.1", "license": "MIT", "workspaces": [ "./src/packages/*" diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 9ca77c5dd1..56f26beea0 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -1,7 +1,7 @@ { "name": "@umbraco-cms/backoffice", "license": "MIT", - "version": "15.3.0", + "version": "15.3.1", "type": "module", "exports": { ".": null, diff --git a/version.json b/version.json index f4ab576f49..8f085faa88 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", - "version": "15.3.0", + "version": "15.3.1", "assemblyVersion": { "precision": "build" }, From 1720692d3ddd89feba6fe93d2c7c1f9d854250eb Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 1 Apr 2025 09:06:24 +0200 Subject: [PATCH 5/5] Ensures date comparisons in schedule integration tests are made only on the datetime part to the second (#18894) * Ensures date comparisons in schedule integration tests are made only on the date part. * Include time part to the second. * Ensure Kind is retained when truncating a date. * Retain Kind for all truncation levels. --- .../Extensions/DateTimeExtensions.cs | 21 +++++++++++++------ .../Services/ContentPublishingServiceTests.cs | 5 ++--- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Core/Extensions/DateTimeExtensions.cs b/src/Umbraco.Core/Extensions/DateTimeExtensions.cs index 35c9f600e5..00191e5a76 100644 --- a/src/Umbraco.Core/Extensions/DateTimeExtensions.cs +++ b/src/Umbraco.Core/Extensions/DateTimeExtensions.cs @@ -7,6 +7,9 @@ namespace Umbraco.Extensions; public static class DateTimeExtensions { + /// + /// Defines the levels to truncate a date to. + /// public enum DateTruncate { Year, @@ -25,33 +28,39 @@ public static class DateTimeExtensions public static string ToIsoString(this DateTime dt) => dt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); + /// + /// Truncates the date to the specified level, i.e. if you pass in DateTruncate.Hour it will truncate the date to the hour. + /// + /// The date. + /// The level to truncate the date to. + /// The truncated date. public static DateTime TruncateTo(this DateTime dt, DateTruncate truncateTo) { if (truncateTo == DateTruncate.Year) { - return new DateTime(dt.Year, 1, 1); + return new DateTime(dt.Year, 1, 1, 0, 0, 0, dt.Kind); } if (truncateTo == DateTruncate.Month) { - return new DateTime(dt.Year, dt.Month, 1); + return new DateTime(dt.Year, dt.Month, 1, 0, 0, 0, dt.Kind); } if (truncateTo == DateTruncate.Day) { - return new DateTime(dt.Year, dt.Month, dt.Day); + return new DateTime(dt.Year, dt.Month, dt.Day, 0, 0, 0, dt.Kind); } if (truncateTo == DateTruncate.Hour) { - return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0); + return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0, dt.Kind); } if (truncateTo == DateTruncate.Minute) { - return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0); + return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, dt.Kind); } - return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second); + return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, dt.Kind); } } diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ContentPublishingServiceTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ContentPublishingServiceTests.cs index 0853bca8c5..050b08582c 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ContentPublishingServiceTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ContentPublishingServiceTests.cs @@ -1,4 +1,3 @@ -using Bogus.DataSets; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; @@ -23,8 +22,8 @@ public class ContentPublishingServiceTests : UmbracoIntegrationTestWithContent { private const string UnknownCulture = "ke-Ke"; - private readonly DateTime _schedulePublishDate = DateTime.UtcNow.AddDays(1); - private readonly DateTime _scheduleUnPublishDate = DateTime.UtcNow.AddDays(2); + private readonly DateTime _schedulePublishDate = DateTime.UtcNow.AddDays(1).TruncateTo(DateTimeExtensions.DateTruncate.Second); + private readonly DateTime _scheduleUnPublishDate = DateTime.UtcNow.AddDays(2).TruncateTo(DateTimeExtensions.DateTruncate.Second); [SetUp] public new void Setup() => ContentRepositoryBase.ThrowOnWarning = true;