From a19b9fb5fe162d21776cdfd269cbed8e41cc1a25 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Oct 2025 16:35:42 +0000 Subject: [PATCH 1/4] UFM: Add camelCase aliases for UFM filters to support UFMJS expressions (closes #20500) (#20501) * Initial plan * Add camelCase aliases for UFM filters with hyphens (stripHtml, titleCase, wordLimit) Co-authored-by: iOvergaard <752371+iOvergaard@users.noreply.github.com> * Add manifest tests for camelCase filter aliases Co-authored-by: iOvergaard <752371+iOvergaard@users.noreply.github.com> * discards tests that are not useful * test: updates imports for stripHtml api * Exports `UmbUfmStripHtmlFilterApi` class --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: iOvergaard <752371+iOvergaard@users.noreply.github.com> Co-authored-by: leekelleher --- .../src/packages/ufm/filters/manifests.ts | 24 ++++++++++ .../ufm/filters/strip-html.filter.test.ts | 47 +++++++++++++++++++ .../packages/ufm/filters/strip-html.filter.ts | 1 + 3 files changed, 72 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/ufm/filters/strip-html.filter.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/filters/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/filters/manifests.ts index 265b99358c..8efad58bd0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/ufm/filters/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/filters/manifests.ts @@ -29,6 +29,14 @@ export const manifests: Array = [ api: () => import('./strip-html.filter.js'), meta: { alias: 'strip-html' }, }, + // TODO: Remove in V18 - replaced by camelCase alias below for UFMJS compatibility + { + type: 'ufmFilter', + alias: 'Umb.Filter.StripHtmlCamelCase', + name: 'Strip HTML UFM Filter (camelCase)', + api: () => import('./strip-html.filter.js'), + meta: { alias: 'stripHtml' }, + }, { type: 'ufmFilter', alias: 'Umb.Filter.TitleCase', @@ -36,6 +44,14 @@ export const manifests: Array = [ api: () => import('./title-case.filter.js'), meta: { alias: 'title-case' }, }, + // TODO: Remove in V18 - replaced by camelCase alias below for UFMJS compatibility + { + type: 'ufmFilter', + alias: 'Umb.Filter.TitleCaseCamelCase', + name: 'Title Case UFM Filter (camelCase)', + api: () => import('./title-case.filter.js'), + meta: { alias: 'titleCase' }, + }, { type: 'ufmFilter', alias: 'Umb.Filter.Truncate', @@ -57,4 +73,12 @@ export const manifests: Array = [ api: () => import('./word-limit.filter.js'), meta: { alias: 'word-limit' }, }, + // TODO: Remove in V18 - replaced by camelCase alias below for UFMJS compatibility + { + type: 'ufmFilter', + alias: 'Umb.Filter.WordLimitCamelCase', + name: 'Word Limit UFM Filter (camelCase)', + api: () => import('./word-limit.filter.js'), + meta: { alias: 'wordLimit' }, + }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/filters/strip-html.filter.test.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/filters/strip-html.filter.test.ts new file mode 100644 index 0000000000..45c39844b6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/filters/strip-html.filter.test.ts @@ -0,0 +1,47 @@ +import { expect } from '@open-wc/testing'; +import { UmbUfmStripHtmlFilterApi } from './strip-html.filter.js'; + +describe('UmbUfmStripHtmlFilter', () => { + let filter: UmbUfmStripHtmlFilterApi; + + beforeEach(() => { + filter = new UmbUfmStripHtmlFilterApi(); + }); + + describe('filter', () => { + it('should strip HTML tags from string', () => { + const result = filter.filter('

Hello World

'); + expect(result).to.equal('Hello World'); + }); + + it('should handle empty string', () => { + const result = filter.filter(''); + expect(result).to.equal(''); + }); + + it('should handle null input', () => { + const result = filter.filter(null); + expect(result).to.equal(''); + }); + + it('should handle undefined input', () => { + const result = filter.filter(undefined); + expect(result).to.equal(''); + }); + + it('should handle markup object', () => { + const result = filter.filter({ markup: '

Test

' }); + expect(result).to.equal('Test'); + }); + + it('should strip complex HTML', () => { + const result = filter.filter('

Title

Paragraph with link

'); + expect(result).to.equal('TitleParagraph with link'); + }); + + it('should handle plain text without HTML', () => { + const result = filter.filter('Plain text'); + expect(result).to.equal('Plain text'); + }); + }); +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/filters/strip-html.filter.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/filters/strip-html.filter.ts index 98b7e394d8..a65969e522 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/ufm/filters/strip-html.filter.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/filters/strip-html.filter.ts @@ -13,3 +13,4 @@ class UmbUfmStripHtmlFilterApi extends UmbUfmFilterBase { } export { UmbUfmStripHtmlFilterApi as api }; +export { UmbUfmStripHtmlFilterApi }; From e22b459d9c72ac7f738fb7ea163713cee86aa07d Mon Sep 17 00:00:00 2001 From: NguyenThuyLan <116753400+NguyenThuyLan@users.noreply.github.com> Date: Wed, 15 Oct 2025 13:33:24 +0700 Subject: [PATCH 2/4] WorkspaceView: Add tests for create and using custom workspace view (#20408) * WorkspaceView: Add tests for create and using custom workspace view * update helper version * Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.spec.ts Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com> * Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.spec.ts Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com> * update format code * Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.spec.ts Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com> * Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.spec.ts Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com> * Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.spec.ts Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com> --------- Co-authored-by: Lan Nguyen Thuy Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com> --- .../workspace-view/umbraco-package.json | 24 ++++++++++ .../workspace-view/workspace-view.js | 28 ++++++++++++ .../workspace-view/workspace-view.js.map | 1 + .../ExtensionRegistry/WorkspaceView.spec.ts | 44 +++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/workspace-view/umbraco-package.json create mode 100644 tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/workspace-view/workspace-view.js create mode 100644 tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/workspace-view/workspace-view.js.map create mode 100644 tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.spec.ts diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/workspace-view/umbraco-package.json b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/workspace-view/umbraco-package.json new file mode 100644 index 0000000000..42d4c1957f --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/workspace-view/umbraco-package.json @@ -0,0 +1,24 @@ +{ + "$schema": "../../umbraco-package-schema.json", + "name": "My workspace", + "version": "0.1.0", + "extensions": [ + { + "type": "workspaceView", + "alias": "My.WorkspaceView", + "name": "My Workspace View", + "element": "/App_Plugins/workspace-view/workspace-view.js", + "meta": { + "label": "My Workspace View", + "pathname": "/my-workspace-view", + "icon": "icon-add" + }, + "conditions": [ + { + "alias": "Umb.Condition.WorkspaceAlias", + "match": "Umb.Workspace.Document" + } + ] + } + ] +} diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/workspace-view/workspace-view.js b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/workspace-view/workspace-view.js new file mode 100644 index 0000000000..b471d1e7cf --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/workspace-view/workspace-view.js @@ -0,0 +1,28 @@ +import { LitElement as n, html as a, css as c, customElement as p } from "@umbraco-cms/backoffice/external/lit"; +import { UmbElementMixin as u } from "@umbraco-cms/backoffice/element-api"; +var w = Object.getOwnPropertyDescriptor, v = (o, s, i, l) => { + for (var e = l > 1 ? void 0 : l ? w(s, i) : s, r = o.length - 1, m; r >= 0; r--) + (m = o[r]) && (e = m(e) || e); + return e; +}; +let t = class extends u(n) { + render() { + return a` + + Welcome to my newly created workspace view. + + `; + } +}; +t.styles = c` + uui-box { + margin: 20px; + } + `; +t = v([ + p("my-workspaceview") +], t); +export { + t as default +}; +//# sourceMappingURL=workspace-view.js.map diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/workspace-view/workspace-view.js.map b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/workspace-view/workspace-view.js.map new file mode 100644 index 0000000000..42be166b3e --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/workspace-view/workspace-view.js.map @@ -0,0 +1 @@ +{"version":3,"file":"workspace-view.js","sources":["../../workspace-view/src/my-element.ts"],"sourcesContent":["import { LitElement, html, customElement, css } from \"@umbraco-cms/backoffice/external/lit\";\nimport { UmbElementMixin } from \"@umbraco-cms/backoffice/element-api\";\n\n@customElement('my-workspaceview')\nexport default class MyWorkspaceViewElement extends UmbElementMixin(LitElement) {\n\n render() {\n return html` \n \n Welcome to my newly created workspace view.\n \n `\n }\n\n static styles = css`\n uui-box {\n margin: 20px;\n }\n `\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'my-workspaceview': MyWorkspaceViewElement\n }\n}\n"],"names":["MyWorkspaceViewElement","UmbElementMixin","LitElement","html","css","__decorateClass","customElement"],"mappings":";;;;;;;AAIA,IAAqBA,IAArB,cAAoDC,EAAgBC,CAAU,EAAE;AAAA,EAE5E,SAAS;AACL,WAAOC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKX;AAOJ;AAfqBH,EAUV,SAASI;AAAA;AAAA;AAAA;AAAA;AAVCJ,IAArBK,EAAA;AAAA,EADCC,EAAc,kBAAkB;AAAA,GACZN,CAAA;"} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.spec.ts new file mode 100644 index 0000000000..b5d166fe86 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/WorkspaceView.spec.ts @@ -0,0 +1,44 @@ +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +// Content +const contentName = 'TestContent'; +// DocumentType +const documentTypeName = 'TestDocumentTypeForContent'; +// DataType +const dataTypeName = 'Textstring'; +// Media +const mediaName = 'TestMedia'; + +test.afterEach(async ({umbracoApi}) => { + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.media.ensureNameNotExists(mediaName); +}); + +test('can see the custom workspace view in the content section', async ({umbracoApi, umbracoUi}) => { + // Arrange + const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, dataTypeName, dataTypeData.id); + await umbracoApi.document.createDocumentWithTextContent(contentName, documentTypeId, 'Test content', dataTypeName); + + // Act + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + await umbracoUi.content.goToContentWithName(contentName); + + // Assert + await umbracoUi.content.isWorkspaceViewTabWithAliasVisible('My.WorkspaceView', true); +}); + +test('cannot see the custom workspace view in the media section', async ({umbracoApi, umbracoUi}) => { + // Arrange + await umbracoApi.media.createDefaultMediaWithImage(mediaName); + + // Act + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.media); + await umbracoUi.media.goToMediaWithName(mediaName); + + // Assert + await umbracoUi.media.isWorkspaceViewTabWithAliasVisible('My.WorkspaceView', false); +}); \ No newline at end of file From 1ab13a970b9d7a8d648c786858c5692e96690178 Mon Sep 17 00:00:00 2001 From: NguyenThuyLan <116753400+NguyenThuyLan@users.noreply.github.com> Date: Wed, 15 Oct 2025 13:33:41 +0700 Subject: [PATCH 3/4] Dashboard: Add tests for create and using custom dashboard (#20253) * add tests for custom dashboard * update test dashboard using helper * remove extensionRegistry for playwright config * update helper version for dashboard * Update tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/CustomDashboard.spec.ts Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com> * fix format code --------- Co-authored-by: Lan Nguyen Thuy Co-authored-by: Nhu Dinh <150406148+nhudinh0309@users.noreply.github.com> --- .../welcome-dashboard/umbraco-package.json | 25 ++++++++++++ .../welcome-dashboard/welcome-dashboard.js | 38 +++++++++++++++++++ .../welcome-dashboard.js.map | 1 + .../ExtensionRegistry/CustomDashboard.spec.ts | 22 +++++++++++ 4 files changed, 86 insertions(+) create mode 100644 tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/welcome-dashboard/umbraco-package.json create mode 100644 tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/welcome-dashboard/welcome-dashboard.js create mode 100644 tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/welcome-dashboard/welcome-dashboard.js.map create mode 100644 tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/CustomDashboard.spec.ts diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/welcome-dashboard/umbraco-package.json b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/welcome-dashboard/umbraco-package.json new file mode 100644 index 0000000000..d8e628762c --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/welcome-dashboard/umbraco-package.json @@ -0,0 +1,25 @@ +{ + "$schema": "../../umbraco-package-schema.json", + "name": "My.WelcomePackage", + "version": "0.1.0", + "extensions": [ + { + "type": "dashboard", + "alias": "my.welcome.dashboard", + "name": "My Welcome Dashboard", + "element": "/App_Plugins/welcome-dashboard/welcome-dashboard.js", + "elementName": "my-welcome-dashboard", + "weight": 30, + "meta": { + "label": "Welcome Dashboard", + "pathname": "welcome-dashboard" + }, + "conditions": [ + { + "alias": "Umb.Condition.SectionAlias", + "match": "Umb.Section.Content" + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/welcome-dashboard/welcome-dashboard.js b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/welcome-dashboard/welcome-dashboard.js new file mode 100644 index 0000000000..8ddf0fbaa3 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/welcome-dashboard/welcome-dashboard.js @@ -0,0 +1,38 @@ +import { css as n, customElement as c, html as d } from "@umbraco-cms/backoffice/external/lit"; +import { UmbLitElement as p } from "@umbraco-cms/backoffice/lit-element"; +var i = Object.getOwnPropertyDescriptor, h = (r, s, l, a) => { + for (var e = a > 1 ? void 0 : a ? i(s, l) : s, o = r.length - 1, m; o >= 0; o--) + (m = r[o]) && (e = m(e) || e); + return e; +}; +let t = class extends p { + render() { + return d` +

Welcome Dashboard

+
+

+ This is the Backoffice. From here, you can modify the content, + media, and settings of your website. +

+

© Sample Company 20XX

+
+ `; + } +}; +t.styles = [ + n` + :host { + display: block; + padding: 24px; + } + ` +]; +t = h([ + c("my-welcome-dashboard") +], t); +const b = t; +export { + t as MyWelcomeDashboardElement, + b as default +}; +//# sourceMappingURL=welcome-dashboard.js.map diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/welcome-dashboard/welcome-dashboard.js.map b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/welcome-dashboard/welcome-dashboard.js.map new file mode 100644 index 0000000000..65539b9a65 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/welcome-dashboard/welcome-dashboard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"welcome-dashboard.js","sources":["../../welcome-dashboard/src/welcome-dashboard.element.ts"],"sourcesContent":["import { css, html, customElement } from '@umbraco-cms/backoffice/external/lit';\r\nimport { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';\r\n\r\n@customElement('my-welcome-dashboard')\r\nexport class MyWelcomeDashboardElement extends UmbLitElement {\r\n\r\n override render() {\r\n return html`\r\n

Welcome Dashboard

\r\n
\r\n

\r\n This is the Backoffice. From here, you can modify the content,\r\n media, and settings of your website.\r\n

\r\n

© Sample Company 20XX

\r\n
\r\n `;\r\n }\r\n\r\n static override readonly styles = [\r\n css`\r\n :host {\r\n display: block;\r\n padding: 24px;\r\n }\r\n `,\r\n ];\r\n}\r\n\r\nexport default MyWelcomeDashboardElement;\r\n\r\ndeclare global {\r\n interface HTMLElementTagNameMap {\r\n 'my-welcome-dashboard': MyWelcomeDashboardElement;\r\n }\r\n}"],"names":["MyWelcomeDashboardElement","UmbLitElement","html","css","__decorateClass","customElement","MyWelcomeDashboardElement$1"],"mappings":";;;;;;;AAIO,IAAMA,IAAN,cAAwCC,EAAc;AAAA,EAEhD,SAAS;AACd,WAAOC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUX;AAUJ;AAvBaF,EAegB,SAAS;AAAA,EAC9BG;AAAA;AAAA;AAAA;AAAA;AAAA;AAMJ;AAtBSH,IAANI,EAAA;AAAA,EADNC,EAAc,sBAAsB;AAAA,GACxBL,CAAA;AAyBb,MAAAM,IAAeN;"} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/CustomDashboard.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/CustomDashboard.spec.ts new file mode 100644 index 0000000000..0303f6b9af --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/CustomDashboard.spec.ts @@ -0,0 +1,22 @@ +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +// Dashboard +const dashboardName = 'Welcome Dashboard'; + +test('can see the custom dashboard in content section', async ({umbracoUi}) => { + // Act + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Assert + await umbracoUi.content.isDashboardTabWithNameVisible(dashboardName, true); +}); + +test('can not see the custom dashboard in media section', async ({umbracoUi}) => { + // Act + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.media); + + // Assert + await umbracoUi.content.isDashboardTabWithNameVisible(dashboardName, false); +}); \ No newline at end of file From fdf759d08debcbf4d86fed2d6d2c6aaed16ef03e Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Wed, 15 Oct 2025 09:41:41 +0200 Subject: [PATCH 4/4] Content Types: Prevent creation of document type with an alias that case insensitively matches an existing alias (closes #20467) (#20471) Prevent creation of document type with an alias that case insensitively matches an existing alias. --- .../ContentTypeEditing/ContentTypeEditingServiceBase.cs | 4 ++-- .../Services/ContentTypeEditingServiceTests.Create.cs | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Services/ContentTypeEditing/ContentTypeEditingServiceBase.cs b/src/Umbraco.Core/Services/ContentTypeEditing/ContentTypeEditingServiceBase.cs index 00708fb439..b1847c80da 100644 --- a/src/Umbraco.Core/Services/ContentTypeEditing/ContentTypeEditingServiceBase.cs +++ b/src/Umbraco.Core/Services/ContentTypeEditing/ContentTypeEditingServiceBase.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.ContentTypeEditing; using Umbraco.Cms.Core.Services.OperationStatus; using Umbraco.Cms.Core.Strings; @@ -407,7 +407,7 @@ internal abstract class ContentTypeEditingServiceBase _contentTypeService.GetAllContentTypeAliases().Contains(alias); + private bool ContentTypeAliasIsInUse(string alias) => _contentTypeService.GetAllContentTypeAliases().InvariantContains(alias); private bool ContentTypeAliasCanBeUsedFor(string alias, Guid key) { diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ContentTypeEditingServiceTests.Create.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ContentTypeEditingServiceTests.Create.cs index 814d1b5be7..a987e9a4f7 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ContentTypeEditingServiceTests.Create.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ContentTypeEditingServiceTests.Create.cs @@ -861,14 +861,15 @@ internal sealed partial class ContentTypeEditingServiceTests Assert.AreEqual(ContentTypeOperationStatus.InvalidAlias, result.Status); } - [Test] - public async Task Cannot_Use_Existing_Alias() + [TestCase("test")] // Matches alias case sensitively. + [TestCase("Test")] // Matches alias case insensitively. + public async Task Cannot_Use_Existing_Alias(string newAlias) { var createModel = ContentTypeCreateModel("Test", "test"); var result = await ContentTypeEditingService.CreateAsync(createModel, Constants.Security.SuperUserKey); Assert.IsTrue(result.Success); - createModel = ContentTypeCreateModel("Test 2", "test"); + createModel = ContentTypeCreateModel("Test 2", newAlias); result = await ContentTypeEditingService.CreateAsync(createModel, Constants.Security.SuperUserKey); Assert.IsFalse(result.Success); Assert.AreEqual(ContentTypeOperationStatus.DuplicateAlias, result.Status);