diff --git a/Build/Sources/TypeScript/backend/drag-uploader.ts b/Build/Sources/TypeScript/backend/drag-uploader.ts index b6ef7050a0f6ece5a8633402cebed069344d6483..6446ccfe02e43ea237933ff7ac52c92683bdb0d9 100644 --- a/Build/Sources/TypeScript/backend/drag-uploader.ts +++ b/Build/Sources/TypeScript/backend/drag-uploader.ts @@ -811,9 +811,9 @@ class FileQueueItem { if (data.upload[0].icon) { this.iconCol .innerHTML = ( - '<a href="#" data-contextmenu-trigger="click" data-contextmenu-uid="' + '<button type="button" class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-uid="' + combinedIdentifier + '" data-contextmenu-table="sys_file">' - + data.upload[0].icon + '</span></a>' + + data.upload[0].icon + '</span></button>' ); } diff --git a/typo3/sysext/backend/Classes/Utility/BackendUtility.php b/typo3/sysext/backend/Classes/Utility/BackendUtility.php index 4b5e3274b10f3cc4364e264072a8ce94c4e28f84..27c02820c053fb02508c82be05c687204f1a78b3 100644 --- a/typo3/sysext/backend/Classes/Utility/BackendUtility.php +++ b/typo3/sysext/backend/Classes/Utility/BackendUtility.php @@ -1952,8 +1952,8 @@ class BackendUtility */ public static function wrapClickMenuOnIcon($content, $table, $uid = 0, $context = ''): string { - $attributes = self::getContextMenuAttributes((string)$table, $uid, (string)$context, 'click'); - return '<a href="#" ' . GeneralUtility::implodeAttributes($attributes, true) . '>' . $content . '</a>'; + $attributes = self::getContextMenuAttributes((string)$table, $uid, (string)$context); + return '<button type="button" class="btn btn-link p-0" ' . GeneralUtility::implodeAttributes($attributes, true) . '>' . $content . '</button>'; } /** diff --git a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/RecordDefault/Header.html b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/RecordDefault/Header.html index 538f92086ad601cfcc80651936abe32d431c4827..90857ea24d9919b01157c0ca8e03aa03d266b10d 100644 --- a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/RecordDefault/Header.html +++ b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/RecordDefault/Header.html @@ -34,9 +34,10 @@ <core:icon identifier="actions-edit-delete" size="small" /> </a> </f:if> - <button aria-haspopup="true" + <button type="button" + aria-haspopup="true" aria-controls="contentMenu0" aria-label="{f:translate(key: 'LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:advancedFunctions')}" - class="btn btn-default btn-borderless btn-sm" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="tt_content" data-contextmenu-uid="{item.record.uid}"> diff --git a/typo3/sysext/backend/Resources/Private/Templates/Page/NewPages.html b/typo3/sysext/backend/Resources/Private/Templates/Page/NewPages.html index 99eb90f5f89e0b7a995db2d0faf0cae8c22c95d3..7b48524947f7d463a783328a32d62d318226a274 100644 --- a/typo3/sysext/backend/Resources/Private/Templates/Page/NewPages.html +++ b/typo3/sysext/backend/Resources/Private/Templates/Page/NewPages.html @@ -66,13 +66,14 @@ <td> <span class="treeline-icon treeline-icon-join{f:if(condition: '{pageIterator.isLast}', then: 'bottom')}"></span> <span title="id={page.uid}"><f:spaceless> - <a href="#" + <button type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="pages" data-contextmenu-uid="{page.uid}" > <core:iconForRecord table="pages" row="{page}" /> - </a> + </button> {page.title -> f:format.crop(maxCharacters: maxTitleLength)} </f:spaceless></span> </td> diff --git a/typo3/sysext/backend/Resources/Private/Templates/PageTsConfig/RecordsOverview.html b/typo3/sysext/backend/Resources/Private/Templates/PageTsConfig/RecordsOverview.html index c942c174a92bebf8239c142ed530b92640045279..2edea3fa06778bc4db128c392f42f3938fee8cda 100644 --- a/typo3/sysext/backend/Resources/Private/Templates/PageTsConfig/RecordsOverview.html +++ b/typo3/sysext/backend/Resources/Private/Templates/PageTsConfig/RecordsOverview.html @@ -47,8 +47,9 @@ <tr> <td class="align-top nowrap"><f:spaceless> <span style="margin-left: {line.padding}px"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="pages" data-contextmenu-uid="{line.title}" @@ -56,7 +57,7 @@ <span title="id={line.title}"> <f:format.raw>{line.icon}</f:format.raw> </span> - </a> + </button> <f:format.raw>{line.pageTitle}</f:format.raw> </span> </f:spaceless></td> diff --git a/typo3/sysext/backend/Resources/Private/Templates/SiteConfiguration/Overview.html b/typo3/sysext/backend/Resources/Private/Templates/SiteConfiguration/Overview.html index 78e2c76bd976a375d04d21b583715e0f36372f35..7d0ea4d7336676abaa942c24b112cc93dc1c7b7e 100644 --- a/typo3/sysext/backend/Resources/Private/Templates/SiteConfiguration/Overview.html +++ b/typo3/sysext/backend/Resources/Private/Templates/SiteConfiguration/Overview.html @@ -81,14 +81,15 @@ </f:for> <tr> <td class="col-icon align-top"> - <a href="#" + <button type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="pages" data-contextmenu-uid="{rootPage.uid}"> <span style="margin-left: {rootLinePage.margin}px"> <core:iconForRecord table="pages" row="{rootPage}" /> </span> - </a> + </button> </td> <td class="align-top"> {rootPage.title} diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/drag-uploader.js b/typo3/sysext/backend/Resources/Public/JavaScript/drag-uploader.js index 68561b6a4a6ec4da74d3b14361a850417858851c..b438225eb5c253bd85c220c101823c3acf6ca858 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/drag-uploader.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/drag-uploader.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import{DateTime}from"luxon";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import NProgress from"nprogress";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{default as Modal,Sizes as ModalSizes}from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import ImmediateAction from"@typo3/backend/action-button/immediate-action.js";import Md5 from"@typo3/backend/hashing/md5.js";import"@typo3/backend/element/icon-element.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DomHelper from"@typo3/backend/utility/dom-helper.js";import{KeyTypesEnum}from"@typo3/backend/enum/key-types.js";var Action;!function(e){e.OVERRIDE="replace",e.RENAME="rename",e.SKIP="cancel",e.USE_EXISTING="useExisting"}(Action||(Action={}));export default class DragUploader{constructor(e){this.askForOverride=[],this.percentagePerFile=1,this.dragStartedInDocument=!1,this.hideDropzone=e=>{e.stopPropagation(),e.preventDefault(),this.dropzone.setAttribute("hidden","hidden"),this.dropzone.classList.remove("drop-status-ok"),this.manuallyTriggered=!1},this.dragFileIntoDocument=e=>!this.dragStartedInDocument&&(!!e.dataTransfer.types.includes("Files")&&(e.stopPropagation(),e.preventDefault(),e.currentTarget.classList.add("drop-in-progress"),this.element.offsetParent&&this.showDropzone(),!1)),this.dragAborted=e=>(e.stopPropagation(),e.preventDefault(),e.currentTarget.classList.remove("drop-in-progress"),this.dragStartedInDocument=!1,!1),this.ignoreDrop=e=>(e.stopPropagation(),e.preventDefault(),this.dragAborted(e),!1),this.handleDrop=e=>{this.ignoreDrop(e),this.hideDropzone(e),this.processFiles(e.dataTransfer.files)},this.fileInDropzone=()=>{this.dropzone.classList.add("drop-status-ok")},this.fileOutOfDropzone=()=>{this.dropzone.classList.remove("drop-status-ok"),this.manuallyTriggered||this.dropzone.setAttribute("hidden","hidden")},this.body=document.querySelector("body"),this.element=e;const t=void 0!==this.element.dataset.dropzoneTrigger;this.trigger=document.querySelector(this.element.dataset.dropzoneTrigger),this.defaultAction=this.element.dataset.defaultAction||Action.SKIP,this.dropzone=document.createElement("div"),this.dropzone.classList.add("dropzone"),this.dropzone.setAttribute("hidden","hidden"),this.irreObjectUid=this.element.dataset.fileIrreObject;const i=document.querySelector(this.element.dataset.dropzoneTarget);if(this.irreObjectUid&&0!==DomHelper.nextAll(i).length?(this.dropZoneInsertBefore=!0,i.before(this.dropzone)):(this.dropZoneInsertBefore=!1,i.after(this.dropzone)),this.fileInput=document.createElement("input"),this.fileInput.setAttribute("type","file"),this.fileInput.setAttribute("multiple","multiple"),this.fileInput.setAttribute("name","files[]"),this.fileInput.classList.add("upload-file-picker"),this.body.append(this.fileInput),this.fileList=document.querySelector(this.element.dataset.progressContainer),this.fileListColumnCount=this.fileList?.querySelectorAll("thead tr:first-child th").length+1,this.filesExtensionsAllowed=this.element.dataset.fileAllowed,this.filesExtensionsDisallowed=this.element.dataset.fileDisallowed,this.fileDenyPattern=this.element.dataset.fileDenyPattern?new RegExp(this.element.dataset.fileDenyPattern,"i"):null,this.maxFileSize=parseInt(this.element.dataset.maxFileSize,10),this.target=this.element.dataset.targetFolder,this.reloadUrl=this.element.dataset.reloadUrl,this.browserCapabilities={fileReader:"undefined"!=typeof FileReader,DnD:"draggable"in document.createElement("span"),Progress:"upload"in new XMLHttpRequest},!this.browserCapabilities.DnD)return void console.warn("Browser has no Drag and drop capabilities; cannot initialize DragUploader");this.body.addEventListener("dragstart",(()=>{this.dragStartedInDocument=!0})),this.body.addEventListener("dragover",this.dragFileIntoDocument),this.body.addEventListener("dragend",this.dragAborted),this.body.addEventListener("drop",this.ignoreDrop),this.dropzone.innerHTML='<button type="button" class="dropzone-hint" aria-labelledby="dropzone-title"><div class="dropzone-hint-media"><div class="dropzone-hint-icon"></div></div><div class="dropzone-hint-body"><h3 id="dropzone-title" class="dropzone-hint-title">'+TYPO3.lang["file_upload.dropzonehint.title"]+'</h3><p class="dropzone-hint-message">'+TYPO3.lang["file_upload.dropzonehint.message"]+"</p></div></div>",this.dropzoneMask=document.createElement("div"),this.dropzoneMask.classList.add("dropzone-mask"),this.dropzone.append(this.dropzoneMask),this.dropzone.addEventListener("dragenter",this.fileInDropzone),this.dropzoneMask.addEventListener("dragenter",this.fileInDropzone),this.dropzoneMask.addEventListener("dragleave",this.fileOutOfDropzone),this.dropzoneMask.addEventListener("drop",(e=>this.handleDrop(e))),this.dropzone.addEventListener("click",(()=>{this.fileInput.click()}));const s=document.createElement("button");if(s.classList.add("dropzone-close"),s.setAttribute("aria-label",TYPO3.lang["file_upload.dropzone.close"]),s.addEventListener("click",this.hideDropzone),this.dropzone.append(s),null===this.fileList){this.fileList=document.createElement("table"),this.fileList.setAttribute("id","typo3-filelist"),this.fileList.classList.add("table","table-striped","table-hover","upload-queue"),this.fileList.innerHTML="<tbody></tbody>";const e=document.createElement("div");e.classList.add("table-fit"),e.setAttribute("hidden","hidden"),e.append(this.fileList),this.dropZoneInsertBefore?this.dropzone.after(e):this.dropzone.before(e),this.fileListColumnCount=8,this.manualTable=!0}this.fileInput.addEventListener("change",(e=>{this.hideDropzone(e),this.processFiles(this.fileInput.files)})),document.addEventListener("keydown",(e=>{e.key!==KeyTypesEnum.ENTER||this.dropzone.hasAttribute("hidden")||this.hideDropzone(e)})),this.bindUploadButton(!0===t?this.trigger:this.element)}static init(){DocumentService.ready().then((()=>{document.querySelectorAll(".t3js-drag-uploader").forEach((e=>{new DragUploader(e)}))}))}static fileSizeAsString(e){const t=e/1024;let i="";return i=t>1024?(t/1024).toFixed(1)+" MB":t.toFixed(1)+" KB",i}static addFileToIrre(e,t){const i={actionName:"typo3:foreignRelation:insert",objectGroup:e,table:"sys_file",uid:t.uid};MessageUtility.send(i)}showDropzone(){this.dropzone.removeAttribute("hidden")}processFiles(e){this.queueLength=e.length,this.fileList.parentElement.hasAttribute("hidden")&&(this.fileList.parentElement.removeAttribute("hidden"),this.fileList.closest(".t3-filelist-table-container")?.classList.remove("hidden"),this.fileList.closest("form")?.querySelector(".t3-filelist-info-container")?.setAttribute("hidden","hidden")),NProgress.start(),this.percentagePerFile=1/e.length;const t=[];Array.from(e).forEach((e=>{const i=new AjaxRequest(TYPO3.settings.ajaxUrls.file_exists).withQueryArguments({fileName:e.name,fileTarget:this.target}).get({cache:"no-cache"}).then((async t=>{const i=await t.resolve();void 0!==i.uid?(this.askForOverride.push({original:i,uploaded:e,action:this.irreObjectUid?Action.USE_EXISTING:this.defaultAction}),NProgress.inc(this.percentagePerFile)):new FileQueueItem(this,e,Action.SKIP)}));t.push(i)})),Promise.all(t).then((()=>{this.drawOverrideModal(),NProgress.done()})),this.fileInput.value=""}bindUploadButton(e){e.addEventListener("click",(e=>{e.preventDefault(),this.fileInput.click(),this.showDropzone(),this.manuallyTriggered=!0}))}decrementQueueLength(e){if(this.queueLength>0&&(this.queueLength--,0===this.queueLength)){const t=e&&e.length?5e3:0;if(t)for(const t of e)Notification.showMessage(t.title,t.message,t.severity);this.reloadUrl&&setTimeout((()=>{Notification.info(TYPO3.lang["file_upload.reload.filelist"],TYPO3.lang["file_upload.reload.filelist.message"],10,[{label:TYPO3.lang["file_upload.reload.filelist.actions.dismiss"]},{label:TYPO3.lang["file_upload.reload.filelist.actions.reload"],action:new ImmediateAction((()=>{top.list_frame.document.location.href=this.reloadUrl}))}])}),t)}}drawOverrideModal(){const e=Object.keys(this.askForOverride).length;if(0===e)return;const t=document.createElement("div");let i=`\n <p>${TYPO3.lang["file_upload.existingfiles.description"]}</p>\n <table class="table">\n <thead>\n <tr>\n <th></th>\n <th>${TYPO3.lang["file_upload.header.originalFile"]}</th>\n <th>${TYPO3.lang["file_upload.header.uploadedFile"]}</th>\n <th>${TYPO3.lang["file_upload.header.action"]}</th>\n </tr>\n </thead>\n <tbody>\n `;for(let t=0;t<e;++t){i+=`\n <tr>\n <td>\n ${""!==this.askForOverride[t].original.thumbUrl?`<img src="${this.askForOverride[t].original.thumbUrl}" height="40" />`:this.askForOverride[t].original.icon}\n </td>\n <td>\n ${this.askForOverride[t].original.name} (${DragUploader.fileSizeAsString(this.askForOverride[t].original.size)})<br />\n ${DateTime.fromSeconds(this.askForOverride[t].original.mtime).toLocaleString(DateTime.DATETIME_MED)}\n </td>\n <td>\n ${this.askForOverride[t].uploaded.name} (${DragUploader.fileSizeAsString(this.askForOverride[t].uploaded.size)})<br />\n ${DateTime.fromMillis(this.askForOverride[t].uploaded.lastModified).toLocaleString(DateTime.DATETIME_MED)}\n </td>\n <td>\n <select class="form-select t3js-actions" data-override="${t}">\n ${this.irreObjectUid?`<option value="${Action.USE_EXISTING}">${TYPO3.lang["file_upload.actions.use_existing"]}</option>`:""}\n <option value="${Action.SKIP}" ${this.defaultAction===Action.SKIP?"selected":""}>${TYPO3.lang["file_upload.actions.skip"]}</option>\n <option value="${Action.RENAME}" ${this.defaultAction===Action.RENAME?"selected":""}>${TYPO3.lang["file_upload.actions.rename"]}</option>\n <option value="${Action.OVERRIDE}" ${this.defaultAction===Action.OVERRIDE?"selected":""}>${TYPO3.lang["file_upload.actions.override"]}</option>\n </select>\n </td>\n </tr>\n `}i+="</tbody></table>",t.innerHTML=i;const s=Modal.advanced({title:TYPO3.lang["file_upload.existingfiles.title"],content:t,severity:SeverityEnum.warning,buttons:[{text:TYPO3.lang["file_upload.button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang["file_upload.button.continue"]||"Continue with selected actions",btnClass:"btn-warning",name:"continue"}],additionalCssClasses:["modal-inner-scroll"],size:ModalSizes.large,callback:e=>{const t=e.querySelector(".modal-footer"),i=document.createElement("label");i.textContent=TYPO3.lang["file_upload.actions.all.label"];const s=document.createElement("span");s.innerHTML=`\n <select class="form-select t3js-actions-all">\n <option value="">${TYPO3.lang["file_upload.actions.all.empty"]}</option>\n ${this.irreObjectUid?`<option value="${Action.USE_EXISTING}">${TYPO3.lang["file_upload.actions.all.use_existing"]}</option>`:""}\n <option value="${Action.SKIP}" ${this.defaultAction===Action.SKIP?"selected":""}>${TYPO3.lang["file_upload.actions.all.skip"]}</option>\n <option value="${Action.RENAME}" ${this.defaultAction===Action.RENAME?"selected":""}>${TYPO3.lang["file_upload.actions.all.rename"]}</option>\n <option value="${Action.OVERRIDE}" ${this.defaultAction===Action.OVERRIDE?"selected":""}>${TYPO3.lang["file_upload.actions.all.override"]}</option>\n </select>\n `,t.prepend(i,s)}});new RegularEvent("change",(e=>{const t=e.currentTarget.value;if(""!==t)for(const e of s.querySelectorAll(".t3js-actions")){const i=parseInt(e.dataset.override,10);e.value=t,e.disabled=!0,this.askForOverride[i].action=e.value}else s.querySelectorAll(".t3js-actions").forEach((e=>e.disabled=!1))})).delegateTo(s,".t3js-actions-all"),new RegularEvent("change",(e=>{const t=e.target,i=parseInt(t.dataset.override,10);this.askForOverride[i].action=t.value})).delegateTo(s,".t3js-actions"),s.addEventListener("button.clicked",(e=>{const t=e.target;if("cancel"===t.name)this.askForOverride=[],Modal.dismiss();else if("continue"===t.name){for(const e of this.askForOverride)e.action===Action.USE_EXISTING?DragUploader.addFileToIrre(this.irreObjectUid,e.original):e.action!==Action.SKIP&&new FileQueueItem(this,e.uploaded,e.action);this.askForOverride=[],s.hideModal()}})),s.addEventListener("typo3-modal-hidden",(()=>{this.askForOverride=[]}))}}class FileQueueItem{constructor(e,t,i){if(this.dragUploader=e,this.file=t,this.override=i,this.row=document.createElement("tr"),this.row.classList.add("upload-queue-item","uploading"),this.dragUploader.manualTable||(this.selector=document.createElement("td"),this.selector.classList.add("col-checkbox"),this.row.append(this.selector)),this.iconCol=document.createElement("td"),this.iconCol.classList.add("col-icon"),this.row.append(this.iconCol),this.fileName=document.createElement("td"),this.fileName.classList.add("col-title","col-responsive"),this.fileName.textContent=t.name,this.row.append(this.fileName),this.progress=document.createElement("td"),this.progress.classList.add("col-progress"),this.progress.setAttribute("colspan",String(this.dragUploader.fileListColumnCount-this.row.querySelectorAll("td").length)),this.row.append(this.progress),this.progressContainer=document.createElement("div"),this.progressContainer.classList.add("upload-queue-progress"),this.progress.append(this.progressContainer),this.progressBar=document.createElement("div"),this.progressBar.classList.add("upload-queue-progress-bar"),this.progressContainer.append(this.progressBar),this.progressPercentage=document.createElement("span"),this.progressPercentage.classList.add("upload-queue-progress-percentage"),this.progressContainer.append(this.progressPercentage),this.progressMessage=document.createElement("span"),this.progressMessage.classList.add("upload-queue-progress-message"),this.progressContainer.append(this.progressMessage),0===this.dragUploader.fileList.querySelectorAll("tbody tr.upload-queue-item").length?(this.dragUploader.fileList.querySelector("tbody").prepend(this.row),this.row.classList.add("last")):this.dragUploader.fileList.querySelector("tbody tr.upload-queue-item:first-child").before(this.row),this.selector&&(this.selector.innerHTML='<span class="form-check form-check-type-toggle"><input type="checkbox" class="form-check-input t3js-multi-record-selection-check" disabled/></span>'),this.iconCol.innerHTML='<typo3-backend-icon identifier="mimetypes-other-other" />',this.dragUploader.maxFileSize>0&&this.file.size>this.dragUploader.maxFileSize)this.updateMessage(TYPO3.lang["file_upload.maxFileSizeExceeded"].replace(/\{0\}/g,this.file.name).replace(/\{1\}/g,DragUploader.fileSizeAsString(this.dragUploader.maxFileSize))),this.row.classList.add("error");else if(this.dragUploader.fileDenyPattern&&this.file.name.match(this.dragUploader.fileDenyPattern))this.updateMessage(TYPO3.lang["file_upload.fileNotAllowed"].replace(/\{0\}/g,this.file.name)),this.row.classList.add("error");else if(this.checkAllowedExtensions())if(this.checkDisallowedExtensions()){this.updateMessage("- "+DragUploader.fileSizeAsString(this.file.size));const e=new FormData;e.append("data[upload][1][target]",this.dragUploader.target),e.append("data[upload][1][data]","1"),e.append("overwriteExistingFiles",this.override),e.append("redirect",""),e.append("upload_1",this.file);const t=new XMLHttpRequest;t.onreadystatechange=()=>{if(t.readyState===XMLHttpRequest.DONE)if(200===t.status)try{const e=JSON.parse(t.responseText);e.hasErrors?this.uploadError(t):this.uploadSuccess(e)}catch(e){this.uploadError(t)}else this.uploadError(t)},t.upload.addEventListener("progress",(e=>this.updateProgress(e))),t.open("POST",TYPO3.settings.ajaxUrls.file_process),t.send(e)}else this.updateMessage(TYPO3.lang["file_upload.fileExtensionDisallowed"].replace(/\{0\}/g,this.dragUploader.filesExtensionsDisallowed)),this.row.classList.add("error");else this.updateMessage(TYPO3.lang["file_upload.fileExtensionExpected"].replace(/\{0\}/g,this.dragUploader.filesExtensionsAllowed)),this.row.classList.add("error")}updateMessage(e){this.progressMessage.textContent=e}removeProgress(){this.progress&&this.progress.remove()}uploadError(e){const t=TYPO3.lang["file_upload.uploadFailed"].replace(/\{0\}/g,this.file.name);this.updateMessage(t);try{const t=JSON.parse(e.responseText).messages;if(this.progressPercentage.textContent="",t&&t.length)for(const e of t)Notification.showMessage(e.title,e.message,e.severity,10)}catch(e){}this.row.classList.add("error"),this.dragUploader.decrementQueueLength(),this.dragUploader.trigger?.dispatchEvent(new CustomEvent("uploadError",{detail:[this,e]}))}updateProgress(e){const t=Math.round(e.loaded/e.total*100)+"%";this.progressBar.style.width=t,this.progressPercentage.textContent=t,this.dragUploader.trigger?.dispatchEvent(new CustomEvent("updateProgress",{detail:[this,t,e]}))}uploadSuccess(e){if(e.upload){this.dragUploader.decrementQueueLength(e.messages),this.row.classList.remove("uploading"),this.row.setAttribute("data-type","file"),this.row.setAttribute("data-file-uid",String(e.upload[0].uid)),this.fileName.textContent=e.upload[0].name,this.progressPercentage.textContent="",this.progressMessage.textContent="100%",this.progressBar.style.width="100%";const t=String(e.upload[0].id);if(this.selector){const e=this.selector.querySelector("input");e&&(e.removeAttribute("disabled"),e.setAttribute("name","CBC[_FILE|"+Md5.hash(t)+"]"),e.setAttribute("value",t))}e.upload[0].icon&&(this.iconCol.innerHTML='<a href="#" data-contextmenu-trigger="click" data-contextmenu-uid="'+t+'" data-contextmenu-table="sys_file">'+e.upload[0].icon+"</span></a>"),this.dragUploader.irreObjectUid?(DragUploader.addFileToIrre(this.dragUploader.irreObjectUid,e.upload[0]),setTimeout((()=>{this.row.remove(),0===this.dragUploader.fileList.querySelectorAll("tr").length&&(this.dragUploader.fileList.setAttribute("hidden","hidden"),this.dragUploader.fileList.closest(".t3-filelist-table-container")?.classList.add("hidden"),this.dragUploader.trigger?.dispatchEvent(new CustomEvent("uploadSuccess",{detail:[this,e]})))}),3e3)):setTimeout((()=>{this.showFileInfo(e.upload[0]),this.dragUploader.trigger?.dispatchEvent(new CustomEvent("uploadSuccess",{detail:[this,e]}))}),3e3)}}showFileInfo(e){if(this.removeProgress(),document.querySelector("#filelist-searchterm")?.value){const t=document.createElement("td");t.textContent=e.path,this.row.append(t)}const t=document.createElement("td");t.classList.add("col-control"),this.row.append(t);const i=document.createElement("td");i.textContent=TYPO3.lang["type.file"]+" ("+e.extension.toUpperCase()+")",this.row.append(i);const s=document.createElement("td");s.textContent=DragUploader.fileSizeAsString(e.size),this.row.append(s);let o="";e.permissions.read&&(o+='<strong class="text-danger">'+TYPO3.lang["permissions.read"]+"</strong>"),e.permissions.write&&(o+='<strong class="text-danger">'+TYPO3.lang["permissions.write"]+"</strong>");const r=document.createElement("td");r.innerHTML=o,this.row.append(r);const a=document.createElement("td");a.textContent="-",this.row.append(a);for(let e=this.row.querySelectorAll("td").length;e<this.dragUploader.fileListColumnCount;e++)this.row.append(document.createElement("td"))}checkAllowedExtensions(){if(!this.dragUploader.filesExtensionsAllowed)return!0;const e=this.file.name.split(".").pop();return this.dragUploader.filesExtensionsAllowed.split(",").includes(e.toLowerCase())}checkDisallowedExtensions(){if(!this.dragUploader.filesExtensionsDisallowed)return!0;const e=this.file.name.split(".").pop();return this.dragUploader.filesExtensionsDisallowed.split(",").includes(e.toLowerCase())}}DragUploader.init(); \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import{DateTime}from"luxon";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import NProgress from"nprogress";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{default as Modal,Sizes as ModalSizes}from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import ImmediateAction from"@typo3/backend/action-button/immediate-action.js";import Md5 from"@typo3/backend/hashing/md5.js";import"@typo3/backend/element/icon-element.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DomHelper from"@typo3/backend/utility/dom-helper.js";import{KeyTypesEnum}from"@typo3/backend/enum/key-types.js";var Action;!function(e){e.OVERRIDE="replace",e.RENAME="rename",e.SKIP="cancel",e.USE_EXISTING="useExisting"}(Action||(Action={}));export default class DragUploader{constructor(e){this.askForOverride=[],this.percentagePerFile=1,this.dragStartedInDocument=!1,this.hideDropzone=e=>{e.stopPropagation(),e.preventDefault(),this.dropzone.setAttribute("hidden","hidden"),this.dropzone.classList.remove("drop-status-ok"),this.manuallyTriggered=!1},this.dragFileIntoDocument=e=>!this.dragStartedInDocument&&(!!e.dataTransfer.types.includes("Files")&&(e.stopPropagation(),e.preventDefault(),e.currentTarget.classList.add("drop-in-progress"),this.element.offsetParent&&this.showDropzone(),!1)),this.dragAborted=e=>(e.stopPropagation(),e.preventDefault(),e.currentTarget.classList.remove("drop-in-progress"),this.dragStartedInDocument=!1,!1),this.ignoreDrop=e=>(e.stopPropagation(),e.preventDefault(),this.dragAborted(e),!1),this.handleDrop=e=>{this.ignoreDrop(e),this.hideDropzone(e),this.processFiles(e.dataTransfer.files)},this.fileInDropzone=()=>{this.dropzone.classList.add("drop-status-ok")},this.fileOutOfDropzone=()=>{this.dropzone.classList.remove("drop-status-ok"),this.manuallyTriggered||this.dropzone.setAttribute("hidden","hidden")},this.body=document.querySelector("body"),this.element=e;const t=void 0!==this.element.dataset.dropzoneTrigger;this.trigger=document.querySelector(this.element.dataset.dropzoneTrigger),this.defaultAction=this.element.dataset.defaultAction||Action.SKIP,this.dropzone=document.createElement("div"),this.dropzone.classList.add("dropzone"),this.dropzone.setAttribute("hidden","hidden"),this.irreObjectUid=this.element.dataset.fileIrreObject;const i=document.querySelector(this.element.dataset.dropzoneTarget);if(this.irreObjectUid&&0!==DomHelper.nextAll(i).length?(this.dropZoneInsertBefore=!0,i.before(this.dropzone)):(this.dropZoneInsertBefore=!1,i.after(this.dropzone)),this.fileInput=document.createElement("input"),this.fileInput.setAttribute("type","file"),this.fileInput.setAttribute("multiple","multiple"),this.fileInput.setAttribute("name","files[]"),this.fileInput.classList.add("upload-file-picker"),this.body.append(this.fileInput),this.fileList=document.querySelector(this.element.dataset.progressContainer),this.fileListColumnCount=this.fileList?.querySelectorAll("thead tr:first-child th").length+1,this.filesExtensionsAllowed=this.element.dataset.fileAllowed,this.filesExtensionsDisallowed=this.element.dataset.fileDisallowed,this.fileDenyPattern=this.element.dataset.fileDenyPattern?new RegExp(this.element.dataset.fileDenyPattern,"i"):null,this.maxFileSize=parseInt(this.element.dataset.maxFileSize,10),this.target=this.element.dataset.targetFolder,this.reloadUrl=this.element.dataset.reloadUrl,this.browserCapabilities={fileReader:"undefined"!=typeof FileReader,DnD:"draggable"in document.createElement("span"),Progress:"upload"in new XMLHttpRequest},!this.browserCapabilities.DnD)return void console.warn("Browser has no Drag and drop capabilities; cannot initialize DragUploader");this.body.addEventListener("dragstart",(()=>{this.dragStartedInDocument=!0})),this.body.addEventListener("dragover",this.dragFileIntoDocument),this.body.addEventListener("dragend",this.dragAborted),this.body.addEventListener("drop",this.ignoreDrop),this.dropzone.innerHTML='<button type="button" class="dropzone-hint" aria-labelledby="dropzone-title"><div class="dropzone-hint-media"><div class="dropzone-hint-icon"></div></div><div class="dropzone-hint-body"><h3 id="dropzone-title" class="dropzone-hint-title">'+TYPO3.lang["file_upload.dropzonehint.title"]+'</h3><p class="dropzone-hint-message">'+TYPO3.lang["file_upload.dropzonehint.message"]+"</p></div></div>",this.dropzoneMask=document.createElement("div"),this.dropzoneMask.classList.add("dropzone-mask"),this.dropzone.append(this.dropzoneMask),this.dropzone.addEventListener("dragenter",this.fileInDropzone),this.dropzoneMask.addEventListener("dragenter",this.fileInDropzone),this.dropzoneMask.addEventListener("dragleave",this.fileOutOfDropzone),this.dropzoneMask.addEventListener("drop",(e=>this.handleDrop(e))),this.dropzone.addEventListener("click",(()=>{this.fileInput.click()}));const s=document.createElement("button");if(s.classList.add("dropzone-close"),s.setAttribute("aria-label",TYPO3.lang["file_upload.dropzone.close"]),s.addEventListener("click",this.hideDropzone),this.dropzone.append(s),null===this.fileList){this.fileList=document.createElement("table"),this.fileList.setAttribute("id","typo3-filelist"),this.fileList.classList.add("table","table-striped","table-hover","upload-queue"),this.fileList.innerHTML="<tbody></tbody>";const e=document.createElement("div");e.classList.add("table-fit"),e.setAttribute("hidden","hidden"),e.append(this.fileList),this.dropZoneInsertBefore?this.dropzone.after(e):this.dropzone.before(e),this.fileListColumnCount=8,this.manualTable=!0}this.fileInput.addEventListener("change",(e=>{this.hideDropzone(e),this.processFiles(this.fileInput.files)})),document.addEventListener("keydown",(e=>{e.key!==KeyTypesEnum.ENTER||this.dropzone.hasAttribute("hidden")||this.hideDropzone(e)})),this.bindUploadButton(!0===t?this.trigger:this.element)}static init(){DocumentService.ready().then((()=>{document.querySelectorAll(".t3js-drag-uploader").forEach((e=>{new DragUploader(e)}))}))}static fileSizeAsString(e){const t=e/1024;let i="";return i=t>1024?(t/1024).toFixed(1)+" MB":t.toFixed(1)+" KB",i}static addFileToIrre(e,t){const i={actionName:"typo3:foreignRelation:insert",objectGroup:e,table:"sys_file",uid:t.uid};MessageUtility.send(i)}showDropzone(){this.dropzone.removeAttribute("hidden")}processFiles(e){this.queueLength=e.length,this.fileList.parentElement.hasAttribute("hidden")&&(this.fileList.parentElement.removeAttribute("hidden"),this.fileList.closest(".t3-filelist-table-container")?.classList.remove("hidden"),this.fileList.closest("form")?.querySelector(".t3-filelist-info-container")?.setAttribute("hidden","hidden")),NProgress.start(),this.percentagePerFile=1/e.length;const t=[];Array.from(e).forEach((e=>{const i=new AjaxRequest(TYPO3.settings.ajaxUrls.file_exists).withQueryArguments({fileName:e.name,fileTarget:this.target}).get({cache:"no-cache"}).then((async t=>{const i=await t.resolve();void 0!==i.uid?(this.askForOverride.push({original:i,uploaded:e,action:this.irreObjectUid?Action.USE_EXISTING:this.defaultAction}),NProgress.inc(this.percentagePerFile)):new FileQueueItem(this,e,Action.SKIP)}));t.push(i)})),Promise.all(t).then((()=>{this.drawOverrideModal(),NProgress.done()})),this.fileInput.value=""}bindUploadButton(e){e.addEventListener("click",(e=>{e.preventDefault(),this.fileInput.click(),this.showDropzone(),this.manuallyTriggered=!0}))}decrementQueueLength(e){if(this.queueLength>0&&(this.queueLength--,0===this.queueLength)){const t=e&&e.length?5e3:0;if(t)for(const t of e)Notification.showMessage(t.title,t.message,t.severity);this.reloadUrl&&setTimeout((()=>{Notification.info(TYPO3.lang["file_upload.reload.filelist"],TYPO3.lang["file_upload.reload.filelist.message"],10,[{label:TYPO3.lang["file_upload.reload.filelist.actions.dismiss"]},{label:TYPO3.lang["file_upload.reload.filelist.actions.reload"],action:new ImmediateAction((()=>{top.list_frame.document.location.href=this.reloadUrl}))}])}),t)}}drawOverrideModal(){const e=Object.keys(this.askForOverride).length;if(0===e)return;const t=document.createElement("div");let i=`\n <p>${TYPO3.lang["file_upload.existingfiles.description"]}</p>\n <table class="table">\n <thead>\n <tr>\n <th></th>\n <th>${TYPO3.lang["file_upload.header.originalFile"]}</th>\n <th>${TYPO3.lang["file_upload.header.uploadedFile"]}</th>\n <th>${TYPO3.lang["file_upload.header.action"]}</th>\n </tr>\n </thead>\n <tbody>\n `;for(let t=0;t<e;++t){i+=`\n <tr>\n <td>\n ${""!==this.askForOverride[t].original.thumbUrl?`<img src="${this.askForOverride[t].original.thumbUrl}" height="40" />`:this.askForOverride[t].original.icon}\n </td>\n <td>\n ${this.askForOverride[t].original.name} (${DragUploader.fileSizeAsString(this.askForOverride[t].original.size)})<br />\n ${DateTime.fromSeconds(this.askForOverride[t].original.mtime).toLocaleString(DateTime.DATETIME_MED)}\n </td>\n <td>\n ${this.askForOverride[t].uploaded.name} (${DragUploader.fileSizeAsString(this.askForOverride[t].uploaded.size)})<br />\n ${DateTime.fromMillis(this.askForOverride[t].uploaded.lastModified).toLocaleString(DateTime.DATETIME_MED)}\n </td>\n <td>\n <select class="form-select t3js-actions" data-override="${t}">\n ${this.irreObjectUid?`<option value="${Action.USE_EXISTING}">${TYPO3.lang["file_upload.actions.use_existing"]}</option>`:""}\n <option value="${Action.SKIP}" ${this.defaultAction===Action.SKIP?"selected":""}>${TYPO3.lang["file_upload.actions.skip"]}</option>\n <option value="${Action.RENAME}" ${this.defaultAction===Action.RENAME?"selected":""}>${TYPO3.lang["file_upload.actions.rename"]}</option>\n <option value="${Action.OVERRIDE}" ${this.defaultAction===Action.OVERRIDE?"selected":""}>${TYPO3.lang["file_upload.actions.override"]}</option>\n </select>\n </td>\n </tr>\n `}i+="</tbody></table>",t.innerHTML=i;const s=Modal.advanced({title:TYPO3.lang["file_upload.existingfiles.title"],content:t,severity:SeverityEnum.warning,buttons:[{text:TYPO3.lang["file_upload.button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang["file_upload.button.continue"]||"Continue with selected actions",btnClass:"btn-warning",name:"continue"}],additionalCssClasses:["modal-inner-scroll"],size:ModalSizes.large,callback:e=>{const t=e.querySelector(".modal-footer"),i=document.createElement("label");i.textContent=TYPO3.lang["file_upload.actions.all.label"];const s=document.createElement("span");s.innerHTML=`\n <select class="form-select t3js-actions-all">\n <option value="">${TYPO3.lang["file_upload.actions.all.empty"]}</option>\n ${this.irreObjectUid?`<option value="${Action.USE_EXISTING}">${TYPO3.lang["file_upload.actions.all.use_existing"]}</option>`:""}\n <option value="${Action.SKIP}" ${this.defaultAction===Action.SKIP?"selected":""}>${TYPO3.lang["file_upload.actions.all.skip"]}</option>\n <option value="${Action.RENAME}" ${this.defaultAction===Action.RENAME?"selected":""}>${TYPO3.lang["file_upload.actions.all.rename"]}</option>\n <option value="${Action.OVERRIDE}" ${this.defaultAction===Action.OVERRIDE?"selected":""}>${TYPO3.lang["file_upload.actions.all.override"]}</option>\n </select>\n `,t.prepend(i,s)}});new RegularEvent("change",(e=>{const t=e.currentTarget.value;if(""!==t)for(const e of s.querySelectorAll(".t3js-actions")){const i=parseInt(e.dataset.override,10);e.value=t,e.disabled=!0,this.askForOverride[i].action=e.value}else s.querySelectorAll(".t3js-actions").forEach((e=>e.disabled=!1))})).delegateTo(s,".t3js-actions-all"),new RegularEvent("change",(e=>{const t=e.target,i=parseInt(t.dataset.override,10);this.askForOverride[i].action=t.value})).delegateTo(s,".t3js-actions"),s.addEventListener("button.clicked",(e=>{const t=e.target;if("cancel"===t.name)this.askForOverride=[],Modal.dismiss();else if("continue"===t.name){for(const e of this.askForOverride)e.action===Action.USE_EXISTING?DragUploader.addFileToIrre(this.irreObjectUid,e.original):e.action!==Action.SKIP&&new FileQueueItem(this,e.uploaded,e.action);this.askForOverride=[],s.hideModal()}})),s.addEventListener("typo3-modal-hidden",(()=>{this.askForOverride=[]}))}}class FileQueueItem{constructor(e,t,i){if(this.dragUploader=e,this.file=t,this.override=i,this.row=document.createElement("tr"),this.row.classList.add("upload-queue-item","uploading"),this.dragUploader.manualTable||(this.selector=document.createElement("td"),this.selector.classList.add("col-checkbox"),this.row.append(this.selector)),this.iconCol=document.createElement("td"),this.iconCol.classList.add("col-icon"),this.row.append(this.iconCol),this.fileName=document.createElement("td"),this.fileName.classList.add("col-title","col-responsive"),this.fileName.textContent=t.name,this.row.append(this.fileName),this.progress=document.createElement("td"),this.progress.classList.add("col-progress"),this.progress.setAttribute("colspan",String(this.dragUploader.fileListColumnCount-this.row.querySelectorAll("td").length)),this.row.append(this.progress),this.progressContainer=document.createElement("div"),this.progressContainer.classList.add("upload-queue-progress"),this.progress.append(this.progressContainer),this.progressBar=document.createElement("div"),this.progressBar.classList.add("upload-queue-progress-bar"),this.progressContainer.append(this.progressBar),this.progressPercentage=document.createElement("span"),this.progressPercentage.classList.add("upload-queue-progress-percentage"),this.progressContainer.append(this.progressPercentage),this.progressMessage=document.createElement("span"),this.progressMessage.classList.add("upload-queue-progress-message"),this.progressContainer.append(this.progressMessage),0===this.dragUploader.fileList.querySelectorAll("tbody tr.upload-queue-item").length?(this.dragUploader.fileList.querySelector("tbody").prepend(this.row),this.row.classList.add("last")):this.dragUploader.fileList.querySelector("tbody tr.upload-queue-item:first-child").before(this.row),this.selector&&(this.selector.innerHTML='<span class="form-check form-check-type-toggle"><input type="checkbox" class="form-check-input t3js-multi-record-selection-check" disabled/></span>'),this.iconCol.innerHTML='<typo3-backend-icon identifier="mimetypes-other-other" />',this.dragUploader.maxFileSize>0&&this.file.size>this.dragUploader.maxFileSize)this.updateMessage(TYPO3.lang["file_upload.maxFileSizeExceeded"].replace(/\{0\}/g,this.file.name).replace(/\{1\}/g,DragUploader.fileSizeAsString(this.dragUploader.maxFileSize))),this.row.classList.add("error");else if(this.dragUploader.fileDenyPattern&&this.file.name.match(this.dragUploader.fileDenyPattern))this.updateMessage(TYPO3.lang["file_upload.fileNotAllowed"].replace(/\{0\}/g,this.file.name)),this.row.classList.add("error");else if(this.checkAllowedExtensions())if(this.checkDisallowedExtensions()){this.updateMessage("- "+DragUploader.fileSizeAsString(this.file.size));const e=new FormData;e.append("data[upload][1][target]",this.dragUploader.target),e.append("data[upload][1][data]","1"),e.append("overwriteExistingFiles",this.override),e.append("redirect",""),e.append("upload_1",this.file);const t=new XMLHttpRequest;t.onreadystatechange=()=>{if(t.readyState===XMLHttpRequest.DONE)if(200===t.status)try{const e=JSON.parse(t.responseText);e.hasErrors?this.uploadError(t):this.uploadSuccess(e)}catch(e){this.uploadError(t)}else this.uploadError(t)},t.upload.addEventListener("progress",(e=>this.updateProgress(e))),t.open("POST",TYPO3.settings.ajaxUrls.file_process),t.send(e)}else this.updateMessage(TYPO3.lang["file_upload.fileExtensionDisallowed"].replace(/\{0\}/g,this.dragUploader.filesExtensionsDisallowed)),this.row.classList.add("error");else this.updateMessage(TYPO3.lang["file_upload.fileExtensionExpected"].replace(/\{0\}/g,this.dragUploader.filesExtensionsAllowed)),this.row.classList.add("error")}updateMessage(e){this.progressMessage.textContent=e}removeProgress(){this.progress&&this.progress.remove()}uploadError(e){const t=TYPO3.lang["file_upload.uploadFailed"].replace(/\{0\}/g,this.file.name);this.updateMessage(t);try{const t=JSON.parse(e.responseText).messages;if(this.progressPercentage.textContent="",t&&t.length)for(const e of t)Notification.showMessage(e.title,e.message,e.severity,10)}catch(e){}this.row.classList.add("error"),this.dragUploader.decrementQueueLength(),this.dragUploader.trigger?.dispatchEvent(new CustomEvent("uploadError",{detail:[this,e]}))}updateProgress(e){const t=Math.round(e.loaded/e.total*100)+"%";this.progressBar.style.width=t,this.progressPercentage.textContent=t,this.dragUploader.trigger?.dispatchEvent(new CustomEvent("updateProgress",{detail:[this,t,e]}))}uploadSuccess(e){if(e.upload){this.dragUploader.decrementQueueLength(e.messages),this.row.classList.remove("uploading"),this.row.setAttribute("data-type","file"),this.row.setAttribute("data-file-uid",String(e.upload[0].uid)),this.fileName.textContent=e.upload[0].name,this.progressPercentage.textContent="",this.progressMessage.textContent="100%",this.progressBar.style.width="100%";const t=String(e.upload[0].id);if(this.selector){const e=this.selector.querySelector("input");e&&(e.removeAttribute("disabled"),e.setAttribute("name","CBC[_FILE|"+Md5.hash(t)+"]"),e.setAttribute("value",t))}e.upload[0].icon&&(this.iconCol.innerHTML='<button type="button" class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-uid="'+t+'" data-contextmenu-table="sys_file">'+e.upload[0].icon+"</span></button>"),this.dragUploader.irreObjectUid?(DragUploader.addFileToIrre(this.dragUploader.irreObjectUid,e.upload[0]),setTimeout((()=>{this.row.remove(),0===this.dragUploader.fileList.querySelectorAll("tr").length&&(this.dragUploader.fileList.setAttribute("hidden","hidden"),this.dragUploader.fileList.closest(".t3-filelist-table-container")?.classList.add("hidden"),this.dragUploader.trigger?.dispatchEvent(new CustomEvent("uploadSuccess",{detail:[this,e]})))}),3e3)):setTimeout((()=>{this.showFileInfo(e.upload[0]),this.dragUploader.trigger?.dispatchEvent(new CustomEvent("uploadSuccess",{detail:[this,e]}))}),3e3)}}showFileInfo(e){if(this.removeProgress(),document.querySelector("#filelist-searchterm")?.value){const t=document.createElement("td");t.textContent=e.path,this.row.append(t)}const t=document.createElement("td");t.classList.add("col-control"),this.row.append(t);const i=document.createElement("td");i.textContent=TYPO3.lang["type.file"]+" ("+e.extension.toUpperCase()+")",this.row.append(i);const s=document.createElement("td");s.textContent=DragUploader.fileSizeAsString(e.size),this.row.append(s);let o="";e.permissions.read&&(o+='<strong class="text-danger">'+TYPO3.lang["permissions.read"]+"</strong>"),e.permissions.write&&(o+='<strong class="text-danger">'+TYPO3.lang["permissions.write"]+"</strong>");const r=document.createElement("td");r.innerHTML=o,this.row.append(r);const n=document.createElement("td");n.textContent="-",this.row.append(n);for(let e=this.row.querySelectorAll("td").length;e<this.dragUploader.fileListColumnCount;e++)this.row.append(document.createElement("td"))}checkAllowedExtensions(){if(!this.dragUploader.filesExtensionsAllowed)return!0;const e=this.file.name.split(".").pop();return this.dragUploader.filesExtensionsAllowed.split(",").includes(e.toLowerCase())}checkDisallowedExtensions(){if(!this.dragUploader.filesExtensionsDisallowed)return!0;const e=this.file.name.split(".").pop();return this.dragUploader.filesExtensionsDisallowed.split(",").includes(e.toLowerCase())}}DragUploader.init(); \ No newline at end of file diff --git a/typo3/sysext/beuser/Resources/Private/Partials/BackendUser/PaginatedList.html b/typo3/sysext/beuser/Resources/Private/Partials/BackendUser/PaginatedList.html index f46f056c58fb938cc6f39cc266a170a325494661..b364dddbf210a237c0cde28dcfe3a345d2569621 100644 --- a/typo3/sysext/beuser/Resources/Private/Partials/BackendUser/PaginatedList.html +++ b/typo3/sysext/beuser/Resources/Private/Partials/BackendUser/PaginatedList.html @@ -20,15 +20,16 @@ <f:for each="{paginator.paginatedItems}" as="backendUser"> <tr> <td class="col-avatar"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="be_users" data-contextmenu-uid="{backendUser.uid}" title="{f:if(condition: '{backendUser.description}', then: '{backendUser.description} ')}(id={backendUser.uid})" > <backend:avatar backendUser="{backendUser.uid}" showIcon="TRUE" /> - </a> + </button> </td> <td class="col-title"> <backend:link.editRecord table="be_users" uid="{backendUser.uid}" title="{f:translate(key:'edit')}"> diff --git a/typo3/sysext/beuser/Resources/Private/Partials/BackendUserGroup/PaginatedList.html b/typo3/sysext/beuser/Resources/Private/Partials/BackendUserGroup/PaginatedList.html index be96a40452ff3bb9905e7f03c6688da9a26a0daa..8501122398ef8a6a08a3f810f8310f1bce078b1e 100644 --- a/typo3/sysext/beuser/Resources/Private/Partials/BackendUserGroup/PaginatedList.html +++ b/typo3/sysext/beuser/Resources/Private/Partials/BackendUserGroup/PaginatedList.html @@ -19,15 +19,16 @@ <f:for each="{paginator.paginatedItems}" as="backendUserGroup"> <tr> <td class="col-icon"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="be_groups" data-contextmenu-uid="{backendUserGroup.uid}" title="{f:if(condition: '{backendUserGroup.description}', then: '{backendUserGroup.description} ')}(id={backendUserGroup.uid})" > <beuser:spriteIconForRecord table="be_groups" object="{backendUserGroup}" /> - </a> + </button> </td> <td class="title"> <backend:link.editRecord table="be_groups" uid="{backendUserGroup.uid}" title="{f:translate(key:'edit')}"> diff --git a/typo3/sysext/beuser/Resources/Private/Partials/Compare/Information.html b/typo3/sysext/beuser/Resources/Private/Partials/Compare/Information.html index 2f00b70f74d70793cfac84e2877d932cc47573f3..7dc05d83d3b7186f06ef3b5b529cd05d1930524f 100644 --- a/typo3/sysext/beuser/Resources/Private/Partials/Compare/Information.html +++ b/typo3/sysext/beuser/Resources/Private/Partials/Compare/Information.html @@ -44,15 +44,16 @@ <f:for each="{groups.all}" as="group"> <tr> <td class="col-title col-responsive nowrap"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="be_groups" data-contextmenu-uid="{group.row.uid}" title="id={group.row.uid}" > <core:iconForRecord table="be_groups" row="{group.row}"/> - </a> + </button> <backend:link.editRecord table="be_groups" uid="{group.row.uid}"> {group.row.title} </backend:link.editRecord> @@ -74,15 +75,16 @@ <f:for each="{dbMounts}" as="item"> <tr> <td class="col-icon"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="pages" data-contextmenu-uid="{item.uid}" title="id={item.uid}" > <core:iconForRecord table="pages" row="{item}"/> - </a> + </button> </td> <td class="col-title">{item.title} <code>[{item.uid}]</code></td> </tr> @@ -99,15 +101,16 @@ <f:for each="{fileMounts}" as="item"> <tr> <td class="col-icon"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="sys_filemounts" data-contextmenu-uid="{item.uid}" title="id={item.uid}" > <core:iconForRecord table="sys_filemounts" row="{item}"/> - </a> + </button> </td> <td class="col-title">{item.title} <code>[{item.uid}]</code></td> </tr> @@ -144,15 +147,16 @@ <f:for each="{categories}" as="item"> <tr> <td class="col-icon"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="sys_category" data-contextmenu-uid="{item.uid}" title="id={item.uid}" > <core:iconForRecord table="sys_category" row="{item}"/> - </a> + </button> </td> <td class="col-title">{item.title} <code>[{item.uid}]</code></td> </tr> @@ -166,8 +170,9 @@ <f:if condition="{workspaces}"> <f:if condition="{workspaces.record.uid}"> <strong><f:translate key="information.defaultWorkspace" />:</strong> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="sys_workspace" data-contextmenu-uid="{workspaces.record.uid}" @@ -175,7 +180,7 @@ > <core:iconForRecord table="sys_workspaces" row="{workspaces.record}"/> {workspaces.record.title} - </a> + </button> </f:if> </f:if> </f:section> diff --git a/typo3/sysext/beuser/Resources/Private/Partials/Filemount/PaginatedList.html b/typo3/sysext/beuser/Resources/Private/Partials/Filemount/PaginatedList.html index 94a2c9e55886eb321e092532afcd14f6cf239fc4..dd4a5ac25e0116acff033da9b7c7d5ed01b39e5f 100644 --- a/typo3/sysext/beuser/Resources/Private/Partials/Filemount/PaginatedList.html +++ b/typo3/sysext/beuser/Resources/Private/Partials/Filemount/PaginatedList.html @@ -22,15 +22,16 @@ <f:for each="{paginator.paginatedItems}" as="fileMount"> <tr> <td class="col-icon"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="sys_filemounts" data-contextmenu-uid="{fileMount.uid}" title="{f:if(condition: '{fileMount.title}', then: '{fileMount.title} ')}(id={fileMount.uid})" > <beuser:spriteIconForRecord table="sys_filemounts" object="{fileMount}" /> - </a> + </button> </td> <td class="col-title">{fileMount.title}</td> <td>{fileMount.description}</td> diff --git a/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/Compare.html b/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/Compare.html index b7bde5fc1eadd34e78605b649b5dc46f8befb793..50b971d5cbfd8a74fccf23138b229be75c01cf32 100644 --- a/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/Compare.html +++ b/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/Compare.html @@ -38,14 +38,15 @@ <th><f:translate key="avatar" /></th> <f:for each="{compareUserList}" as="compareData"> <td> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="be_users" data-contextmenu-uid="{compareData.user.uid}" title="id={compareData.user.uid}"> <backend:avatar backendUser="{compareData.user.uid}" showIcon="true" /> - </a> + </button> </td> </f:for> </tr> diff --git a/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/List.html b/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/List.html index a8c3a652c8d1b8d2b44f7dffdaf85651aec394bb..147f19e76a69879acac39a30479db7334f92da59 100644 --- a/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/List.html +++ b/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/List.html @@ -18,15 +18,16 @@ <f:for each="{compareUserList}" as="compareUser"> <tr> <td class="col-avatar"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="be_users" data-contextmenu-uid="{compareUser.uid}" title="id={compareUser.uid}" > <backend:avatar backendUser="{compareUser.uid}" showIcon="TRUE" /> - </a> + </button> </td> <td class="col-title"> <backend:link.editRecord table="be_users" uid="{compareUser.uid}" title="{f:translate(key:'edit')}"> diff --git a/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/Online.html b/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/Online.html index 96c8637cfe4eeca7d43e110da228ce9ed0ad764b..b276f463335e6f51146e5fb720d22433cee1cbf8 100644 --- a/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/Online.html +++ b/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/Online.html @@ -27,15 +27,16 @@ <f:if condition="{it.isFirst}"> <f:then> <td class="col-avatar"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="be_users" data-contextmenu-uid="{onlineUser.backendUser.uid}" title="{f:if(condition: '{onlineUser.backendUser.description}', then: '{onlineUser.backendUser.description} ')}(id={onlineUser.backendUser.uid})" > <backend:avatar backendUser="{onlineUser.backendUser.uid}" showIcon="true" /> - </a> + </button> </td> <td class="col-title"> <b>{onlineUser.backendUser.userName}</b> diff --git a/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/Show.html b/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/Show.html index a6bd288eb4745745d5fd2eb1942f3ae66c40431c..d0a4fb40bb7e972b43a2463be26d06908ab21a49 100644 --- a/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/Show.html +++ b/typo3/sysext/beuser/Resources/Private/Templates/BackendUser/Show.html @@ -25,15 +25,16 @@ <tr> <th>{f:translate(key:'realName')}</th> <td class="col-title"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="be_users" data-contextmenu-uid="{data.user.uid}" title="id={data.user.uid}" > <backend:avatar backendUser="{data.user.uid}" showIcon="true"/> - </a> + </button> {data.user.realName} </td> </tr> diff --git a/typo3/sysext/beuser/Resources/Private/Templates/BackendUserGroup/List.html b/typo3/sysext/beuser/Resources/Private/Templates/BackendUserGroup/List.html index 0627a08c539c3150d91b39b38cc3d9ef07f91f73..9cf71b38e1e820b01646e20ecac927ea7967ebc3 100644 --- a/typo3/sysext/beuser/Resources/Private/Templates/BackendUserGroup/List.html +++ b/typo3/sysext/beuser/Resources/Private/Templates/BackendUserGroup/List.html @@ -18,15 +18,16 @@ <f:for each="{compareGroupList}" as="compareGroup"> <tr> <td class="col-icon"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="be_usergroup" data-contextmenu-uid="{compareGroup.uid}" title="id={compareGroup.uid}" > <core:iconForRecord table="be_groups" row="{compareGroup}"/> - </a> + </button> </td> <td class="col-title"> {compareGroup.title} diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Impexp/ExportCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Impexp/ExportCest.php index c7215b131f2aaf67af80ae7ef565c20a5bd08b6b..c88539412444e0c6da8491fc6d01732cfe954014 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Impexp/ExportCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Impexp/ExportCest.php @@ -91,7 +91,7 @@ final class ExportCest extends AbstractCest $rootPageTitle = 'New TYPO3 site'; $recordPageTitle = 'elements t3editor'; $recordTable = '#recordlist-tx_styleguide_elements_t3editor'; - $recordIcon = 'tr:first-child a[data-contextmenu-trigger]'; + $recordIcon = 'tr:first-child button[data-contextmenu-trigger]'; $pageTree->openPath(['styleguide TCA demo', $recordPageTitle]); $I->switchToContentFrame(); @@ -234,7 +234,7 @@ final class ExportCest extends AbstractCest $rootPage = '#typo3-pagetree-treeContainer [role="treeitem"][data-id="0"] .node-contentlabel'; $rootPageTitle = 'New TYPO3 site'; $sysLanguageTable = '#recordlist-be_groups'; - $sysLanguageIcon = 'tr:first-child a[data-contextmenu-trigger]'; + $sysLanguageIcon = 'tr:first-child button[data-contextmenu-trigger]'; $tabExport = 'a[href="#export-filepreset"]'; $contentExport = '#export-filepreset'; $buttonSaveToFile = 'tx_impexp[save_export]'; diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Impexp/UsersCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Impexp/UsersCest.php index 79f9faa0f365da6b25261ac0eee11e2c5b4392b5..9c52addb601fff8cacba4e83f34f284a7a72b6ae 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Impexp/UsersCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Impexp/UsersCest.php @@ -197,7 +197,7 @@ final class UsersCest extends AbstractCest $I->waitForElementVisible($this->inModuleHeader . ' [name=BackendUserModuleMenu]'); $I->selectOption($this->inModuleHeader . ' [name=BackendUserModuleMenu]', ['text' => 'Backend user groups']); $I->waitForText('Backend user groups'); - $I->click('//table/tbody/tr[descendant::a[@data-contextmenu-uid="' . $userGroupId . '"]]/td[2]/a'); + $I->click('//table/tbody/tr[descendant::button[@data-contextmenu-uid="' . $userGroupId . '"]]/td[2]/a'); $I->waitForElementVisible('#EditDocumentController'); $I->click('//form[@id="EditDocumentController"]//ul/li[2]/a'); @@ -230,7 +230,7 @@ final class UsersCest extends AbstractCest $I->waitForElementVisible($this->inModuleHeader . ' [name=BackendUserModuleMenu]'); $I->selectOption($this->inModuleHeader . ' [name=BackendUserModuleMenu]', ['text' => 'Backend users']); $I->waitForElement('#typo3-backend-user-list'); - $I->click('//table[@id="typo3-backend-user-list"]/tbody/tr[descendant::a[@data-contextmenu-uid="' . $userId . '"]]//a[@title="Edit"]'); + $I->click('//table[@id="typo3-backend-user-list"]/tbody/tr[descendant::button[@data-contextmenu-uid="' . $userId . '"]]//a[@title="Edit"]'); $I->waitForElement('#EditDocumentController'); $I->click('//form[@id="EditDocumentController"]//ul/li[5]/a'); $I->waitForElementVisible($codeMirrorSelector); diff --git a/typo3/sysext/reactions/Resources/Private/Templates/Management/Overview.html b/typo3/sysext/reactions/Resources/Private/Templates/Management/Overview.html index 172baaf844194c555e9b8c12bcd9eeed89dc837a..c28f784a10429f03df9b3bb767b104ef01c1f238 100644 --- a/typo3/sysext/reactions/Resources/Private/Templates/Management/Overview.html +++ b/typo3/sysext/reactions/Resources/Private/Templates/Management/Overview.html @@ -127,15 +127,16 @@ <f:if condition="{reactionTypes.{reaction.type}}"> <f:then> <td class="col-icon"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="sys_reaction" data-contextmenu-uid="{reaction.uid}" title="{reaction.name}" > <core:iconForRecord table="sys_reaction" row="{reactionRecord}" /> - </a> + </button> </td> <td> <be:link.editRecord diff --git a/typo3/sysext/sys_note/Resources/Private/Templates/List.html b/typo3/sysext/sys_note/Resources/Private/Templates/List.html index 92ca047982d9c057b9ac5f7ed1bcd8409d7c0f71..1e1d2f3a7efa7151fd4f71f8f0d15551aeea8195 100644 --- a/typo3/sysext/sys_note/Resources/Private/Templates/List.html +++ b/typo3/sysext/sys_note/Resources/Private/Templates/List.html @@ -9,15 +9,15 @@ <div class="note note-category-{note.category}"> <div class="note-header"> <div class="note-header-bar"> - <a - href="#" - class="note-icon" + <button + type="button" + class="btn btn-link p-0 note-icon" data-contextmenu-trigger="click" data-contextmenu-table="sys_note" data-contextmenu-uid="{note.uid}" > <core:icon identifier="sysnote-type-{note.category}" /> - </a> + </button> <span class="note-author"> <f:translate key="LLL:EXT:sys_note/Resources/Private/Language/locallang.xlf:author" />: <f:if condition="{note.authorDisabled} || {note.authorDeleted} || !{note.authorUsername}"> diff --git a/typo3/sysext/webhooks/Resources/Private/Templates/Management/Overview.html b/typo3/sysext/webhooks/Resources/Private/Templates/Management/Overview.html index a0f3fb8e780fb6d70f1b086b9e442c6e6866986e..e458c401ff5dedde49c5d8d16c466c90b9473d48 100644 --- a/typo3/sysext/webhooks/Resources/Private/Templates/Management/Overview.html +++ b/typo3/sysext/webhooks/Resources/Private/Templates/Management/Overview.html @@ -82,15 +82,16 @@ <f:if condition="{webhookTypes.{webhook.webhookType.identifier}}"> <f:then> <td class="col-icon"> - <a - href="#" + <button + type="button" + class="btn btn-link p-0" data-contextmenu-trigger="click" data-contextmenu-table="sys_webhook" data-contextmenu-uid="{webhook.uid}" title="{webhook.name}" > <core:iconForRecord table="sys_webhook" row="{webhook.row}" /> - </a> + </button> </td> <td> <be:link.editRecord