typo3-typescript-sources 14.3.6 → 14.3.7

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.
Files changed (29) hide show
  1. package/.last-synced-tag +1 -1
  2. package/README-TYPO3.md +1 -1
  3. package/README.md +1 -1
  4. package/backend/element/contextual-record-edit-trigger.ts +9 -1
  5. package/backend/form-engine/element/extra/select-box-filter.ts +8 -5
  6. package/backend/form-engine/element/select-tree.ts +16 -2
  7. package/backend/form-engine/field-control/add-record.ts +27 -1
  8. package/backend/form-engine.ts +80 -1
  9. package/backend/localization/localization-wizard.ts +3 -0
  10. package/backend/localization/steps/handler-selection-step.ts +12 -17
  11. package/backend/localization/steps/mode-step.ts +9 -14
  12. package/backend/localization/steps/source-language-step.ts +8 -13
  13. package/backend/localization/steps/target-language-step.ts +12 -17
  14. package/backend/modal.ts +124 -1
  15. package/backend/tests/form-engine/element/extra/select-box-filter-test.ts +215 -0
  16. package/backend/tests/form-engine/element/select-tree-test.ts +121 -0
  17. package/backend/wizard/steps/finisher-step.ts +2 -2
  18. package/backend/wizard/wizard.ts +11 -4
  19. package/core/ajax/ajax-request.ts +18 -0
  20. package/core/tests/ajax/ajax-request-test.ts +1 -1
  21. package/core/tests/utility/text-normalizer-test.ts +138 -0
  22. package/core/utility/text-normalizer.ts +98 -0
  23. package/form/backend/form-editor/view-model.ts +1 -1
  24. package/form/backend/form-wizard/steps/settings-step.ts +6 -3
  25. package/package.json +2 -2
  26. package/workspaces/backend.ts +7 -2
  27. package/workspaces/renderable/comment-view.ts +4 -2
  28. package/workspaces/renderable/record-table.ts +41 -4
  29. package/workspaces/workspaces.ts +65 -12
@@ -184,6 +184,11 @@ export class SettingsStep implements WizardStepInterface, WizardStepValueInterfa
184
184
 
185
185
  let templatesFormGroup: TemplateResult | typeof nothing = nothing;
186
186
  if (templates.length > 0) {
187
+ // Auto-select first template if none selected yet
188
+ if (!this.data.template) {
189
+ this.setValue({ template: templates[0].value });
190
+ }
191
+
187
192
  templatesFormGroup = html `
188
193
  <div class="form-group">
189
194
  <label class="form-label" for="new-form-template">${formManagerLabels.get('formManager.form_template')}</label>
@@ -226,11 +231,9 @@ export class SettingsStep implements WizardStepInterface, WizardStepValueInterfa
226
231
  }
227
232
 
228
233
  private setPrototype(currentPrototype: string ): void {
229
- const currentTemplates = this.context.formManager.getTemplatesForPrototype(currentPrototype);
230
-
231
234
  this.setValue({
232
235
  prototype: currentPrototype,
233
- template: (currentTemplates[0]?.value ?? '')
236
+ template: ''
234
237
  });
235
238
  }
236
239
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typo3-typescript-sources",
3
- "version": "14.3.6",
3
+ "version": "14.3.7",
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.6",
31
+ "sourceTag": "v14.3.7",
32
32
  "sourcePath": "Build/Sources/TypeScript"
33
33
  }
34
34
  }
@@ -469,6 +469,7 @@ class Backend extends Workspaces {
469
469
  }
470
470
 
471
471
  const workspacesRecordTable = document.querySelector('typo3-workspaces-record-table');
472
+ workspacesRecordTable.additionalColumns = result.additionalColumns ?? {};
472
473
  workspacesRecordTable.results = result.data;
473
474
  }
474
475
 
@@ -517,9 +518,13 @@ class Backend extends Workspaces {
517
518
  filterFields: true
518
519
  }),
519
520
  ).then(async (response: AjaxResponse): Promise<void> => {
520
- const item = (await response.resolve())[0].result.data[0];
521
- const modalButtons = [];
521
+ const details = await response.resolve();
522
+ const item = details[0]?.result.data[0];
523
+ if (item === undefined) {
524
+ return;
525
+ }
522
526
 
527
+ const modalButtons = [];
523
528
  const content = document.createElement('typo3-workspaces-record-information');
524
529
  content.record = item;
525
530
 
@@ -16,12 +16,13 @@ import { html, LitElement, nothing, type TemplateResult } from 'lit';
16
16
  import { repeat } from 'lit/directives/repeat.js';
17
17
  import { unsafeHTML } from 'lit/directives/unsafe-html.js';
18
18
  import { nl2br } from '@typo3/core/directive/nl2br';
19
+ import labels from '~labels/workspaces.messages';
19
20
 
20
21
  type Comment = {
21
22
  user_comment: string;
22
23
  previous_stage_title: string;
23
24
  stage_title: string;
24
- tstamp: number;
25
+ tstamp: string;
25
26
  user_username: string;
26
27
  user_avatar: string
27
28
  };
@@ -64,7 +65,8 @@ export class CommentViewElement extends LitElement {
64
65
  ${comment.previous_stage_title} ⇾ ${comment.stage_title}
65
66
  </span>
66
67
  <span class="badge badge-info">
67
- ${comment.tstamp}
68
+ ${comment.tstamp ? labels.get('comment.tstamp.value', { tstamp: new Date(comment.tstamp) }) : nothing}
69
+ </span>
68
70
  </div>
69
71
  </div>
70
72
  </div>
@@ -24,6 +24,14 @@ import '@typo3/backend/element/icon-element';
24
24
  import '@typo3/backend/element/qrcode-modal-button';
25
25
  import 'bootstrap'; // for data-bs-toggle="dropdown"
26
26
 
27
+ export type AdditionalColumnValue = {
28
+ label?: string,
29
+ value?: string,
30
+ icon?: string,
31
+ title?: string,
32
+ url?: string
33
+ };
34
+
27
35
  export type RecordData = {
28
36
  table: string,
29
37
  id: string,
@@ -38,7 +46,7 @@ export type RecordData = {
38
46
  value_nextStage: number,
39
47
  value_prevStage: number,
40
48
  path_Workspace: string,
41
- lastChangedFormatted: string,
49
+ lastChanged: string,
42
50
  lastEditorId: number,
43
51
  lastEditorName: string,
44
52
  lastEditorRealName: string,
@@ -67,7 +75,8 @@ export type RecordData = {
67
75
  integrity: {
68
76
  status: string,
69
77
  messages: string
70
- }
78
+ },
79
+ additional?: Record<string, AdditionalColumnValue>
71
80
  };
72
81
 
73
82
  @customElement('typo3-workspaces-record-table')
@@ -75,6 +84,12 @@ export class RecordTableElement extends LitElement {
75
84
  @property({ type: Array })
76
85
  public results: RecordData[] = [];
77
86
 
87
+ /**
88
+ * Labels of columns added by third party extensions, indexed by column identifier.
89
+ */
90
+ @property({ type: Object })
91
+ public additionalColumns: Record<string, string> = {};
92
+
78
93
  private latestPath: string | null = null;
79
94
 
80
95
  protected override createRenderRoot(): HTMLElement | ShadowRoot {
@@ -139,6 +154,7 @@ export class RecordTableElement extends LitElement {
139
154
  <th class="col-state">${labels.get('column.wsStateAction')}</th>
140
155
  <th class="col-state">${labels.get('column.integrity')}</th>
141
156
  <th>${labels.get('column.stage')}</th>
157
+ ${Object.values(this.additionalColumns).map((label: string) => html`<th>${label}</th>`)}
142
158
  <th class="col-control nowrap">
143
159
  <span class="visually-hidden">${coreLabels.get('labels._CONTROL_')}</span>
144
160
  </th>
@@ -200,7 +216,7 @@ export class RecordTableElement extends LitElement {
200
216
  return html`
201
217
  ${latestPathChanged ? html`
202
218
  <tr>
203
- <th colspan="8" class="col-white-space-normal">
219
+ <th colspan=${8 + Object.keys(this.additionalColumns).length} class="col-white-space-normal">
204
220
  <a href=${data.urlToPage}>${data.path_Workspace}</a>
205
221
  </th>
206
222
  </tr>
@@ -251,7 +267,7 @@ export class RecordTableElement extends LitElement {
251
267
  ` : html`
252
268
  <span class="text-variant">${labels.get('column.editor.unknown')}</span>
253
269
  `}
254
- ${data.lastChangedFormatted}
270
+ ${data.lastChanged ? labels.get('column.last_change.value', { last_change: new Date(data.lastChanged) }) : nothing}
255
271
  </div>
256
272
  </td>
257
273
  <td class="col-state">
@@ -263,6 +279,7 @@ export class RecordTableElement extends LitElement {
263
279
  </span>
264
280
  ` : nothing}</td>
265
281
  <td>${data.label_Stage}</td>
282
+ ${Object.keys(this.additionalColumns).map((identifier: string) => this.renderAdditionalColumn(data, identifier))}
266
283
  <td class="col-control nowrap">
267
284
  <div class="btn-group">${this.renderElementActions(data)}</div>
268
285
  <div class="btn-group">${this.renderVersioningActions(data)}</div>
@@ -271,6 +288,26 @@ export class RecordTableElement extends LitElement {
271
288
  `;
272
289
  }
273
290
 
291
+ protected renderAdditionalColumn(data: RecordData, identifier: string): TemplateResult {
292
+ const column = data.additional?.[identifier];
293
+ if (column === undefined) {
294
+ return html`<td></td>`;
295
+ }
296
+
297
+ const content = html`
298
+ ${column.icon ? html`
299
+ <typo3-backend-icon identifier=${column.icon} size="small"></typo3-backend-icon>
300
+ ` : nothing}
301
+ ${column.value ?? ''}
302
+ `;
303
+
304
+ return html`
305
+ <td title=${ifDefined(column.title || undefined)}>
306
+ ${column.url ? html`<a href=${column.url}>${content}</a>` : content}
307
+ </td>
308
+ `;
309
+ }
310
+
274
311
  protected renderIndent(level: number) {
275
312
  return html`<span class="indent indent-inline-block" style="--indent-level: ${level}"></span>`;
276
313
  }
@@ -11,14 +11,28 @@
11
11
  * The TYPO3 project - inspiring people to share!
12
12
  */
13
13
 
14
- import type { AjaxResponse } from '@typo3/core/ajax/ajax-response';
14
+ import { AjaxResponse } from '@typo3/core/ajax/ajax-response';
15
15
  import AjaxRequest from '@typo3/core/ajax/ajax-request';
16
16
  import { SeverityEnum } from '@typo3/backend/enum/severity';
17
17
  import { ProgressBarElement } from '@typo3/backend/element/progress-bar-element';
18
+ import Notification from '@typo3/backend/notification';
18
19
  import { default as Modal, type ModalElement } from '@typo3/backend/modal';
19
20
  import { html } from 'lit';
20
21
  import labels from '~labels/workspaces.messages';
21
22
 
23
+ /**
24
+ * Result of a single call of a dispatched call stack, carrying either
25
+ * `result` if the call succeeded, or `error` if it did not.
26
+ */
27
+ interface RemoteCallResult {
28
+ method: string;
29
+ result?: unknown;
30
+ error?: {
31
+ message: string;
32
+ code: number;
33
+ };
34
+ }
35
+
22
36
  export default class Workspaces {
23
37
  protected readonly ajaxRoute: string = 'workspace_dispatch';
24
38
  protected progressBar: ProgressBarElement | null = null;
@@ -73,18 +87,22 @@ export default class Workspaces {
73
87
  this.progressBar = document.createElement('typo3-backend-progress-bar');
74
88
  document.querySelector(progressContainer).prepend(this.progressBar);
75
89
  this.progressBar.start();
76
- return (new AjaxRequest(TYPO3.settings.ajaxUrls[this.ajaxRoute])).post(
77
- payload,
78
- {
79
- headers: {
80
- 'Content-Type': 'application/json; charset=utf-8'
90
+ const headers = {
91
+ 'Content-Type': 'application/json; charset=utf-8'
92
+ };
93
+ return new AjaxRequest(TYPO3.settings.ajaxUrls[this.ajaxRoute])
94
+ .post(payload, { headers })
95
+ .catch(async (reason: unknown): Promise<never> => {
96
+ await this.notifyRequestFailure(reason);
97
+ // Rejection is kept up, so that callers do not continue with a payload
98
+ // that carries errors instead of the results they are written for.
99
+ throw reason;
100
+ })
101
+ .finally(() => {
102
+ if (this.progressBar) {
103
+ this.progressBar.done();
81
104
  }
82
- }
83
- ).finally(() => {
84
- if (this.progressBar) {
85
- this.progressBar.done();
86
- }
87
- });
105
+ });
88
106
  }
89
107
 
90
108
  /**
@@ -105,4 +123,39 @@ export default class Workspaces {
105
123
  method: method,
106
124
  };
107
125
  }
126
+
127
+ /**
128
+ * Reports a failed request to the user. The server describes failing calls in the
129
+ * JSON body, anything else - an unreachable server, a session timeout redirect or
130
+ * an error page that is no JSON at all - can only be reported in a generic way.
131
+ */
132
+ private async notifyRequestFailure(reason: unknown): Promise<void> {
133
+ const title = labels.get('error.dispatch.title');
134
+ if (!(reason instanceof AjaxResponse)) {
135
+ Notification.error(title, labels.get('error.dispatch.unavailable'));
136
+ return;
137
+ }
138
+
139
+ let details: unknown;
140
+ try {
141
+ details = await reason.resolve('json');
142
+ } catch {
143
+ Notification.error(title, labels.get('error.dispatch.unavailable'));
144
+ return;
145
+ }
146
+
147
+ const messages: string[] = [];
148
+ if (Array.isArray(details)) {
149
+ for (const item of details as RemoteCallResult[]) {
150
+ if (item?.error?.message) {
151
+ messages.push(item.error.message);
152
+ }
153
+ }
154
+ }
155
+ if (messages.length === 0) {
156
+ Notification.error(title, labels.get('error.dispatch.unknown'));
157
+ return;
158
+ }
159
+ messages.forEach((message: string): void => Notification.error(title, message));
160
+ }
108
161
  }