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.
- package/.github/workflows/sync-and-publish.yml +265 -0
- package/.last-synced-tag +1 -0
- package/LICENSE.txt +339 -0
- package/README-TYPO3.md +166 -0
- package/README.md +28 -0
- package/adminpanel/admin-panel.ts +375 -0
- package/adminpanel/modules/cache.ts +27 -0
- package/adminpanel/modules/preview.ts +75 -0
- package/backend/action-button/abstract-action.ts +22 -0
- package/backend/action-button/deferred-action.ts +41 -0
- package/backend/action-button/immediate-action.ts +31 -0
- package/backend/action-dispatcher.ts +104 -0
- package/backend/ajax-data-handler/message-interface.ts +18 -0
- package/backend/ajax-data-handler/response-interface.ts +20 -0
- package/backend/ajax-data-handler.ts +214 -0
- package/backend/backend-exception.ts +22 -0
- package/backend/broadcast-message.ts +50 -0
- package/backend/broadcast-service.ts +52 -0
- package/backend/browse-database.ts +38 -0
- package/backend/clear-cache.ts +88 -0
- package/backend/clipboard-panel.ts +240 -0
- package/backend/close-current-window.ts +2 -0
- package/backend/code-editor/autocomplete/completion-result.ts +105 -0
- package/backend/code-editor/autocomplete/ts-code-completion.ts +142 -0
- package/backend/code-editor/autocomplete/ts-parser.ts +485 -0
- package/backend/code-editor/autocomplete/ts-ref.ts +164 -0
- package/backend/code-editor/element/code-mirror-element.ts +282 -0
- package/backend/code-editor/language/typoscript.ts +181 -0
- package/backend/code-editor/stream-parser/typoscript.ts +1600 -0
- package/backend/color-picker.ts +158 -0
- package/backend/color-scheme-switch.ts +179 -0
- package/backend/column-selector-button.ts +302 -0
- package/backend/context-help.ts +68 -0
- package/backend/context-menu-actions.ts +322 -0
- package/backend/context-menu.ts +751 -0
- package/backend/copy-to-clipboard.ts +79 -0
- package/backend/date-time-picker.ts +270 -0
- package/backend/document-save-actions.ts +159 -0
- package/backend/drag-tooltip.ts +285 -0
- package/backend/drag-uploader.ts +914 -0
- package/backend/element/alert-element.ts +116 -0
- package/backend/element/dispatch-modal-button.ts +50 -0
- package/backend/element/draggable-resizable-element.ts +249 -0
- package/backend/element/editable-page-title.ts +306 -0
- package/backend/element/icon-element.ts +84 -0
- package/backend/element/immediate-action-element.ts +89 -0
- package/backend/element/pagination.ts +59 -0
- package/backend/element/progress-bar-element.ts +172 -0
- package/backend/element/progress-tracker-element.ts +119 -0
- package/backend/element/pseudo-button.ts +58 -0
- package/backend/element/spinner-element.ts +51 -0
- package/backend/element/thumbnail-element.ts +80 -0
- package/backend/element-browser.ts +182 -0
- package/backend/enum/data-transfer-types.ts +21 -0
- package/backend/enum/icon-types.ts +31 -0
- package/backend/enum/key-types.ts +27 -0
- package/backend/enum/severity.ts +20 -0
- package/backend/enum/viewport/scaffold-identifier.ts +24 -0
- package/backend/event/client-request.ts +25 -0
- package/backend/event/consumable.ts +18 -0
- package/backend/event/consumer-scope.ts +54 -0
- package/backend/event/event-dispatcher.ts +26 -0
- package/backend/event/interaction-request-assignment.ts +24 -0
- package/backend/event/interaction-request-map.ts +75 -0
- package/backend/event/interaction-request.ts +48 -0
- package/backend/event/trigger-request.ts +56 -0
- package/backend/form/submit-interceptor.ts +66 -0
- package/backend/form-engine/container/files-control-container.ts +716 -0
- package/backend/form-engine/container/flex-form-container-container.ts +166 -0
- package/backend/form-engine/container/flex-form-section-container.ts +210 -0
- package/backend/form-engine/container/inline-control-container.ts +1230 -0
- package/backend/form-engine/container/site-language-container.ts +616 -0
- package/backend/form-engine/element/abstract-sortable-select-items.ts +186 -0
- package/backend/form-engine/element/category-element.ts +160 -0
- package/backend/form-engine/element/color-element.ts +81 -0
- package/backend/form-engine/element/datetime-element.ts +66 -0
- package/backend/form-engine/element/extra/char-counter.ts +112 -0
- package/backend/form-engine/element/extra/select-box-filter.ts +85 -0
- package/backend/form-engine/element/folder-element.ts +66 -0
- package/backend/form-engine/element/group-element.ts +47 -0
- package/backend/form-engine/element/json-element.ts +52 -0
- package/backend/form-engine/element/link-element.ts +121 -0
- package/backend/form-engine/element/mfa-info-element.ts +169 -0
- package/backend/form-engine/element/modifier/resizable.ts +27 -0
- package/backend/form-engine/element/modifier/tabbable.ts +29 -0
- package/backend/form-engine/element/online-media-form-element.ts +65 -0
- package/backend/form-engine/element/password-element.ts +71 -0
- package/backend/form-engine/element/select-multiple-side-by-side-element.ts +83 -0
- package/backend/form-engine/element/select-single-element.ts +82 -0
- package/backend/form-engine/element/select-tree-element.ts +148 -0
- package/backend/form-engine/element/select-tree-toolbar.ts +118 -0
- package/backend/form-engine/element/select-tree.ts +210 -0
- package/backend/form-engine/element/slug-element.ts +243 -0
- package/backend/form-engine/element/suggest/result-container.ts +118 -0
- package/backend/form-engine/element/suggest/result-item.ts +90 -0
- package/backend/form-engine/element/table-permission-element.ts +101 -0
- package/backend/form-engine/element/table-wizard-element.ts +427 -0
- package/backend/form-engine/element/text-element.ts +52 -0
- package/backend/form-engine/element/text-table-element.ts +31 -0
- package/backend/form-engine/field-control/add-record.ts +40 -0
- package/backend/form-engine/field-control/edit-popup.ts +64 -0
- package/backend/form-engine/field-control/insert-clipboard.ts +50 -0
- package/backend/form-engine/field-control/link-popup.ts +53 -0
- package/backend/form-engine/field-control/list-module.ts +40 -0
- package/backend/form-engine/field-control/password-generator.ts +102 -0
- package/backend/form-engine/field-control/reset-selection.ts +48 -0
- package/backend/form-engine/field-wizard/localization-state-selector.ts +67 -0
- package/backend/form-engine/field-wizard/value-picker.ts +123 -0
- package/backend/form-engine/field-wizard/value-slider.ts +86 -0
- package/backend/form-engine/inline-relation/ajax-dispatcher.ts +124 -0
- package/backend/form-engine/inline-relation/inline-response-interface.ts +41 -0
- package/backend/form-engine-link-browser-adapter.ts +129 -0
- package/backend/form-engine-review.ts +155 -0
- package/backend/form-engine-suggest.ts +133 -0
- package/backend/form-engine-validation.ts +686 -0
- package/backend/form-engine.ts +1391 -0
- package/backend/global-event-handler.ts +252 -0
- package/backend/grid-editor.ts +1077 -0
- package/backend/hashing/md5.ts +252 -0
- package/backend/helper.ts +24 -0
- package/backend/hotkeys/hotkey-storage.ts +63 -0
- package/backend/hotkeys.ts +216 -0
- package/backend/icons.ts +236 -0
- package/backend/image-manipulation.ts +885 -0
- package/backend/info-window.ts +46 -0
- package/backend/input/clearable.ts +128 -0
- package/backend/input/index.d.ts +8 -0
- package/backend/java-script-module-import-event-handler.ts +19 -0
- package/backend/key-bindings.ts +42 -0
- package/backend/layout-module/drag-drop.ts +359 -0
- package/backend/layout-module/paste.ts +165 -0
- package/backend/link-browser.ts +89 -0
- package/backend/live-search/element/backend-search.ts +34 -0
- package/backend/live-search/element/hint.ts +33 -0
- package/backend/live-search/element/provider/default-result-item.ts +52 -0
- package/backend/live-search/element/provider/page-provider-result-item.ts +52 -0
- package/backend/live-search/element/result/item/action/action-container.ts +135 -0
- package/backend/live-search/element/result/item/action/action.ts +56 -0
- package/backend/live-search/element/result/item/item-container.ts +224 -0
- package/backend/live-search/element/result/item/item.ts +74 -0
- package/backend/live-search/element/result/result-container.ts +110 -0
- package/backend/live-search/element/result/result-detail-container.ts +50 -0
- package/backend/live-search/element/result/result-pagination.ts +122 -0
- package/backend/live-search/element/search-option-item.ts +70 -0
- package/backend/live-search/live-search-configurator.ts +54 -0
- package/backend/live-search/live-search-shortcut.ts +29 -0
- package/backend/live-search/result-types/backend-module-result-type.ts +8 -0
- package/backend/live-search/result-types/default-result-type.ts +21 -0
- package/backend/live-search/result-types/page-result-type.ts +23 -0
- package/backend/localization.ts +390 -0
- package/backend/login-refresh.ts +525 -0
- package/backend/login.ts +179 -0
- package/backend/mail-link-handler.ts +45 -0
- package/backend/modal-interface.ts +19 -0
- package/backend/modal.ts +685 -0
- package/backend/module/iframe.ts +125 -0
- package/backend/module/router.ts +275 -0
- package/backend/module-menu.ts +550 -0
- package/backend/module.ts +133 -0
- package/backend/multi-record-selection-action.ts +44 -0
- package/backend/multi-record-selection-delete-action.ts +108 -0
- package/backend/multi-record-selection-edit-action.ts +56 -0
- package/backend/multi-record-selection.ts +468 -0
- package/backend/multi-step-wizard.ts +568 -0
- package/backend/new-content-element-wizard-button.ts +51 -0
- package/backend/new-multiple-pages.ts +108 -0
- package/backend/new-record-wizard.ts +680 -0
- package/backend/notification.ts +366 -0
- package/backend/offset.ts +40 -0
- package/backend/online-media.ts +110 -0
- package/backend/page-actions.ts +91 -0
- package/backend/page-link-handler.ts +51 -0
- package/backend/pagetsconfig/pagetsconfig-includes.ts +77 -0
- package/backend/popover.ts +165 -0
- package/backend/record-download-button.ts +104 -0
- package/backend/record-link-handler.ts +31 -0
- package/backend/record-search.ts +44 -0
- package/backend/recordlist.ts +230 -0
- package/backend/resource/resource.ts +35 -0
- package/backend/security/element/csp-reports.ts +406 -0
- package/backend/security/element/sudo-mode.ts +249 -0
- package/backend/security/sudo-mode-interceptor.ts +19 -0
- package/backend/settings/editor/editable-setting.ts +231 -0
- package/backend/settings/editor.ts +451 -0
- package/backend/settings/enum/settings-mode.enum.ts +24 -0
- package/backend/settings/type/base.ts +200 -0
- package/backend/settings/type/bool.ts +56 -0
- package/backend/settings/type/color.ts +64 -0
- package/backend/settings/type/int.ts +85 -0
- package/backend/settings/type/number.ts +61 -0
- package/backend/settings/type/page.ts +100 -0
- package/backend/settings/type/string.ts +77 -0
- package/backend/settings/type/stringlist.ts +87 -0
- package/backend/severity.ts +88 -0
- package/backend/site-inline-actions.ts +32 -0
- package/backend/sortable-table.ts +208 -0
- package/backend/storage/abstract-client-storage.ts +76 -0
- package/backend/storage/browser-session.ts +28 -0
- package/backend/storage/client.ts +28 -0
- package/backend/storage/module-state-storage.ts +118 -0
- package/backend/storage/persistent.ts +204 -0
- package/backend/switch-user.ts +82 -0
- package/backend/tabs.ts +72 -0
- package/backend/telephone-link-handler.ts +40 -0
- package/backend/tests/backend/storage/browser-storage-test.ts +87 -0
- package/backend/tests/backend/storage/client-test.ts +87 -0
- package/backend/tests/backend/utility/dom-helper-test.ts +51 -0
- package/backend/tests/backend-exception-test.ts +28 -0
- package/backend/tests/element/immediate-action-element-test.ts +81 -0
- package/backend/tests/form-engine-validation-test.ts +304 -0
- package/backend/tests/grid-editor-test.ts +29 -0
- package/backend/tests/hashing/md5Test.ts +28 -0
- package/backend/tests/icons-test.ts +23 -0
- package/backend/tests/notification-test.ts +161 -0
- package/backend/tests/popover-test.ts +83 -0
- package/backend/toolbar/clear-cache-menu.ts +88 -0
- package/backend/toolbar/live-search.ts +284 -0
- package/backend/toolbar/shortcut-menu.ts +254 -0
- package/backend/toolbar/system-information-menu.ts +161 -0
- package/backend/toolbar.ts +41 -0
- package/backend/tree/file-storage-browser.ts +194 -0
- package/backend/tree/file-storage-tree-container.ts +465 -0
- package/backend/tree/file-storage-tree.ts +42 -0
- package/backend/tree/page-browser.ts +272 -0
- package/backend/tree/page-tree-element.ts +602 -0
- package/backend/tree/page-tree.ts +152 -0
- package/backend/tree/tree-module-state.ts +159 -0
- package/backend/tree/tree-node-toggle.ts +31 -0
- package/backend/tree/tree-node.ts +97 -0
- package/backend/tree/tree-toolbar.ts +117 -0
- package/backend/tree/tree.ts +1595 -0
- package/backend/url-link-handler.ts +36 -0
- package/backend/user-pass-login.ts +255 -0
- package/backend/user-settings-manager.ts +189 -0
- package/backend/utility/collapse-state-persister.ts +205 -0
- package/backend/utility/collapse-state-search.ts +183 -0
- package/backend/utility/dom-helper.ts +91 -0
- package/backend/utility/drag-drop-utility.ts +47 -0
- package/backend/utility/message-utility.ts +45 -0
- package/backend/utility/top-level-module-import.ts +32 -0
- package/backend/utility.ts +194 -0
- package/backend/viewport/abstract-container.ts +22 -0
- package/backend/viewport/content-container.ts +98 -0
- package/backend/viewport/loader.ts +28 -0
- package/backend/viewport/navigation-container.ts +119 -0
- package/backend/viewport/resizable-navigation.ts +176 -0
- package/backend/viewport/toolbar.ts +27 -0
- package/backend/viewport/topbar.ts +39 -0
- package/backend/viewport.ts +46 -0
- package/backend/window-manager.ts +84 -0
- package/backend/wizard/move-content-element.ts +107 -0
- package/backend/wizard/move-page.ts +74 -0
- package/backend/wizard.ts +331 -0
- package/belog/backend-log.ts +88 -0
- package/beuser/backend-user-listing.ts +40 -0
- package/beuser/permissions.ts +291 -0
- package/core/ajax/ajax-request-types.ts +15 -0
- package/core/ajax/ajax-request.ts +202 -0
- package/core/ajax/ajax-response.ts +57 -0
- package/core/ajax/input-transformer.ts +92 -0
- package/core/ajax/simple-response-interface.ts +5 -0
- package/core/authentication/mfa-provider/totp.ts +54 -0
- package/core/cache/dedupe-async-task.ts +95 -0
- package/core/directive/markdown.ts +82 -0
- package/core/directive/nl2br.ts +16 -0
- package/core/document-service.ts +35 -0
- package/core/event/debounce-event.ts +40 -0
- package/core/event/event-interface.ts +24 -0
- package/core/event/regular-event.ts +58 -0
- package/core/event/request-animation-frame-event.ts +42 -0
- package/core/event/throttle-event.ts +54 -0
- package/core/java-script-item-handler.ts +29 -0
- package/core/java-script-item-processor.ts +171 -0
- package/core/lit-helper.ts +126 -0
- package/core/literals.ts +17 -0
- package/core/referrer-refresh.ts +2 -0
- package/core/security-utility.ts +89 -0
- package/core/tests/ajax/ajax-request-test.ts +290 -0
- package/core/tests/ajax/input-transformer-test.ts +64 -0
- package/core/tests/cache/dedupe-async-task-test.ts +152 -0
- package/core/tests/event/throttle-event-test.ts +47 -0
- package/core/tests/literals-test.ts +24 -0
- package/core/tests/security-utility-test.ts +50 -0
- package/core/utility/types.ts +9 -0
- package/dashboard/chart-initializer.ts +136 -0
- package/dashboard/dashboard.ts +1487 -0
- package/extensionmanager/distribution-image.ts +55 -0
- package/extensionmanager/main.ts +291 -0
- package/extensionmanager/repository.ts +195 -0
- package/extensionmanager/update.ts +118 -0
- package/extensionmanager/upload-form.ts +42 -0
- package/filelist/browse-files.ts +148 -0
- package/filelist/browse-folders.ts +94 -0
- package/filelist/context-menu-actions.ts +335 -0
- package/filelist/create-folder.ts +69 -0
- package/filelist/file-delete.ts +69 -0
- package/filelist/file-list-actions.ts +132 -0
- package/filelist/file-list-dragdrop.ts +139 -0
- package/filelist/file-list-rename-handler.ts +128 -0
- package/filelist/file-list-transfer-handler.ts +138 -0
- package/filelist/file-list.ts +396 -0
- package/filelist/file-replace.ts +37 -0
- package/filelist/linkbrowser-file-handler.ts +94 -0
- package/filelist/linkbrowser-folder-handler.ts +74 -0
- package/filelist/rename-file.ts +109 -0
- package/form/backend/form-editor/component/property-grid-editor.ts +340 -0
- package/form/backend/form-editor/core.ts +2104 -0
- package/form/backend/form-editor/helper.ts +226 -0
- package/form/backend/form-editor/inspector-component.ts +2323 -0
- package/form/backend/form-editor/mediator.ts +941 -0
- package/form/backend/form-editor/modals-component.ts +433 -0
- package/form/backend/form-editor/stage-component.ts +908 -0
- package/form/backend/form-editor/tree-component.ts +548 -0
- package/form/backend/form-editor/view-model.ts +1343 -0
- package/form/backend/form-editor.ts +846 -0
- package/form/backend/form-manager/main.ts +39 -0
- package/form/backend/form-manager/view-model.ts +789 -0
- package/form/backend/form-manager.ts +180 -0
- package/form/backend/helper.ts +60 -0
- package/form/frontend/date-picker.ts +38 -0
- package/frontend/default_frontend.ts +82 -0
- package/impexp/context-menu-actions.ts +50 -0
- package/impexp/import-export.ts +62 -0
- package/indexed-search/pagination.ts +26 -0
- package/indexed-search/statistics.ts +50 -0
- package/info/translation-status.ts +48 -0
- package/install/ajax/ajax-queue.ts +83 -0
- package/install/init-install.ts +2 -0
- package/install/init-installer.ts +2 -0
- package/install/install.ts +25 -0
- package/install/installer.ts +458 -0
- package/install/message-interface.ts +18 -0
- package/install/module/abstract-inline-module.ts +27 -0
- package/install/module/abstract-interactable-module.ts +107 -0
- package/install/module/environment/environment-check.ts +92 -0
- package/install/module/environment/folder-structure.ts +145 -0
- package/install/module/environment/image-processing.ts +141 -0
- package/install/module/environment/mail-test.ts +127 -0
- package/install/module/environment/php-info.ts +50 -0
- package/install/module/environment/system-information.ts +50 -0
- package/install/module/maintenance/cache.ts +59 -0
- package/install/module/maintenance/clear-tables.ts +134 -0
- package/install/module/maintenance/clear-typo3temp-files.ts +137 -0
- package/install/module/maintenance/create-admin.ts +126 -0
- package/install/module/maintenance/database-analyzer.ts +266 -0
- package/install/module/maintenance/dump-autoload.ts +58 -0
- package/install/module/maintenance/language-packs.ts +338 -0
- package/install/module/maintenance/reset-backend-user-uc.ts +58 -0
- package/install/module/password-strength.ts +56 -0
- package/install/module/settings/change-install-tool-password.ts +101 -0
- package/install/module/settings/extension-configuration.ts +224 -0
- package/install/module/settings/features.ts +102 -0
- package/install/module/settings/local-configuration.ts +316 -0
- package/install/module/settings/presets.ts +147 -0
- package/install/module/settings/system-maintainer.ts +111 -0
- package/install/module/upgrade/core-update.ts +255 -0
- package/install/module/upgrade/extension-compat-tester.ts +195 -0
- package/install/module/upgrade/extension-scanner.ts +381 -0
- package/install/module/upgrade/tca-ext-tables-check.ts +93 -0
- package/install/module/upgrade/tca-migrations-check.ts +101 -0
- package/install/module/upgrade/upgrade-docs.ts +310 -0
- package/install/module/upgrade/upgrade-wizards.ts +612 -0
- package/install/renderable/clearable.ts +113 -0
- package/install/renderable/flash-message.ts +64 -0
- package/install/renderable/info-box.ts +66 -0
- package/install/renderable/language-packs.ts +304 -0
- package/install/renderable/offset-group.ts +38 -0
- package/install/renderable/severity.ts +49 -0
- package/install/renderable/wrap-group.ts +36 -0
- package/install/router.ts +468 -0
- package/linkvalidator/linkvalidator-modal.ts +45 -0
- package/linkvalidator/linkvalidator.ts +101 -0
- package/lowlevel/query-generator.ts +132 -0
- package/lowlevel/reference-index.ts +44 -0
- package/opendocs/toolbar/opendocs-menu.ts +117 -0
- package/package.json +34 -0
- package/recycler/recycler.ts +539 -0
- package/redirects/event-handler.ts +108 -0
- package/redirects/form-engine-evaluation.ts +34 -0
- package/redirects/redirects-module.ts +58 -0
- package/rte-ckeditor/ckeditor5.ts +402 -0
- package/rte-ckeditor/css-prefixer.ts +94 -0
- package/rte-ckeditor/legacy/ckeditor5-bundle.ts +17 -0
- package/rte-ckeditor/legacy/ckeditor5-inspector.ts +2 -0
- package/rte-ckeditor/plugin/typo3-link.ts +810 -0
- package/rte-ckeditor/plugin/whitespace.ts +114 -0
- package/rte-ckeditor/rte-link-browser.ts +112 -0
- package/rte-ckeditor/tests/css-prefixer-test.ts +314 -0
- package/scheduler/scheduler-add-group.ts +119 -0
- package/scheduler/scheduler-editable-group-name.ts +307 -0
- package/scheduler/scheduler-sortable-groups.ts +59 -0
- package/scheduler/scheduler.ts +466 -0
- package/setup/setup-module.ts +122 -0
- package/styleguide/checkbox-demo.ts +5 -0
- package/styleguide/element/theme-switcher-element.ts +101 -0
- package/styleguide/filter.ts +22 -0
- package/styleguide/find-icons.ts +78 -0
- package/styleguide/processing-indicator.ts +78 -0
- package/styleguide/render-notifications.ts +64 -0
- package/sys-note/element/delete-button.ts +78 -0
- package/tstemplate/constant-editor.ts +64 -0
- package/tstemplate/information-module.ts +31 -0
- package/tstemplate/template-analyzer.ts +77 -0
- package/viewpage/main.ts +253 -0
- package/workspaces/backend.ts +979 -0
- package/workspaces/preview.ts +232 -0
- package/workspaces/renderable/comment-view.ts +79 -0
- package/workspaces/renderable/diff-view.ts +58 -0
- package/workspaces/renderable/history-view.ts +97 -0
- package/workspaces/renderable/record-information.ts +118 -0
- package/workspaces/renderable/record-table.ts +355 -0
- package/workspaces/renderable/send-to-stage-form.ts +108 -0
- package/workspaces/toolbar/workspaces-menu.ts +190 -0
- package/workspaces/utility/icon-helper.ts +57 -0
- package/workspaces/workspaces.ts +136 -0
|
@@ -0,0 +1,914 @@
|
|
|
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 DocumentService from '@typo3/core/document-service';
|
|
15
|
+
import { DateTime } from 'luxon';
|
|
16
|
+
import type { AjaxResponse } from '@typo3/core/ajax/ajax-response';
|
|
17
|
+
import { SeverityEnum } from './enum/severity';
|
|
18
|
+
import { MessageUtility } from './utility/message-utility';
|
|
19
|
+
import NProgress from 'nprogress';
|
|
20
|
+
import AjaxRequest from '@typo3/core/ajax/ajax-request';
|
|
21
|
+
import { default as Modal, type ModalElement, Sizes as ModalSizes } from './modal';
|
|
22
|
+
import Notification from './notification';
|
|
23
|
+
import ImmediateAction from '@typo3/backend/action-button/immediate-action';
|
|
24
|
+
import Md5 from '@typo3/backend/hashing/md5';
|
|
25
|
+
import '@typo3/backend/element/icon-element';
|
|
26
|
+
import RegularEvent from '@typo3/core/event/regular-event';
|
|
27
|
+
import DomHelper from '@typo3/backend/utility/dom-helper';
|
|
28
|
+
import { KeyTypesEnum } from '@typo3/backend/enum/key-types';
|
|
29
|
+
import '@typo3/backend/element/progress-bar-element';
|
|
30
|
+
import type { ProgressBarElement } from '@typo3/backend/element/progress-bar-element';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Possible actions for conflicts w/ existing files
|
|
34
|
+
*/
|
|
35
|
+
enum Action {
|
|
36
|
+
OVERRIDE = 'replace',
|
|
37
|
+
RENAME = 'rename',
|
|
38
|
+
SKIP = 'cancel',
|
|
39
|
+
USE_EXISTING = 'useExisting',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Properties of a file as returned from the AJAX action; essential, this is a serialized instance of
|
|
44
|
+
* \TYPO3\CMS\Core\Resource\File plus some extra properties (see FileController::flattenResultDataValue())
|
|
45
|
+
*/
|
|
46
|
+
interface UploadedFile {
|
|
47
|
+
name: string;
|
|
48
|
+
id: number;
|
|
49
|
+
uid: number;
|
|
50
|
+
icon: string;
|
|
51
|
+
extension: string;
|
|
52
|
+
permissions: { read: boolean; write: boolean };
|
|
53
|
+
size: number;
|
|
54
|
+
// formatted as ddmmyy
|
|
55
|
+
date: string;
|
|
56
|
+
|
|
57
|
+
mtime: number;
|
|
58
|
+
thumbUrl: string;
|
|
59
|
+
type: string;
|
|
60
|
+
path: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface FileConflict {
|
|
64
|
+
original: UploadedFile;
|
|
65
|
+
uploaded: File;
|
|
66
|
+
action: Action;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface FlashMessage {
|
|
70
|
+
title: string,
|
|
71
|
+
message: string,
|
|
72
|
+
severity: number
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default class DragUploader {
|
|
76
|
+
public irreObjectUid: string;
|
|
77
|
+
public fileList: HTMLElement;
|
|
78
|
+
public fileListColumnCount: number;
|
|
79
|
+
public filesExtensionsAllowed: string;
|
|
80
|
+
public filesExtensionsDisallowed: string;
|
|
81
|
+
public fileDenyPattern: RegExp | null;
|
|
82
|
+
public maxFileSize: number;
|
|
83
|
+
public trigger: HTMLElement;
|
|
84
|
+
public target: string;
|
|
85
|
+
public reloadUrl: string;
|
|
86
|
+
public manualTable: boolean;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Array of files which are asked for being overridden
|
|
90
|
+
*/
|
|
91
|
+
private askForOverride: Array<FileConflict> = [];
|
|
92
|
+
|
|
93
|
+
private percentagePerFile: number = 1;
|
|
94
|
+
|
|
95
|
+
private readonly body: HTMLElement;
|
|
96
|
+
private readonly element: HTMLElement;
|
|
97
|
+
private readonly dropzone: HTMLElement;
|
|
98
|
+
private readonly dropzoneMask: HTMLElement;
|
|
99
|
+
private readonly fileInput: HTMLInputElement;
|
|
100
|
+
private readonly browserCapabilities: { fileReader: boolean; DnD: boolean; Progress: boolean };
|
|
101
|
+
private readonly dropZoneInsertBefore: boolean;
|
|
102
|
+
private queueLength: number;
|
|
103
|
+
private readonly defaultAction: Action;
|
|
104
|
+
private manuallyTriggered: boolean;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* This property controls whether a drag was started within the document. If true, dropzones become unavailable.
|
|
108
|
+
*/
|
|
109
|
+
private dragStartedInDocument: boolean = false;
|
|
110
|
+
|
|
111
|
+
constructor(element: HTMLElement) {
|
|
112
|
+
this.body = document.querySelector('body');
|
|
113
|
+
this.element = element;
|
|
114
|
+
const hasTrigger = this.element.dataset.dropzoneTrigger !== undefined;
|
|
115
|
+
this.trigger = document.querySelector(this.element.dataset.dropzoneTrigger);
|
|
116
|
+
this.defaultAction = this.element.dataset.defaultAction as Action || Action.SKIP;
|
|
117
|
+
this.dropzone = document.createElement('div');
|
|
118
|
+
this.dropzone.classList.add('dropzone');
|
|
119
|
+
this.dropzone.setAttribute('hidden', 'hidden');
|
|
120
|
+
this.irreObjectUid = this.element.dataset.fileIrreObject;
|
|
121
|
+
|
|
122
|
+
const dropZoneEscapedTarget = document.querySelector(this.element.dataset.dropzoneTarget);
|
|
123
|
+
if (this.irreObjectUid && DomHelper.nextAll(dropZoneEscapedTarget).length !== 0) {
|
|
124
|
+
this.dropZoneInsertBefore = true;
|
|
125
|
+
dropZoneEscapedTarget.before(this.dropzone);
|
|
126
|
+
} else {
|
|
127
|
+
this.dropZoneInsertBefore = false;
|
|
128
|
+
dropZoneEscapedTarget.after(this.dropzone);
|
|
129
|
+
}
|
|
130
|
+
this.fileInput = <HTMLInputElement>document.createElement('input');
|
|
131
|
+
this.fileInput.setAttribute('type', 'file');
|
|
132
|
+
this.fileInput.setAttribute('multiple', 'multiple');
|
|
133
|
+
this.fileInput.setAttribute('name', 'files[]');
|
|
134
|
+
this.fileInput.classList.add('upload-file-picker');
|
|
135
|
+
this.body.append(this.fileInput);
|
|
136
|
+
|
|
137
|
+
this.fileList = document.querySelector(this.element.dataset.progressContainer);
|
|
138
|
+
this.fileListColumnCount = this.fileList?.querySelectorAll('thead tr:first-child th').length + 1;
|
|
139
|
+
this.filesExtensionsAllowed = this.element.dataset.fileAllowed;
|
|
140
|
+
this.filesExtensionsDisallowed = this.element.dataset.fileDisallowed;
|
|
141
|
+
this.fileDenyPattern = this.element.dataset.fileDenyPattern ? new RegExp(this.element.dataset.fileDenyPattern, 'i') : null;
|
|
142
|
+
this.maxFileSize = parseInt(this.element.dataset.maxFileSize, 10);
|
|
143
|
+
this.target = this.element.dataset.targetFolder;
|
|
144
|
+
this.reloadUrl = this.element.dataset.reloadUrl;
|
|
145
|
+
|
|
146
|
+
this.browserCapabilities = {
|
|
147
|
+
fileReader: typeof FileReader !== 'undefined',
|
|
148
|
+
DnD: 'draggable' in document.createElement('span'),
|
|
149
|
+
Progress: 'upload' in new XMLHttpRequest,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
if (!this.browserCapabilities.DnD) {
|
|
154
|
+
console.warn('Browser has no Drag and drop capabilities; cannot initialize DragUploader');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
this.body.addEventListener('dragstart', (): void => {
|
|
159
|
+
this.dragStartedInDocument = true;
|
|
160
|
+
});
|
|
161
|
+
this.body.addEventListener('dragover', this.dragFileIntoDocument);
|
|
162
|
+
this.body.addEventListener('dragend', this.dragAborted);
|
|
163
|
+
this.body.addEventListener('drop', this.ignoreDrop);
|
|
164
|
+
|
|
165
|
+
this.dropzone.innerHTML = ('<button type="button" class="dropzone-hint" aria-labelledby="dropzone-title">' +
|
|
166
|
+
'<div class="dropzone-hint-media">' +
|
|
167
|
+
'<div class="dropzone-hint-icon"></div>' +
|
|
168
|
+
'</div>' +
|
|
169
|
+
'<div class="dropzone-hint-body">' +
|
|
170
|
+
'<h3 id="dropzone-title" class="dropzone-hint-title">' +
|
|
171
|
+
TYPO3.lang['file_upload.dropzonehint.title'] +
|
|
172
|
+
'</h3>' +
|
|
173
|
+
'<p class="dropzone-hint-message">' +
|
|
174
|
+
TYPO3.lang['file_upload.dropzonehint.message'] +
|
|
175
|
+
'</p>' +
|
|
176
|
+
'</div>' +
|
|
177
|
+
'</div>'
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
this.dropzoneMask = document.createElement('div');
|
|
181
|
+
this.dropzoneMask.classList.add('dropzone-mask');
|
|
182
|
+
this.dropzone.append(this.dropzoneMask);
|
|
183
|
+
|
|
184
|
+
this.dropzone.addEventListener('dragenter', this.fileInDropzone);
|
|
185
|
+
this.dropzoneMask.addEventListener('dragenter', this.fileInDropzone);
|
|
186
|
+
this.dropzoneMask.addEventListener('dragleave', this.fileOutOfDropzone);
|
|
187
|
+
this.dropzoneMask.addEventListener('drop', (ev: Event) => this.handleDrop(<DragEvent>ev));
|
|
188
|
+
|
|
189
|
+
this.dropzone.addEventListener('click', () => {
|
|
190
|
+
this.fileInput.click();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const dropzoneCloseButton = document.createElement('button');
|
|
194
|
+
dropzoneCloseButton.classList.add('dropzone-close');
|
|
195
|
+
dropzoneCloseButton.type = 'button';
|
|
196
|
+
dropzoneCloseButton.setAttribute('aria-label', TYPO3.lang['file_upload.dropzone.close']);
|
|
197
|
+
dropzoneCloseButton.addEventListener('click', this.hideDropzone);
|
|
198
|
+
this.dropzone.append(dropzoneCloseButton);
|
|
199
|
+
|
|
200
|
+
// no filelist then create own progress table
|
|
201
|
+
if (this.fileList === null) {
|
|
202
|
+
this.fileList = document.createElement('table');
|
|
203
|
+
this.fileList.setAttribute('id', 'typo3-filelist');
|
|
204
|
+
this.fileList.classList.add('table', 'table-striped', 'table-hover', 'upload-queue');
|
|
205
|
+
this.fileList.innerHTML = '<tbody></tbody>';
|
|
206
|
+
const $tableContainer = document.createElement('div');
|
|
207
|
+
$tableContainer.classList.add('table-fit');
|
|
208
|
+
$tableContainer.setAttribute('hidden', 'hidden');
|
|
209
|
+
$tableContainer.append(this.fileList);
|
|
210
|
+
|
|
211
|
+
if (this.dropZoneInsertBefore) {
|
|
212
|
+
this.dropzone.after($tableContainer);
|
|
213
|
+
} else {
|
|
214
|
+
this.dropzone.before($tableContainer);
|
|
215
|
+
}
|
|
216
|
+
this.fileListColumnCount = 8;
|
|
217
|
+
this.manualTable = true;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
this.fileInput.addEventListener('change', (event: Event) => {
|
|
221
|
+
this.hideDropzone(event);
|
|
222
|
+
this.processFiles(this.fileInput.files);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Allow the user to hide the dropzone with the "Escape" key
|
|
226
|
+
document.addEventListener('keydown', (event: KeyboardEvent): void => {
|
|
227
|
+
if (event.key === KeyTypesEnum.ENTER && !this.dropzone.hasAttribute('hidden')) {
|
|
228
|
+
this.hideDropzone(event);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
this.bindUploadButton(hasTrigger === true ? this.trigger : this.element);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
public static init(): void {
|
|
236
|
+
const observer = new MutationObserver((mutations: MutationRecord[]): void => {
|
|
237
|
+
for (const mutation of mutations) {
|
|
238
|
+
if (mutation.type === 'childList') {
|
|
239
|
+
for (const node of [...mutation.addedNodes.values()]) {
|
|
240
|
+
if (!(node instanceof HTMLElement)) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (node.matches('.t3js-drag-uploader')) {
|
|
244
|
+
new DragUploader(node);
|
|
245
|
+
} else {
|
|
246
|
+
node.querySelectorAll('.t3js-drag-uploader').forEach((element: HTMLElement): void => {
|
|
247
|
+
new DragUploader(element);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
observer.observe(document, { childList: true, subtree: true });
|
|
255
|
+
|
|
256
|
+
DocumentService.ready().then((): void => {
|
|
257
|
+
document.querySelectorAll('.t3js-drag-uploader').forEach((element: HTMLElement): void => {
|
|
258
|
+
new DragUploader(element);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
public static fileSizeAsString(size: number): string {
|
|
264
|
+
const sizeKB: number = size / 1024;
|
|
265
|
+
let str = '';
|
|
266
|
+
|
|
267
|
+
if (sizeKB > 1024) {
|
|
268
|
+
str = (sizeKB / 1024).toFixed(1) + ' MB';
|
|
269
|
+
} else {
|
|
270
|
+
str = sizeKB.toFixed(1) + ' KB';
|
|
271
|
+
}
|
|
272
|
+
return str;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @param {string} irreObject
|
|
277
|
+
* @param {UploadedFile} file
|
|
278
|
+
*/
|
|
279
|
+
public static addFileToIrre(irreObject: string, file: UploadedFile): void {
|
|
280
|
+
const message = {
|
|
281
|
+
actionName: 'typo3:foreignRelation:insert',
|
|
282
|
+
objectGroup: irreObject,
|
|
283
|
+
table: 'sys_file',
|
|
284
|
+
uid: file.uid,
|
|
285
|
+
};
|
|
286
|
+
MessageUtility.send(message);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
public showDropzone(): void {
|
|
290
|
+
this.dropzone.removeAttribute('hidden');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
*
|
|
295
|
+
* @param {Event} event
|
|
296
|
+
*/
|
|
297
|
+
public hideDropzone = (event: Event): void => {
|
|
298
|
+
event.stopPropagation();
|
|
299
|
+
event.preventDefault();
|
|
300
|
+
this.dropzone.setAttribute('hidden', 'hidden');
|
|
301
|
+
this.dropzone.classList.remove('drop-status-ok');
|
|
302
|
+
// User manually hides the dropzone, so we can reset the flag
|
|
303
|
+
this.manuallyTriggered = false;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* @param {Event} event
|
|
308
|
+
* @returns {boolean}
|
|
309
|
+
*/
|
|
310
|
+
public dragFileIntoDocument = (event: DragEvent): boolean => {
|
|
311
|
+
if (this.dragStartedInDocument) {
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (!event.dataTransfer.types.includes('Files')) {
|
|
316
|
+
// Not a file upload (item drag)
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
event.stopPropagation();
|
|
321
|
+
event.preventDefault();
|
|
322
|
+
(event.currentTarget as HTMLElement).classList.add('drop-in-progress');
|
|
323
|
+
// Only show dropzone in case $element is currently visible. This prevents
|
|
324
|
+
// use cases, such as opening the dropzone in a non visible tab in FormEngine.
|
|
325
|
+
if (this.element.offsetParent) {
|
|
326
|
+
this.showDropzone();
|
|
327
|
+
}
|
|
328
|
+
return false;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
*
|
|
333
|
+
* @param {Event} event
|
|
334
|
+
* @returns {Boolean}
|
|
335
|
+
*/
|
|
336
|
+
public dragAborted = (event: Event): boolean => {
|
|
337
|
+
event.stopPropagation();
|
|
338
|
+
event.preventDefault();
|
|
339
|
+
(event.currentTarget as HTMLElement).classList.remove('drop-in-progress');
|
|
340
|
+
this.dragStartedInDocument = false;
|
|
341
|
+
return false;
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
public ignoreDrop = (event: Event): boolean => {
|
|
345
|
+
// stops the browser from redirecting.
|
|
346
|
+
event.stopPropagation();
|
|
347
|
+
event.preventDefault();
|
|
348
|
+
this.dragAborted(event);
|
|
349
|
+
return false;
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
public handleDrop = (event: DragEvent): void => {
|
|
353
|
+
this.ignoreDrop(event);
|
|
354
|
+
this.hideDropzone(event);
|
|
355
|
+
this.processFiles(event.dataTransfer.files);
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* @param {FileList} files
|
|
360
|
+
*/
|
|
361
|
+
public processFiles(files: FileList): void {
|
|
362
|
+
this.queueLength = files.length;
|
|
363
|
+
|
|
364
|
+
if (this.fileList.parentElement.hasAttribute('hidden')) {
|
|
365
|
+
// Show the filelist (table)
|
|
366
|
+
this.fileList.parentElement.removeAttribute('hidden');
|
|
367
|
+
// Remove hidden state from table container (also makes column selection etc. visible)
|
|
368
|
+
this.fileList.closest('.t3-filelist-container')?.classList.remove('hidden');
|
|
369
|
+
// Hide the information container
|
|
370
|
+
this.fileList.closest('.filelist-main')?.querySelector('.t3-filelist-info-container')?.setAttribute('hidden', 'hidden');
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
NProgress.start();
|
|
374
|
+
this.percentagePerFile = 1 / files.length;
|
|
375
|
+
|
|
376
|
+
// Check for each file if is already exist before adding it to the queue
|
|
377
|
+
const ajaxCalls: Promise<void>[] = [];
|
|
378
|
+
Array.from(files).forEach((file: File) => {
|
|
379
|
+
const request = new AjaxRequest(TYPO3.settings.ajaxUrls.file_exists).withQueryArguments({
|
|
380
|
+
fileName: file.name,
|
|
381
|
+
fileTarget: this.target,
|
|
382
|
+
}).get({ cache: 'no-cache' }).then(async (response: AjaxResponse): Promise<void> => {
|
|
383
|
+
const data = await response.resolve();
|
|
384
|
+
const fileExists = typeof data.uid !== 'undefined';
|
|
385
|
+
if (fileExists) {
|
|
386
|
+
this.askForOverride.push({
|
|
387
|
+
original: data,
|
|
388
|
+
uploaded: file,
|
|
389
|
+
action: this.irreObjectUid ? Action.USE_EXISTING : this.defaultAction,
|
|
390
|
+
});
|
|
391
|
+
NProgress.inc(this.percentagePerFile);
|
|
392
|
+
} else {
|
|
393
|
+
new FileQueueItem(this, file, Action.SKIP);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
ajaxCalls.push(request);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
Promise.all(ajaxCalls).then((): void => {
|
|
400
|
+
this.drawOverrideModal();
|
|
401
|
+
NProgress.done();
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
this.fileInput.value = '';
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
public fileInDropzone = (): void => {
|
|
408
|
+
this.dropzone.classList.add('drop-status-ok');
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
public fileOutOfDropzone = (): void => {
|
|
412
|
+
this.dropzone.classList.remove('drop-status-ok');
|
|
413
|
+
// In case dropzone was not manually triggered and this is no manual (fake) table, hide it when leaving
|
|
414
|
+
if (!this.manuallyTriggered) {
|
|
415
|
+
this.dropzone.setAttribute('hidden', 'hidden');
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Bind file picker to default upload button
|
|
421
|
+
*
|
|
422
|
+
* @param {Object} button
|
|
423
|
+
*/
|
|
424
|
+
public bindUploadButton(button: HTMLElement): void {
|
|
425
|
+
button.addEventListener('click', (event: Event) => {
|
|
426
|
+
event.preventDefault();
|
|
427
|
+
this.fileInput.click();
|
|
428
|
+
// In case user manually triggers the dropzone, we add a flag
|
|
429
|
+
this.manuallyTriggered = true;
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Decrements the queue and renders a flash message if queue is empty
|
|
435
|
+
*/
|
|
436
|
+
public decrementQueueLength(messages?: FlashMessage[]): void {
|
|
437
|
+
if (this.queueLength > 0) {
|
|
438
|
+
this.queueLength--;
|
|
439
|
+
if (this.queueLength === 0) {
|
|
440
|
+
const timeout: number = messages && messages.length ? 5000 : 0;
|
|
441
|
+
if (timeout) {
|
|
442
|
+
for (const flashMessage of messages) {
|
|
443
|
+
Notification.showMessage(flashMessage.title, flashMessage.message, flashMessage.severity);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (this.reloadUrl) {
|
|
448
|
+
// After 5 seconds (when flash messages have disappeared), provide the user the option to reload the module
|
|
449
|
+
setTimeout(() => {
|
|
450
|
+
Notification.info(
|
|
451
|
+
TYPO3.lang['file_upload.reload.filelist'],
|
|
452
|
+
TYPO3.lang['file_upload.reload.filelist.message'],
|
|
453
|
+
10,
|
|
454
|
+
[
|
|
455
|
+
{
|
|
456
|
+
label: TYPO3.lang['file_upload.reload.filelist.actions.dismiss'],
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
label: TYPO3.lang['file_upload.reload.filelist.actions.reload'],
|
|
460
|
+
action: new ImmediateAction((): void => {
|
|
461
|
+
top.list_frame.document.location.href = this.reloadUrl;
|
|
462
|
+
})
|
|
463
|
+
}
|
|
464
|
+
]
|
|
465
|
+
);
|
|
466
|
+
}, timeout);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Renders the modal for existing files
|
|
474
|
+
*/
|
|
475
|
+
public drawOverrideModal(): void {
|
|
476
|
+
const amountOfItems = Object.keys(this.askForOverride).length;
|
|
477
|
+
if (amountOfItems === 0) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const $modalContent = document.createElement('div');
|
|
481
|
+
let htmlContent = `
|
|
482
|
+
<p>${TYPO3.lang['file_upload.existingfiles.description']}</p>
|
|
483
|
+
<table class="table">
|
|
484
|
+
<thead>
|
|
485
|
+
<tr>
|
|
486
|
+
<th></th>
|
|
487
|
+
<th>${TYPO3.lang['file_upload.header.originalFile']}</th>
|
|
488
|
+
<th>${TYPO3.lang['file_upload.header.uploadedFile']}</th>
|
|
489
|
+
<th>${TYPO3.lang['file_upload.header.action']}</th>
|
|
490
|
+
</tr>
|
|
491
|
+
</thead>
|
|
492
|
+
<tbody>
|
|
493
|
+
`;
|
|
494
|
+
for (let i = 0; i < amountOfItems; ++i) {
|
|
495
|
+
const $record = `
|
|
496
|
+
<tr>
|
|
497
|
+
<td>
|
|
498
|
+
${this.askForOverride[i].original.thumbUrl !== ''
|
|
499
|
+
? `<img src="${this.askForOverride[i].original.thumbUrl}" height="40" />`
|
|
500
|
+
: this.askForOverride[i].original.icon}
|
|
501
|
+
</td>
|
|
502
|
+
<td>
|
|
503
|
+
${this.askForOverride[i].original.name} (${DragUploader.fileSizeAsString(this.askForOverride[i].original.size)})<br />
|
|
504
|
+
${DateTime.fromSeconds(this.askForOverride[i].original.mtime).toLocaleString(DateTime.DATETIME_MED)}
|
|
505
|
+
</td>
|
|
506
|
+
<td>
|
|
507
|
+
${this.askForOverride[i].uploaded.name} (${DragUploader.fileSizeAsString(this.askForOverride[i].uploaded.size)})<br />
|
|
508
|
+
${DateTime.fromMillis(this.askForOverride[i].uploaded.lastModified).toLocaleString(DateTime.DATETIME_MED)}
|
|
509
|
+
</td>
|
|
510
|
+
<td>
|
|
511
|
+
<select class="form-select t3js-actions" data-override="${i}">
|
|
512
|
+
${this.irreObjectUid ? `<option value="${Action.USE_EXISTING}">${TYPO3.lang['file_upload.actions.use_existing']}</option>` : ''}
|
|
513
|
+
<option value="${Action.SKIP}" ${this.defaultAction === Action.SKIP ? 'selected' : ''}>${TYPO3.lang['file_upload.actions.skip']}</option>
|
|
514
|
+
<option value="${Action.RENAME}" ${this.defaultAction === Action.RENAME ? 'selected' : ''}>${TYPO3.lang['file_upload.actions.rename']}</option>
|
|
515
|
+
<option value="${Action.OVERRIDE}" ${this.defaultAction === Action.OVERRIDE ? 'selected' : ''}>${TYPO3.lang['file_upload.actions.override']}</option>
|
|
516
|
+
</select>
|
|
517
|
+
</td>
|
|
518
|
+
</tr>
|
|
519
|
+
`;
|
|
520
|
+
htmlContent += $record;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
htmlContent += '</tbody></table>';
|
|
524
|
+
$modalContent.innerHTML = htmlContent;
|
|
525
|
+
|
|
526
|
+
const modal = Modal.advanced({
|
|
527
|
+
title: TYPO3.lang['file_upload.existingfiles.title'],
|
|
528
|
+
content: $modalContent,
|
|
529
|
+
severity: SeverityEnum.warning,
|
|
530
|
+
buttons: [
|
|
531
|
+
{
|
|
532
|
+
text: TYPO3.lang['file_upload.button.cancel'] || 'Cancel',
|
|
533
|
+
active: true,
|
|
534
|
+
btnClass: 'btn-default',
|
|
535
|
+
name: 'cancel',
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
text: TYPO3.lang['file_upload.button.continue'] || 'Continue with selected actions',
|
|
539
|
+
btnClass: 'btn-warning',
|
|
540
|
+
name: 'continue',
|
|
541
|
+
},
|
|
542
|
+
],
|
|
543
|
+
additionalCssClasses: ['modal-inner-scroll'],
|
|
544
|
+
size: ModalSizes.large,
|
|
545
|
+
callback: (modal: ModalElement): void => {
|
|
546
|
+
const modalFooter = modal.querySelector('.modal-footer');
|
|
547
|
+
|
|
548
|
+
const allActionLabel = document.createElement('label');
|
|
549
|
+
allActionLabel.textContent = TYPO3.lang['file_upload.actions.all.label'];
|
|
550
|
+
|
|
551
|
+
const allActionSelect = document.createElement('span');
|
|
552
|
+
allActionSelect.innerHTML = `
|
|
553
|
+
<select class="form-select t3js-actions-all">
|
|
554
|
+
<option value="" selected>${TYPO3.lang['file_upload.actions.all.empty']}</option>
|
|
555
|
+
${this.irreObjectUid ? `<option value="${Action.USE_EXISTING}">${TYPO3.lang['file_upload.actions.all.use_existing']}</option>` : ''}
|
|
556
|
+
<option value="${Action.SKIP}">${TYPO3.lang['file_upload.actions.all.skip']}</option>
|
|
557
|
+
<option value="${Action.RENAME}">${TYPO3.lang['file_upload.actions.all.rename']}</option>
|
|
558
|
+
<option value="${Action.OVERRIDE}">${TYPO3.lang['file_upload.actions.all.override']}</option>
|
|
559
|
+
</select>
|
|
560
|
+
`;
|
|
561
|
+
|
|
562
|
+
modalFooter.prepend(allActionLabel, allActionSelect);
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
new RegularEvent('change', (event: Event, target: HTMLSelectElement) => {
|
|
567
|
+
if (target.value !== '') {
|
|
568
|
+
// mass action was selected, apply action to every file
|
|
569
|
+
for (const select of modal.querySelectorAll('.t3js-actions') as NodeListOf<HTMLSelectElement>) {
|
|
570
|
+
const index = parseInt(select.dataset.override, 10);
|
|
571
|
+
select.value = target.value;
|
|
572
|
+
select.disabled = true;
|
|
573
|
+
this.askForOverride[index].action = <Action>select.value;
|
|
574
|
+
}
|
|
575
|
+
} else {
|
|
576
|
+
modal.querySelectorAll('.t3js-actions').forEach((select: HTMLSelectElement) => select.disabled = false);
|
|
577
|
+
}
|
|
578
|
+
}).delegateTo(modal, '.t3js-actions-all');
|
|
579
|
+
|
|
580
|
+
new RegularEvent('change', (event: Event) => {
|
|
581
|
+
const actionSelect = event.target as HTMLSelectElement,
|
|
582
|
+
index = parseInt(actionSelect.dataset.override, 10);
|
|
583
|
+
this.askForOverride[index].action = <Action>actionSelect.value;
|
|
584
|
+
}).delegateTo(modal, '.t3js-actions');
|
|
585
|
+
|
|
586
|
+
modal.addEventListener('button.clicked', (e: Event): void => {
|
|
587
|
+
const button = e.target as HTMLButtonElement;
|
|
588
|
+
if (button.name === 'cancel') {
|
|
589
|
+
this.askForOverride = [];
|
|
590
|
+
Modal.dismiss();
|
|
591
|
+
} else if (button.name === 'continue') {
|
|
592
|
+
for (const fileInfo of this.askForOverride) {
|
|
593
|
+
if (fileInfo.action === Action.USE_EXISTING) {
|
|
594
|
+
DragUploader.addFileToIrre(
|
|
595
|
+
this.irreObjectUid,
|
|
596
|
+
fileInfo.original,
|
|
597
|
+
);
|
|
598
|
+
} else if (fileInfo.action !== Action.SKIP) {
|
|
599
|
+
new FileQueueItem(this, fileInfo.uploaded, fileInfo.action);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
this.askForOverride = [];
|
|
603
|
+
modal.hideModal();
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
modal.addEventListener('typo3-modal-hidden', () => {
|
|
608
|
+
this.askForOverride = [];
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
class FileQueueItem {
|
|
614
|
+
private readonly row: HTMLElement;
|
|
615
|
+
private readonly progress: HTMLElement;
|
|
616
|
+
private readonly file: File;
|
|
617
|
+
private readonly override: Action;
|
|
618
|
+
private readonly selector: HTMLElement;
|
|
619
|
+
private readonly iconCol: HTMLElement;
|
|
620
|
+
private readonly fileName: HTMLElement;
|
|
621
|
+
private readonly progressBar: ProgressBarElement;
|
|
622
|
+
private readonly dragUploader: DragUploader;
|
|
623
|
+
|
|
624
|
+
constructor(dragUploader: DragUploader, file: File, override: Action) {
|
|
625
|
+
this.dragUploader = dragUploader;
|
|
626
|
+
this.file = file;
|
|
627
|
+
this.override = override;
|
|
628
|
+
|
|
629
|
+
this.row = document.createElement('tr');
|
|
630
|
+
this.row.classList.add('upload-queue-item');
|
|
631
|
+
|
|
632
|
+
if (!this.dragUploader.manualTable) {
|
|
633
|
+
// Add selector cell, if this is a real table (e.g. not in FormEngine)
|
|
634
|
+
this.selector = document.createElement('td');
|
|
635
|
+
this.selector.classList.add('col-checkbox');
|
|
636
|
+
this.row.append(this.selector);
|
|
637
|
+
}
|
|
638
|
+
this.iconCol = document.createElement('td');
|
|
639
|
+
this.iconCol.classList.add('col-icon');
|
|
640
|
+
this.row.append(this.iconCol);
|
|
641
|
+
|
|
642
|
+
this.fileName = document.createElement('td');
|
|
643
|
+
this.fileName.classList.add('col-title', 'col-responsive');
|
|
644
|
+
this.fileName.textContent = file.name;
|
|
645
|
+
this.row.append(this.fileName);
|
|
646
|
+
|
|
647
|
+
this.progress = document.createElement('td');
|
|
648
|
+
this.progress.classList.add('col-progress');
|
|
649
|
+
this.progress.setAttribute('colspan', String(this.dragUploader.fileListColumnCount - this.row.querySelectorAll('td').length));
|
|
650
|
+
this.row.append(this.progress);
|
|
651
|
+
|
|
652
|
+
this.progressBar = document.createElement('typo3-backend-progress-bar');
|
|
653
|
+
this.progress.append(this.progressBar);
|
|
654
|
+
|
|
655
|
+
// position queue item in filelist
|
|
656
|
+
if (this.dragUploader.fileList.querySelectorAll('tbody tr.upload-queue-item').length === 0) {
|
|
657
|
+
this.dragUploader.fileList.querySelector('tbody').prepend(this.row);
|
|
658
|
+
this.row.classList.add('last');
|
|
659
|
+
} else {
|
|
660
|
+
this.dragUploader.fileList.querySelector('tbody tr.upload-queue-item:first-child').before(this.row);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Set a disabled checkbox to the selector column, if available
|
|
664
|
+
if (this.selector) {
|
|
665
|
+
this.selector.innerHTML = (
|
|
666
|
+
'<span class="form-check form-check-type-toggle">' +
|
|
667
|
+
'<input type="checkbox" class="form-check-input t3js-multi-record-selection-check" disabled/>' +
|
|
668
|
+
'</span>'
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// set dummy file icon
|
|
673
|
+
this.iconCol.innerHTML = '<typo3-backend-icon identifier="mimetypes-other-other" />';
|
|
674
|
+
|
|
675
|
+
// check file size
|
|
676
|
+
if (this.dragUploader.maxFileSize > 0 && this.file.size > this.dragUploader.maxFileSize) {
|
|
677
|
+
this.updateMessage(TYPO3.lang['file_upload.maxFileSizeExceeded']
|
|
678
|
+
.replace(/\{0\}/g, this.file.name)
|
|
679
|
+
.replace(/\{1\}/g, DragUploader.fileSizeAsString(this.dragUploader.maxFileSize)));
|
|
680
|
+
this.progressBar.value = 100;
|
|
681
|
+
this.progressBar.severity = SeverityEnum.error;
|
|
682
|
+
|
|
683
|
+
// check filename/extension against deny pattern
|
|
684
|
+
} else if (this.dragUploader.fileDenyPattern && this.file.name.match(this.dragUploader.fileDenyPattern)) {
|
|
685
|
+
this.updateMessage(TYPO3.lang['file_upload.fileNotAllowed'].replace(/\{0\}/g, this.file.name));
|
|
686
|
+
this.progressBar.value = 100;
|
|
687
|
+
this.progressBar.severity = SeverityEnum.error;
|
|
688
|
+
|
|
689
|
+
} else if (this.isProhibitedByAllowedExtensionsList()) {
|
|
690
|
+
this.updateMessage(TYPO3.lang['file_upload.fileExtensionExpected'].replace(/\{0\}/g, this.dragUploader.filesExtensionsAllowed));
|
|
691
|
+
this.progressBar.value = 100;
|
|
692
|
+
this.progressBar.severity = SeverityEnum.error;
|
|
693
|
+
} else if (this.isProhibitedByDisallowedExtensionsList()) {
|
|
694
|
+
this.updateMessage(TYPO3.lang['file_upload.fileExtensionDisallowed'].replace(/\{0\}/g, this.dragUploader.filesExtensionsDisallowed));
|
|
695
|
+
this.progressBar.value = 100;
|
|
696
|
+
this.progressBar.severity = SeverityEnum.error;
|
|
697
|
+
} else {
|
|
698
|
+
this.updateMessage('- ' + DragUploader.fileSizeAsString(this.file.size));
|
|
699
|
+
|
|
700
|
+
const formData = new FormData();
|
|
701
|
+
formData.append('data[upload][1][target]', this.dragUploader.target);
|
|
702
|
+
formData.append('data[upload][1][data]', '1');
|
|
703
|
+
formData.append('overwriteExistingFiles', this.override);
|
|
704
|
+
formData.append('redirect', '');
|
|
705
|
+
formData.append('upload_1', this.file);
|
|
706
|
+
|
|
707
|
+
// We use XMLHttpRequest as we need the `progress` event which isn't supported by fetch()
|
|
708
|
+
const xhr = new XMLHttpRequest();
|
|
709
|
+
xhr.onreadystatechange = (): void => {
|
|
710
|
+
if (xhr.readyState === XMLHttpRequest.DONE) {
|
|
711
|
+
if (xhr.status === 200) {
|
|
712
|
+
try {
|
|
713
|
+
const response = JSON.parse(xhr.responseText);
|
|
714
|
+
if (!response.hasErrors) {
|
|
715
|
+
this.uploadSuccess(response);
|
|
716
|
+
} else {
|
|
717
|
+
this.uploadError(xhr);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
catch {
|
|
721
|
+
// In case JSON can not be parsed, the upload failed due to server errors,
|
|
722
|
+
// e.g. "POST Content-Length exceeds limit". Just handle as upload error.
|
|
723
|
+
this.uploadError(xhr);
|
|
724
|
+
}
|
|
725
|
+
} else {
|
|
726
|
+
this.uploadError(xhr);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
xhr.upload.addEventListener('progress', (e: ProgressEvent) => this.updateProgress(e));
|
|
731
|
+
xhr.open('POST', TYPO3.settings.ajaxUrls.file_process);
|
|
732
|
+
xhr.send(formData);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* @param {string} message
|
|
738
|
+
*/
|
|
739
|
+
public updateMessage(message: string): void {
|
|
740
|
+
this.progressBar.label = message;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Remove the progress bar
|
|
745
|
+
*/
|
|
746
|
+
public removeProgress(): void {
|
|
747
|
+
if (this.progress) {
|
|
748
|
+
this.progress.remove();
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* @param {XMLHttpRequest} response
|
|
754
|
+
*/
|
|
755
|
+
public uploadError(response: XMLHttpRequest): void {
|
|
756
|
+
const errorText = TYPO3.lang['file_upload.uploadFailed'].replace(/\{0\}/g, this.file.name);
|
|
757
|
+
this.updateMessage(errorText);
|
|
758
|
+
try {
|
|
759
|
+
const jsonResponse = JSON.parse(response.responseText) as any;
|
|
760
|
+
const messages = jsonResponse.messages as FlashMessage[];
|
|
761
|
+
if (messages && messages.length) {
|
|
762
|
+
for (const flashMessage of messages) {
|
|
763
|
+
Notification.showMessage(flashMessage.title, flashMessage.message, flashMessage.severity, 10);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
} catch {
|
|
767
|
+
// do nothing in case JSON could not be parsed
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
this.progressBar.severity = SeverityEnum.error;
|
|
771
|
+
this.dragUploader.decrementQueueLength();
|
|
772
|
+
this.dragUploader.trigger?.dispatchEvent(new CustomEvent('uploadError', { detail: [this, response] }));
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* @param {ProgressEvent} event
|
|
777
|
+
*/
|
|
778
|
+
public updateProgress(event: ProgressEvent): void {
|
|
779
|
+
const percentage = Math.round((event.loaded / event.total) * 100);
|
|
780
|
+
this.progressBar.value = percentage;
|
|
781
|
+
this.progressBar.label = `${TYPO3.lang['file_upload.upload-in-progress']} ${percentage}%`;
|
|
782
|
+
this.dragUploader.trigger?.dispatchEvent(new CustomEvent('updateProgress', { detail: [this, percentage, event] }));
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* @param {{upload?: UploadedFile[]}} data
|
|
787
|
+
*/
|
|
788
|
+
public uploadSuccess(data: { upload?: UploadedFile[], messages?: FlashMessage[] }): void {
|
|
789
|
+
if (data.upload) {
|
|
790
|
+
this.dragUploader.decrementQueueLength(data.messages);
|
|
791
|
+
this.row.setAttribute('data-type', 'file');
|
|
792
|
+
this.row.setAttribute('data-file-uid', String(data.upload[0].uid));
|
|
793
|
+
this.fileName.textContent = data.upload[0].name;
|
|
794
|
+
this.progressBar.value = 100;
|
|
795
|
+
this.progressBar.label = TYPO3.lang['file_upload.uploadSucceeded'];
|
|
796
|
+
this.progressBar.severity = SeverityEnum.ok;
|
|
797
|
+
|
|
798
|
+
const combinedIdentifier: string = String(data.upload[0].id);
|
|
799
|
+
|
|
800
|
+
// Enable checkbox, if available
|
|
801
|
+
if (this.selector) {
|
|
802
|
+
const checkbox: HTMLInputElement = <HTMLInputElement>this.selector.querySelector('input');
|
|
803
|
+
if (checkbox) {
|
|
804
|
+
checkbox.removeAttribute('disabled');
|
|
805
|
+
checkbox.setAttribute('name', 'CBC[_FILE|' + Md5.hash(combinedIdentifier) + ']');
|
|
806
|
+
checkbox.setAttribute('value', combinedIdentifier);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// replace file icon
|
|
811
|
+
if (data.upload[0].icon) {
|
|
812
|
+
this.iconCol
|
|
813
|
+
.innerHTML = (
|
|
814
|
+
'<button type="button" class="btn btn-link" data-contextmenu-trigger="click" data-contextmenu-uid="'
|
|
815
|
+
+ combinedIdentifier + '" data-contextmenu-table="sys_file" aria-label="'
|
|
816
|
+
+ (TYPO3.lang['labels.contextMenu.open'] || 'Open context menu') + '">'
|
|
817
|
+
+ data.upload[0].icon + '</span></button>'
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (this.dragUploader.irreObjectUid) {
|
|
822
|
+
DragUploader.addFileToIrre(
|
|
823
|
+
this.dragUploader.irreObjectUid,
|
|
824
|
+
data.upload[0],
|
|
825
|
+
);
|
|
826
|
+
setTimeout(
|
|
827
|
+
() => {
|
|
828
|
+
this.row.remove();
|
|
829
|
+
if (this.dragUploader.fileList.querySelectorAll('tr').length === 0) {
|
|
830
|
+
this.dragUploader.fileList.setAttribute('hidden', 'hidden');
|
|
831
|
+
this.dragUploader.fileList.closest('.t3-filelist-container')?.classList.add('hidden');
|
|
832
|
+
this.dragUploader.trigger?.dispatchEvent(new CustomEvent('uploadSuccess', { detail: [this, data] }));
|
|
833
|
+
}
|
|
834
|
+
},
|
|
835
|
+
3000);
|
|
836
|
+
} else {
|
|
837
|
+
setTimeout(
|
|
838
|
+
() => {
|
|
839
|
+
this.showFileInfo(data.upload[0]);
|
|
840
|
+
this.dragUploader.trigger?.dispatchEvent(new CustomEvent('uploadSuccess', { detail: [this, data] }));
|
|
841
|
+
},
|
|
842
|
+
3000);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* @param {UploadedFile} fileInfo
|
|
849
|
+
*/
|
|
850
|
+
public showFileInfo(fileInfo: UploadedFile): void {
|
|
851
|
+
this.removeProgress();
|
|
852
|
+
if ((document.querySelector('#filelist-searchterm') as HTMLInputElement)?.value) {
|
|
853
|
+
// When search is active, the PATH column is always present so we add it
|
|
854
|
+
const pathColumn = document.createElement('td');
|
|
855
|
+
pathColumn.textContent = fileInfo.path;
|
|
856
|
+
this.row.append(pathColumn);
|
|
857
|
+
}
|
|
858
|
+
// Controls column is deliberately empty
|
|
859
|
+
const controlsColumn = document.createElement('td');
|
|
860
|
+
controlsColumn.classList.add('col-control');
|
|
861
|
+
this.row.append(controlsColumn);
|
|
862
|
+
|
|
863
|
+
const fileExtColumn = document.createElement('td');
|
|
864
|
+
fileExtColumn.textContent = TYPO3.lang['type.file'] + ' (' + fileInfo.extension.toUpperCase() + ')';
|
|
865
|
+
this.row.append(fileExtColumn);
|
|
866
|
+
|
|
867
|
+
const fileSizeColumn = document.createElement('td');
|
|
868
|
+
fileSizeColumn.textContent = DragUploader.fileSizeAsString(fileInfo.size);
|
|
869
|
+
this.row.append(fileSizeColumn);
|
|
870
|
+
|
|
871
|
+
let permissions = '';
|
|
872
|
+
if (fileInfo.permissions.read) {
|
|
873
|
+
permissions += '<strong class="text-danger">' + TYPO3.lang['permissions.read'] + '</strong>';
|
|
874
|
+
}
|
|
875
|
+
if (fileInfo.permissions.write) {
|
|
876
|
+
permissions += '<strong class="text-danger">' + TYPO3.lang['permissions.write'] + '</strong>';
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
const permissionsColumn = document.createElement('td');
|
|
880
|
+
permissionsColumn.innerHTML = permissions;
|
|
881
|
+
this.row.append(permissionsColumn);
|
|
882
|
+
|
|
883
|
+
const emptyColumn = document.createElement('td');
|
|
884
|
+
emptyColumn.textContent = '-';
|
|
885
|
+
this.row.append(emptyColumn);
|
|
886
|
+
|
|
887
|
+
// add spacing cells when more columns are displayed (column selector)
|
|
888
|
+
for (let i = this.row.querySelectorAll('td').length; i < this.dragUploader.fileListColumnCount; i++) {
|
|
889
|
+
this.row.append(document.createElement('td'));
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
public isProhibitedByAllowedExtensionsList(): boolean {
|
|
894
|
+
if (!this.dragUploader.filesExtensionsAllowed) {
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
897
|
+
const extension = this.file.name.split('.').pop();
|
|
898
|
+
const allowed = this.dragUploader.filesExtensionsAllowed.split(',');
|
|
899
|
+
|
|
900
|
+
return !allowed.includes(extension.toLowerCase());
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
public isProhibitedByDisallowedExtensionsList(): boolean {
|
|
904
|
+
if (!this.dragUploader.filesExtensionsDisallowed) {
|
|
905
|
+
return false;
|
|
906
|
+
}
|
|
907
|
+
const extension = this.file.name.split('.').pop();
|
|
908
|
+
const disallowed = this.dragUploader.filesExtensionsDisallowed.split(',');
|
|
909
|
+
|
|
910
|
+
return disallowed.includes(extension.toLowerCase());
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
DragUploader.init();
|