typo3-typescript-sources 14.3.4 → 14.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.last-synced-tag +1 -1
- package/README.md +1 -1
- package/backend/drag-uploader.ts +21 -4
- package/backend/element/editable-page-title.ts +3 -1
- package/backend/element/status-indicator-element.ts +93 -0
- package/backend/form-engine/container/inline-control-container.ts +7 -0
- package/backend/form-engine/element/slug-element.ts +13 -5
- package/backend/form-engine/field-wizard/shortcut-validation.ts +61 -0
- package/backend/form-engine/inline-relation/ajax-dispatcher.ts +5 -0
- package/backend/image-manipulation.ts +1 -1
- package/backend/layout-module/sticky-language-header.ts +186 -0
- package/backend/live-search/element/result/item/item.ts +2 -0
- package/backend/live-search/element/result/result-detail-container.ts +14 -1
- package/backend/module-docheader.ts +58 -0
- package/backend/page-wizard/steps/position-step.ts +5 -0
- package/backend/settings/type/stringlist.ts +7 -0
- package/backend/tests/settings/type/stringlist-test.ts +90 -0
- package/backend/toolbar/system-information-menu.ts +1 -0
- package/backend/tree/page-position-select.ts +25 -0
- package/backend/wizard/finisher/event-finisher.ts +62 -0
- package/backend/wizard/wizard.ts +99 -3
- package/dashboard/dashboard-wizard.ts +86 -0
- package/dashboard/dashboard.ts +28 -76
- package/dashboard/wizard/finisher/dashboard-wizard-submission-service.ts +43 -0
- package/dashboard/wizard/steps/preset-step.ts +164 -0
- package/dashboard/wizard/steps/title-step.ts +90 -0
- package/form/backend/form-editor/core.ts +22 -9
- package/form/backend/form-editor/inspector-component.ts +61 -38
- package/form/backend/form-editor/stage-component.ts +2 -2
- package/form/backend/form-wizard/form-wizard.ts +1 -0
- package/form/backend/form-wizard/steps/settings-step.ts +0 -10
- package/install/router.ts +1 -0
- package/package.json +2 -2
- package/redirects/event-handler.ts +3 -3
- package/scheduler/scheduler.ts +2 -2
- package/workspaces/renderable/record-table.ts +1 -1
- package/workspaces/workspace-state.ts +1 -1
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the TYPO3 CMS project.
|
|
3
|
+
*
|
|
4
|
+
* It is free software; you can redistribute it and/or modify it under
|
|
5
|
+
* the terms of the GNU General Public License, either version 2
|
|
6
|
+
* of the License, or any later version.
|
|
7
|
+
*
|
|
8
|
+
* For the full copyright and license information, please read the
|
|
9
|
+
* LICENSE.txt file that was distributed with this source code.
|
|
10
|
+
*
|
|
11
|
+
* The TYPO3 project - inspiring people to share!
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { html, type TemplateResult } from 'lit';
|
|
15
|
+
import { live } from 'lit/directives/live.js';
|
|
16
|
+
import { Task, TaskStatus } from '@lit/task';
|
|
17
|
+
import type { WizardStepInterface } from '@typo3/backend/wizard/steps/wizard-step-interface';
|
|
18
|
+
import type { WizardStepValueInterface } from '@typo3/backend/wizard/steps/wizard-step-value-interface';
|
|
19
|
+
import type { WizardStepSummaryInterface } from '@typo3/backend/wizard/steps/wizard-step-summary-interface';
|
|
20
|
+
import type { WizardStepAfterRenderInterface } from '@typo3/backend/wizard/steps/wizard-step-after-render-interface';
|
|
21
|
+
import type { SummaryItem } from '@typo3/backend/wizard/steps/summary-item-interface';
|
|
22
|
+
import AjaxRequest from '@typo3/core/ajax/ajax-request';
|
|
23
|
+
import type { DashboardWizardContext } from '@typo3/dashboard/dashboard-wizard';
|
|
24
|
+
import type { DashboardPresetInterface } from '@typo3/dashboard/dashboard';
|
|
25
|
+
import labels from '~labels/dashboard.messages';
|
|
26
|
+
|
|
27
|
+
export class PresetStep implements WizardStepInterface, WizardStepValueInterface, WizardStepSummaryInterface, WizardStepAfterRenderInterface {
|
|
28
|
+
readonly key = 'preset';
|
|
29
|
+
readonly title = labels.get('dashboard.wizard.preset.title');
|
|
30
|
+
readonly autoAdvance = true;
|
|
31
|
+
|
|
32
|
+
private task: Task<[], DashboardPresetInterface[]>;
|
|
33
|
+
private selectedPreset: string | null = null;
|
|
34
|
+
private hasDispatchedAutoAdvance: boolean = false;
|
|
35
|
+
|
|
36
|
+
constructor(private readonly context: DashboardWizardContext) {
|
|
37
|
+
this.initPresetsTask();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public isComplete(): boolean {
|
|
41
|
+
return this.getValue() !== null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public render(): TemplateResult {
|
|
45
|
+
if (this.task.status === TaskStatus.INITIAL) {
|
|
46
|
+
this.task.run();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return this.task.render({
|
|
50
|
+
complete: (presets: DashboardPresetInterface[]) => {
|
|
51
|
+
let shouldAutoAdvance = false;
|
|
52
|
+
|
|
53
|
+
// Initialize from store, otherwise preselect the first preset
|
|
54
|
+
if (this.getValue() === null) {
|
|
55
|
+
const storedValue = this.context.getStoreData(this.key);
|
|
56
|
+
if (storedValue != null) {
|
|
57
|
+
this.setValue(storedValue);
|
|
58
|
+
} else if (presets.length > 0) {
|
|
59
|
+
this.setValue(presets[0].identifier);
|
|
60
|
+
shouldAutoAdvance = presets.length === 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (shouldAutoAdvance && !this.hasDispatchedAutoAdvance) {
|
|
65
|
+
this.hasDispatchedAutoAdvance = true;
|
|
66
|
+
this.context.dispatchAutoAdvance();
|
|
67
|
+
return this.context.wizard.renderLoader();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return html`
|
|
71
|
+
<div class="dashboard-wizard-preset">
|
|
72
|
+
<h2 class="h4">${labels.get('dashboard.wizard.preset.headline')}</h2>
|
|
73
|
+
<div class="form-check-card-container">
|
|
74
|
+
${presets.map((preset: DashboardPresetInterface) => html`
|
|
75
|
+
<div class="form-check form-check-type-card">
|
|
76
|
+
<input
|
|
77
|
+
class="form-check-input"
|
|
78
|
+
type="radio"
|
|
79
|
+
name="preset"
|
|
80
|
+
id="preset-${preset.identifier}"
|
|
81
|
+
value="${preset.identifier}"
|
|
82
|
+
.checked="${live(this.getValue() === preset.identifier)}"
|
|
83
|
+
@change="${() => this.setValue(preset.identifier)}"
|
|
84
|
+
>
|
|
85
|
+
<label class="form-check-label" for="preset-${preset.identifier}">
|
|
86
|
+
<span class="form-check-label-header form-check-label-header-inherit">
|
|
87
|
+
<typo3-backend-icon identifier="${preset.icon}" size="small"></typo3-backend-icon>
|
|
88
|
+
${preset.title}
|
|
89
|
+
</span>
|
|
90
|
+
<span class="form-check-label-body">${preset.description}</span>
|
|
91
|
+
</label>
|
|
92
|
+
</div>
|
|
93
|
+
`)}
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
`;
|
|
97
|
+
},
|
|
98
|
+
error: (error: unknown) => this.context.wizard.renderError(labels.get('dashboard.wizard.preset.loadError'), error),
|
|
99
|
+
pending: () => this.context.wizard.renderLoader()
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public reset(): void {
|
|
104
|
+
this.selectedPreset = null;
|
|
105
|
+
this.context.clearStoreData(this.key);
|
|
106
|
+
this.initPresetsTask();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public getValue(): string {
|
|
110
|
+
return this.selectedPreset;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public setValue(value: string): void {
|
|
114
|
+
this.selectedPreset = value;
|
|
115
|
+
this.context.wizard.requestUpdate();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
public beforeAdvance(): void {
|
|
119
|
+
this.context.setStoreData(this.key, this.getValue());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
public getSummaryData(): SummaryItem[] {
|
|
123
|
+
const selectedIdentifier = this.context.getStoreData(this.key);
|
|
124
|
+
if (!selectedIdentifier || !this.task.value) {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const selectedPreset = this.task.value.find((preset: DashboardPresetInterface) => preset.identifier === selectedIdentifier);
|
|
129
|
+
if (!selectedPreset) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return [{
|
|
134
|
+
label: this.title,
|
|
135
|
+
value: html`
|
|
136
|
+
<typo3-backend-icon identifier="${selectedPreset.icon}" size="small" class="me-1"></typo3-backend-icon>
|
|
137
|
+
${selectedPreset.title}
|
|
138
|
+
`
|
|
139
|
+
}];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public async afterRender(): Promise<void> {
|
|
143
|
+
// Presets load asynchronously; signal readiness so the wizard moves focus
|
|
144
|
+
// (onto the selected preset) only once they are rendered.
|
|
145
|
+
try {
|
|
146
|
+
await this.task.taskComplete;
|
|
147
|
+
} catch {
|
|
148
|
+
// ignore — the error state is rendered by the step
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private initPresetsTask(): void {
|
|
153
|
+
this.task = new Task(this.context.wizard, {
|
|
154
|
+
task: async (): Promise<DashboardPresetInterface[]> => {
|
|
155
|
+
const response = await new AjaxRequest(TYPO3.settings.ajaxUrls.dashboard_presets_get).get({ cache: 'no-cache' });
|
|
156
|
+
const data = await response.resolve();
|
|
157
|
+
return Object.values(data as Record<string, DashboardPresetInterface>).filter((preset: DashboardPresetInterface) => preset.showInWizard);
|
|
158
|
+
},
|
|
159
|
+
autoRun: false
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export default PresetStep;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the TYPO3 CMS project.
|
|
3
|
+
*
|
|
4
|
+
* It is free software; you can redistribute it and/or modify it under
|
|
5
|
+
* the terms of the GNU General Public License, either version 2
|
|
6
|
+
* of the License, or any later version.
|
|
7
|
+
*
|
|
8
|
+
* For the full copyright and license information, please read the
|
|
9
|
+
* LICENSE.txt file that was distributed with this source code.
|
|
10
|
+
*
|
|
11
|
+
* The TYPO3 project - inspiring people to share!
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { html, type TemplateResult } from 'lit';
|
|
15
|
+
import { live } from 'lit/directives/live.js';
|
|
16
|
+
import type { WizardStepInterface } from '@typo3/backend/wizard/steps/wizard-step-interface';
|
|
17
|
+
import type { WizardStepValueInterface } from '@typo3/backend/wizard/steps/wizard-step-value-interface';
|
|
18
|
+
import type { WizardStepSummaryInterface } from '@typo3/backend/wizard/steps/wizard-step-summary-interface';
|
|
19
|
+
import type { SummaryItem } from '@typo3/backend/wizard/steps/summary-item-interface';
|
|
20
|
+
import type { DashboardWizardContext } from '@typo3/dashboard/dashboard-wizard';
|
|
21
|
+
import labels from '~labels/dashboard.messages';
|
|
22
|
+
|
|
23
|
+
export class TitleStep implements WizardStepInterface, WizardStepValueInterface, WizardStepSummaryInterface {
|
|
24
|
+
readonly key = 'title';
|
|
25
|
+
readonly title = labels.get('dashboard.wizard.title.title');
|
|
26
|
+
readonly autoAdvance = false;
|
|
27
|
+
|
|
28
|
+
private titleValue: string = '';
|
|
29
|
+
|
|
30
|
+
constructor(private readonly context: DashboardWizardContext) {
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public isComplete(): boolean {
|
|
34
|
+
return this.getValue().trim() !== '';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public render(): TemplateResult {
|
|
38
|
+
if (this.titleValue === '') {
|
|
39
|
+
this.titleValue = this.context.getStoreData(this.key) ?? '';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return html`
|
|
43
|
+
<div class="dashboard-wizard-title">
|
|
44
|
+
<div class="form-group">
|
|
45
|
+
<label class="form-label" for="dashboard-wizard-title-input">${labels.get('dashboard.title')}</label>
|
|
46
|
+
<input
|
|
47
|
+
type="text"
|
|
48
|
+
id="dashboard-wizard-title-input"
|
|
49
|
+
class="form-control"
|
|
50
|
+
required="required"
|
|
51
|
+
.value="${live(this.titleValue)}"
|
|
52
|
+
@input="${(event: InputEvent) => this.setValue((event.target as HTMLInputElement).value)}"
|
|
53
|
+
>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public reset(): void {
|
|
60
|
+
this.titleValue = '';
|
|
61
|
+
this.context.clearStoreData(this.key);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public getValue(): string {
|
|
65
|
+
return this.titleValue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public setValue(value: string): void {
|
|
69
|
+
this.titleValue = value;
|
|
70
|
+
this.context.wizard.requestUpdate();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public beforeAdvance(): void {
|
|
74
|
+
this.context.setStoreData(this.key, this.getValue());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public getSummaryData(): SummaryItem[] {
|
|
78
|
+
const title = this.context.getStoreData(this.key);
|
|
79
|
+
if (!title) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return [{
|
|
84
|
+
label: this.title,
|
|
85
|
+
value: title
|
|
86
|
+
}];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default TitleStep;
|
|
@@ -720,17 +720,30 @@ function extendModel<D extends object, T extends ModelData<D>>(
|
|
|
720
720
|
modelToExtend.set(pathPrefix, modelExtension, disablePublishersOnSet);
|
|
721
721
|
} else {
|
|
722
722
|
const _modelExtension = { ...modelExtension } as Record<string, T | [] | Record<string, never>>;
|
|
723
|
-
for (const key of Object.keys(_modelExtension)) {
|
|
724
|
-
const path = (pathPrefix === '') ? key : pathPrefix + '.' + key;
|
|
725
723
|
|
|
726
|
-
|
|
724
|
+
// A "leaf map" is an object whose values are all scalars (e.g. select
|
|
725
|
+
// options or finisher recipients keyed by their value). Its keys may
|
|
726
|
+
// contain dots (e.g. email addresses used as recipient keys) and must
|
|
727
|
+
// therefore be stored as a whole, otherwise Model.set() would split the
|
|
728
|
+
// key into a nested object path and thus corrupt the data.
|
|
729
|
+
const isLeafMap = pathPrefix !== '' && Object.values(_modelExtension).every(
|
|
730
|
+
(value) => value === null || typeof value !== 'object'
|
|
731
|
+
);
|
|
727
732
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
733
|
+
if (isLeafMap) {
|
|
734
|
+
modelToExtend.on(pathPrefix, 'core/formElement/somePropertyChanged');
|
|
735
|
+
modelToExtend.set(pathPrefix, modelExtension, disablePublishersOnSet);
|
|
736
|
+
} else {
|
|
737
|
+
for (const key of Object.keys(_modelExtension)) {
|
|
738
|
+
const path = (pathPrefix === '') ? key : pathPrefix + '.' + key;
|
|
739
|
+
|
|
740
|
+
modelToExtend.on(path, 'core/formElement/somePropertyChanged');
|
|
741
|
+
|
|
742
|
+
if (_modelExtension[key] !== null && (typeof (_modelExtension[key]) === 'object' || Array.isArray(_modelExtension[key]))) {
|
|
743
|
+
extendModel(modelToExtend, _modelExtension[key], path, disablePublishersOnSet);
|
|
744
|
+
} else {
|
|
745
|
+
modelToExtend.set(path, _modelExtension[key], disablePublishersOnSet);
|
|
746
|
+
}
|
|
734
747
|
}
|
|
735
748
|
}
|
|
736
749
|
}
|
|
@@ -22,6 +22,7 @@ import Icons from '@typo3/backend/icons';
|
|
|
22
22
|
import Modal from '@typo3/backend/modal';
|
|
23
23
|
import { MessageUtility } from '@typo3/backend/utility/message-utility';
|
|
24
24
|
import Sortable from 'sortablejs';
|
|
25
|
+
import dompurify from 'dompurify';
|
|
25
26
|
import {
|
|
26
27
|
type PropertyGridEditorEntry,
|
|
27
28
|
PropertyGridEditorUpdateEvent
|
|
@@ -158,6 +159,37 @@ function getFormElementDefinition<T extends keyof FormElementDefinition>(
|
|
|
158
159
|
return getFormEditorApp().getFormElementDefinition(formElement, formElementDefinitionKey);
|
|
159
160
|
}
|
|
160
161
|
|
|
162
|
+
const DOMPURIFY_LABEL_CONFIG = {
|
|
163
|
+
ALLOWED_TAGS: ['abbr', 'b', 'br', 'code', 'em', 'i', 'kbd', 'span', 'strong', 'u'],
|
|
164
|
+
ALLOWED_ATTR: ['class', 'title', 'role'],
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const DOMPURIFY_DESCRIPTION_CONFIG = {
|
|
168
|
+
ALLOWED_TAGS: ['a', 'abbr', 'blockquote', 'b', 'br', 'code', 'em', 'i', 'kbd', 'li', 'p', 'pre', 'span', 'strong', 'u', 'ul', 'ol'],
|
|
169
|
+
ALLOWED_ATTR: ['class', 'href', 'title', 'target', 'role', 'rel'],
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Safely sets sanitized HTML as label content on a DOM element.
|
|
174
|
+
* Only phrasing content (inline elements) is allowed, as required by the HTML
|
|
175
|
+
* specification for <label> elements.
|
|
176
|
+
*/
|
|
177
|
+
function setLabelHtmlContent(element: HTMLElement | null | undefined, html: string): void {
|
|
178
|
+
if (element) {
|
|
179
|
+
element.innerHTML = dompurify.sanitize(html, DOMPURIFY_LABEL_CONFIG);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Safely sets sanitized HTML as description content on a DOM element.
|
|
185
|
+
* Allows limited block-level markup (e.g. <p>, <ul>) in addition to inline elements.
|
|
186
|
+
*/
|
|
187
|
+
function setHtmlContent(element: HTMLElement | null | undefined, html: string): void {
|
|
188
|
+
if (element) {
|
|
189
|
+
element.innerHTML = dompurify.sanitize(html, DOMPURIFY_DESCRIPTION_CONFIG);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
161
193
|
/**
|
|
162
194
|
* @publish view/inspector/editor/insert/perform
|
|
163
195
|
*/
|
|
@@ -853,7 +885,7 @@ export function renderCollectionElementSelectionEditor(
|
|
|
853
885
|
collectionContainer?.replaceChildren();
|
|
854
886
|
|
|
855
887
|
const labelEl = getHelper().getTemplatePropertyElement('label', editorHtml);
|
|
856
|
-
|
|
888
|
+
setLabelHtmlContent(labelEl, editorConfiguration.label);
|
|
857
889
|
const selectElement = getHelper().getTemplatePropertyElement('selectOptions', editorHtml) as HTMLSelectElement | null;
|
|
858
890
|
const hasAlreadySelectedCollectionElements = (
|
|
859
891
|
!getUtility().isUndefinedOrNull(alreadySelectedCollectionElements) &&
|
|
@@ -897,7 +929,7 @@ export function renderCollectionElementSelectionEditor(
|
|
|
897
929
|
selectGroup?.remove();
|
|
898
930
|
const labelNoSelect = getHelper().getTemplatePropertyElement('label-no-select', editorHtml);
|
|
899
931
|
if (hasAlreadySelectedCollectionElements) {
|
|
900
|
-
|
|
932
|
+
setLabelHtmlContent(labelNoSelect, editorConfiguration.label);
|
|
901
933
|
} else {
|
|
902
934
|
labelNoSelect?.remove();
|
|
903
935
|
}
|
|
@@ -1047,7 +1079,7 @@ export function renderCollectionElementHeaderEditor(
|
|
|
1047
1079
|
const panelTitle = getHelper().getTemplatePropertyElement('panel-title', editorHtml);
|
|
1048
1080
|
if (panelTitle) {
|
|
1049
1081
|
panelTitle.removeAttribute('data-template-property');
|
|
1050
|
-
panelTitle
|
|
1082
|
+
setLabelHtmlContent(panelTitle, editorConfiguration.label);
|
|
1051
1083
|
}
|
|
1052
1084
|
}
|
|
1053
1085
|
}
|
|
@@ -1075,7 +1107,7 @@ export function renderFileMaxSizeEditor(
|
|
|
1075
1107
|
if (editorConfiguration.label) {
|
|
1076
1108
|
const element = getHelper().getTemplatePropertyElement('label', editorHtml);
|
|
1077
1109
|
const maximumFileSize = element?.getAttribute(getHelper().getDomElementDataAttribute('maximumFileSize'));
|
|
1078
|
-
element
|
|
1110
|
+
setLabelHtmlContent(element, editorConfiguration.label.replace('{0}', maximumFileSize ?? ''));
|
|
1079
1111
|
}
|
|
1080
1112
|
}
|
|
1081
1113
|
|
|
@@ -1106,8 +1138,7 @@ export function renderTextEditor(
|
|
|
1106
1138
|
1475421056
|
|
1107
1139
|
);
|
|
1108
1140
|
|
|
1109
|
-
getHelper().getTemplatePropertyElement('label', editorHtml)
|
|
1110
|
-
?.append(document.createTextNode(editorConfiguration.label));
|
|
1141
|
+
setLabelHtmlContent(getHelper().getTemplatePropertyElement('label', editorHtml), editorConfiguration.label);
|
|
1111
1142
|
renderDescription(editorConfiguration, editorHtml);
|
|
1112
1143
|
|
|
1113
1144
|
if (getUtility().isNonEmptyString(editorConfiguration.placeholder)) {
|
|
@@ -1194,8 +1225,7 @@ export function renderValidationErrorMessageEditor(
|
|
|
1194
1225
|
1489874124
|
|
1195
1226
|
);
|
|
1196
1227
|
|
|
1197
|
-
getHelper().getTemplatePropertyElement('label', editorHtml)
|
|
1198
|
-
?.append(document.createTextNode(editorConfiguration.label));
|
|
1228
|
+
setLabelHtmlContent(getHelper().getTemplatePropertyElement('label', editorHtml), editorConfiguration.label);
|
|
1199
1229
|
renderDescription(editorConfiguration, editorHtml);
|
|
1200
1230
|
|
|
1201
1231
|
const propertyPath = getFormEditorApp().buildPropertyPath(editorConfiguration.propertyPath);
|
|
@@ -1254,8 +1284,7 @@ export function renderCountrySelectEditor(
|
|
|
1254
1284
|
collectionName
|
|
1255
1285
|
);
|
|
1256
1286
|
|
|
1257
|
-
getHelper().getTemplatePropertyElement('label', editorHtml)
|
|
1258
|
-
?.append(document.createTextNode(editorConfiguration.label));
|
|
1287
|
+
setLabelHtmlContent(getHelper().getTemplatePropertyElement('label', editorHtml), editorConfiguration.label);
|
|
1259
1288
|
renderDescription(editorConfiguration, editorHtml);
|
|
1260
1289
|
|
|
1261
1290
|
const selectElement = getHelper().getTemplatePropertyElement('selectOptions', editorHtml) as HTMLSelectElement | null;
|
|
@@ -1326,8 +1355,7 @@ export function renderSingleSelectEditor(
|
|
|
1326
1355
|
collectionName
|
|
1327
1356
|
);
|
|
1328
1357
|
|
|
1329
|
-
getHelper().getTemplatePropertyElement('label', editorHtml)
|
|
1330
|
-
?.append(document.createTextNode(editorConfiguration.label));
|
|
1358
|
+
setLabelHtmlContent(getHelper().getTemplatePropertyElement('label', editorHtml), editorConfiguration.label);
|
|
1331
1359
|
renderDescription(editorConfiguration, editorHtml);
|
|
1332
1360
|
|
|
1333
1361
|
const selectElement = getHelper().getTemplatePropertyElement('selectOptions', editorHtml) as HTMLSelectElement | null;
|
|
@@ -1386,8 +1414,7 @@ export function renderMultiSelectEditor(
|
|
|
1386
1414
|
collectionName
|
|
1387
1415
|
);
|
|
1388
1416
|
|
|
1389
|
-
getHelper().getTemplatePropertyElement('label', editorHtml)
|
|
1390
|
-
?.append(document.createTextNode(editorConfiguration.label));
|
|
1417
|
+
setLabelHtmlContent(getHelper().getTemplatePropertyElement('label', editorHtml), editorConfiguration.label);
|
|
1391
1418
|
renderDescription(editorConfiguration, editorHtml);
|
|
1392
1419
|
|
|
1393
1420
|
const selectElement = getHelper().getTemplatePropertyElement('selectOptions', editorHtml) as HTMLSelectElement | null;
|
|
@@ -1457,8 +1484,7 @@ export function renderGridColumnViewPortConfigurationEditor(
|
|
|
1457
1484
|
return;
|
|
1458
1485
|
}
|
|
1459
1486
|
|
|
1460
|
-
getHelper().getTemplatePropertyElement('label', editorHtml)
|
|
1461
|
-
?.append(document.createTextNode(editorConfiguration.label));
|
|
1487
|
+
setLabelHtmlContent(getHelper().getTemplatePropertyElement('label', editorHtml), editorConfiguration.label);
|
|
1462
1488
|
|
|
1463
1489
|
const viewportButtonSel = getHelper().getDomElementDataIdentifierSelector('viewportButton');
|
|
1464
1490
|
const viewportButtonTemplate = editorHtml.querySelector(viewportButtonSel)?.cloneNode(true) as HTMLElement | null;
|
|
@@ -1480,15 +1506,15 @@ export function renderGridColumnViewPortConfigurationEditor(
|
|
|
1480
1506
|
|
|
1481
1507
|
const labelEl = getHelper().getTemplatePropertyElement('numbersOfColumnsToUse-label', numbersOfColumnsTemplateClone);
|
|
1482
1508
|
if (labelEl) {
|
|
1483
|
-
labelEl
|
|
1509
|
+
setLabelHtmlContent(labelEl,
|
|
1484
1510
|
editorConfiguration.configurationOptions.numbersOfColumnsToUse.label
|
|
1485
1511
|
.replace('{@viewPortLabel}', element.dataset.viewPortLabel ?? '')
|
|
1486
|
-
)
|
|
1512
|
+
);
|
|
1487
1513
|
}
|
|
1488
1514
|
|
|
1489
1515
|
const descEl = getHelper().getTemplatePropertyElement('numbersOfColumnsToUse-description', numbersOfColumnsTemplateClone);
|
|
1490
1516
|
if (descEl) {
|
|
1491
|
-
descEl
|
|
1517
|
+
setHtmlContent(descEl, editorConfiguration.configurationOptions.numbersOfColumnsToUse.description);
|
|
1492
1518
|
}
|
|
1493
1519
|
|
|
1494
1520
|
const propertyPath = editorConfiguration.configurationOptions.numbersOfColumnsToUse.propertyPath
|
|
@@ -1579,8 +1605,7 @@ export function renderPropertyGridEditor(
|
|
|
1579
1605
|
1475419232
|
|
1580
1606
|
);
|
|
1581
1607
|
|
|
1582
|
-
getHelper().getTemplatePropertyElement('label', editorHtml)
|
|
1583
|
-
?.append(document.createTextNode(editorConfiguration.label));
|
|
1608
|
+
setLabelHtmlContent(getHelper().getTemplatePropertyElement('label', editorHtml), editorConfiguration.label);
|
|
1584
1609
|
renderDescription(editorConfiguration, editorHtml);
|
|
1585
1610
|
|
|
1586
1611
|
const propertyPathPrefix = (() => {
|
|
@@ -1740,8 +1765,7 @@ export function renderRequiredValidatorEditor(
|
|
|
1740
1765
|
);
|
|
1741
1766
|
|
|
1742
1767
|
const validatorIdentifier = editorConfiguration.validatorIdentifier;
|
|
1743
|
-
getHelper().getTemplatePropertyElement('label', editorHtml)
|
|
1744
|
-
?.append(document.createTextNode(editorConfiguration.label));
|
|
1768
|
+
setLabelHtmlContent(getHelper().getTemplatePropertyElement('label', editorHtml), editorConfiguration.label);
|
|
1745
1769
|
|
|
1746
1770
|
let propertyValue: string;
|
|
1747
1771
|
let propertyPath: string;
|
|
@@ -1767,11 +1791,15 @@ export function renderRequiredValidatorEditor(
|
|
|
1767
1791
|
const validationErrorMessageTemplateClone = validationErrorMessageTemplate?.cloneNode(true) as HTMLElement | null;
|
|
1768
1792
|
getEditorWrapperDomElement(editorHtml)?.after(validationErrorMessageTemplateClone);
|
|
1769
1793
|
|
|
1770
|
-
|
|
1771
|
-
|
|
1794
|
+
setLabelHtmlContent(
|
|
1795
|
+
getHelper().getTemplatePropertyElement('validationErrorMessage-label', validationErrorMessageTemplateClone),
|
|
1796
|
+
editorConfiguration.configurationOptions.validationErrorMessage.label
|
|
1797
|
+
);
|
|
1772
1798
|
|
|
1773
|
-
|
|
1774
|
-
|
|
1799
|
+
setHtmlContent(
|
|
1800
|
+
getHelper().getTemplatePropertyElement('validationErrorMessage-description', validationErrorMessageTemplateClone),
|
|
1801
|
+
editorConfiguration.configurationOptions.validationErrorMessage.description
|
|
1802
|
+
);
|
|
1775
1803
|
|
|
1776
1804
|
propertyData = getCurrentlySelectedFormElement().get(validationErrorMessagePropertyPath);
|
|
1777
1805
|
if (getUtility().isUndefinedOrNull(propertyData)) {
|
|
@@ -1863,8 +1891,7 @@ export function renderCheckboxEditor(
|
|
|
1863
1891
|
1476218674
|
|
1864
1892
|
);
|
|
1865
1893
|
|
|
1866
|
-
getHelper().getTemplatePropertyElement('label', editorHtml)
|
|
1867
|
-
?.append(document.createTextNode(editorConfiguration.label));
|
|
1894
|
+
setLabelHtmlContent(getHelper().getTemplatePropertyElement('label', editorHtml), editorConfiguration.label);
|
|
1868
1895
|
renderDescription(editorConfiguration, editorHtml);
|
|
1869
1896
|
|
|
1870
1897
|
const propertyPath = getFormEditorApp()
|
|
@@ -1927,8 +1954,7 @@ export function renderTextareaEditor(
|
|
|
1927
1954
|
const propertyPath = getFormEditorApp()
|
|
1928
1955
|
.buildPropertyPath(editorConfiguration.propertyPath, collectionElementIdentifier, collectionName);
|
|
1929
1956
|
|
|
1930
|
-
getHelper().getTemplatePropertyElement('label', editorHtml)
|
|
1931
|
-
?.append(document.createTextNode(editorConfiguration.label));
|
|
1957
|
+
setLabelHtmlContent(getHelper().getTemplatePropertyElement('label', editorHtml), editorConfiguration.label);
|
|
1932
1958
|
renderDescription(editorConfiguration, editorHtml);
|
|
1933
1959
|
|
|
1934
1960
|
const propertyData = getCurrentlySelectedFormElement().get(propertyPath);
|
|
@@ -2023,10 +2049,8 @@ export function renderTypo3WinBrowserEditor(
|
|
|
2023
2049
|
1477300590
|
|
2024
2050
|
);
|
|
2025
2051
|
|
|
2026
|
-
getHelper().getTemplatePropertyElement('label', editorHtml)
|
|
2027
|
-
|
|
2028
|
-
getHelper().getTemplatePropertyElement('buttonLabel', editorHtml)
|
|
2029
|
-
?.append(document.createTextNode(editorConfiguration.buttonLabel));
|
|
2052
|
+
setLabelHtmlContent(getHelper().getTemplatePropertyElement('label', editorHtml), editorConfiguration.label);
|
|
2053
|
+
setLabelHtmlContent(getHelper().getTemplatePropertyElement('buttonLabel', editorHtml), editorConfiguration.buttonLabel);
|
|
2030
2054
|
renderDescription(editorConfiguration, editorHtml);
|
|
2031
2055
|
|
|
2032
2056
|
const formEl = editorHtml.querySelector('form');
|
|
@@ -2222,8 +2246,7 @@ export function renderDateEditor(
|
|
|
2222
2246
|
collectionName
|
|
2223
2247
|
);
|
|
2224
2248
|
|
|
2225
|
-
getHelper().getTemplatePropertyElement('label', editorHtml)
|
|
2226
|
-
?.append(document.createTextNode(editorConfiguration.label || ''));
|
|
2249
|
+
setLabelHtmlContent(getHelper().getTemplatePropertyElement('label', editorHtml), editorConfiguration.label || '');
|
|
2227
2250
|
renderDescription(editorConfiguration, editorHtml);
|
|
2228
2251
|
|
|
2229
2252
|
const editorElement = editorHtml.querySelector('typo3-form-date-editor');
|
|
@@ -2266,7 +2289,7 @@ export function renderDescription(
|
|
|
2266
2289
|
): void {
|
|
2267
2290
|
const descEl = getHelper().getTemplatePropertyElement('description', editorHtml);
|
|
2268
2291
|
if (getUtility().isNonEmptyString(editorConfiguration.description)) {
|
|
2269
|
-
|
|
2292
|
+
setHtmlContent(descEl, editorConfiguration.description);
|
|
2270
2293
|
} else {
|
|
2271
2294
|
descEl?.remove();
|
|
2272
2295
|
}
|
|
@@ -273,8 +273,8 @@ function renderNestedSortableListItem(formElement: FormElement): HTMLElement {
|
|
|
273
273
|
const childFormElements = formElement.get('renderables');
|
|
274
274
|
const hasChildren = Array.isArray(childFormElements) && childFormElements.length > 0;
|
|
275
275
|
|
|
276
|
-
// Show "Create new element" placeholder
|
|
277
|
-
if (!hasChildren) {
|
|
276
|
+
// Show "Create new element" placeholder only for composite containers (not SummaryPage).
|
|
277
|
+
if (!hasChildren && isCompositeFormElement) {
|
|
278
278
|
childList.append(createNewElementPlaceholder(formElement, 'inside'));
|
|
279
279
|
}
|
|
280
280
|
|
|
@@ -107,6 +107,7 @@ export class FormWizard extends LitElement {
|
|
|
107
107
|
<typo3-backend-wizard .steps="${this.steps}"
|
|
108
108
|
.submissionService="${this.submissionService}"
|
|
109
109
|
confirm-button-label="${formManagerLabels.get('formManager.newFormWizard.step1.title')}"
|
|
110
|
+
skip-summary
|
|
110
111
|
></typo3-backend-wizard>
|
|
111
112
|
`;
|
|
112
113
|
}
|
|
@@ -158,7 +158,6 @@ export class SettingsStep implements WizardStepInterface, WizardStepValueInterfa
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
private renderFormNameInput(): TemplateResult {
|
|
161
|
-
this.focusInput('#new-form-name');
|
|
162
161
|
return html `
|
|
163
162
|
<div class="form-group">
|
|
164
163
|
<label class="form-label" for="new-form-name">${formManagerLabels.get('formManager.form_name')}</label>
|
|
@@ -234,15 +233,6 @@ export class SettingsStep implements WizardStepInterface, WizardStepValueInterfa
|
|
|
234
233
|
template: (currentTemplates[0]?.value ?? '')
|
|
235
234
|
});
|
|
236
235
|
}
|
|
237
|
-
|
|
238
|
-
private focusInput(selector: string): void {
|
|
239
|
-
this.context.wizard.updateComplete.then(() => {
|
|
240
|
-
const input = this.context.wizard.renderRoot.querySelector<HTMLInputElement>(selector);
|
|
241
|
-
if (input) {
|
|
242
|
-
input.focus();
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
236
|
}
|
|
247
237
|
|
|
248
238
|
export default SettingsStep;
|
package/install/router.ts
CHANGED
|
@@ -25,6 +25,7 @@ import RegularEvent from '@typo3/core/event/regular-event';
|
|
|
25
25
|
import '@typo3/backend/element/progress-bar-element';
|
|
26
26
|
import { ScaffoldState } from '@typo3/backend/viewport/scaffold-state';
|
|
27
27
|
import '@typo3/backend/element/sidebar-toggle-element';
|
|
28
|
+
import 'bootstrap'; // required for panels in `handleAjaxError()`
|
|
28
29
|
|
|
29
30
|
class Router {
|
|
30
31
|
private readonly rootSelector: string = '.t3js-body';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typo3-typescript-sources",
|
|
3
|
-
"version": "14.3.
|
|
3
|
+
"version": "14.3.6",
|
|
4
4
|
"description": "TYPO3 Core TypeScript Sources - Extracted from TYPO3/typo3 repository",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"typo3": {
|
|
30
30
|
"sourceRepository": "https://github.com/TYPO3/typo3",
|
|
31
|
-
"sourceTag": "v14.3.
|
|
31
|
+
"sourceTag": "v14.3.6",
|
|
32
32
|
"sourcePath": "Build/Sources/TypeScript"
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -89,9 +89,9 @@ class EventHandler {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
private revert(correlationIds: string[]): Promise<AjaxResponse> {
|
|
92
|
-
const request = new AjaxRequest(TYPO3.settings.ajaxUrls.redirects_revert_correlation).
|
|
93
|
-
correlation_ids: correlationIds
|
|
94
|
-
})
|
|
92
|
+
const request = new AjaxRequest(TYPO3.settings.ajaxUrls.redirects_revert_correlation).post({
|
|
93
|
+
correlation_ids: correlationIds,
|
|
94
|
+
});
|
|
95
95
|
|
|
96
96
|
request.then(async (response: AjaxResponse): Promise<void> => {
|
|
97
97
|
const json = await response.resolve();
|
package/scheduler/scheduler.ts
CHANGED
|
@@ -90,14 +90,14 @@ class Scheduler {
|
|
|
90
90
|
// Schedule selected tasks for next cron run
|
|
91
91
|
const goCron: HTMLInputElement = document.createElement('input');
|
|
92
92
|
goCron.setAttribute('type', 'hidden');
|
|
93
|
-
goCron.setAttribute('name', 'scheduleCron');
|
|
93
|
+
goCron.setAttribute('name', 'action[scheduleCron]');
|
|
94
94
|
goCron.setAttribute('value', taskIds.join(','));
|
|
95
95
|
form.append(goCron);
|
|
96
96
|
} else {
|
|
97
97
|
// Execute selected tasks directly
|
|
98
98
|
const executeTasks: HTMLInputElement = document.createElement('input');
|
|
99
99
|
executeTasks.setAttribute('type', 'hidden');
|
|
100
|
-
executeTasks.setAttribute('name', 'execute');
|
|
100
|
+
executeTasks.setAttribute('name', 'action[execute]');
|
|
101
101
|
executeTasks.setAttribute('value', taskIds.join(','));
|
|
102
102
|
form.append(executeTasks);
|
|
103
103
|
}
|
|
@@ -278,7 +278,7 @@ export class RecordTableElement extends LitElement {
|
|
|
278
278
|
private renderElementActions(data: RecordData): TemplateResult[] {
|
|
279
279
|
return [
|
|
280
280
|
this.getAction(
|
|
281
|
-
data.allowedAction_view,
|
|
281
|
+
(data.allowedAction_view && data.previewUrl !== ''),
|
|
282
282
|
'preview',
|
|
283
283
|
'actions-version-workspace-preview',
|
|
284
284
|
{
|