/*
 * This file is part of the TYPO3 CMS project.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */
import"bootstrap";import $ from"jquery";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxQueue from"@typo3/install/ajax/ajax-queue.js";import Router from"@typo3/install/router.js";class ExtensionScanner extends AbstractInteractableModule{constructor(){super(...arguments),this.listOfAffectedRestFileHashes=[],this.selectorExtensionContainer=".t3js-extensionScanner-extension",this.selectorNumberOfFiles=".t3js-extensionScanner-number-of-files",this.selectorScanSingleTrigger=".t3js-extensionScanner-scan-single",this.selectorExtensionScanButton=".t3js-extensionScanner-scan-all"}initialize(e){this.currentModal=e,this.getData(),e.on("show.bs.collapse",this.selectorExtensionContainer,(e=>{const n=$(e.currentTarget);if(void 0===n.data("scanned")){const e=n.data("extension");this.scanSingleExtension(e),n.data("scanned",!0)}})).on("typo3-modal-hide",(()=>{AjaxQueue.flush()})).on("click",this.selectorScanSingleTrigger,(e=>{e.preventDefault();const n=$(e.currentTarget).closest(this.selectorExtensionContainer).data("extension");this.scanSingleExtension(n)})).on("click",this.selectorExtensionScanButton,(n=>{n.preventDefault(),this.setModalButtonsState(!1);const t=e.find(this.selectorExtensionContainer);this.scanAll(t)}))}getData(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("extensionScannerGetData")).get().then((async n=>{const t=await n.resolve();!0===t.success?(e.empty().append(t.html),Modal.setButtons(t.buttons)):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(n=>{Router.handleAjaxError(n,e)}))}getExtensionSelector(e){return this.selectorExtensionContainer+"-"+e}async scanAll(e){this.findInModal(this.selectorExtensionContainer).removeClass("panel-danger panel-warning panel-success").find(".panel-progress-bar").css("width",0).attr("aria-valuenow",0).find("span").text("0%"),this.setProgressForAll();const n=$.map(e,(async e=>{const n=$(e),t=n.data("extension");try{await this.scanSingleExtension(t)}finally{n.data("scanned",!0)}}));try{await Promise.allSettled(n)}finally{this.setModalButtonsState(!0),Notification.success("Scan finished","All extensions have been scanned.");try{const e=await new AjaxRequest(Router.getUrl()).post({install:{action:"extensionScannerMarkFullyScannedRestFiles",token:this.getModuleContent().data("extension-scanner-mark-fully-scanned-rest-files-token"),hashes:Array.from(new Set(this.listOfAffectedRestFileHashes))}}),n=await e.resolve();!0===n.success&&Notification.success("Marked not affected files","Marked "+n.markedAsNotAffected+" ReST files as not affected.")}catch(e){Router.handleAjaxError(e,this.getModalBody())}}}setStatusMessageForScan(e,n,t){this.findInModal(this.getExtensionSelector(e)).find(this.selectorNumberOfFiles).text("Checked "+n+" of "+t+" files")}setProgressForScan(e,n,t){const s=n/t*100;this.findInModal(this.getExtensionSelector(e)).find(".panel-progress-bar").css("width",s+"%").attr("aria-valuenow",s).find("span").text(s+"%")}setProgressForAll(){const e=this.findInModal(this.selectorExtensionContainer).length,n=this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-success").length+this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-warning").length+this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-danger").length,t=n/e*100,s=n+" of "+e+" scanned",a=this.findInModal(".t3js-extensionScanner-progress-all-extension");a.attr("aria-valuenow",String(t)).attr("aria-label",s);a.find(".progress-bar").css("width",t+"%").text(s)}async scanSingleExtension(e){const n=this.getModuleContent().data("extension-scanner-files-token"),t=this.getModalBody(),s=this.findInModal(this.getExtensionSelector(e));let a=!1;s.addClass("panel-default"),s.removeClass("panel-danger panel-warning panel-success t3js-extensionscan-finished"),s.data("hasRun","true"),s.find(".t3js-extensionScanner-scan-single").text("Scanning...").attr("disabled","disabled"),s.find(".t3js-extensionScanner-extension-body-loc").empty().text("0"),s.find(".t3js-extensionScanner-extension-body-ignored-files").empty().text("0"),s.find(".t3js-extensionScanner-extension-body-ignored-lines").empty().text("0"),this.setProgressForAll();try{const i=await new AjaxRequest(Router.getUrl()).post({install:{action:"extensionScannerFiles",token:n,extension:e}}),o=await i.resolve();if(!0===o.success&&Array.isArray(o.files)){const n=o.files.length;if(n<=0)return void Notification.warning("No files found","The extension "+e+" contains no scannable files");this.setStatusMessageForScan(e,0,n),s.find(".t3js-extensionScanner-extension-body").text(""),s.addClass("panel-has-progress");let i=0;const r=o.files.map((o=>new Promise(((r,l)=>{AjaxQueue.add({method:"POST",data:{install:{action:"extensionScannerScanFile",token:this.getModuleContent().data("extension-scanner-scan-file-token"),extension:e,file:o}},url:Router.getUrl(),onfulfilled:async l=>{const c=await l.resolve();if(i++,this.setStatusMessageForScan(e,i,n),this.setProgressForScan(e,i,n),c.success&&Array.isArray(c.matches)&&c.matches.forEach((e=>{a=!0;const n=t.find("#t3js-extensionScanner-file-hit-template").find(".panel").clone();n.find(".t3js-extensionScanner-hit-file-panel-head").attr("href","#collapse"+e.uniqueId),n.find(".t3js-extensionScanner-hit-file-panel-body").attr("id","collapse"+e.uniqueId),n.find(".t3js-extensionScanner-hit-filename").text(o),n.find(".t3js-extensionScanner-hit-message").text(e.message),"strong"===e.indicator?n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge badge-danger" title="Reliable match, false positive unlikely">strong</span>'):n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge badge-warning" title="Probable match, but can be a false positive">weak</span>'),!0===e.silenced&&n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge badge-info" title="Match has been annotated by extension author as false positive match">silenced</span>'),n.find(".t3js-extensionScanner-hit-file-lineContent").empty().text(e.lineContent),n.find(".t3js-extensionScanner-hit-file-line").empty().text(e.line+": "),Array.isArray(e.restFiles)&&e.restFiles.forEach((e=>{const s=t.find("#t3js-extensionScanner-file-hit-rest-template").find(".panel").clone();s.find(".t3js-extensionScanner-hit-rest-panel-head").attr("href","#collapse"+e.uniqueId),s.find(".t3js-extensionScanner-hit-rest-panel-head .badge").empty().text(e.version),s.find(".t3js-extensionScanner-hit-rest-panel-body").attr("id","collapse"+e.uniqueId),s.find(".t3js-extensionScanner-hit-rest-headline").text(e.headline),s.find(".t3js-extensionScanner-hit-rest-body").text(e.content),s.addClass("panel-"+e.class),n.find(".t3js-extensionScanner-hit-file-rest-container").append(s),this.listOfAffectedRestFileHashes.push(e.file_hash)}));const i=n.find(".panel-breaking, .t3js-extensionScanner-hit-file-rest-container").length>0?"panel-danger":"panel-warning";n.addClass(i),n.removeClass("panel-default"),s.find(".t3js-extensionScanner-extension-body").removeClass("hide").append(n),s.removeClass("panel-default"),"panel-danger"===i&&(s.removeClass("panel-warning"),s.addClass(i)),"panel-warning"!==i||s.hasClass("panel-danger")||s.addClass(i)})),c.success){const e=parseInt(s.find(".t3js-extensionScanner-extension-body-loc").text(),10);if(s.find(".t3js-extensionScanner-extension-body-loc").empty().text(e+c.effectiveCodeLines),c.isFileIgnored){const e=parseInt(s.find(".t3js-extensionScanner-extension-body-ignored-files").text(),10);s.find(".t3js-extensionScanner-extension-body-ignored-files").empty().text(e+1)}const n=parseInt(s.find(".t3js-extensionScanner-extension-body-ignored-lines").text(),10);s.find(".t3js-extensionScanner-extension-body-ignored-lines").empty().text(n+c.ignoredLines)}r()},onrejected:t=>{l(),i+=1,this.setStatusMessageForScan(e,i,n),this.setProgressForScan(e,i,n),s.removeClass("panel-has-progress"),this.setProgressForAll(),console.error(t)}})}))));await Promise.allSettled(r),a||(s.removeClass("panel-default"),s.addClass("panel-success")),s.addClass("t3js-extensionscan-finished"),s.removeClass("panel-has-progress"),this.setProgressForAll(),s.find(".t3js-extensionScanner-scan-single").text("Rescan").attr("disabled",null)}else Notification.error("Oops, an error occurred","Please look at the browser console output for details"),console.error(o)}catch(e){Router.handleAjaxError(e,t)}}}export default new ExtensionScanner;