Skip to content

Commit 2755372

Browse files
committed
Merge branch 'v15/dev' into v15/bugfix/17372
2 parents 677a859 + d900022 commit 2755372

File tree

43 files changed

+545
-130
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+545
-130
lines changed

src/Umbraco.Cms.Api.Management/Controllers/Document/UpdateDocumentControllerBase.cs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,18 @@ protected UpdateDocumentControllerBase(IAuthorizationService authorizationServic
1717

1818
protected async Task<IActionResult> HandleRequest(Guid id, UpdateDocumentRequestModel requestModel, Func<Task<IActionResult>> authorizedHandler)
1919
{
20-
// TODO This have temporarily been uncommented, to support the client sends values from all cultures, even when the user do not have access to the languages.
21-
// The values are ignored in the ContentEditingService
20+
// We intentionally don't pass in cultures here.
21+
// This is to support the client sending values for all cultures even if the user doesn't have access to the language.
22+
// Values for unauthorized languages are later ignored in the ContentEditingService.
23+
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
24+
User,
25+
ContentPermissionResource.WithKeys(ActionUpdate.ActionLetter, id),
26+
AuthorizationPolicies.ContentPermissionByResource);
2227

23-
// IEnumerable<string> cultures = requestModel.Variants
24-
// .Where(v => v.Culture is not null)
25-
// .Select(v => v.Culture!);
26-
// AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
27-
// User,
28-
// ContentPermissionResource.WithKeys(ActionUpdate.ActionLetter, id, cultures),
29-
// AuthorizationPolicies.ContentPermissionByResource);
30-
//
31-
// if (!authorizationResult.Succeeded)
32-
// {
33-
// return Forbidden();
34-
// }
28+
if (authorizationResult.Succeeded is false)
29+
{
30+
return Forbidden();
31+
}
3532

3633
return await authorizedHandler();
3734
}

src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,10 +536,14 @@ export default {
536536
linkToMedia: 'Link til medie',
537537
selectContentStartNode: 'Vælg startnode for indhold',
538538
selectMedia: 'Vælg medie',
539+
chooseMedia: 'Vælg medie',
540+
chooseMediaStartNode: 'Vælg startnode for medie',
539541
selectMediaType: 'Vælg medietype',
540542
selectIcon: 'Vælg ikon',
541543
selectItem: 'Vælg item',
542544
selectLink: 'Vælg link',
545+
addLink: 'Tilføj Link',
546+
updateLink: 'Opdater Link',
543547
selectMacro: 'Vælg makro',
544548
selectContent: 'Vælg indhold',
545549
selectContentType: 'Vælg indholdstype',

src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,10 +568,13 @@ export default {
568568
linkToMedia: 'Link to media',
569569
selectContentStartNode: 'Select content start node',
570570
selectMedia: 'Select media',
571+
chooseMediaStartNode: 'Choose Media Start nodes',
571572
selectMediaType: 'Select media type',
572573
selectIcon: 'Select icon',
573574
selectItem: 'Select item',
574575
selectLink: 'Configure link',
576+
addLink: 'Add Link',
577+
updateLink: 'Update Link',
575578
selectMacro: 'Select macro',
576579
selectContent: 'Select content',
577580
selectContentType: 'Select content type',

src/Umbraco.Web.UI.Client/src/assets/lang/en.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,10 +559,14 @@ export default {
559559
selectContentStartNode: 'Select content start node',
560560
selectEvent: 'Select event',
561561
selectMedia: 'Select media',
562+
chooseMedia: 'Choose media',
563+
chooseMediaStartNode: 'Choose Media Start nodes',
562564
selectMediaType: 'Select media type',
563565
selectIcon: 'Select icon',
564566
selectItem: 'Select item',
565567
selectLink: 'Configure link',
568+
addLink: 'Add Link',
569+
updateLink: 'Update Link',
566570
selectMacro: 'Select macro',
567571
selectContent: 'Select content',
568572
selectContentType: 'Select content type',
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Node, mergeAttributes } from '@tiptap/core';
2+
3+
export interface DivOptions {
4+
/**
5+
* HTML attributes to add to the element.
6+
* @default {}
7+
* @example { class: 'foo' }
8+
*/
9+
HTMLAttributes: Record<string, any>;
10+
}
11+
12+
export const Div = Node.create<DivOptions>({
13+
name: 'div',
14+
15+
priority: 50,
16+
17+
group: 'block',
18+
19+
content: 'inline*',
20+
21+
addOptions() {
22+
return { HTMLAttributes: {} };
23+
},
24+
25+
parseHTML() {
26+
return [{ tag: 'div' }];
27+
},
28+
29+
renderHTML({ HTMLAttributes }) {
30+
return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
31+
},
32+
});
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { Extension } from '@tiptap/core';
2+
3+
/**
4+
* Converts camelCase to kebab-case.
5+
* @param {string} str - The string to convert.
6+
* @returns {string} The converted string.
7+
*/
8+
function camelCaseToKebabCase(str: string): string {
9+
return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, ($, ofs) => (ofs ? '-' : '') + $.toLowerCase());
10+
}
11+
12+
export interface HtmlGlobalAttributesOptions {
13+
/**
14+
* The types where the text align attribute can be applied.
15+
* @default []
16+
* @example ['heading', 'paragraph']
17+
*/
18+
types: Array<string>;
19+
}
20+
21+
export const HtmlGlobalAttributes = Extension.create<HtmlGlobalAttributesOptions>({
22+
name: 'htmlGlobalAttributes',
23+
24+
addOptions() {
25+
return { types: [] };
26+
},
27+
28+
addGlobalAttributes() {
29+
return [
30+
{
31+
types: this.options.types,
32+
attributes: {
33+
class: {},
34+
dataset: {
35+
parseHTML: (element) => element.dataset,
36+
renderHTML: (attributes) => {
37+
const keys = attributes.dataset ? Object.keys(attributes.dataset) : [];
38+
if (!keys.length) return {};
39+
const dataAtrrs: Record<string, string> = {};
40+
keys.forEach((key) => {
41+
dataAtrrs['data-' + camelCaseToKebabCase(key)] = attributes.dataset[key];
42+
});
43+
return dataAtrrs;
44+
},
45+
},
46+
id: {},
47+
style: {},
48+
},
49+
},
50+
];
51+
},
52+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Node, mergeAttributes } from '@tiptap/core';
2+
3+
export interface SpanOptions {
4+
/**
5+
* HTML attributes to add to the element.
6+
* @default {}
7+
* @example { class: 'foo' }
8+
*/
9+
HTMLAttributes: Record<string, any>;
10+
}
11+
12+
export const Span = Node.create<SpanOptions>({
13+
name: 'span',
14+
15+
group: 'inline',
16+
17+
inline: true,
18+
19+
content: 'inline*',
20+
21+
addOptions() {
22+
return { HTMLAttributes: {} };
23+
},
24+
25+
parseHTML() {
26+
return [{ tag: 'span' }];
27+
},
28+
29+
renderHTML({ HTMLAttributes }) {
30+
return ['span', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
31+
},
32+
});

src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const umbEmbeddedMedia = Node.create({
88
inline() {
99
return this.options.inline;
1010
},
11+
1112
atom: true,
1213
marks: '',
1314
draggable: true,
@@ -19,12 +20,18 @@ export const umbEmbeddedMedia = Node.create({
1920
'data-embed-height': { default: 240 },
2021
'data-embed-url': { default: null },
2122
'data-embed-width': { default: 360 },
22-
markup: { default: null },
23+
markup: { default: null, parseHTML: (element) => element.innerHTML },
2324
};
2425
},
2526

2627
parseHTML() {
27-
return [{ tag: 'div', class: 'umb-embed-holder', getAttrs: (node) => ({ markup: node.innerHTML }) }];
28+
return [
29+
{
30+
tag: 'div',
31+
priority: 100,
32+
getAttrs: (dom) => dom.classList.contains('umb-embed-holder') && null,
33+
},
34+
];
2835
},
2936

3037
renderHTML({ HTMLAttributes }) {

src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@ export { TextAlign } from '@tiptap/extension-text-align';
2828
export { Underline } from '@tiptap/extension-underline';
2929

3030
// CUSTOM EXTENSIONS
31-
export * from './extensions/tiptap-umb-embedded-media.extension.js';
31+
export * from './extensions/tiptap-div.extension.js';
3232
export * from './extensions/tiptap-figcaption.extension.js';
3333
export * from './extensions/tiptap-figure.extension.js';
34+
export * from './extensions/tiptap-span.extension.js';
35+
export * from './extensions/tiptap-html-global-attributes.extension.js';
36+
export * from './extensions/tiptap-umb-embedded-media.extension.js';
3437
export * from './extensions/tiptap-umb-image.extension.js';
3538
export * from './extensions/tiptap-umb-link.extension.js';

src/Umbraco.Web.UI.Client/src/packages/code-editor/code-editor-modal/code-editor-modal.element.ts

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import type { UmbCodeEditorElement } from '../components/code-editor.element.js';
22
import type { UmbCodeEditorModalData, UmbCodeEditorModalValue } from './code-editor-modal.token.js';
3-
import { css, html, ifDefined, customElement, query } from '@umbraco-cms/backoffice/external/lit';
3+
import { css, customElement, html, ifDefined, query } from '@umbraco-cms/backoffice/external/lit';
44
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
55

6-
const elementName = 'umb-code-editor-modal';
7-
8-
@customElement(elementName)
6+
@customElement('umb-code-editor-modal')
97
export class UmbCodeEditorModalElement extends UmbModalBaseElement<UmbCodeEditorModalData, UmbCodeEditorModalValue> {
108
@query('umb-code-editor')
119
_codeEditor?: UmbCodeEditorElement;
@@ -18,29 +16,39 @@ export class UmbCodeEditorModalElement extends UmbModalBaseElement<UmbCodeEditor
1816
#handleCancel() {
1917
this.modalContext?.reject();
2018
}
19+
20+
#onLoaded() {
21+
if (this.data?.formatOnLoad) {
22+
setTimeout(() => {
23+
this._codeEditor?.editor?.monacoEditor?.getAction('editor.action.formatDocument')?.run();
24+
}, 100);
25+
}
26+
}
27+
2128
override render() {
2229
return html`
2330
<umb-body-layout .headline=${this.data?.headline ?? 'Code Editor'}>
2431
<div id="editor-box">${this.#renderCodeEditor()}</div>
25-
<div slot="actions">
26-
<uui-button
27-
id="cancel"
28-
label=${this.localize.term('general_cancel')}
29-
@click=${this.#handleCancel}></uui-button>
30-
<uui-button
31-
id="confirm"
32-
color="${this.data?.color || 'positive'}"
33-
look="primary"
34-
label="${this.data?.confirmLabel || this.localize.term('general_submit')}"
35-
@click=${this.#handleConfirm}></uui-button>
36-
</div>
32+
<uui-button
33+
slot="actions"
34+
label=${this.localize.term('general_cancel')}
35+
@click=${this.#handleCancel}></uui-button>
36+
<uui-button
37+
slot="actions"
38+
color=${this.data?.color || 'positive'}
39+
look="primary"
40+
label=${this.data?.confirmLabel || this.localize.term('general_submit')}
41+
@click=${this.#handleConfirm}></uui-button>
3742
</umb-body-layout>
3843
`;
3944
}
4045

4146
#renderCodeEditor() {
4247
return html`
43-
<umb-code-editor language=${ifDefined(this.data?.language)} .code=${this.data?.content ?? ''}></umb-code-editor>
48+
<umb-code-editor
49+
language=${ifDefined(this.data?.language)}
50+
.code=${this.data?.content ?? ''}
51+
@loaded=${this.#onLoaded}></umb-code-editor>
4452
`;
4553
}
4654

@@ -63,6 +71,6 @@ export default UmbCodeEditorModalElement;
6371

6472
declare global {
6573
interface HTMLElementTagNameMap {
66-
[elementName]: UmbCodeEditorModalElement;
74+
'umb-code-editor-modal': UmbCodeEditorModalElement;
6775
}
6876
}

0 commit comments

Comments
 (0)