From b2a9b9b268ac4a15bc06be9a0d2e4264b8c5d390 Mon Sep 17 00:00:00 2001
From: Oliver Bartsch <bo@cedev.de>
Date: Fri, 22 Mar 2024 18:10:35 +0100
Subject: [PATCH] [BUGFIX] Check language access for edit default metadata
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

At various places, it's possible to edit the metadata
of a file in the default language. However, users might
not have access to the default language. To prevent
moving to FormEngine and facing access permission
errors, those places now use proper language access
checks and do not display corresponding action if
the user does not have access.

Resolves: #103432
Releases: main, 12.4
Change-Id: I22c3755a17888cfc623ce05b25fd655b323cc553
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/83573
Reviewed-by: Stefan Bürk <stefan@buerk.tech>
Tested-by: Oliver Bartsch <bo@cedev.de>
Tested-by: core-ci <typo3@b13.com>
Tested-by: Jochen Roth <rothjochen@gmail.com>
Reviewed-by: Jochen Roth <rothjochen@gmail.com>
Tested-by: Stefan Bürk <stefan@buerk.tech>
Reviewed-by: Oliver Bartsch <bo@cedev.de>
---
 .../Sources/TypeScript/filelist/file-list.ts  |  3 +-
 .../ItemProviders/FileProvider.php            |  9 +++---
 .../Classes/Controller/FileListController.php | 29 ++++++++++++-------
 typo3/sysext/filelist/Classes/FileList.php    |  7 ++++-
 .../Private/Templates/File/List.html          | 10 ++++---
 .../Private/Templates/Filelist/Tiles.html     |  3 +-
 .../Resources/Public/JavaScript/file-list.js  |  2 +-
 7 files changed, 41 insertions(+), 22 deletions(-)

diff --git a/Build/Sources/TypeScript/filelist/file-list.ts b/Build/Sources/TypeScript/filelist/file-list.ts
index 898d0ceb0145..79a12789ed17 100644
--- a/Build/Sources/TypeScript/filelist/file-list.ts
+++ b/Build/Sources/TypeScript/filelist/file-list.ts
@@ -90,7 +90,8 @@ export default class Filelist {
     new RegularEvent(FileListActionEvent.primary, (event: CustomEvent): void => {
       const detail: FileListActionDetail = event.detail;
       const resource = detail.resources[0];
-      if (resource.type === 'file') {
+      const resourceElement: HTMLElement = detail.trigger.closest('[data-default-language-access]') as HTMLElement;
+      if (resource.type === 'file' && resourceElement !== null) {
         window.location.href = top.TYPO3.settings.FormEngine.moduleUrl
           + '&edit[sys_file_metadata][' + resource.metaUid + ']=edit'
           + '&returnUrl=' + Filelist.getReturnUrl('');
diff --git a/typo3/sysext/filelist/Classes/ContextMenu/ItemProviders/FileProvider.php b/typo3/sysext/filelist/Classes/ContextMenu/ItemProviders/FileProvider.php
index 74eb098a4046..f0e46fc35577 100644
--- a/typo3/sysext/filelist/Classes/ContextMenu/ItemProviders/FileProvider.php
+++ b/typo3/sysext/filelist/Classes/ContextMenu/ItemProviders/FileProvider.php
@@ -234,10 +234,11 @@ class FileProvider extends AbstractProvider
     protected function canEditMetadata(): bool
     {
         return $this->isFile()
-           && $this->record->isIndexed()
-           && $this->record->checkActionPermission('editMeta')
-           && $this->record->getMetaData()->offsetExists('uid')
-           && $this->backendUser->check('tables_modify', 'sys_file_metadata');
+            && $this->record->isIndexed()
+            && $this->record->checkActionPermission('editMeta')
+            && $this->record->getMetaData()->offsetExists('uid')
+            && $this->backendUser->check('tables_modify', 'sys_file_metadata')
+            && $this->backendUser->checkLanguageAccess(0);
     }
 
     protected function canBeRenamed(): bool
diff --git a/typo3/sysext/filelist/Classes/Controller/FileListController.php b/typo3/sysext/filelist/Classes/Controller/FileListController.php
index 325953d23da3..ef25a3642204 100644
--- a/typo3/sysext/filelist/Classes/Controller/FileListController.php
+++ b/typo3/sysext/filelist/Classes/Controller/FileListController.php
@@ -384,19 +384,28 @@ class FileListController implements LoggerAwareInterface
                 'fileUploadUrl' => $this->getFileUploadUrl(),
                 'totalItems' => $this->filelist->totalItems,
             ]);
+
+            // Add edit metadata configuration, if user can edit default language
+            if ($this->getBackendUser()->checkLanguageAccess(0)) {
+                $this->view->assign(
+                    'editActionConfiguration',
+                    GeneralUtility::jsonEncodeForHtmlAttribute([
+                        'idField' => 'filelistMetaUid',
+                        'table' => 'sys_file_metadata',
+                        'returnUrl' => $this->filelist->createModuleUri(),
+                    ])
+                );
+            }
+
             // Assign meta information for the multi record selection
-            $this->view->assignMultiple([
-                'editActionConfiguration' => GeneralUtility::jsonEncodeForHtmlAttribute([
-                    'idField' => 'filelistMetaUid',
-                    'table' => 'sys_file_metadata',
-                    'returnUrl' => $this->filelist->createModuleUri(),
-                ], true),
-                'deleteActionConfiguration' => GeneralUtility::jsonEncodeForHtmlAttribute([
+            $this->view->assign(
+                'deleteActionConfiguration',
+                GeneralUtility::jsonEncodeForHtmlAttribute([
                     'ok' => $lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.delete'),
                     'title' => $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:clip_deleteMarked'),
                     'content' => $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:clip_deleteMarkedWarning'),
-                ], true),
-            ]);
+                ]),
+            );
 
             // Add download button configuration, if file download is enabled
             if ($this->getBackendUser()->getTSConfig()['options.']['file_list.']['fileDownload.']['enabled'] ?? true) {
@@ -404,7 +413,7 @@ class FileListController implements LoggerAwareInterface
                     'downloadActionConfiguration',
                     GeneralUtility::jsonEncodeForHtmlAttribute([
                         'downloadUrl' => (string)$this->uriBuilder->buildUriFromRoute('file_download'),
-                    ], true)
+                    ])
                 );
             }
         } else {
diff --git a/typo3/sysext/filelist/Classes/FileList.php b/typo3/sysext/filelist/Classes/FileList.php
index dfcd09962399..28c479c18dd3 100644
--- a/typo3/sysext/filelist/Classes/FileList.php
+++ b/typo3/sysext/filelist/Classes/FileList.php
@@ -263,6 +263,7 @@ class FileList
     {
         $view->assign('displayThumbs', $this->thumbs);
         $view->assign('displayCheckbox', $this->resourceSelectableMatcher ? true : false);
+        $view->assign('defaultLanguageAccess', $this->getBackendUser()->checkLanguageAccess(0));
         $view->assign('pagination', [
             'backward' => $this->getPaginationLinkForDirection($paginator, NavigationDirection::BACKWARD),
             'forward' => $this->getPaginationLinkForDirection($paginator, NavigationDirection::FORWARD),
@@ -496,6 +497,9 @@ class FileList
                 'data-multi-record-selection-element' => 'true',
                 'draggable' => $resourceView->canMove() ? 'true' : 'false',
             ];
+            if ($this->getBackendUser()->checkLanguageAccess(0)) {
+                $attributes['data-default-language-access'] = 'true';
+            }
             foreach ($this->fieldArray as $field) {
                 switch ($field) {
                     case 'icon':
@@ -998,7 +1002,7 @@ class FileList
 
     protected function createControlEditMetaData(ResourceView $resourceView): ?ButtonInterface
     {
-        if (!$resourceView->getMetaDataUid()) {
+        if (!$resourceView->getMetaDataUid() || !$this->getBackendUser()->checkLanguageAccess(0)) {
             return null;
         }
 
@@ -1333,6 +1337,7 @@ class FileList
     {
         if (!($resourceView->resource instanceof File)
             || !$resourceView->canEditMetadata()
+            || !$this->getBackendUser()->checkLanguageAccess(0)
             || !$this->onlineMediaHelperRegistry->hasOnlineMediaHelper($resourceView->resource->getExtension())
         ) {
             return null;
diff --git a/typo3/sysext/filelist/Resources/Private/Templates/File/List.html b/typo3/sysext/filelist/Resources/Private/Templates/File/List.html
index 57d56e71b550..475ba9e74015 100644
--- a/typo3/sysext/filelist/Resources/Private/Templates/File/List.html
+++ b/typo3/sysext/filelist/Resources/Private/Templates/File/List.html
@@ -93,13 +93,15 @@
                         <div class="col">
                             <strong><f:translate key="LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.selection"/></strong>
                         </div>
-                        <div class="col">
-                            <button type="button" class="btn btn-default btn-sm disabled" data-multi-record-selection-action="edit" data-multi-record-selection-action-config="{editActionConfiguration -> f:format.raw()}">
+                        <f:if condition="{editActionConfiguration}">
+                            <div class="col">
+                                <button type="button" class="btn btn-default btn-sm disabled" data-multi-record-selection-action="edit" data-multi-record-selection-action-config="{editActionConfiguration -> f:format.raw()}">
                                 <span title="{f:translate(key: 'LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:editMarked')}">
                                     <core:icon identifier="actions-open" size="small" /> <f:translate key="LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:editMarked" />
                                 </span>
-                            </button>
-                        </div>
+                                </button>
+                            </div>
+                        </f:if>
                         <f:if condition="{downloadActionConfiguration}">
                             <div class="col">
                                 <button type="button" class="btn btn-default btn-sm" data-multi-record-selection-action="download" data-multi-record-selection-action-config="{downloadActionConfiguration -> f:format.raw()}">
diff --git a/typo3/sysext/filelist/Resources/Private/Templates/Filelist/Tiles.html b/typo3/sysext/filelist/Resources/Private/Templates/Filelist/Tiles.html
index 93ada3864224..98aab9fbcdbb 100644
--- a/typo3/sysext/filelist/Resources/Private/Templates/Filelist/Tiles.html
+++ b/typo3/sysext/filelist/Resources/Private/Templates/Filelist/Tiles.html
@@ -8,7 +8,7 @@
             }" />
         </f:if>
         <f:for each="{resources}" as="resource">
-            <f:render section="ResourceTile" arguments="{resource: resource, displayThumbs: displayThumbs, displayCheckbox: displayCheckbox}" />
+            <f:render section="ResourceTile" arguments="{resource: resource, displayThumbs: displayThumbs, displayCheckbox: displayCheckbox, defaultLanguageAccess: defaultLanguageAccess}" />
         </f:for>
         <f:if condition="{pagination.forward}">
             <f:render section="PaginationTile" arguments="{
@@ -55,6 +55,7 @@
         data-filelist-selectable="{f:if(condition: resource.isSelectable, then: 'true', else: 'false')}"
         data-filelist-selected="{f:if(condition: resource.isSelected, then: 'true', else: 'false')}"
         data-multi-record-selection-element="true"
+        {f:if(condition: defaultLanguageAccess, then: 'data-default-language-access="true"')}
         draggable="{resource.canMove ? 'true' : 'false'}"
     >
         <button type="button" title="{resource.name}" data-filelist-action="primary">
diff --git a/typo3/sysext/filelist/Resources/Public/JavaScript/file-list.js b/typo3/sysext/filelist/Resources/Public/JavaScript/file-list.js
index f0b5c5a376d1..95ea5acb235e 100644
--- a/typo3/sysext/filelist/Resources/Public/JavaScript/file-list.js
+++ b/typo3/sysext/filelist/Resources/Public/JavaScript/file-list.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import{lll}from"@typo3/core/lit-helper.js";import DocumentService from"@typo3/core/document-service.js";import Notification from"@typo3/backend/notification.js";import InfoWindow from"@typo3/backend/info-window.js";import{BroadcastMessage}from"@typo3/backend/broadcast-message.js";import broadcastService from"@typo3/backend/broadcast-service.js";import{FileListActionEvent,FileListActionSelector,FileListActionUtility}from"@typo3/filelist/file-list-actions.js";import NProgress from"nprogress";import Icons from"@typo3/backend/icons.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";import{default as Modal}from"@typo3/backend/modal.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import Severity from"@typo3/backend/severity.js";import{MultiRecordSelectionSelectors}from"@typo3/backend/multi-record-selection.js";import ContextMenu from"@typo3/backend/context-menu.js";var Selectors;!function(e){e.fileListFormSelector='form[name="fileListForm"]',e.commandSelector='input[name="cmd"]',e.searchFieldSelector='input[name="searchTerm"]',e.pointerFieldSelector='input[name="pointer"]'}(Selectors||(Selectors={}));export const fileListOpenElementBrowser="typo3:filelist:openElementBrowser";export default class Filelist{constructor(){this.downloadFilesAndFolders=e=>{e.preventDefault();const t=e.target,o=e.detail,i=o.configuration,n=[];o.checkboxes.forEach((e=>{if(e.checked){const t=e.closest(FileListActionSelector.elementSelector),o=FileListActionUtility.getResourceForElement(t);n.unshift(o.identifier)}})),n.length?this.triggerDownload(n,i.downloadUrl,t):Notification.warning(lll("file_download.invalidSelection"))},Filelist.processTriggers(),new RegularEvent(fileListOpenElementBrowser,(e=>{const t=new URL(e.detail.actionUrl,window.location.origin);t.searchParams.set("expandFolder",e.detail.identifier),t.searchParams.set("mode",e.detail.mode);Modal.advanced({type:Modal.types.iframe,content:t.toString(),size:Modal.sizes.large}).addEventListener("typo3-modal-hidden",(()=>{top.list_frame.document.location.reload()}))})).bindTo(document),new RegularEvent(FileListActionEvent.primary,(e=>{const t=e.detail.resources[0];if("file"===t.type&&(window.location.href=top.TYPO3.settings.FormEngine.moduleUrl+"&edit[sys_file_metadata]["+t.metaUid+"]=edit&returnUrl="+Filelist.getReturnUrl("")),"folder"===t.type){const e=Filelist.parseQueryParameters(document.location);e.id=t.identifier;let o="";Object.keys(e).forEach((t=>{""!==e[t]&&(o=o+"&"+t+"="+e[t])})),window.location.href=window.location.pathname+"?"+o.substring(1)}})).bindTo(document),new RegularEvent(FileListActionEvent.primaryContextmenu,(e=>{const t=e.detail,o=t.resources[0];ContextMenu.show("sys_file",o.identifier,"","","",t.trigger,t.event)})).bindTo(document),new RegularEvent(FileListActionEvent.show,(e=>{const t=e.detail.resources[0];Filelist.openInfoPopup("_"+t.type.toUpperCase(),t.identifier)})).bindTo(document),new RegularEvent(FileListActionEvent.download,(e=>{const t=e.detail,o=t.resources[0];this.triggerDownload([o.identifier],t.url,t.trigger)})).bindTo(document),new RegularEvent(FileListActionEvent.updateOnlineMedia,(e=>{const t=e.detail,o=t.resources[0];this.updateOnlineMedia(o,t.url)})).bindTo(document),DocumentService.ready().then((()=>{new RegularEvent("click",((e,t)=>{e.preventDefault(),document.dispatchEvent(new CustomEvent(fileListOpenElementBrowser,{detail:{actionUrl:t.href,identifier:t.dataset.identifier,mode:t.dataset.mode}}))})).delegateTo(document,".t3js-element-browser")})),new RegularEvent("multiRecordSelection:action:edit",this.editFileMetadata).bindTo(document),new RegularEvent("multiRecordSelection:action:delete",this.deleteMultiple).bindTo(document),new RegularEvent("multiRecordSelection:action:download",this.downloadFilesAndFolders).bindTo(document),new RegularEvent("multiRecordSelection:action:copyMarked",(e=>{Filelist.submitClipboardFormWithCommand("copyMarked",e.target)})).bindTo(document),new RegularEvent("multiRecordSelection:action:removeMarked",(e=>{Filelist.submitClipboardFormWithCommand("removeMarked",e.target)})).bindTo(document);const e=""!==document.querySelector([Selectors.fileListFormSelector,Selectors.searchFieldSelector].join(" "))?.value;new RegularEvent("search",(t=>{const o=t.target;""===o.value&&e&&o.closest(Selectors.fileListFormSelector)?.submit()})).delegateTo(document,Selectors.searchFieldSelector)}static submitClipboardFormWithCommand(e,t){const o=t.closest(Selectors.fileListFormSelector);if(!o)return;const i=o.querySelector(Selectors.commandSelector);if(i){if(i.value=e,"copyMarked"===e||"removeMarked"===e){const e=o.querySelector(Selectors.pointerFieldSelector),t=Filelist.parseQueryParameters(document.location).pointer;e&&t&&(e.value=t)}o.submit()}}static openInfoPopup(e,t){InfoWindow.showItem(e,t)}static processTriggers(){const e=document.querySelector(".filelist-main");if(null===e)return;const t=encodeURIComponent(e.dataset.filelistCurrentIdentifier);ModuleStateStorage.update("media",t,!0,void 0),Filelist.emitTreeUpdateRequest(e.dataset.filelistCurrentIdentifier)}static emitTreeUpdateRequest(e){const t=new BroadcastMessage("filelist","treeUpdateRequested",{type:"folder",identifier:e});broadcastService.post(t)}static parseQueryParameters(e){const t={};if(e&&Object.prototype.hasOwnProperty.call(e,"search")){const o=e.search.substr(1).split("&");for(let e=0;e<o.length;e++){const i=o[e].split("=");t[decodeURIComponent(i[0])]=decodeURIComponent(i[1])}}return t}static getReturnUrl(e){return""===e&&(e=top.list_frame.document.location.pathname+top.list_frame.document.location.search),encodeURIComponent(e)}deleteMultiple(e){e.preventDefault();const t=e.detail.configuration;Modal.advanced({title:t.title||"Delete",content:t.content||"Are you sure you want to delete those files and folders?",severity:SeverityEnum.warning,buttons:[{text:TYPO3.lang["button.close"]||"Close",active:!0,btnClass:"btn-default",trigger:(e,t)=>t.hideModal()},{text:t.ok||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.warning),trigger:(t,o)=>{Filelist.submitClipboardFormWithCommand("delete",e.target),o.hideModal()}}]})}editFileMetadata(e){e.preventDefault();const t=e.detail,o=t.configuration;if(!o||!o.idField||!o.table)return;const i=[];t.checkboxes.forEach((e=>{const t=e.closest(MultiRecordSelectionSelectors.elementSelector);null!==t&&t.dataset[o.idField]&&i.push(t.dataset[o.idField])})),i.length?window.location.href=top.TYPO3.settings.FormEngine.moduleUrl+"&edit["+o.table+"]["+i.join(",")+"]=edit&returnUrl="+Filelist.getReturnUrl(o.returnUrl||""):Notification.warning("The selected elements can not be edited.")}triggerDownload(e,t,o){Notification.info(lll("file_download.prepare"),"",2);const i=o?.innerHTML;o&&(o.setAttribute("disabled","disabled"),Icons.getIcon("spinner-circle",Icons.sizes.small).then((e=>{o.innerHTML=e}))),NProgress.configure({parent:"#typo3-filelist",showSpinner:!1}).start(),new AjaxRequest(t).post({items:e}).then((async e=>{let t=e.response.headers.get("Content-Disposition");if(!t){const t=await e.resolve();return void(!1===t.success&&t.status?Notification.warning(lll("file_download."+t.status),lll("file_download."+t.status+".message"),10):Notification.error(lll("file_download.error")))}t=t.substring(t.indexOf(" filename=")+10);const o=await e.raw().arrayBuffer(),i=new Blob([o],{type:e.raw().headers.get("Content-Type")}),n=URL.createObjectURL(i),r=document.createElement("a");r.href=n,r.download=t,document.body.appendChild(r),r.click(),URL.revokeObjectURL(n),document.body.removeChild(r),Notification.success(lll("file_download.success"),"",2)})).catch((()=>{Notification.error(lll("file_download.error"))})).finally((()=>{NProgress.done(),o&&(o.removeAttribute("disabled"),o.innerHTML=i)}))}updateOnlineMedia(e,t){t&&e.uid&&"file"===e.type&&(NProgress.configure({parent:"#typo3-filelist",showSpinner:!1}).start(),new AjaxRequest(t).post({resource:e}).then((()=>{Notification.success(lll("online_media.update.success"))})).catch((()=>{Notification.error(lll("online_media.update.error"))})).finally((()=>{NProgress.done(),window.location.reload()})))}}
\ No newline at end of file
+import{lll}from"@typo3/core/lit-helper.js";import DocumentService from"@typo3/core/document-service.js";import Notification from"@typo3/backend/notification.js";import InfoWindow from"@typo3/backend/info-window.js";import{BroadcastMessage}from"@typo3/backend/broadcast-message.js";import broadcastService from"@typo3/backend/broadcast-service.js";import{FileListActionEvent,FileListActionSelector,FileListActionUtility}from"@typo3/filelist/file-list-actions.js";import NProgress from"nprogress";import Icons from"@typo3/backend/icons.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";import{default as Modal}from"@typo3/backend/modal.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import Severity from"@typo3/backend/severity.js";import{MultiRecordSelectionSelectors}from"@typo3/backend/multi-record-selection.js";import ContextMenu from"@typo3/backend/context-menu.js";var Selectors;!function(e){e.fileListFormSelector='form[name="fileListForm"]',e.commandSelector='input[name="cmd"]',e.searchFieldSelector='input[name="searchTerm"]',e.pointerFieldSelector='input[name="pointer"]'}(Selectors||(Selectors={}));export const fileListOpenElementBrowser="typo3:filelist:openElementBrowser";export default class Filelist{constructor(){this.downloadFilesAndFolders=e=>{e.preventDefault();const t=e.target,o=e.detail,i=o.configuration,n=[];o.checkboxes.forEach((e=>{if(e.checked){const t=e.closest(FileListActionSelector.elementSelector),o=FileListActionUtility.getResourceForElement(t);n.unshift(o.identifier)}})),n.length?this.triggerDownload(n,i.downloadUrl,t):Notification.warning(lll("file_download.invalidSelection"))},Filelist.processTriggers(),new RegularEvent(fileListOpenElementBrowser,(e=>{const t=new URL(e.detail.actionUrl,window.location.origin);t.searchParams.set("expandFolder",e.detail.identifier),t.searchParams.set("mode",e.detail.mode);Modal.advanced({type:Modal.types.iframe,content:t.toString(),size:Modal.sizes.large}).addEventListener("typo3-modal-hidden",(()=>{top.list_frame.document.location.reload()}))})).bindTo(document),new RegularEvent(FileListActionEvent.primary,(e=>{const t=e.detail,o=t.resources[0],i=t.trigger.closest("[data-default-language-access]");if("file"===o.type&&null!==i&&(window.location.href=top.TYPO3.settings.FormEngine.moduleUrl+"&edit[sys_file_metadata]["+o.metaUid+"]=edit&returnUrl="+Filelist.getReturnUrl("")),"folder"===o.type){const e=Filelist.parseQueryParameters(document.location);e.id=o.identifier;let t="";Object.keys(e).forEach((o=>{""!==e[o]&&(t=t+"&"+o+"="+e[o])})),window.location.href=window.location.pathname+"?"+t.substring(1)}})).bindTo(document),new RegularEvent(FileListActionEvent.primaryContextmenu,(e=>{const t=e.detail,o=t.resources[0];ContextMenu.show("sys_file",o.identifier,"","","",t.trigger,t.event)})).bindTo(document),new RegularEvent(FileListActionEvent.show,(e=>{const t=e.detail.resources[0];Filelist.openInfoPopup("_"+t.type.toUpperCase(),t.identifier)})).bindTo(document),new RegularEvent(FileListActionEvent.download,(e=>{const t=e.detail,o=t.resources[0];this.triggerDownload([o.identifier],t.url,t.trigger)})).bindTo(document),new RegularEvent(FileListActionEvent.updateOnlineMedia,(e=>{const t=e.detail,o=t.resources[0];this.updateOnlineMedia(o,t.url)})).bindTo(document),DocumentService.ready().then((()=>{new RegularEvent("click",((e,t)=>{e.preventDefault(),document.dispatchEvent(new CustomEvent(fileListOpenElementBrowser,{detail:{actionUrl:t.href,identifier:t.dataset.identifier,mode:t.dataset.mode}}))})).delegateTo(document,".t3js-element-browser")})),new RegularEvent("multiRecordSelection:action:edit",this.editFileMetadata).bindTo(document),new RegularEvent("multiRecordSelection:action:delete",this.deleteMultiple).bindTo(document),new RegularEvent("multiRecordSelection:action:download",this.downloadFilesAndFolders).bindTo(document),new RegularEvent("multiRecordSelection:action:copyMarked",(e=>{Filelist.submitClipboardFormWithCommand("copyMarked",e.target)})).bindTo(document),new RegularEvent("multiRecordSelection:action:removeMarked",(e=>{Filelist.submitClipboardFormWithCommand("removeMarked",e.target)})).bindTo(document);const e=""!==document.querySelector([Selectors.fileListFormSelector,Selectors.searchFieldSelector].join(" "))?.value;new RegularEvent("search",(t=>{const o=t.target;""===o.value&&e&&o.closest(Selectors.fileListFormSelector)?.submit()})).delegateTo(document,Selectors.searchFieldSelector)}static submitClipboardFormWithCommand(e,t){const o=t.closest(Selectors.fileListFormSelector);if(!o)return;const i=o.querySelector(Selectors.commandSelector);if(i){if(i.value=e,"copyMarked"===e||"removeMarked"===e){const e=o.querySelector(Selectors.pointerFieldSelector),t=Filelist.parseQueryParameters(document.location).pointer;e&&t&&(e.value=t)}o.submit()}}static openInfoPopup(e,t){InfoWindow.showItem(e,t)}static processTriggers(){const e=document.querySelector(".filelist-main");if(null===e)return;const t=encodeURIComponent(e.dataset.filelistCurrentIdentifier);ModuleStateStorage.update("media",t,!0,void 0),Filelist.emitTreeUpdateRequest(e.dataset.filelistCurrentIdentifier)}static emitTreeUpdateRequest(e){const t=new BroadcastMessage("filelist","treeUpdateRequested",{type:"folder",identifier:e});broadcastService.post(t)}static parseQueryParameters(e){const t={};if(e&&Object.prototype.hasOwnProperty.call(e,"search")){const o=e.search.substr(1).split("&");for(let e=0;e<o.length;e++){const i=o[e].split("=");t[decodeURIComponent(i[0])]=decodeURIComponent(i[1])}}return t}static getReturnUrl(e){return""===e&&(e=top.list_frame.document.location.pathname+top.list_frame.document.location.search),encodeURIComponent(e)}deleteMultiple(e){e.preventDefault();const t=e.detail.configuration;Modal.advanced({title:t.title||"Delete",content:t.content||"Are you sure you want to delete those files and folders?",severity:SeverityEnum.warning,buttons:[{text:TYPO3.lang["button.close"]||"Close",active:!0,btnClass:"btn-default",trigger:(e,t)=>t.hideModal()},{text:t.ok||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.warning),trigger:(t,o)=>{Filelist.submitClipboardFormWithCommand("delete",e.target),o.hideModal()}}]})}editFileMetadata(e){e.preventDefault();const t=e.detail,o=t.configuration;if(!o||!o.idField||!o.table)return;const i=[];t.checkboxes.forEach((e=>{const t=e.closest(MultiRecordSelectionSelectors.elementSelector);null!==t&&t.dataset[o.idField]&&i.push(t.dataset[o.idField])})),i.length?window.location.href=top.TYPO3.settings.FormEngine.moduleUrl+"&edit["+o.table+"]["+i.join(",")+"]=edit&returnUrl="+Filelist.getReturnUrl(o.returnUrl||""):Notification.warning("The selected elements can not be edited.")}triggerDownload(e,t,o){Notification.info(lll("file_download.prepare"),"",2);const i=o?.innerHTML;o&&(o.setAttribute("disabled","disabled"),Icons.getIcon("spinner-circle",Icons.sizes.small).then((e=>{o.innerHTML=e}))),NProgress.configure({parent:"#typo3-filelist",showSpinner:!1}).start(),new AjaxRequest(t).post({items:e}).then((async e=>{let t=e.response.headers.get("Content-Disposition");if(!t){const t=await e.resolve();return void(!1===t.success&&t.status?Notification.warning(lll("file_download."+t.status),lll("file_download."+t.status+".message"),10):Notification.error(lll("file_download.error")))}t=t.substring(t.indexOf(" filename=")+10);const o=await e.raw().arrayBuffer(),i=new Blob([o],{type:e.raw().headers.get("Content-Type")}),n=URL.createObjectURL(i),r=document.createElement("a");r.href=n,r.download=t,document.body.appendChild(r),r.click(),URL.revokeObjectURL(n),document.body.removeChild(r),Notification.success(lll("file_download.success"),"",2)})).catch((()=>{Notification.error(lll("file_download.error"))})).finally((()=>{NProgress.done(),o&&(o.removeAttribute("disabled"),o.innerHTML=i)}))}updateOnlineMedia(e,t){t&&e.uid&&"file"===e.type&&(NProgress.configure({parent:"#typo3-filelist",showSpinner:!1}).start(),new AjaxRequest(t).post({resource:e}).then((()=>{Notification.success(lll("online_media.update.success"))})).catch((()=>{Notification.error(lll("online_media.update.error"))})).finally((()=>{NProgress.done(),window.location.reload()})))}}
\ No newline at end of file
-- 
GitLab