typo3-typescript-sources 14.3.7 → 14.3.8

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.
@@ -54,6 +54,13 @@ const defaultConfiguration: Partial<HelperConfiguration> = {
54
54
  rowItem: 'rowItem',
55
55
  rowLink: 'rowLink',
56
56
  rowsContainer: 'rowsContainer',
57
+ elementIdentifier: 'elementIdentifier',
58
+ elementType: 'elementType',
59
+ validationErrors: 'validationErrors',
60
+ validationErrorGroup: 'validationErrorGroup',
61
+ validationErrorGroupLabel: 'validationErrorGroupLabel',
62
+ validationErrorGroupItems: 'validationErrorGroupItems',
63
+ validationError: 'validationError',
57
64
  templateInsertElements: 'Modal-InsertElements',
58
65
  templateInsertPages: 'Modal-InsertPages',
59
66
  templateValidationErrors: 'Modal-ValidationErrors'
@@ -206,7 +213,7 @@ function _validationErrorsModalSetup(
206
213
  modalContent: DocumentFragment,
207
214
  validationResults: ValidationResultsRecursive
208
215
  ): void {
209
- let formElement, newRowItem;
216
+ let formElement: FormElement, newRowItem: HTMLElement | null;
210
217
 
211
218
  assert(
212
219
  Array.isArray(validationResults),
@@ -235,13 +242,70 @@ function _validationErrorsModalSetup(
235
242
  formElement = getFormEditorApp()
236
243
  .getFormElementByIdentifierPath(validationResults[i].formElementIdentifierPath);
237
244
  newRowItem = rowItemTemplate?.cloneNode(true) as HTMLElement | null;
238
- const rowLink = newRowItem?.querySelector(getHelper().getDomElementDataIdentifierSelector('rowLink'));
239
- if (rowLink) {
245
+ if (newRowItem) {
246
+ const rowLink = newRowItem;
240
247
  rowLink.setAttribute(
241
248
  getHelper().getDomElementDataAttribute('elementIdentifier'),
242
249
  validationResults[i].formElementIdentifierPath
243
250
  );
244
- rowLink.replaceChildren(_buildTitleByFormElement(formElement));
251
+ const rowLinkTitle = rowLink.querySelector('.form-editor-validation-error-title');
252
+ rowLinkTitle?.replaceChildren(_buildTitleByFormElement(formElement));
253
+ rowLink.querySelector(
254
+ getHelper().getDomElementDataIdentifierSelector('elementIdentifier')
255
+ )?.replaceChildren(document.createTextNode(formElement.get('identifier')));
256
+ rowLink.querySelector(
257
+ getHelper().getDomElementDataIdentifierSelector('elementType')
258
+ )?.replaceChildren(document.createTextNode(
259
+ getFormElementDefinition(formElement, 'label') || formElement.get('type')
260
+ ));
261
+ const validationErrors = rowLink.querySelector(
262
+ getHelper().getDomElementDataIdentifierSelector('validationErrors')
263
+ );
264
+ const validationErrorGroupTemplate = validationErrors?.querySelector(
265
+ getHelper().getDomElementDataIdentifierSelector('validationErrorGroup')
266
+ );
267
+ const validationErrorTemplate = validationErrors?.querySelector(
268
+ getHelper().getDomElementDataIdentifierSelector('validationError')
269
+ );
270
+ validationErrors?.replaceChildren();
271
+ const validationErrorGroups = new Map<string, HTMLElement>();
272
+ validationResults[i].validationResults.forEach((propertyValidationResult) => {
273
+ const propertyLabel = _getPropertyLabelByPath(formElement, propertyValidationResult.propertyPath);
274
+ const finisherIndex = _getFinisherIndexByPropertyPath(propertyValidationResult.propertyPath);
275
+ const validatorIdentifier = _getValidatorIdentifierByPropertyPath(formElement, propertyValidationResult.propertyPath);
276
+ const groupLabel = finisherIndex
277
+ ? _getFinisherLabelByIdentifier(formElement, finisherIndex)
278
+ : validatorIdentifier;
279
+ const groupKey = finisherIndex
280
+ ? `finisher-${finisherIndex}`
281
+ : `validator-${validatorIdentifier}`;
282
+ const validationMessage = propertyValidationResult.validationResults.join(' ');
283
+ if (groupLabel && validationErrorGroupTemplate) {
284
+ let validationErrorGroup = validationErrorGroups.get(groupKey);
285
+ if (!validationErrorGroup) {
286
+ validationErrorGroup = validationErrorGroupTemplate.cloneNode(true) as HTMLElement;
287
+ validationErrorGroup.querySelector(
288
+ getHelper().getDomElementDataIdentifierSelector('validationErrorGroupLabel')
289
+ )?.replaceChildren(document.createTextNode(groupLabel));
290
+ validationErrorGroup.querySelector(
291
+ getHelper().getDomElementDataIdentifierSelector('validationErrorGroupItems')
292
+ )?.replaceChildren();
293
+ validationErrorGroups.set(groupKey, validationErrorGroup);
294
+ validationErrors?.append(validationErrorGroup);
295
+ }
296
+ const validationError = validationErrorTemplate?.cloneNode() as HTMLElement | undefined;
297
+ validationError?.replaceChildren(document.createTextNode(
298
+ propertyLabel ? `${propertyLabel}: ${validationMessage}` : validationMessage
299
+ ));
300
+ validationErrorGroup.querySelector(
301
+ getHelper().getDomElementDataIdentifierSelector('validationErrorGroupItems')
302
+ )?.append(validationError);
303
+ } else {
304
+ const validationError = validationErrorTemplate?.cloneNode() as HTMLElement | undefined;
305
+ validationError?.replaceChildren(document.createTextNode(propertyLabel ? `${propertyLabel}: ${validationMessage}` : validationMessage));
306
+ validationErrors?.append(validationError);
307
+ }
308
+ });
245
309
  }
246
310
  const rowsContainer = modalContent.querySelector(getHelper().getDomElementDataIdentifierSelector('rowsContainer'));
247
311
  if (rowsContainer && newRowItem) {
@@ -251,7 +315,8 @@ function _validationErrorsModalSetup(
251
315
  }
252
316
 
253
317
  modalContent.querySelectorAll<HTMLAnchorElement>('a').forEach((a) => {
254
- a.addEventListener('click', function() {
318
+ a.addEventListener('click', function(event: MouseEvent) {
319
+ event.preventDefault();
255
320
  getPublisherSubscriber().publish('view/modal/validationErrors/element/clicked', [
256
321
  a.getAttribute(getHelper().getDomElementDataAttribute('elementIdentifier'))
257
322
  ]);
@@ -272,6 +337,50 @@ function _buildTitleByFormElement(formElement: FormElement): HTMLElement {
272
337
  return span;
273
338
  }
274
339
 
340
+ function _getPropertyLabelByPath(formElement: FormElement, propertyPath: string): string {
341
+ return _getEditorConfigurationByPropertyPath(formElement, propertyPath)?.label || '';
342
+ }
343
+
344
+ function _getValidatorIdentifierByPropertyPath(formElement: FormElement, propertyPath: string): string {
345
+ const editorConfiguration = _getEditorConfigurationByPropertyPath(formElement, propertyPath);
346
+ const validatorIdentifier = editorConfiguration?.propertyValidators?.find((identifier) => identifier !== 'NotEmpty');
347
+ return validatorIdentifier || '';
348
+ }
349
+
350
+ function _getEditorConfigurationByPropertyPath(
351
+ formElement: FormElement,
352
+ propertyPath: string
353
+ ): FormElementDefinition['editors'][number] | undefined {
354
+ const formElementDefinition = getFormElementDefinition(formElement, undefined) as FormElementDefinition;
355
+ const editors = [
356
+ ...(formElementDefinition.editors || []),
357
+ ...Object.values(formElementDefinition.propertyCollections || {}).flatMap((collection) =>
358
+ collection.flatMap((collectionElement) => collectionElement.editors || [])
359
+ )
360
+ ];
361
+ return editors.find((editorConfiguration) =>
362
+ editorConfiguration.propertyPath === propertyPath
363
+ || propertyPath.endsWith(`.${editorConfiguration.propertyPath}`)
364
+ );
365
+ }
366
+
367
+ function _getFinisherIndexByPropertyPath(propertyPath: string): string {
368
+ const match = propertyPath.match(/^finishers\.(\d+)\./);
369
+ if (!match) {
370
+ return '';
371
+ }
372
+ return match[1];
373
+ }
374
+
375
+ function _getFinisherLabelByIdentifier(formElement: FormElement, finisherIndex: string): string {
376
+ const finishers = formElement.get('finishers') as Array<{ identifier: string }> | undefined;
377
+ const finisherIdentifier = finishers?.[Number(finisherIndex)]?.identifier;
378
+ if (!finisherIdentifier) {
379
+ return '';
380
+ }
381
+ return getFormEditorApp().getFormEditorDefinition('finishers', finisherIdentifier)?.label || '';
382
+ }
383
+
275
384
  /* *************************************************************
276
385
  * Public Methods
277
386
  * ************************************************************/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typo3-typescript-sources",
3
- "version": "14.3.7",
3
+ "version": "14.3.8",
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.7",
31
+ "sourceTag": "v14.3.8",
32
32
  "sourcePath": "Build/Sources/TypeScript"
33
33
  }
34
34
  }
@@ -12,6 +12,5 @@ export * as Widget from '@ckeditor/ckeditor5-widget';
12
12
 
13
13
  // single or prefixed exports
14
14
  export { WordCount } from '@ckeditor/ckeditor5-word-count';
15
- export { LinkUtils } from '@ckeditor/ckeditor5-link';
16
15
 
17
16
  console.warn('The module "@typo3/ckeditor5-bundle.js" has been marked as deprecated. Please use "@ckeditor/ckeditor5-*" modules directly.');
@@ -5,11 +5,9 @@ import * as Typing from '@ckeditor/ckeditor5-typing';
5
5
  import * as Widget from '@ckeditor/ckeditor5-widget';
6
6
  import * as Utils from '@ckeditor/ckeditor5-utils';
7
7
  import * as Link from '@ckeditor/ckeditor5-link';
8
- import { LinkUtils } from '@ckeditor/ckeditor5-link';
9
8
  import { default as modalObject, type ModalElement } from '@typo3/backend/modal';
10
9
  import type { ViewAttributeElement, ViewElement, ModelSchema, ModelWriter } from '@ckeditor/ckeditor5-engine';
11
- import type { GeneralHtmlSupport, DataFilter } from '@ckeditor/ckeditor5-html-support';
12
- import type { GHSViewAttributes } from '@ckeditor/ckeditor5-html-support/src/utils';
10
+ import type { GeneralHtmlSupport, DataFilter, GHSViewAttributes } from '@ckeditor/ckeditor5-html-support';
13
11
  import { IconLink, IconPencil, IconUnlink } from '@ckeditor/ckeditor5-icons';
14
12
 
15
13
  export const LINK_ALLOWED_ATTRIBUTES = ['href', 'title', 'class', 'target', 'rel', 'download'];
@@ -54,7 +52,7 @@ export class Typo3LinkCommand extends Core.Command {
54
52
 
55
53
  // A check for any integration that allows linking elements (e.g. `LinkImage`).
56
54
  // Currently, the selection reads attributes from text nodes only. See #7429 and #7465.
57
- const sourceSelection = LinkUtils.isLinkableElement(selectedElement, model.schema) ? selectedElement : selection;
55
+ const sourceSelection = Link.isLinkableElement(selectedElement, model.schema) ? selectedElement : selection;
58
56
  if (sourceSelection === selectedElement) {
59
57
  this.value = selectedElement.getAttribute('linkHref') as string;
60
58
  this.isEnabled = model.schema.checkAttribute(selectedElement, 'linkHref');
@@ -227,7 +225,7 @@ export class Typo3UnlinkCommand extends Core.Command {
227
225
  const selection = model.document.selection;
228
226
  const selectedElement = selection.getSelectedElement();
229
227
 
230
- if (LinkUtils.isLinkableElement(selectedElement, model.schema)) {
228
+ if (Link.isLinkableElement(selectedElement, model.schema)) {
231
229
  this.isEnabled = model.schema.checkAttribute(selectedElement, 'linkHref');
232
230
  } else {
233
231
  this.isEnabled = model.schema.checkAttributeInSelection(selection, 'linkHref');
@@ -457,7 +455,7 @@ export class Typo3LinkUI extends Core.Plugin {
457
455
  const t = editor.t;
458
456
 
459
457
  // Handle the `Ctrl+K` keystroke and show the panel.
460
- editor.keystrokes.set(LinkUtils.LINK_KEYSTROKE, (keyEvtData, cancel) => {
458
+ editor.keystrokes.set(Link._LINK_KEYSTROKE, (keyEvtData, cancel) => {
461
459
  // Prevent focusing the search bar in FF, Chrome and Edge. See https://github.com/ckeditor/ckeditor5/issues/4811.
462
460
  cancel();
463
461
  if (linkCommand.isEnabled) {
@@ -471,7 +469,7 @@ export class Typo3LinkUI extends Core.Plugin {
471
469
  linkButton.isEnabled = true;
472
470
  linkButton.label = t('Link');
473
471
  linkButton.icon = IconLink;
474
- linkButton.keystroke = LinkUtils.LINK_KEYSTROKE;
472
+ linkButton.keystroke = Link._LINK_KEYSTROKE;
475
473
  linkButton.tooltip = true;
476
474
  linkButton.isToggleable = true;
477
475
  linkButton.bind('isEnabled').to(linkCommand, 'isEnabled');
@@ -486,7 +484,7 @@ export class Typo3LinkUI extends Core.Plugin {
486
484
  const linkCommand = editor.commands.get('link');
487
485
  button.bind('isEnabled').to(linkCommand, 'value', href => !!href);
488
486
  button.bind('href').to(linkCommand, 'value', href => {
489
- return href && LinkUtils.ensureSafeUrl(href, allowedProtocols);
487
+ return href && Link._ensureSafeLinkUrl(href, allowedProtocols);
490
488
  });
491
489
 
492
490
  button.icon = undefined;
@@ -753,7 +751,7 @@ export class Typo3LinkUI extends Core.Plugin {
753
751
  }
754
752
 
755
753
  private findLinkElementAncestor(position: any) {
756
- return position.getAncestors().find((ancestor: any) => LinkUtils.isLinkElement(ancestor));
754
+ return position.getAncestors().find((ancestor: any) => Link.isLinkElement(ancestor));
757
755
  }
758
756
 
759
757
  /**
@@ -16,7 +16,7 @@ import Modal from '@typo3/backend/modal';
16
16
  import RegularEvent from '@typo3/core/event/regular-event';
17
17
  import { type Typo3LinkDict, LINK_ALLOWED_ATTRIBUTES, addLinkPrefix } from '@typo3/rte-ckeditor/plugin/typo3-link';
18
18
  import type * as Core from '@ckeditor/ckeditor5-core';
19
- import type { ModelPosition } from '@ckeditor/ckeditor5-engine/src/model/position';
19
+ import type { ModelPosition } from '@ckeditor/ckeditor5-engine';
20
20
  import '@typo3/backend/element/combobox-element';
21
21
 
22
22
  /**
@@ -37,7 +37,7 @@ class SchedulerAddGroups {
37
37
  element.addEventListener('click', (button: MouseEvent) => {
38
38
  button.preventDefault();
39
39
  const content: TemplateResult = html`
40
- <form name="scheduler-create-group" @submit=${this.createGroup}>
40
+ <form id="scheduler-create-group" name="scheduler-create-group" @submit=${this.createGroup}>
41
41
  <label class="form-label" for="actionCreateGroup">Group name</label>
42
42
  <input class="form-control" id="actionCreateGroup" required="" name="action[createGroup]" autofocus type="text">
43
43
  <div class="form-text mt-3">
@@ -46,7 +46,7 @@ class SchedulerAddGroups {
46
46
  </form>
47
47
  `;
48
48
 
49
- const modal = Modal.advanced({
49
+ Modal.advanced({
50
50
  content: content,
51
51
  title: labels.get('function.group.add'),
52
52
  size: Modal.sizes.small,
@@ -57,21 +57,13 @@ class SchedulerAddGroups {
57
57
  btnClass: 'btn-default',
58
58
  name: 'cancel'
59
59
  },{
60
- trigger: (): void => {
61
- const form: HTMLFormElement = Modal.currentModal.querySelector('form[name="scheduler-create-group"]');
62
- form.requestSubmit();
63
- },
64
60
  text: labels.get('button.group.modalOk'),
65
61
  btnClass: 'btn-primary',
66
- name: 'ok'
62
+ name: 'ok',
63
+ form: 'scheduler-create-group'
67
64
  }
68
65
  ]
69
66
  });
70
-
71
- modal.addEventListener('typo3-modal-shown', (): void => {
72
- const input: HTMLInputElement = Modal.currentModal.querySelector('input[name="action[createGroup]"]');
73
- input.focus();
74
- });
75
67
  });
76
68
  }
77
69
  }
@@ -30,6 +30,7 @@ enum Identifiers {
30
30
  workspaceView = '.t3js-workspace-view-workspace',
31
31
  sendToStageAction = '[data-action="send-to-stage"]',
32
32
  discardAction = '[data-action="discard"]',
33
+ publishAction = '[data-action="publish"]',
33
34
  stageButtonsContainer = '.t3js-stage-buttons',
34
35
  previewModeContainer = '.t3js-preview-mode',
35
36
  activePreviewMode = '.t3js-active-preview-mode',
@@ -78,6 +79,7 @@ class Preview extends Workspaces {
78
79
  this.resizeViews();
79
80
  }, 50).bindTo(window);
80
81
  new RegularEvent('click', this.renderDiscardWindow.bind(this)).delegateTo(document, Identifiers.discardAction);
82
+ new RegularEvent('click', this.renderPublishWindow.bind(this)).delegateTo(document, Identifiers.publishAction);
81
83
  new RegularEvent('click', this.renderSendPageToStageWindow.bind(this)).delegateTo(document, Identifiers.sendToStageAction);
82
84
  new RegularEvent('click', () => {
83
85
  window.top.document.querySelectorAll('.t3js-workspace-recipient:not([disabled])').forEach((element: HTMLInputElement) => {
@@ -166,6 +168,43 @@ class Preview extends Workspaces {
166
168
  });
167
169
  }
168
170
 
171
+ private renderPublishWindow(): void {
172
+ const modal = Modal.confirm(
173
+ labels.get('window.publishAll.title'),
174
+ labels.get('window.publishAll.message'),
175
+ SeverityEnum.warning,
176
+ [
177
+ {
178
+ text: labels.get('cancel'),
179
+ active: true,
180
+ btnClass: 'btn-default',
181
+ name: 'cancel',
182
+ trigger: (): void => {
183
+ modal.hideModal();
184
+ },
185
+ },
186
+ {
187
+ text: labels.get('ok'),
188
+ btnClass: 'btn-warning',
189
+ name: 'ok',
190
+ },
191
+ ],
192
+ );
193
+ modal.addEventListener('button.clicked', (e: Event): void => {
194
+ if ((e.target as HTMLButtonElement).name === 'ok') {
195
+ this.sendRemoteRequest([
196
+ this.generateRemotePayloadBody('publishPageDirectly', [TYPO3.settings.Workspaces.id]),
197
+ this.generateRemotePayloadBody('updateStageChangeButtons', [TYPO3.settings.Workspaces.id]),
198
+ ], Identifiers.topbar).then(async (response: AjaxResponse): Promise<void> => {
199
+ modal.hideModal();
200
+ this.renderStageButtons((await response.resolve())[1].result);
201
+ // Reloading live view IFRAME
202
+ this.elements.workspaceView.setAttribute('src', this.elements.workspaceView.getAttribute('src'));
203
+ });
204
+ }
205
+ });
206
+ }
207
+
169
208
  /**
170
209
  * Renders the "send page to stage" window
171
210
  */
@@ -32,6 +32,15 @@ export type AdditionalColumnValue = {
32
32
  url?: string
33
33
  };
34
34
 
35
+ export type ActionData = {
36
+ group?: string,
37
+ enabled?: boolean,
38
+ visible?: boolean,
39
+ icon?: string,
40
+ title?: string,
41
+ url?: string
42
+ };
43
+
35
44
  export type RecordData = {
36
45
  table: string,
37
46
  id: string,
@@ -76,7 +85,8 @@ export type RecordData = {
76
85
  status: string,
77
86
  messages: string
78
87
  },
79
- additional?: Record<string, AdditionalColumnValue>
88
+ additional?: Record<string, AdditionalColumnValue>,
89
+ actions?: Record<string, ActionData>
80
90
  };
81
91
 
82
92
  @customElement('typo3-workspaces-record-table')
@@ -280,10 +290,7 @@ export class RecordTableElement extends LitElement {
280
290
  ` : nothing}</td>
281
291
  <td>${data.label_Stage}</td>
282
292
  ${Object.keys(this.additionalColumns).map((identifier: string) => this.renderAdditionalColumn(data, identifier))}
283
- <td class="col-control nowrap">
284
- <div class="btn-group">${this.renderElementActions(data)}</div>
285
- <div class="btn-group">${this.renderVersioningActions(data)}</div>
286
- </td>
293
+ <td class="col-control nowrap">${this.renderActions(data)}</td>
287
294
  </tr>
288
295
  `;
289
296
  }
@@ -308,42 +315,94 @@ export class RecordTableElement extends LitElement {
308
315
  `;
309
316
  }
310
317
 
318
+ /**
319
+ * The action buttons of a row, rendered as one button group per group declared by the server.
320
+ * Which actions exist is decided there, the module only knows how to present its own ones.
321
+ */
322
+ protected renderActions(data: RecordData): TemplateResult[] {
323
+ const groups = new Map<string, TemplateResult[]>();
324
+
325
+ for (const [ identifier, action ] of Object.entries(data.actions ?? {})) {
326
+ if (action.visible === false) {
327
+ continue;
328
+ }
329
+ const group = action.group ?? 'custom';
330
+ if (!groups.has(group)) {
331
+ groups.set(group, []);
332
+ }
333
+ groups.get(group).push(this.renderAction(identifier, action, data));
334
+ }
335
+
336
+ return Array.from(groups.values()).map((actions: TemplateResult[]) => html`
337
+ <div class="btn-group">${actions}</div>
338
+ `);
339
+ }
340
+
341
+ protected renderAction(identifier: string, action: ActionData, data: RecordData): TemplateResult {
342
+ const enabled = action.enabled !== false;
343
+
344
+ if (identifier === 'qrcode') {
345
+ return this.getQrCodeAction(data, enabled);
346
+ }
347
+
348
+ const defaults = this.getActionDefaults(identifier, data, enabled);
349
+ const attributes: Record<string, string> = { ...defaults.attributes };
350
+ const title = action.title || defaults.title;
351
+ if (title !== '') {
352
+ attributes.title = title;
353
+ }
354
+ const icon = action.icon || defaults.icon;
355
+
356
+ if (action.url && enabled) {
357
+ return html`
358
+ <a class="btn btn-default" href=${action.url} title=${ifDefined(attributes.title)}>
359
+ <typo3-backend-icon identifier=${IconHelper.getIconIdentifier(icon || 'empty-empty')} size="small"></typo3-backend-icon>
360
+ </a>
361
+ `;
362
+ }
363
+
364
+ return this.getAction(enabled, identifier, icon, attributes);
365
+ }
366
+
311
367
  protected renderIndent(level: number) {
312
368
  return html`<span class="indent indent-inline-block" style="--indent-level: ${level}"></span>`;
313
369
  }
314
370
 
315
- private renderElementActions(data: RecordData): TemplateResult[] {
316
- return [
317
- this.getAction(
318
- (data.allowedAction_view && data.previewUrl !== ''),
319
- 'preview',
320
- 'actions-version-workspace-preview',
321
- {
322
- 'title': labels.get('tooltip.viewElementAction')
323
- }
324
- ),
325
- this.getQrCodeAction(data),
326
- this.getAction(
327
- data.allowedAction_edit && data.state_Workspace !== 'deleted',
328
- 'open',
329
- 'actions-open',
330
- {
331
- 'title': labels.get('tooltip.editElementAction')
332
- }
333
- ),
334
- this.getAction(
335
- data.allowedAction_versionPageOpen,
336
- 'version',
337
- 'actions-version-page-open',
338
- {
339
- 'title': labels.get('tooltip.openPage')
340
- }
341
- )
342
- ];
371
+ /**
372
+ * Icon, title and attributes of the actions the module handles itself. Actions of third party
373
+ * extensions are unknown here and are presented with what their payload provides.
374
+ */
375
+ private getActionDefaults(identifier: string, data: RecordData, enabled: boolean): { icon: string, title: string, attributes: Record<string, string> } {
376
+ switch (identifier) {
377
+ case 'preview':
378
+ return { icon: 'actions-version-workspace-preview', title: labels.get('tooltip.viewElementAction'), attributes: {} };
379
+ case 'open':
380
+ return { icon: 'actions-open', title: labels.get('tooltip.editElementAction'), attributes: {} };
381
+ case 'version':
382
+ return { icon: 'actions-version-page-open', title: labels.get('tooltip.openPage'), attributes: {} };
383
+ case 'expand':
384
+ return {
385
+ icon: data.expanded ? 'actions-caret-down' : 'actions-caret-right',
386
+ title: labels.get('tooltip.expand'),
387
+ attributes: {
388
+ 'data-bs-target': '[data-collection="' + data.Workspaces_CollectionCurrent + '"]',
389
+ 'aria-expanded': !enabled || data.expanded ? 'true' : 'false',
390
+ 'data-bs-toggle': 'collapse',
391
+ }
392
+ };
393
+ case 'changes':
394
+ return { icon: 'actions-document-info', title: labels.get('tooltip.showChanges'), attributes: {} };
395
+ case 'publish':
396
+ return { icon: 'actions-version-swap-version', title: labels.get('tooltip.publish'), attributes: {} };
397
+ case 'remove':
398
+ return { icon: 'actions-delete', title: labels.get('tooltip.discardVersion'), attributes: {} };
399
+ default:
400
+ return { icon: '', title: '', attributes: {} };
401
+ }
343
402
  }
344
403
 
345
- private getQrCodeAction(data: RecordData): TemplateResult {
346
- if (!data.previewUrl) {
404
+ private getQrCodeAction(data: RecordData, enabled: boolean): TemplateResult {
405
+ if (!enabled || !data.previewUrl) {
347
406
  return html`
348
407
  <button type="button" class="btn btn-default" disabled>
349
408
  <typo3-backend-icon identifier="empty-empty" size="small"></typo3-backend-icon>
@@ -361,48 +420,6 @@ export class RecordTableElement extends LitElement {
361
420
  `;
362
421
  }
363
422
 
364
- private renderVersioningActions(data: RecordData): TemplateResult[] {
365
- const hasSubitems = data.Workspaces_CollectionChildren > 0 && data.Workspaces_CollectionCurrent !== '';
366
-
367
- return [
368
- this.getAction(
369
- hasSubitems,
370
- 'expand',
371
- (data.expanded ? 'actions-caret-down' : 'actions-caret-right'),
372
- {
373
- 'title': labels.get('tooltip.expand'),
374
- 'data-bs-target': '[data-collection="' + data.Workspaces_CollectionCurrent + '"]',
375
- 'aria-expanded': !hasSubitems || data.expanded ? 'true' : 'false',
376
- 'data-bs-toggle': 'collapse',
377
- }
378
- ),
379
- this.getAction(
380
- data.hasChanges,
381
- 'changes',
382
- 'actions-document-info',
383
- {
384
- 'title': labels.get('tooltip.showChanges')
385
- }
386
- ),
387
- this.getAction(
388
- data.allowedAction_publish && data.Workspaces_CollectionParent === '',
389
- 'publish',
390
- 'actions-version-swap-version',
391
- {
392
- 'title': labels.get('tooltip.publish')
393
- }
394
- ),
395
- this.getAction(
396
- data.allowedAction_delete,
397
- 'remove',
398
- 'actions-delete',
399
- {
400
- 'title': labels.get('tooltip.discardVersion')
401
- }
402
- )
403
- ];
404
- }
405
-
406
423
  /**
407
424
  * Renders the action button based on the user's permission.
408
425
  *