Merge branch 'main' into v17/dev
This commit is contained in:
@@ -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<TContentType, TContentType
|
||||
}
|
||||
|
||||
// This this method gets aliases across documents, members, and media, so it covers it all
|
||||
private bool ContentTypeAliasIsInUse(string alias) => _contentTypeService.GetAllContentTypeAliases().Contains(alias);
|
||||
private bool ContentTypeAliasIsInUse(string alias) => _contentTypeService.GetAllContentTypeAliases().InvariantContains(alias);
|
||||
|
||||
private bool ContentTypeAliasCanBeUsedFor(string alias, Guid key)
|
||||
{
|
||||
|
||||
@@ -29,6 +29,14 @@ export const manifests: Array<ManifestUfmFilter> = [
|
||||
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<ManifestUfmFilter> = [
|
||||
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<ManifestUfmFilter> = [
|
||||
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' },
|
||||
},
|
||||
];
|
||||
|
||||
@@ -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('<p>Hello <strong>World</strong></p>');
|
||||
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: '<p>Test</p>' });
|
||||
expect(result).to.equal('Test');
|
||||
});
|
||||
|
||||
it('should strip complex HTML', () => {
|
||||
const result = filter.filter('<div><h1>Title</h1><p>Paragraph with <a href="#">link</a></p></div>');
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -13,3 +13,4 @@ class UmbUfmStripHtmlFilterApi extends UmbUfmFilterBase {
|
||||
}
|
||||
|
||||
export { UmbUfmStripHtmlFilterApi as api };
|
||||
export { UmbUfmStripHtmlFilterApi };
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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`
|
||||
<h1>Welcome Dashboard</h1>
|
||||
<div>
|
||||
<p>
|
||||
This is the Backoffice. From here, you can modify the content,
|
||||
media, and settings of your website.
|
||||
</p>
|
||||
<p>© Sample Company 20XX</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
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
|
||||
@@ -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 <h1>Welcome Dashboard</h1>\r\n <div>\r\n <p>\r\n This is the Backoffice. From here, you can modify the content,\r\n media, and settings of your website.\r\n </p>\r\n <p>© Sample Company 20XX</p>\r\n </div>\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;"}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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`
|
||||
<uui-box headline="Workspace View">
|
||||
Welcome to my newly created workspace view.
|
||||
</uui-box>
|
||||
`;
|
||||
}
|
||||
};
|
||||
t.styles = c`
|
||||
uui-box {
|
||||
margin: 20px;
|
||||
}
|
||||
`;
|
||||
t = v([
|
||||
p("my-workspaceview")
|
||||
], t);
|
||||
export {
|
||||
t as default
|
||||
};
|
||||
//# sourceMappingURL=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 <uui-box headline=\"Workspace View\">\n Welcome to my newly created workspace view.\n </uui-box> \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;"}
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user