typo3-typescript-sources 13.4.20

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 (415) hide show
  1. package/.github/workflows/sync-and-publish.yml +265 -0
  2. package/.last-synced-tag +1 -0
  3. package/LICENSE.txt +339 -0
  4. package/README-TYPO3.md +166 -0
  5. package/README.md +28 -0
  6. package/adminpanel/admin-panel.ts +375 -0
  7. package/adminpanel/modules/cache.ts +27 -0
  8. package/adminpanel/modules/preview.ts +75 -0
  9. package/backend/action-button/abstract-action.ts +22 -0
  10. package/backend/action-button/deferred-action.ts +41 -0
  11. package/backend/action-button/immediate-action.ts +31 -0
  12. package/backend/action-dispatcher.ts +104 -0
  13. package/backend/ajax-data-handler/message-interface.ts +18 -0
  14. package/backend/ajax-data-handler/response-interface.ts +20 -0
  15. package/backend/ajax-data-handler.ts +214 -0
  16. package/backend/backend-exception.ts +22 -0
  17. package/backend/broadcast-message.ts +50 -0
  18. package/backend/broadcast-service.ts +52 -0
  19. package/backend/browse-database.ts +38 -0
  20. package/backend/clear-cache.ts +88 -0
  21. package/backend/clipboard-panel.ts +240 -0
  22. package/backend/close-current-window.ts +2 -0
  23. package/backend/code-editor/autocomplete/completion-result.ts +105 -0
  24. package/backend/code-editor/autocomplete/ts-code-completion.ts +142 -0
  25. package/backend/code-editor/autocomplete/ts-parser.ts +485 -0
  26. package/backend/code-editor/autocomplete/ts-ref.ts +164 -0
  27. package/backend/code-editor/element/code-mirror-element.ts +282 -0
  28. package/backend/code-editor/language/typoscript.ts +181 -0
  29. package/backend/code-editor/stream-parser/typoscript.ts +1600 -0
  30. package/backend/color-picker.ts +158 -0
  31. package/backend/color-scheme-switch.ts +179 -0
  32. package/backend/column-selector-button.ts +302 -0
  33. package/backend/context-help.ts +68 -0
  34. package/backend/context-menu-actions.ts +322 -0
  35. package/backend/context-menu.ts +751 -0
  36. package/backend/copy-to-clipboard.ts +79 -0
  37. package/backend/date-time-picker.ts +270 -0
  38. package/backend/document-save-actions.ts +159 -0
  39. package/backend/drag-tooltip.ts +285 -0
  40. package/backend/drag-uploader.ts +914 -0
  41. package/backend/element/alert-element.ts +116 -0
  42. package/backend/element/dispatch-modal-button.ts +50 -0
  43. package/backend/element/draggable-resizable-element.ts +249 -0
  44. package/backend/element/editable-page-title.ts +306 -0
  45. package/backend/element/icon-element.ts +84 -0
  46. package/backend/element/immediate-action-element.ts +89 -0
  47. package/backend/element/pagination.ts +59 -0
  48. package/backend/element/progress-bar-element.ts +172 -0
  49. package/backend/element/progress-tracker-element.ts +119 -0
  50. package/backend/element/pseudo-button.ts +58 -0
  51. package/backend/element/spinner-element.ts +51 -0
  52. package/backend/element/thumbnail-element.ts +80 -0
  53. package/backend/element-browser.ts +182 -0
  54. package/backend/enum/data-transfer-types.ts +21 -0
  55. package/backend/enum/icon-types.ts +31 -0
  56. package/backend/enum/key-types.ts +27 -0
  57. package/backend/enum/severity.ts +20 -0
  58. package/backend/enum/viewport/scaffold-identifier.ts +24 -0
  59. package/backend/event/client-request.ts +25 -0
  60. package/backend/event/consumable.ts +18 -0
  61. package/backend/event/consumer-scope.ts +54 -0
  62. package/backend/event/event-dispatcher.ts +26 -0
  63. package/backend/event/interaction-request-assignment.ts +24 -0
  64. package/backend/event/interaction-request-map.ts +75 -0
  65. package/backend/event/interaction-request.ts +48 -0
  66. package/backend/event/trigger-request.ts +56 -0
  67. package/backend/form/submit-interceptor.ts +66 -0
  68. package/backend/form-engine/container/files-control-container.ts +716 -0
  69. package/backend/form-engine/container/flex-form-container-container.ts +166 -0
  70. package/backend/form-engine/container/flex-form-section-container.ts +210 -0
  71. package/backend/form-engine/container/inline-control-container.ts +1230 -0
  72. package/backend/form-engine/container/site-language-container.ts +616 -0
  73. package/backend/form-engine/element/abstract-sortable-select-items.ts +186 -0
  74. package/backend/form-engine/element/category-element.ts +160 -0
  75. package/backend/form-engine/element/color-element.ts +81 -0
  76. package/backend/form-engine/element/datetime-element.ts +66 -0
  77. package/backend/form-engine/element/extra/char-counter.ts +112 -0
  78. package/backend/form-engine/element/extra/select-box-filter.ts +85 -0
  79. package/backend/form-engine/element/folder-element.ts +66 -0
  80. package/backend/form-engine/element/group-element.ts +47 -0
  81. package/backend/form-engine/element/json-element.ts +52 -0
  82. package/backend/form-engine/element/link-element.ts +121 -0
  83. package/backend/form-engine/element/mfa-info-element.ts +169 -0
  84. package/backend/form-engine/element/modifier/resizable.ts +27 -0
  85. package/backend/form-engine/element/modifier/tabbable.ts +29 -0
  86. package/backend/form-engine/element/online-media-form-element.ts +65 -0
  87. package/backend/form-engine/element/password-element.ts +71 -0
  88. package/backend/form-engine/element/select-multiple-side-by-side-element.ts +83 -0
  89. package/backend/form-engine/element/select-single-element.ts +82 -0
  90. package/backend/form-engine/element/select-tree-element.ts +148 -0
  91. package/backend/form-engine/element/select-tree-toolbar.ts +118 -0
  92. package/backend/form-engine/element/select-tree.ts +210 -0
  93. package/backend/form-engine/element/slug-element.ts +243 -0
  94. package/backend/form-engine/element/suggest/result-container.ts +118 -0
  95. package/backend/form-engine/element/suggest/result-item.ts +90 -0
  96. package/backend/form-engine/element/table-permission-element.ts +101 -0
  97. package/backend/form-engine/element/table-wizard-element.ts +427 -0
  98. package/backend/form-engine/element/text-element.ts +52 -0
  99. package/backend/form-engine/element/text-table-element.ts +31 -0
  100. package/backend/form-engine/field-control/add-record.ts +40 -0
  101. package/backend/form-engine/field-control/edit-popup.ts +64 -0
  102. package/backend/form-engine/field-control/insert-clipboard.ts +50 -0
  103. package/backend/form-engine/field-control/link-popup.ts +53 -0
  104. package/backend/form-engine/field-control/list-module.ts +40 -0
  105. package/backend/form-engine/field-control/password-generator.ts +102 -0
  106. package/backend/form-engine/field-control/reset-selection.ts +48 -0
  107. package/backend/form-engine/field-wizard/localization-state-selector.ts +67 -0
  108. package/backend/form-engine/field-wizard/value-picker.ts +123 -0
  109. package/backend/form-engine/field-wizard/value-slider.ts +86 -0
  110. package/backend/form-engine/inline-relation/ajax-dispatcher.ts +124 -0
  111. package/backend/form-engine/inline-relation/inline-response-interface.ts +41 -0
  112. package/backend/form-engine-link-browser-adapter.ts +129 -0
  113. package/backend/form-engine-review.ts +155 -0
  114. package/backend/form-engine-suggest.ts +133 -0
  115. package/backend/form-engine-validation.ts +686 -0
  116. package/backend/form-engine.ts +1391 -0
  117. package/backend/global-event-handler.ts +252 -0
  118. package/backend/grid-editor.ts +1077 -0
  119. package/backend/hashing/md5.ts +252 -0
  120. package/backend/helper.ts +24 -0
  121. package/backend/hotkeys/hotkey-storage.ts +63 -0
  122. package/backend/hotkeys.ts +216 -0
  123. package/backend/icons.ts +236 -0
  124. package/backend/image-manipulation.ts +885 -0
  125. package/backend/info-window.ts +46 -0
  126. package/backend/input/clearable.ts +128 -0
  127. package/backend/input/index.d.ts +8 -0
  128. package/backend/java-script-module-import-event-handler.ts +19 -0
  129. package/backend/key-bindings.ts +42 -0
  130. package/backend/layout-module/drag-drop.ts +359 -0
  131. package/backend/layout-module/paste.ts +165 -0
  132. package/backend/link-browser.ts +89 -0
  133. package/backend/live-search/element/backend-search.ts +34 -0
  134. package/backend/live-search/element/hint.ts +33 -0
  135. package/backend/live-search/element/provider/default-result-item.ts +52 -0
  136. package/backend/live-search/element/provider/page-provider-result-item.ts +52 -0
  137. package/backend/live-search/element/result/item/action/action-container.ts +135 -0
  138. package/backend/live-search/element/result/item/action/action.ts +56 -0
  139. package/backend/live-search/element/result/item/item-container.ts +224 -0
  140. package/backend/live-search/element/result/item/item.ts +74 -0
  141. package/backend/live-search/element/result/result-container.ts +110 -0
  142. package/backend/live-search/element/result/result-detail-container.ts +50 -0
  143. package/backend/live-search/element/result/result-pagination.ts +122 -0
  144. package/backend/live-search/element/search-option-item.ts +70 -0
  145. package/backend/live-search/live-search-configurator.ts +54 -0
  146. package/backend/live-search/live-search-shortcut.ts +29 -0
  147. package/backend/live-search/result-types/backend-module-result-type.ts +8 -0
  148. package/backend/live-search/result-types/default-result-type.ts +21 -0
  149. package/backend/live-search/result-types/page-result-type.ts +23 -0
  150. package/backend/localization.ts +390 -0
  151. package/backend/login-refresh.ts +525 -0
  152. package/backend/login.ts +179 -0
  153. package/backend/mail-link-handler.ts +45 -0
  154. package/backend/modal-interface.ts +19 -0
  155. package/backend/modal.ts +685 -0
  156. package/backend/module/iframe.ts +125 -0
  157. package/backend/module/router.ts +275 -0
  158. package/backend/module-menu.ts +550 -0
  159. package/backend/module.ts +133 -0
  160. package/backend/multi-record-selection-action.ts +44 -0
  161. package/backend/multi-record-selection-delete-action.ts +108 -0
  162. package/backend/multi-record-selection-edit-action.ts +56 -0
  163. package/backend/multi-record-selection.ts +468 -0
  164. package/backend/multi-step-wizard.ts +568 -0
  165. package/backend/new-content-element-wizard-button.ts +51 -0
  166. package/backend/new-multiple-pages.ts +108 -0
  167. package/backend/new-record-wizard.ts +680 -0
  168. package/backend/notification.ts +366 -0
  169. package/backend/offset.ts +40 -0
  170. package/backend/online-media.ts +110 -0
  171. package/backend/page-actions.ts +91 -0
  172. package/backend/page-link-handler.ts +51 -0
  173. package/backend/pagetsconfig/pagetsconfig-includes.ts +77 -0
  174. package/backend/popover.ts +165 -0
  175. package/backend/record-download-button.ts +104 -0
  176. package/backend/record-link-handler.ts +31 -0
  177. package/backend/record-search.ts +44 -0
  178. package/backend/recordlist.ts +230 -0
  179. package/backend/resource/resource.ts +35 -0
  180. package/backend/security/element/csp-reports.ts +406 -0
  181. package/backend/security/element/sudo-mode.ts +249 -0
  182. package/backend/security/sudo-mode-interceptor.ts +19 -0
  183. package/backend/settings/editor/editable-setting.ts +231 -0
  184. package/backend/settings/editor.ts +451 -0
  185. package/backend/settings/enum/settings-mode.enum.ts +24 -0
  186. package/backend/settings/type/base.ts +200 -0
  187. package/backend/settings/type/bool.ts +56 -0
  188. package/backend/settings/type/color.ts +64 -0
  189. package/backend/settings/type/int.ts +85 -0
  190. package/backend/settings/type/number.ts +61 -0
  191. package/backend/settings/type/page.ts +100 -0
  192. package/backend/settings/type/string.ts +77 -0
  193. package/backend/settings/type/stringlist.ts +87 -0
  194. package/backend/severity.ts +88 -0
  195. package/backend/site-inline-actions.ts +32 -0
  196. package/backend/sortable-table.ts +208 -0
  197. package/backend/storage/abstract-client-storage.ts +76 -0
  198. package/backend/storage/browser-session.ts +28 -0
  199. package/backend/storage/client.ts +28 -0
  200. package/backend/storage/module-state-storage.ts +118 -0
  201. package/backend/storage/persistent.ts +204 -0
  202. package/backend/switch-user.ts +82 -0
  203. package/backend/tabs.ts +72 -0
  204. package/backend/telephone-link-handler.ts +40 -0
  205. package/backend/tests/backend/storage/browser-storage-test.ts +87 -0
  206. package/backend/tests/backend/storage/client-test.ts +87 -0
  207. package/backend/tests/backend/utility/dom-helper-test.ts +51 -0
  208. package/backend/tests/backend-exception-test.ts +28 -0
  209. package/backend/tests/element/immediate-action-element-test.ts +81 -0
  210. package/backend/tests/form-engine-validation-test.ts +304 -0
  211. package/backend/tests/grid-editor-test.ts +29 -0
  212. package/backend/tests/hashing/md5Test.ts +28 -0
  213. package/backend/tests/icons-test.ts +23 -0
  214. package/backend/tests/notification-test.ts +161 -0
  215. package/backend/tests/popover-test.ts +83 -0
  216. package/backend/toolbar/clear-cache-menu.ts +88 -0
  217. package/backend/toolbar/live-search.ts +284 -0
  218. package/backend/toolbar/shortcut-menu.ts +254 -0
  219. package/backend/toolbar/system-information-menu.ts +161 -0
  220. package/backend/toolbar.ts +41 -0
  221. package/backend/tree/file-storage-browser.ts +194 -0
  222. package/backend/tree/file-storage-tree-container.ts +465 -0
  223. package/backend/tree/file-storage-tree.ts +42 -0
  224. package/backend/tree/page-browser.ts +272 -0
  225. package/backend/tree/page-tree-element.ts +602 -0
  226. package/backend/tree/page-tree.ts +152 -0
  227. package/backend/tree/tree-module-state.ts +159 -0
  228. package/backend/tree/tree-node-toggle.ts +31 -0
  229. package/backend/tree/tree-node.ts +97 -0
  230. package/backend/tree/tree-toolbar.ts +117 -0
  231. package/backend/tree/tree.ts +1595 -0
  232. package/backend/url-link-handler.ts +36 -0
  233. package/backend/user-pass-login.ts +255 -0
  234. package/backend/user-settings-manager.ts +189 -0
  235. package/backend/utility/collapse-state-persister.ts +205 -0
  236. package/backend/utility/collapse-state-search.ts +183 -0
  237. package/backend/utility/dom-helper.ts +91 -0
  238. package/backend/utility/drag-drop-utility.ts +47 -0
  239. package/backend/utility/message-utility.ts +45 -0
  240. package/backend/utility/top-level-module-import.ts +32 -0
  241. package/backend/utility.ts +194 -0
  242. package/backend/viewport/abstract-container.ts +22 -0
  243. package/backend/viewport/content-container.ts +98 -0
  244. package/backend/viewport/loader.ts +28 -0
  245. package/backend/viewport/navigation-container.ts +119 -0
  246. package/backend/viewport/resizable-navigation.ts +176 -0
  247. package/backend/viewport/toolbar.ts +27 -0
  248. package/backend/viewport/topbar.ts +39 -0
  249. package/backend/viewport.ts +46 -0
  250. package/backend/window-manager.ts +84 -0
  251. package/backend/wizard/move-content-element.ts +107 -0
  252. package/backend/wizard/move-page.ts +74 -0
  253. package/backend/wizard.ts +331 -0
  254. package/belog/backend-log.ts +88 -0
  255. package/beuser/backend-user-listing.ts +40 -0
  256. package/beuser/permissions.ts +291 -0
  257. package/core/ajax/ajax-request-types.ts +15 -0
  258. package/core/ajax/ajax-request.ts +202 -0
  259. package/core/ajax/ajax-response.ts +57 -0
  260. package/core/ajax/input-transformer.ts +92 -0
  261. package/core/ajax/simple-response-interface.ts +5 -0
  262. package/core/authentication/mfa-provider/totp.ts +54 -0
  263. package/core/cache/dedupe-async-task.ts +95 -0
  264. package/core/directive/markdown.ts +82 -0
  265. package/core/directive/nl2br.ts +16 -0
  266. package/core/document-service.ts +35 -0
  267. package/core/event/debounce-event.ts +40 -0
  268. package/core/event/event-interface.ts +24 -0
  269. package/core/event/regular-event.ts +58 -0
  270. package/core/event/request-animation-frame-event.ts +42 -0
  271. package/core/event/throttle-event.ts +54 -0
  272. package/core/java-script-item-handler.ts +29 -0
  273. package/core/java-script-item-processor.ts +171 -0
  274. package/core/lit-helper.ts +126 -0
  275. package/core/literals.ts +17 -0
  276. package/core/referrer-refresh.ts +2 -0
  277. package/core/security-utility.ts +89 -0
  278. package/core/tests/ajax/ajax-request-test.ts +290 -0
  279. package/core/tests/ajax/input-transformer-test.ts +64 -0
  280. package/core/tests/cache/dedupe-async-task-test.ts +152 -0
  281. package/core/tests/event/throttle-event-test.ts +47 -0
  282. package/core/tests/literals-test.ts +24 -0
  283. package/core/tests/security-utility-test.ts +50 -0
  284. package/core/utility/types.ts +9 -0
  285. package/dashboard/chart-initializer.ts +136 -0
  286. package/dashboard/dashboard.ts +1487 -0
  287. package/extensionmanager/distribution-image.ts +55 -0
  288. package/extensionmanager/main.ts +291 -0
  289. package/extensionmanager/repository.ts +195 -0
  290. package/extensionmanager/update.ts +118 -0
  291. package/extensionmanager/upload-form.ts +42 -0
  292. package/filelist/browse-files.ts +148 -0
  293. package/filelist/browse-folders.ts +94 -0
  294. package/filelist/context-menu-actions.ts +335 -0
  295. package/filelist/create-folder.ts +69 -0
  296. package/filelist/file-delete.ts +69 -0
  297. package/filelist/file-list-actions.ts +132 -0
  298. package/filelist/file-list-dragdrop.ts +139 -0
  299. package/filelist/file-list-rename-handler.ts +128 -0
  300. package/filelist/file-list-transfer-handler.ts +138 -0
  301. package/filelist/file-list.ts +396 -0
  302. package/filelist/file-replace.ts +37 -0
  303. package/filelist/linkbrowser-file-handler.ts +94 -0
  304. package/filelist/linkbrowser-folder-handler.ts +74 -0
  305. package/filelist/rename-file.ts +109 -0
  306. package/form/backend/form-editor/component/property-grid-editor.ts +340 -0
  307. package/form/backend/form-editor/core.ts +2104 -0
  308. package/form/backend/form-editor/helper.ts +226 -0
  309. package/form/backend/form-editor/inspector-component.ts +2323 -0
  310. package/form/backend/form-editor/mediator.ts +941 -0
  311. package/form/backend/form-editor/modals-component.ts +433 -0
  312. package/form/backend/form-editor/stage-component.ts +908 -0
  313. package/form/backend/form-editor/tree-component.ts +548 -0
  314. package/form/backend/form-editor/view-model.ts +1343 -0
  315. package/form/backend/form-editor.ts +846 -0
  316. package/form/backend/form-manager/main.ts +39 -0
  317. package/form/backend/form-manager/view-model.ts +789 -0
  318. package/form/backend/form-manager.ts +180 -0
  319. package/form/backend/helper.ts +60 -0
  320. package/form/frontend/date-picker.ts +38 -0
  321. package/frontend/default_frontend.ts +82 -0
  322. package/impexp/context-menu-actions.ts +50 -0
  323. package/impexp/import-export.ts +62 -0
  324. package/indexed-search/pagination.ts +26 -0
  325. package/indexed-search/statistics.ts +50 -0
  326. package/info/translation-status.ts +48 -0
  327. package/install/ajax/ajax-queue.ts +83 -0
  328. package/install/init-install.ts +2 -0
  329. package/install/init-installer.ts +2 -0
  330. package/install/install.ts +25 -0
  331. package/install/installer.ts +458 -0
  332. package/install/message-interface.ts +18 -0
  333. package/install/module/abstract-inline-module.ts +27 -0
  334. package/install/module/abstract-interactable-module.ts +107 -0
  335. package/install/module/environment/environment-check.ts +92 -0
  336. package/install/module/environment/folder-structure.ts +145 -0
  337. package/install/module/environment/image-processing.ts +141 -0
  338. package/install/module/environment/mail-test.ts +127 -0
  339. package/install/module/environment/php-info.ts +50 -0
  340. package/install/module/environment/system-information.ts +50 -0
  341. package/install/module/maintenance/cache.ts +59 -0
  342. package/install/module/maintenance/clear-tables.ts +134 -0
  343. package/install/module/maintenance/clear-typo3temp-files.ts +137 -0
  344. package/install/module/maintenance/create-admin.ts +126 -0
  345. package/install/module/maintenance/database-analyzer.ts +266 -0
  346. package/install/module/maintenance/dump-autoload.ts +58 -0
  347. package/install/module/maintenance/language-packs.ts +338 -0
  348. package/install/module/maintenance/reset-backend-user-uc.ts +58 -0
  349. package/install/module/password-strength.ts +56 -0
  350. package/install/module/settings/change-install-tool-password.ts +101 -0
  351. package/install/module/settings/extension-configuration.ts +224 -0
  352. package/install/module/settings/features.ts +102 -0
  353. package/install/module/settings/local-configuration.ts +316 -0
  354. package/install/module/settings/presets.ts +147 -0
  355. package/install/module/settings/system-maintainer.ts +111 -0
  356. package/install/module/upgrade/core-update.ts +255 -0
  357. package/install/module/upgrade/extension-compat-tester.ts +195 -0
  358. package/install/module/upgrade/extension-scanner.ts +381 -0
  359. package/install/module/upgrade/tca-ext-tables-check.ts +93 -0
  360. package/install/module/upgrade/tca-migrations-check.ts +101 -0
  361. package/install/module/upgrade/upgrade-docs.ts +310 -0
  362. package/install/module/upgrade/upgrade-wizards.ts +612 -0
  363. package/install/renderable/clearable.ts +113 -0
  364. package/install/renderable/flash-message.ts +64 -0
  365. package/install/renderable/info-box.ts +66 -0
  366. package/install/renderable/language-packs.ts +304 -0
  367. package/install/renderable/offset-group.ts +38 -0
  368. package/install/renderable/severity.ts +49 -0
  369. package/install/renderable/wrap-group.ts +36 -0
  370. package/install/router.ts +468 -0
  371. package/linkvalidator/linkvalidator-modal.ts +45 -0
  372. package/linkvalidator/linkvalidator.ts +101 -0
  373. package/lowlevel/query-generator.ts +132 -0
  374. package/lowlevel/reference-index.ts +44 -0
  375. package/opendocs/toolbar/opendocs-menu.ts +117 -0
  376. package/package.json +34 -0
  377. package/recycler/recycler.ts +539 -0
  378. package/redirects/event-handler.ts +108 -0
  379. package/redirects/form-engine-evaluation.ts +34 -0
  380. package/redirects/redirects-module.ts +58 -0
  381. package/rte-ckeditor/ckeditor5.ts +402 -0
  382. package/rte-ckeditor/css-prefixer.ts +94 -0
  383. package/rte-ckeditor/legacy/ckeditor5-bundle.ts +17 -0
  384. package/rte-ckeditor/legacy/ckeditor5-inspector.ts +2 -0
  385. package/rte-ckeditor/plugin/typo3-link.ts +810 -0
  386. package/rte-ckeditor/plugin/whitespace.ts +114 -0
  387. package/rte-ckeditor/rte-link-browser.ts +112 -0
  388. package/rte-ckeditor/tests/css-prefixer-test.ts +314 -0
  389. package/scheduler/scheduler-add-group.ts +119 -0
  390. package/scheduler/scheduler-editable-group-name.ts +307 -0
  391. package/scheduler/scheduler-sortable-groups.ts +59 -0
  392. package/scheduler/scheduler.ts +466 -0
  393. package/setup/setup-module.ts +122 -0
  394. package/styleguide/checkbox-demo.ts +5 -0
  395. package/styleguide/element/theme-switcher-element.ts +101 -0
  396. package/styleguide/filter.ts +22 -0
  397. package/styleguide/find-icons.ts +78 -0
  398. package/styleguide/processing-indicator.ts +78 -0
  399. package/styleguide/render-notifications.ts +64 -0
  400. package/sys-note/element/delete-button.ts +78 -0
  401. package/tstemplate/constant-editor.ts +64 -0
  402. package/tstemplate/information-module.ts +31 -0
  403. package/tstemplate/template-analyzer.ts +77 -0
  404. package/viewpage/main.ts +253 -0
  405. package/workspaces/backend.ts +979 -0
  406. package/workspaces/preview.ts +232 -0
  407. package/workspaces/renderable/comment-view.ts +79 -0
  408. package/workspaces/renderable/diff-view.ts +58 -0
  409. package/workspaces/renderable/history-view.ts +97 -0
  410. package/workspaces/renderable/record-information.ts +118 -0
  411. package/workspaces/renderable/record-table.ts +355 -0
  412. package/workspaces/renderable/send-to-stage-form.ts +108 -0
  413. package/workspaces/toolbar/workspaces-menu.ts +190 -0
  414. package/workspaces/utility/icon-helper.ts +57 -0
  415. package/workspaces/workspaces.ts +136 -0
@@ -0,0 +1,979 @@
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 type { AjaxResponse } from '@typo3/core/ajax/ajax-response';
15
+ import DocumentService from '@typo3/core/document-service';
16
+ import { html } from 'lit';
17
+ import '@typo3/backend/element/icon-element';
18
+ import { SeverityEnum } from '@typo3/backend/enum/severity';
19
+ import '@typo3/backend/input/clearable';
20
+ import '@typo3/workspaces/renderable/record-table';
21
+ import '@typo3/backend/element/pagination';
22
+ import Workspaces from './workspaces';
23
+ import { default as Modal, type ModalElement } from '@typo3/backend/modal';
24
+ import Persistent from '@typo3/backend/storage/persistent';
25
+ import Utility from '@typo3/backend/utility';
26
+ import windowManager from '@typo3/backend/window-manager';
27
+ import RegularEvent from '@typo3/core/event/regular-event';
28
+ import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import';
29
+ import { selector } from '@typo3/core/literals';
30
+ import IconHelper from '@typo3/workspaces/utility/icon-helper';
31
+ import DeferredAction from '@typo3/backend/action-button/deferred-action';
32
+ import type { PaginationElement } from '@typo3/backend/element/pagination';
33
+
34
+ enum Identifiers {
35
+ searchForm = '#workspace-settings-form',
36
+ searchTextField = '#workspace-settings-form input[name="search-text"]',
37
+ searchSubmitBtn = '#workspace-settings-form button[type="submit"]',
38
+ depthSelector = '#workspace-settings-form [name="depth"]',
39
+ languageSelector = '#workspace-settings-form select[name="languages"]',
40
+ stagesSelector = '#workspace-settings-form select[name="stages"]',
41
+ workspaceActions = '.workspace-actions',
42
+ chooseStageAction = '.workspace-actions [name="stage-action"]',
43
+ chooseSelectionAction = '.workspace-actions [name="selection-action"]',
44
+ chooseMassAction = '.workspace-actions [name="mass-action"]',
45
+ publishAction = '[data-action="publish"]',
46
+ prevStageAction = '[data-action="prevstage"]',
47
+ nextStageAction = '[data-action="nextstage"]',
48
+ changesAction = '[data-action="changes"]',
49
+ previewAction = '[data-action="preview"]',
50
+ openAction = '[data-action="open"]',
51
+ versionAction = '[data-action="version"]',
52
+ removeAction = '[data-action="remove"]',
53
+ expandAction = '[data-action="expand"]',
54
+ workspaceRecipientsSelectAll = '.t3js-workspace-recipients-selectall',
55
+ workspaceRecipientsDeselectAll = '.t3js-workspace-recipients-deselectall',
56
+ container = '#workspace-panel',
57
+ contentsContainer = '#workspace-contents',
58
+ noContentsContainer = '#workspace-contents-empty',
59
+ previewLinksButton = '.t3js-preview-link',
60
+ pagination = '#workspace-pagination',
61
+ }
62
+
63
+ /**
64
+ * Backend workspace module. Loaded only in Backend context, not in
65
+ * workspace preview. Contains all JavaScript of the main BE module.
66
+ */
67
+ class Backend extends Workspaces {
68
+ private readonly settings: { [key: string]: string | number } = {
69
+ id: TYPO3.settings.Workspaces.id,
70
+ depth: 1,
71
+ language: 'all',
72
+ limit: 30,
73
+ query: '',
74
+ start: 0,
75
+ filterTxt: '',
76
+ };
77
+ private readonly paging: Record<string, number> = {
78
+ currentPage: 1,
79
+ totalPages: 1,
80
+ totalItems: 0,
81
+ };
82
+ private markedRecordsForMassAction: string[] = [];
83
+
84
+ constructor() {
85
+ super();
86
+
87
+ topLevelModuleImport('@typo3/workspaces/renderable/send-to-stage-form.js');
88
+ topLevelModuleImport('@typo3/workspaces/renderable/record-information.js');
89
+
90
+ DocumentService.ready().then((): void => {
91
+ this.registerEvents();
92
+
93
+ // Set the depth from the main element
94
+ this.settings.depth = (document.querySelector(Identifiers.depthSelector) as HTMLInputElement)?.value;
95
+ this.settings.language = (document.querySelector(Identifiers.languageSelector) as HTMLInputElement)?.value;
96
+ this.settings.stage = (document.querySelector(Identifiers.stagesSelector) as HTMLInputElement)?.value;
97
+
98
+ // Fetch workspace info (listing) if workspace is accessible
99
+ if (document.querySelector(Identifiers.container) !== null) {
100
+ this.getWorkspaceInfos();
101
+ }
102
+ });
103
+ }
104
+
105
+ /**
106
+ * Reloads the page tree
107
+ */
108
+ private static refreshPageTree(): void {
109
+ top.document.dispatchEvent(new CustomEvent('typo3:pagetree:refresh'));
110
+ }
111
+
112
+ /**
113
+ * This changes the checked state of a parent checkbox belonging
114
+ * to the given collection (e.g. sys_file_reference > tt_content).
115
+ *
116
+ * This also sets a data attribute which will be respected by
117
+ * the multi record selection module. This is to prevent the
118
+ * module from overriding the manually changed state.
119
+ */
120
+ private static changeCollectionParentState(collection: string, check: boolean): void {
121
+ const parent: HTMLInputElement = document.querySelector('tr[data-collection-current="' + collection + '"] input[type=checkbox]');
122
+ if (parent !== null && parent.checked !== check) {
123
+ parent.checked = check;
124
+ parent.dataset.manuallyChanged = 'true';
125
+ parent.dispatchEvent(new CustomEvent('multiRecordSelection:checkbox:state:changed', { bubbles: true, cancelable: false }));
126
+ }
127
+ }
128
+
129
+ /**
130
+ * This changes the checked state of all checkboxes belonging
131
+ * to the given collectionCurrent. Those are the child records
132
+ * of a parent record (e.g. tt_content > sys_file_reference).
133
+ *
134
+ * This also sets a data attribute which will be respected by
135
+ * the multi record selection module. This is to prevent the
136
+ * module from overriding the manually changed state.
137
+ */
138
+ private static changeCollectionChildrenState(collectionCurrent: string, check: boolean): void {
139
+ const collectionChildren: NodeListOf<HTMLInputElement> = document.querySelectorAll(selector`tr[data-collection="${collectionCurrent}"] input[type=checkbox]`);
140
+ if (collectionChildren.length) {
141
+ collectionChildren.forEach((checkbox: HTMLInputElement): void => {
142
+ if (checkbox.checked !== check) {
143
+ checkbox.checked = check;
144
+ checkbox.dataset.manuallyChanged = 'true';
145
+ checkbox.dispatchEvent(new CustomEvent('multiRecordSelection:checkbox:state:changed', { bubbles: true, cancelable: false }));
146
+ }
147
+ });
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Checks the integrity of a record
153
+ */
154
+ private checkIntegrity(payload: object): Promise<AjaxResponse> {
155
+ return this.sendRemoteRequest(
156
+ this.generateRemotePayload('checkIntegrity', payload),
157
+ );
158
+ }
159
+
160
+ private registerEvents(): void {
161
+ new RegularEvent('click', (event: Event, target) => {
162
+ const row = target.closest('tr') as HTMLTableRowElement;
163
+
164
+ this.checkIntegrity(
165
+ {
166
+ selection: [
167
+ {
168
+ liveId: row.dataset.uid,
169
+ versionId: row.dataset.t3ver_oid,
170
+ table: row.dataset.table,
171
+ },
172
+ ],
173
+ type: 'selection',
174
+ },
175
+ ).then(async (response: AjaxResponse): Promise<void> => {
176
+ if ((await response.resolve())[0].result.result === 'warning') {
177
+ this.openIntegrityWarningModal().addEventListener('confirm.button.ok', (): void => {
178
+ this.renderPublishModal(row);
179
+ });
180
+ } else {
181
+ this.renderPublishModal(row);
182
+ }
183
+ });
184
+ }).delegateTo(document, Identifiers.publishAction);
185
+
186
+ new RegularEvent('click', (event: Event, target: HTMLElement) => {
187
+ this.sendToStage(target.closest('tr'), 'prev');
188
+ }).delegateTo(document, Identifiers.prevStageAction);
189
+
190
+ new RegularEvent('click', (event: Event, target: HTMLElement) => {
191
+ this.sendToStage(target.closest('tr'), 'next');
192
+ }).delegateTo(document, Identifiers.nextStageAction);
193
+
194
+ new RegularEvent('click', this.viewChanges.bind(this)).delegateTo(document, Identifiers.changesAction);
195
+ new RegularEvent('click', this.openPreview.bind(this)).delegateTo(document, Identifiers.previewAction);
196
+
197
+ new RegularEvent('click', (event: Event, target: HTMLElement) => {
198
+ const row = target.closest('tr') as HTMLTableRowElement;
199
+ const newUrl = TYPO3.settings.FormEngine.moduleUrl
200
+ + '&returnUrl=' + encodeURIComponent(document.location.href)
201
+ + '&id=' + TYPO3.settings.Workspaces.id + '&edit[' + row.dataset.table + '][' + row.dataset.uid + ']=edit';
202
+
203
+ window.location.href = newUrl;
204
+ }).delegateTo(document, Identifiers.openAction);
205
+
206
+ new RegularEvent('click', (event: Event, target: HTMLElement) => {
207
+ const row = target.closest('tr') as HTMLTableRowElement;
208
+ const recordUid = row.dataset.table === 'pages' ? row.dataset.t3ver_oid : row.dataset.pid;
209
+ window.location.href = TYPO3.settings.WebLayout.moduleUrl
210
+ + '&id=' + recordUid;
211
+ }).delegateTo(document, Identifiers.versionAction);
212
+
213
+ new RegularEvent('click', this.confirmDeleteRecordFromWorkspace.bind(this)).delegateTo(document, Identifiers.removeAction);
214
+
215
+ new RegularEvent('click', (event: Event, target: HTMLElement) => {
216
+ let iconIdentifier;
217
+
218
+ if (target.ariaExpanded === 'true') {
219
+ iconIdentifier = 'actions-caret-down';
220
+ } else {
221
+ iconIdentifier = 'actions-caret-right';
222
+ }
223
+
224
+ target.replaceChildren(document.createRange().createContextualFragment(IconHelper.getIcon(iconIdentifier)));
225
+ }).delegateTo(document, Identifiers.expandAction);
226
+
227
+ new RegularEvent('click', () => {
228
+ const workspaceRecipients = window.top.document.querySelectorAll('.t3js-workspace-recipient');
229
+
230
+ workspaceRecipients.forEach((recipient: HTMLInputElement) => {
231
+ if (!recipient.disabled) {
232
+ recipient.checked = true;
233
+ }
234
+ });
235
+ }).delegateTo(window.top.document, Identifiers.workspaceRecipientsSelectAll);
236
+
237
+ new RegularEvent('click', () => {
238
+ const workspaceRecipients = window.top.document.querySelectorAll('.t3js-workspace-recipient');
239
+
240
+ workspaceRecipients.forEach((recipient: HTMLInputElement) => {
241
+ if (!recipient.disabled) {
242
+ recipient.checked = false;
243
+ }
244
+ });
245
+ }).delegateTo(window.top.document, Identifiers.workspaceRecipientsDeselectAll);
246
+
247
+ new RegularEvent('submit', (event: Event) => {
248
+ event.preventDefault();
249
+
250
+ const searchTextField = document.querySelector(Identifiers.searchTextField) as HTMLInputElement;
251
+
252
+ this.settings.filterTxt = searchTextField.value;
253
+ this.getWorkspaceInfos();
254
+ }).delegateTo(document, Identifiers.searchForm);
255
+
256
+ new RegularEvent('input', (event: Event, target: HTMLInputElement) => {
257
+ const searchSubmitButton = document.querySelector(Identifiers.searchSubmitBtn) as HTMLButtonElement;
258
+
259
+ if (target.value !== '') {
260
+ searchSubmitButton.classList.remove('disabled');
261
+ } else {
262
+ searchSubmitButton.classList.add('disabled');
263
+ this.getWorkspaceInfos();
264
+ }
265
+ }).delegateTo(document, Identifiers.searchTextField);
266
+
267
+ const searchTextField = document.querySelector(Identifiers.searchTextField) as HTMLInputElement;
268
+ if (searchTextField !== null) {
269
+ searchTextField.clearable(
270
+ {
271
+ onClear: (): void => {
272
+ const searchSubmitButton = document.querySelector(Identifiers.searchSubmitBtn) as HTMLButtonElement;
273
+ searchSubmitButton.classList.add('disabled');
274
+ this.settings.filterTxt = '';
275
+ this.getWorkspaceInfos();
276
+ },
277
+ },
278
+ );
279
+ }
280
+
281
+ // checkboxes in the table
282
+ new RegularEvent('multiRecordSelection:checkbox:state:changed', this.handleCheckboxStateChanged).bindTo(document);
283
+
284
+ // Listen for depth changes
285
+ new RegularEvent('change', (event: Event, target: HTMLSelectElement) => {
286
+ const depth = target.value;
287
+ Persistent.set('moduleData.workspaces_admin.depth', depth);
288
+ this.settings.depth = depth;
289
+ this.getWorkspaceInfos();
290
+ }).delegateTo(document, Identifiers.depthSelector);
291
+
292
+ // Generate preview links
293
+ new RegularEvent('click', this.generatePreviewLinks.bind(this)).delegateTo(document, Identifiers.previewLinksButton);
294
+
295
+ // Listen for language changes
296
+ new RegularEvent('change', (event: Event, target: HTMLSelectElement) => {
297
+ Persistent.set('moduleData.workspaces_admin.language', target.value);
298
+ this.settings.language = target.value;
299
+ this.sendRemoteRequest(
300
+ this.generateRemotePayload('getWorkspaceInfos', this.settings),
301
+ ).then(async (response: AjaxResponse): Promise<void> => {
302
+ const actionResponse = await response.resolve();
303
+ target.previousElementSibling.innerHTML = (target.querySelector('option:checked') as HTMLElement).dataset.icon;
304
+ this.renderWorkspaceInfos(actionResponse[0].result);
305
+ });
306
+ }).delegateTo(document, Identifiers.languageSelector);
307
+
308
+ new RegularEvent('change', (event: Event, target: HTMLSelectElement) => {
309
+ const stage = target.value;
310
+ Persistent.set('moduleData.workspaces_admin.stage', stage);
311
+ this.settings.stage = stage;
312
+ this.getWorkspaceInfos();
313
+ }).delegateTo(document, Identifiers.stagesSelector);
314
+
315
+ // Listen for actions
316
+ new RegularEvent('change', this.sendToSpecificStageAction.bind(this)).delegateTo(document, Identifiers.chooseStageAction);
317
+ new RegularEvent('change', this.runSelectionAction.bind(this)).delegateTo(document, Identifiers.chooseSelectionAction);
318
+ new RegularEvent('change', this.runMassAction.bind(this)).delegateTo(document, Identifiers.chooseMassAction);
319
+
320
+ // clicking an action in the paginator
321
+ new RegularEvent('click', (event: Event) => {
322
+ event.preventDefault();
323
+
324
+ const paginator = (event.target as HTMLElement).closest('button') as HTMLButtonElement;
325
+
326
+ let reload = false;
327
+
328
+ switch (paginator.dataset.action) {
329
+ case 'previous':
330
+ if (this.paging.currentPage > 1) {
331
+ this.paging.currentPage--;
332
+ reload = true;
333
+ }
334
+ break;
335
+ case 'next':
336
+ if (this.paging.currentPage < this.paging.totalPages) {
337
+ this.paging.currentPage++;
338
+ reload = true;
339
+ }
340
+ break;
341
+ case 'page':
342
+ this.paging.currentPage = parseInt(paginator.dataset.page, 10);
343
+ reload = true;
344
+ break;
345
+ default:
346
+ throw 'Unknown action "' + paginator.dataset.action + '"';
347
+ }
348
+
349
+ if (reload) {
350
+ // Adjust settings
351
+ this.settings.start = parseInt(this.settings.limit.toString(), 10) * (this.paging.currentPage - 1);
352
+ this.getWorkspaceInfos();
353
+ }
354
+ }).delegateTo(document, Identifiers.pagination);
355
+ }
356
+
357
+ private readonly handleCheckboxStateChanged = (event: Event): void => {
358
+ const checkbox = event.target as HTMLInputElement;
359
+ const tableRow = checkbox.closest('tr') as HTMLTableRowElement;
360
+ const checked = checkbox.checked;
361
+ const table = tableRow.dataset.table;
362
+ const uid = tableRow.dataset.uid;
363
+ const t3ver_oid = tableRow.dataset.t3ver_oid;
364
+ const record = table + ':' + uid + ':' + t3ver_oid;
365
+
366
+ if (checked) {
367
+ this.markedRecordsForMassAction.push(record);
368
+ } else {
369
+ const index = this.markedRecordsForMassAction.indexOf(record);
370
+ if (index > -1) {
371
+ this.markedRecordsForMassAction.splice(index, 1);
372
+ }
373
+ }
374
+
375
+ if (tableRow.dataset.collectionCurrent) {
376
+ // change checked state from all collection children
377
+ Backend.changeCollectionChildrenState(tableRow.dataset.collectionCurrent, checked);
378
+ } else if (tableRow.dataset.collection) {
379
+ // change checked state from all collection children and the collection parent
380
+ Backend.changeCollectionChildrenState(tableRow.dataset.collection, checked);
381
+ Backend.changeCollectionParentState(tableRow.dataset.collection, checked);
382
+ }
383
+
384
+ const chooseMassAction = document.querySelector(Identifiers.chooseMassAction) as HTMLSelectElement;
385
+ if (chooseMassAction !== null) {
386
+ chooseMassAction.disabled = this.markedRecordsForMassAction.length > 0;
387
+ }
388
+ };
389
+
390
+ /**
391
+ * Sends a record to a stage
392
+ */
393
+ private sendToStage(row: HTMLTableRowElement, direction: string): void {
394
+ let nextStage: string;
395
+ let stageWindowAction: string;
396
+ let stageExecuteAction: string;
397
+
398
+ if (direction === 'next') {
399
+ nextStage = row.dataset.nextStage;
400
+ stageWindowAction = 'sendToNextStageWindow';
401
+ stageExecuteAction = 'sendToNextStageExecute';
402
+ } else if (direction === 'prev') {
403
+ nextStage = row.dataset.prevStage;
404
+ stageWindowAction = 'sendToPrevStageWindow';
405
+ stageExecuteAction = 'sendToPrevStageExecute';
406
+ } else {
407
+ throw 'Invalid direction given.';
408
+ }
409
+
410
+ this.sendRemoteRequest(
411
+ this.generateRemoteActionsPayload(stageWindowAction, [
412
+ row.dataset.uid, row.dataset.table, row.dataset.t3ver_oid,
413
+ ]),
414
+ ).then(async (response: AjaxResponse): Promise<void> => {
415
+ const modal = this.renderSendToStageWindow(await response.resolve());
416
+ modal.addEventListener('button.clicked', (modalEvent: Event): void => {
417
+ const target = modalEvent.target as HTMLButtonElement;
418
+ if (target.name === 'ok') {
419
+ const serializedForm = Utility.convertFormToObject(modal.querySelector('form'));
420
+ serializedForm.affects = {
421
+ table: row.dataset.table,
422
+ nextStage: nextStage,
423
+ t3ver_oid: row.dataset.t3ver_oid,
424
+ uid: row.dataset.uid,
425
+ elements: [],
426
+ };
427
+
428
+ this.sendRemoteRequest([
429
+ this.generateRemoteActionsPayload(stageExecuteAction, [serializedForm]),
430
+ this.generateRemotePayload('getWorkspaceInfos', this.settings),
431
+ ]).then(async (response: AjaxResponse): Promise<void> => {
432
+ const requestResponse = await response.resolve();
433
+ modal.hideModal();
434
+ this.renderWorkspaceInfos(requestResponse[1].result);
435
+ Backend.refreshPageTree();
436
+ });
437
+ }
438
+ });
439
+ });
440
+ }
441
+
442
+ /**
443
+ * Gets the workspace infos (= filling the contents).
444
+ */
445
+ private getWorkspaceInfos(): void {
446
+ this.sendRemoteRequest(
447
+ this.generateRemotePayload('getWorkspaceInfos', this.settings),
448
+ ).then(async (response: AjaxResponse): Promise<void> => {
449
+ this.renderWorkspaceInfos((await response.resolve())[0].result);
450
+ });
451
+ }
452
+
453
+ /**
454
+ * Renders fetched workspace information
455
+ */
456
+ private renderWorkspaceInfos(result: any): void {
457
+ const contentsContainer = document.querySelector(Identifiers.contentsContainer) as HTMLElement;
458
+ const noContentsContainer = document.querySelector(Identifiers.noContentsContainer) as HTMLElement;
459
+
460
+ this.resetMassActionState(result.data.length);
461
+ this.buildPagination(result.total);
462
+
463
+ // disable the contents area
464
+ if (result.total === 0) {
465
+ contentsContainer.style.display = 'none';
466
+ noContentsContainer.style.display = 'block';
467
+ } else {
468
+ contentsContainer.style.display = 'block';
469
+ noContentsContainer.style.display = 'none';
470
+ }
471
+
472
+ const workspacesRecordTable = document.querySelector('typo3-workspaces-record-table');
473
+ workspacesRecordTable.results = result.data;
474
+ }
475
+
476
+ /**
477
+ * Renders the pagination
478
+ */
479
+ private buildPagination(totalItems: number): void {
480
+ const paginationContainer = document.querySelector(Identifiers.pagination) as HTMLElement;
481
+
482
+ if (totalItems === 0) {
483
+ paginationContainer.replaceChildren();
484
+ return;
485
+ }
486
+
487
+ this.paging.totalItems = totalItems;
488
+ this.paging.totalPages = Math.ceil(totalItems / parseInt(this.settings.limit.toString(), 10));
489
+
490
+ if (this.paging.totalPages === 1) {
491
+ // early abort if only one page is available
492
+ paginationContainer.replaceChildren();
493
+ return;
494
+ }
495
+
496
+ let pagination = paginationContainer.querySelector('typo3-backend-pagination') as PaginationElement | null;
497
+ if (pagination === null) {
498
+ pagination = document.createElement('typo3-backend-pagination');
499
+ paginationContainer.append(pagination);
500
+ }
501
+
502
+ pagination.paging = { ...this.paging };
503
+ }
504
+
505
+ /**
506
+ * View changes of a record
507
+ */
508
+ private viewChanges(event: Event, target: HTMLElement): void {
509
+ event.preventDefault();
510
+
511
+ const tableRow = target.closest('tr') as HTMLTableRowElement;
512
+ this.sendRemoteRequest(
513
+ this.generateRemotePayload('getRowDetails', {
514
+ stage: parseInt(tableRow.dataset.stage, 10),
515
+ t3ver_oid: parseInt(tableRow.dataset.t3ver_oid, 10),
516
+ table: tableRow.dataset.table,
517
+ uid: parseInt(tableRow.dataset.uid, 10),
518
+ filterFields: true
519
+ }),
520
+ ).then(async (response: AjaxResponse): Promise<void> => {
521
+ const item = (await response.resolve())[0].result.data[0];
522
+ const modalButtons = [];
523
+
524
+ const content = document.createElement('typo3-workspaces-record-information');
525
+ content.record = item;
526
+ content.TYPO3lang = TYPO3.lang;
527
+
528
+ if (item.label_PrevStage !== false && tableRow.dataset.stage !== tableRow.dataset.prevStage) {
529
+ modalButtons.push({
530
+ text: item.label_PrevStage.title,
531
+ active: true,
532
+ btnClass: 'btn-default',
533
+ name: 'prevstage',
534
+ trigger: (e: Event, modal: ModalElement) => {
535
+ modal.hideModal();
536
+ this.sendToStage(tableRow, 'prev');
537
+ },
538
+ });
539
+ }
540
+
541
+ if (item.label_NextStage !== false) {
542
+ modalButtons.push({
543
+ text: item.label_NextStage.title,
544
+ active: true,
545
+ btnClass: 'btn-default',
546
+ name: 'nextstage',
547
+ trigger: (e: Event, modal: ModalElement) => {
548
+ modal.hideModal();
549
+ this.sendToStage(tableRow, 'next');
550
+ },
551
+ });
552
+ }
553
+ modalButtons.push({
554
+ text: TYPO3.lang.close,
555
+ active: true,
556
+ btnClass: 'btn-info',
557
+ name: 'cancel',
558
+ trigger: (e: Event, modal: ModalElement) => modal.hideModal(),
559
+ });
560
+
561
+ Modal.advanced({
562
+ type: Modal.types.default,
563
+ title: TYPO3.lang['window.recordInformation'].replace('{0}', (tableRow.querySelector('.t3js-title-workspace') as HTMLElement).innerText.trim()),
564
+ content: content,
565
+ severity: SeverityEnum.info,
566
+ buttons: modalButtons,
567
+ size: Modal.sizes.medium,
568
+ });
569
+ });
570
+ }
571
+
572
+ /**
573
+ * Opens a record in a preview window
574
+ */
575
+ private openPreview(event: Event, target: HTMLElement): void {
576
+ const tableRow = target.closest('tr') as HTMLTableRowElement;
577
+
578
+ this.sendRemoteRequest(
579
+ this.generateRemoteActionsPayload('viewSingleRecord', [
580
+ tableRow.dataset.table, tableRow.dataset.uid,
581
+ ]),
582
+ ).then(async (response: AjaxResponse): Promise<void> => {
583
+ const previewUri: string = (await response.resolve())[0].result;
584
+ windowManager.localOpen(previewUri);
585
+ });
586
+ }
587
+
588
+ /**
589
+ * Shows a confirmation modal and deletes the selected record from workspace.
590
+ */
591
+ private confirmDeleteRecordFromWorkspace(e: Event, target: HTMLElement): void {
592
+ const tableRow = target.closest('tr') as HTMLTableRowElement;
593
+
594
+ const modal = Modal.confirm(
595
+ TYPO3.lang['window.discard.title'],
596
+ TYPO3.lang['window.discard.message'],
597
+ SeverityEnum.warning,
598
+ [
599
+ {
600
+ text: TYPO3.lang.cancel,
601
+ active: true,
602
+ btnClass: 'btn-default',
603
+ name: 'cancel',
604
+ trigger: (): void => {
605
+ modal.hideModal();
606
+ },
607
+ },
608
+ {
609
+ text: TYPO3.lang.ok,
610
+ btnClass: 'btn-warning',
611
+ name: 'ok',
612
+ },
613
+ ],
614
+ );
615
+
616
+ modal.addEventListener('button.clicked', (modalEvent: Event): void => {
617
+ if ((<HTMLAnchorElement>modalEvent.target).name === 'ok') {
618
+ this.sendRemoteRequest([
619
+ this.generateRemoteActionsPayload('deleteSingleRecord', [
620
+ tableRow.dataset.table,
621
+ tableRow.dataset.uid,
622
+ ]),
623
+ ]).then((): void => {
624
+ modal.hideModal();
625
+ this.getWorkspaceInfos();
626
+ Backend.refreshPageTree();
627
+ });
628
+ }
629
+ });
630
+ }
631
+
632
+ /**
633
+ * Runs a mass action
634
+ */
635
+ private runSelectionAction(event: Event, target: HTMLSelectElement): void {
636
+ const selectedAction = target.value;
637
+ const integrityCheckRequired = selectedAction !== 'discard';
638
+
639
+ if (selectedAction.length === 0) {
640
+ // Don't do anything if that value is empty
641
+ return;
642
+ }
643
+
644
+ const affectedRecords: Array<object> = [];
645
+ for (let i = 0; i < this.markedRecordsForMassAction.length; ++i) {
646
+ const affected = this.markedRecordsForMassAction[i].split(':');
647
+ affectedRecords.push({
648
+ table: affected[0],
649
+ liveId: affected[2],
650
+ versionId: affected[1],
651
+ });
652
+ }
653
+
654
+ if (!integrityCheckRequired) {
655
+ this.renderSelectionActionModal(selectedAction, affectedRecords);
656
+ } else {
657
+ this.checkIntegrity(
658
+ {
659
+ selection: affectedRecords,
660
+ type: 'selection',
661
+ },
662
+ ).then(async (response: AjaxResponse): Promise<void> => {
663
+ if ((await response.resolve())[0].result.result === 'warning') {
664
+ this.openIntegrityWarningModal().addEventListener('confirm.button.ok', (): void => {
665
+ this.renderSelectionActionModal(selectedAction, affectedRecords);
666
+ });
667
+ } else {
668
+ this.renderSelectionActionModal(selectedAction, affectedRecords);
669
+ }
670
+ });
671
+ }
672
+ }
673
+
674
+ private readonly openIntegrityWarningModal = (): ModalElement => {
675
+ const modal = Modal.confirm(
676
+ TYPO3.lang['window.integrity_warning.title'],
677
+ html`<p>${TYPO3.lang['integrity.hasIssuesDescription']}<br>${TYPO3.lang['integrity.hasIssuesQuestion']}</p>`,
678
+ SeverityEnum.warning
679
+ );
680
+ modal.addEventListener('button.clicked', (): void => modal.hideModal());
681
+
682
+ return modal;
683
+ };
684
+
685
+ private renderPublishModal(row: HTMLTableRowElement): void {
686
+ const modal = Modal.advanced({
687
+ title: TYPO3.lang['window.publish.title'],
688
+ content: TYPO3.lang['window.publish.message'],
689
+ severity: SeverityEnum.info,
690
+ staticBackdrop: true,
691
+ buttons: [
692
+ {
693
+ text: TYPO3.lang.cancel,
694
+ btnClass: 'btn-default',
695
+ trigger: function(): void {
696
+ modal.hideModal();
697
+ },
698
+ }, {
699
+ text: TYPO3.lang.label_doaction_publish,
700
+ btnClass: 'btn-info',
701
+ action: new DeferredAction(async (): Promise<void> => {
702
+ await this.sendRemoteRequest(
703
+ this.generateRemoteActionsPayload('publishSingleRecord', [
704
+ row.dataset.table,
705
+ row.dataset.t3ver_oid,
706
+ row.dataset.uid,
707
+ ]),
708
+ );
709
+ this.getWorkspaceInfos();
710
+ Backend.refreshPageTree();
711
+ }),
712
+ },
713
+ ]
714
+ });
715
+ }
716
+
717
+ private renderSelectionActionModal(selectedAction: string, affectedRecords: Array<object>): void {
718
+ const modal = Modal.advanced({
719
+ title: TYPO3.lang['window.selectionAction.title'],
720
+ content: html`<p>${TYPO3.lang['tooltip.' + selectedAction + 'Selected']}</p>`,
721
+ severity: SeverityEnum.warning,
722
+ staticBackdrop: true,
723
+ buttons: [
724
+ {
725
+ text: TYPO3.lang.cancel,
726
+ btnClass: 'btn-default',
727
+ trigger: function(): void {
728
+ modal.hideModal();
729
+ },
730
+ }, {
731
+ text: TYPO3.lang['label_doaction_' + selectedAction],
732
+ btnClass: 'btn-warning',
733
+ action: new DeferredAction(async (): Promise<void> => {
734
+ await this.sendRemoteRequest(
735
+ this.generateRemoteActionsPayload('executeSelectionAction', {
736
+ action: selectedAction,
737
+ selection: affectedRecords,
738
+ }),
739
+ );
740
+ this.markedRecordsForMassAction = [];
741
+ this.getWorkspaceInfos();
742
+ Backend.refreshPageTree();
743
+ }),
744
+ },
745
+ ]
746
+ });
747
+ modal.addEventListener('typo3-modal-hidden', (): void => {
748
+ const chooseSelectionAction = document.querySelector(Identifiers.chooseSelectionAction) as HTMLSelectElement;
749
+ if (chooseSelectionAction !== null) {
750
+ chooseSelectionAction.value = '';
751
+ }
752
+ });
753
+ }
754
+
755
+ /**
756
+ * Runs a mass action
757
+ */
758
+ private runMassAction(event: Event, target: HTMLSelectElement): void {
759
+ const selectedAction = target.value;
760
+ const integrityCheckRequired = selectedAction !== 'discard';
761
+
762
+ if (selectedAction.length === 0) {
763
+ // Don't do anything if that value is empty
764
+ return;
765
+ }
766
+
767
+ if (!integrityCheckRequired) {
768
+ this.renderMassActionModal(selectedAction);
769
+ } else {
770
+ this.checkIntegrity(
771
+ {
772
+ language: this.settings.language,
773
+ type: selectedAction,
774
+ },
775
+ ).then(async (response: AjaxResponse): Promise<void> => {
776
+ if ((await response.resolve())[0].result.result === 'warning') {
777
+ this.openIntegrityWarningModal().addEventListener('confirm.button.ok', (): void => {
778
+ this.renderMassActionModal(selectedAction);
779
+ });
780
+ } else {
781
+ this.renderMassActionModal(selectedAction);
782
+ }
783
+ });
784
+ }
785
+ }
786
+
787
+ private renderMassActionModal(selectedAction: string): void {
788
+ let massAction: string;
789
+ let continueButtonLabel: string;
790
+
791
+ switch (selectedAction) {
792
+ case 'publish':
793
+ massAction = 'publishWorkspace';
794
+ continueButtonLabel = TYPO3.lang.label_doaction_publish;
795
+ break;
796
+ case 'discard':
797
+ massAction = 'flushWorkspace';
798
+ continueButtonLabel = TYPO3.lang.label_doaction_discard;
799
+ break;
800
+ default:
801
+ throw 'Invalid mass action ' + selectedAction + ' called.';
802
+ }
803
+
804
+ const sendRequestsUntilAllProcessed = async (response: AjaxResponse): Promise<void> => {
805
+ const result = (await response.resolve())[0].result;
806
+ // Make sure to process all items
807
+ if (result.processed < result.total) {
808
+ this.sendRemoteRequest(
809
+ this.generateRemoteMassActionsPayload(massAction, result),
810
+ ).then(sendRequestsUntilAllProcessed);
811
+ } else {
812
+ this.getWorkspaceInfos();
813
+ Modal.dismiss();
814
+ }
815
+ };
816
+
817
+ const modal = Modal.advanced({
818
+ title: TYPO3.lang['window.massAction.title'],
819
+ content: html`
820
+ <p>${TYPO3.lang['tooltip.' + selectedAction + 'All']}</p>
821
+ <p>${TYPO3.lang['tooltip.affectWholeWorkspace']}</p>
822
+ `,
823
+ severity: SeverityEnum.warning,
824
+ staticBackdrop: true,
825
+ buttons: [
826
+ {
827
+ text: TYPO3.lang.cancel,
828
+ btnClass: 'btn-default',
829
+ trigger: function(): void {
830
+ modal.hideModal();
831
+ },
832
+ }, {
833
+ text: continueButtonLabel,
834
+ btnClass: 'btn-warning',
835
+ action: new DeferredAction(async (): Promise<void> => {
836
+ const response = await this.sendRemoteRequest(
837
+ this.generateRemoteMassActionsPayload(massAction, {
838
+ init: true,
839
+ total: 0,
840
+ processed: 0,
841
+ language: this.settings.language
842
+ }),
843
+ );
844
+ await sendRequestsUntilAllProcessed(response);
845
+ }),
846
+ },
847
+ ]
848
+ });
849
+ modal.addEventListener('typo3-modal-hidden', (): void => {
850
+ const chooseMassAction = document.querySelector(Identifiers.chooseMassAction) as HTMLSelectElement;
851
+ if (chooseMassAction !== null) {
852
+ chooseMassAction.value = '';
853
+ }
854
+ });
855
+ }
856
+
857
+ /**
858
+ * Sends marked records to a stage
859
+ */
860
+ private sendToSpecificStageAction(event: Event, target: HTMLInputElement): void {
861
+ const affectedRecords: Array<{ [key: string]: number | string }> = [];
862
+ const stage = target.value;
863
+ for (let i = 0; i < this.markedRecordsForMassAction.length; ++i) {
864
+ const affected = this.markedRecordsForMassAction[i].split(':');
865
+ affectedRecords.push({
866
+ table: affected[0],
867
+ uid: affected[1],
868
+ t3ver_oid: affected[2],
869
+ });
870
+ }
871
+ this.sendRemoteRequest(
872
+ this.generateRemoteActionsPayload('sendToSpecificStageWindow', [stage]),
873
+ ).then(async (response: AjaxResponse): Promise<void> => {
874
+ const modal = this.renderSendToStageWindow(await response.resolve());
875
+ modal.addEventListener('button.clicked', (modalEvent: Event): void => {
876
+ const target = modalEvent.target as HTMLButtonElement;
877
+ if (target.name === 'ok') {
878
+ const serializedForm = Utility.convertFormToObject(modal.querySelector('form'));
879
+ serializedForm.affects = {
880
+ elements: affectedRecords,
881
+ nextStage: stage,
882
+ };
883
+
884
+ this.sendRemoteRequest([
885
+ this.generateRemoteActionsPayload('sendToSpecificStageExecute', [serializedForm]),
886
+ this.generateRemotePayload('getWorkspaceInfos', this.settings),
887
+ ]).then(async (response: AjaxResponse): Promise<void> => {
888
+ const actionResponse = await response.resolve();
889
+ modal.hideModal();
890
+ this.renderWorkspaceInfos(actionResponse[1].result);
891
+ Backend.refreshPageTree();
892
+ });
893
+ }
894
+ });
895
+ modal.addEventListener('typo3-modal-hide', (): void => {
896
+ const chooseStageAction = document.querySelector(Identifiers.chooseStageAction) as HTMLSelectElement;
897
+ if (chooseStageAction !== null) {
898
+ chooseStageAction.value = '';
899
+ }
900
+ });
901
+ });
902
+ }
903
+
904
+ /**
905
+ * Fetches and renders available preview links
906
+ */
907
+ private generatePreviewLinks(): void {
908
+ this.sendRemoteRequest(
909
+ this.generateRemoteActionsPayload('generateWorkspacePreviewLinksForAllLanguages', [
910
+ this.settings.id,
911
+ ]),
912
+ ).then(async (response: AjaxResponse): Promise<void> => {
913
+ const result: Record<string, string> = (await response.resolve())[0].result;
914
+ const list = document.createElement('dl');
915
+
916
+ for (const [language, url] of Object.entries(result)) {
917
+ const title = document.createElement('dt');
918
+ title.textContent = language;
919
+
920
+ const link = document.createElement('a');
921
+ link.href = url;
922
+ link.target = '_blank';
923
+ link.textContent = url;
924
+
925
+ const listItem = document.createElement('dd');
926
+ listItem.appendChild(link);
927
+
928
+ list.append(title, listItem);
929
+ }
930
+
931
+ Modal.show(
932
+ TYPO3.lang.previewLink,
933
+ list,
934
+ SeverityEnum.info,
935
+ [{
936
+ text: TYPO3.lang.ok,
937
+ active: true,
938
+ btnClass: 'btn-info',
939
+ name: 'ok',
940
+ trigger: (e: Event, modal: ModalElement) => modal.hideModal(),
941
+ }],
942
+ ['modal-inner-scroll'],
943
+ );
944
+ });
945
+ }
946
+
947
+ /**
948
+ * This is used to reset the records, internally stored for
949
+ * mass actions. This is needed as those records may no
950
+ * longer be available in the current view and would therefore
951
+ * led to misbehaviour as "unrelated" records get processed.
952
+ *
953
+ * Furthermore, the mass action "bar" is initialized in case the
954
+ * current view contains records. Also a custom event is being
955
+ * dispatched to hide the mass actions, which are only available
956
+ * when at least one record is selected.
957
+ *
958
+ * @param hasRecords Whether the current view contains records
959
+ */
960
+ private resetMassActionState(hasRecords: boolean): void {
961
+ this.markedRecordsForMassAction = [];
962
+ if (hasRecords) {
963
+ const workspaceActions = document.querySelector(Identifiers.workspaceActions) as HTMLElement;
964
+ if (workspaceActions !== null) {
965
+ workspaceActions.classList.remove('hidden');
966
+ }
967
+
968
+ const chooseMassAction = document.querySelector(Identifiers.chooseMassAction) as HTMLSelectElement;
969
+ if (chooseMassAction !== null) {
970
+ chooseMassAction.disabled = false;
971
+ }
972
+ }
973
+ // Hide actions and also uncheck all checkboxes
974
+ document.dispatchEvent(new CustomEvent('multiRecordSelection:actions:hide'));
975
+ document.dispatchEvent(new CustomEvent('multiRecordSelection:checkboxes:uncheck'));
976
+ }
977
+ }
978
+
979
+ export default new Backend();