diff --git a/Build/Sources/TypeScript/install/module/environment/environment-check.ts b/Build/Sources/TypeScript/install/module/environment/environment-check.ts
index ea9f97b34cf713f5daa5b388c04b1291be8ea46d..08ae603d472a8968a8cc57853b8013d23ddd4066 100644
--- a/Build/Sources/TypeScript/install/module/environment/environment-check.ts
+++ b/Build/Sources/TypeScript/install/module/environment/environment-check.ts
@@ -97,7 +97,9 @@ class EnvironmentCheck extends AbstractInteractableModule {
         (error: AjaxResponse): void => {
           Router.handleAjaxError(error, modalContent);
         }
-      );
+      ).finally((): void => {
+        this.setModalButtonsState(true);
+      });
   }
 }
 
diff --git a/Build/Sources/TypeScript/install/module/environment/folder-structure.ts b/Build/Sources/TypeScript/install/module/environment/folder-structure.ts
index e189b92fefdf195f4a3ed73a9fd00dc290c2e5d4..ea92389ca50ed1eb4b7f23410cf1c9e77fbd9cbf 100644
--- a/Build/Sources/TypeScript/install/module/environment/folder-structure.ts
+++ b/Build/Sources/TypeScript/install/module/environment/folder-structure.ts
@@ -64,9 +64,6 @@ class FolderStructure extends AbstractInteractableModule {
     const modalContent = this.getModalBody();
     const $errorBadge = $(this.selectorGridderBadge);
     $errorBadge.text('').hide();
-    modalContent.find(this.selectorOutputContainer).empty().append(
-      ProgressBar.render(Severity.loading, 'Loading...', ''),
-    );
     (new AjaxRequest(Router.getUrl('folderStructureGetStatus')))
       .get({ cache: 'no-cache' })
       .then(
@@ -113,7 +110,9 @@ class FolderStructure extends AbstractInteractableModule {
         (error: AjaxResponse): void => {
           Router.handleAjaxError(error, modalContent);
         }
-      );
+      ).finally((): void => {
+        this.setModalButtonsState(true);
+      });
   }
 
   private fix(): void {
diff --git a/Build/Sources/TypeScript/install/module/environment/image-processing.ts b/Build/Sources/TypeScript/install/module/environment/image-processing.ts
index 19294f862e4324c88701a211be119614d6429e78..ae9fb1136bb34e7c74cddd1e5c5e000e126ceb43 100644
--- a/Build/Sources/TypeScript/install/module/environment/image-processing.ts
+++ b/Build/Sources/TypeScript/install/module/environment/image-processing.ts
@@ -76,7 +76,6 @@ class ImageProcessing extends AbstractInteractableModule {
 
   private runTests(): void {
     const modalContent = this.getModalBody();
-    const $triggerButton = this.findInModal(this.selectorExecuteTrigger);
     this.setModalButtonsState(false);
 
     const $twinImageTemplate = this.findInModal(this.selectorTwinImageTemplate);
@@ -128,7 +127,7 @@ class ImageProcessing extends AbstractInteractableModule {
     });
 
     Promise.all(promises).then((): void => {
-      $triggerButton.removeClass('disabled').prop('disabled', false);
+      this.setModalButtonsState(true);
     });
   }
 }
diff --git a/Build/Sources/TypeScript/install/module/maintenance/clear-tables.ts b/Build/Sources/TypeScript/install/module/maintenance/clear-tables.ts
index 23b3598a663630297a21861b58dfea70015a8667..6c97b3aa3b82f4845ebd9d33ab1b7f78f2e17bcd 100644
--- a/Build/Sources/TypeScript/install/module/maintenance/clear-tables.ts
+++ b/Build/Sources/TypeScript/install/module/maintenance/clear-tables.ts
@@ -94,7 +94,9 @@ class ClearTables extends AbstractInteractableModule {
         (error: AjaxResponse): void => {
           Router.handleAjaxError(error, modalContent);
         }
-      );
+      ).finally((): void => {
+        this.setModalButtonsState(true);
+      });
   }
 
   private clear(table: string): void {
diff --git a/Build/Sources/TypeScript/install/module/maintenance/clear-typo3temp-files.ts b/Build/Sources/TypeScript/install/module/maintenance/clear-typo3temp-files.ts
index a2a00c982099169d39f1a6aee991febe50444a48..fadaedbf5e56afa0106eb29686a867166ff71378 100644
--- a/Build/Sources/TypeScript/install/module/maintenance/clear-typo3temp-files.ts
+++ b/Build/Sources/TypeScript/install/module/maintenance/clear-typo3temp-files.ts
@@ -123,7 +123,9 @@ class ClearTypo3tempFiles extends AbstractInteractableModule {
         (error: AjaxResponse): void => {
           Router.handleAjaxError(error, modalContent);
         }
-      );
+      ).finally((): void => {
+        this.setModalButtonsState(true);
+      });
   }
 }
 
diff --git a/Build/Sources/TypeScript/install/module/maintenance/database-analyzer.ts b/Build/Sources/TypeScript/install/module/maintenance/database-analyzer.ts
index 25fe3d2faef4c51880eb91923d7692d8155c0bc0..135b727ac8fd7683720d6bde9816bc2c7f0289c2 100644
--- a/Build/Sources/TypeScript/install/module/maintenance/database-analyzer.ts
+++ b/Build/Sources/TypeScript/install/module/maintenance/database-analyzer.ts
@@ -163,10 +163,14 @@ class DatabaseAnalyzer extends AbstractInteractableModule {
             }
           } else {
             Notification.error('Something went wrong', 'The request was not processed successfully. Please check the browser\'s console and TYPO3\'s log.');
+            this.setModalButtonState(analyzeTrigger, true);
+            this.setModalButtonState(executeTrigger, false);
           }
         },
         (error: AjaxResponse): void => {
           Router.handleAjaxError(error, modalContent);
+          this.setModalButtonState(analyzeTrigger, true);
+          this.setModalButtonState(executeTrigger, false);
         }
       );
   }
@@ -205,7 +209,10 @@ class DatabaseAnalyzer extends AbstractInteractableModule {
         (error: AjaxResponse): void => {
           Router.handleAjaxError(error, modalContent);
         }
-      );
+      ).finally((): void => {
+        this.setModalButtonState(this.getModalFooter().find(this.selectorAnalyzeTrigger), true);
+        this.setModalButtonState(this.getModalFooter().find(this.selectorExecuteTrigger), false);
+      });
   }
 }
 
diff --git a/Build/Sources/TypeScript/install/module/upgrade/extension-compat-tester.ts b/Build/Sources/TypeScript/install/module/upgrade/extension-compat-tester.ts
index 0bcdc7b98f588f04f8323219863554f6f068cf52..45ee70f50b27bbd2400d454e9c30137d9d1cea7a 100644
--- a/Build/Sources/TypeScript/install/module/upgrade/extension-compat-tester.ts
+++ b/Build/Sources/TypeScript/install/module/upgrade/extension-compat-tester.ts
@@ -94,17 +94,19 @@ class ExtensionCompatTester extends AbstractInteractableModule {
             });
           } else {
             Notification.error('Something went wrong', 'The request was not processed successfully. Please check the browser\'s console and TYPO3\'s log.');
+            this.unlockModal();
           }
         },
         (error: AjaxResponse): void => {
           Router.handleAjaxError(error, modalContent);
+          this.unlockModal();
         }
       );
   }
 
   private unlockModal(): void {
     this.findInModal(this.selectorOutputContainer).find('.alert-loading').remove();
-    this.findInModal(this.selectorCheckTrigger).removeClass('disabled').prop('disabled', false);
+    this.setModalButtonsState(true);
   }
 
   private renderFailureMessages(scope: string, brokenExtensions: Array<BrokenExtension>, $outputContainer: JQuery): void {
diff --git a/Build/Sources/TypeScript/install/module/upgrade/extension-scanner.ts b/Build/Sources/TypeScript/install/module/upgrade/extension-scanner.ts
index 8ad149cd4ccd5ac28a2c958bf0e9553a7c8958f7..86aed8c44f2b2a438dc321259a4725ab4372debc 100644
--- a/Build/Sources/TypeScript/install/module/upgrade/extension-scanner.ts
+++ b/Build/Sources/TypeScript/install/module/upgrade/extension-scanner.ts
@@ -110,7 +110,7 @@ class ExtensionScanner extends AbstractInteractableModule {
     return this.selectorExtensionContainer + '-' + extension;
   }
 
-  private scanAll($extensions: JQuery): void {
+  private async scanAll($extensions: JQuery): Promise<void> {
     this.findInModal(this.selectorExtensionContainer)
       .removeClass('panel-danger panel-warning panel-success')
       .find('.panel-progress-bar')
@@ -119,12 +119,40 @@ class ExtensionScanner extends AbstractInteractableModule {
       .find('span')
       .text('0%');
     this.setProgressForAll();
-    $extensions.each((index: number, element: Element): void => {
-      const $me: JQuery = $(element);
-      const extension = $me.data('extension');
-      this.scanSingleExtension(extension);
-      $me.data('scanned', true);
+
+    const scannerPromises = $.map($extensions, async (element: HTMLElement) => {
+      const $extension = $(element);
+      const extension = $extension.data('extension');
+      try {
+        await this.scanSingleExtension(extension);
+      } finally {
+        $extension.data('scanned', true);
+      }
     });
+
+    try {
+      await Promise.allSettled(scannerPromises);
+    } finally {
+      this.setModalButtonsState(true);
+
+      Notification.success('Scan finished', 'All extensions have been scanned.');
+
+      try {
+        const response = 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)),
+          },
+        });
+        const data = await response.resolve();
+        if (data.success === true) {
+          Notification.success('Marked not affected files', 'Marked ' + data.markedAsNotAffected + ' ReST files as not affected.');
+        }
+      } catch (error: unknown) {
+        Router.handleAjaxError(error as AjaxResponse, this.getModalBody());
+      }
+    }
   }
 
   private setStatusMessageForScan(extension: string, doneFiles: number, numberOfFiles: number): void {
@@ -153,41 +181,17 @@ class ExtensionScanner extends AbstractInteractableModule {
       + '.t3js-extensionscan-finished.panel-danger').length;
     const numberOfScannedExtensions: number = numberOfSuccess + numberOfWarning + numberOfError;
     const percent: number = (numberOfScannedExtensions / numberOfExtensions) * 100;
-    const modalContent: JQuery = this.getModalBody();
     this.findInModal('.t3js-extensionScanner-progress-all-extension .progress-bar')
       .css('width', percent + '%')
       .attr('aria-valuenow', percent)
       .find('span')
       .text(numberOfScannedExtensions + ' of ' + numberOfExtensions + ' scanned');
-
-    if (numberOfScannedExtensions === numberOfExtensions) {
-      this.findInModal(this.selectorExtensionScanButton).removeClass('disabled').prop('disabled', false);
-      Notification.success('Scan finished', 'All extensions have been scanned.');
-
-      (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)),
-        },
-      }).then(
-        async (response: AjaxResponse): Promise<void> => {
-          const data = await response.resolve();
-          if (data.success === true) {
-            Notification.success('Marked not affected files', 'Marked ' + data.markedAsNotAffected + ' ReST files as not affected.');
-          }
-        },
-        (error: AjaxResponse): void => {
-          Router.handleAjaxError(error, modalContent);
-        }
-      );
-    }
   }
 
   /**
    * Handle a single extension scan
    */
-  private scanSingleExtension(extension: string): void {
+  private async scanSingleExtension(extension: string): Promise<void> {
     const executeToken = this.getModuleContent().data('extension-scanner-files-token');
     const modalContent = this.getModalBody();
     const $extensionContainer = this.findInModal(this.getExtensionSelector(extension));
@@ -202,143 +206,146 @@ class ExtensionScanner extends AbstractInteractableModule {
     $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').empty().text('0');
     $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').empty().text('0');
     this.setProgressForAll();
-    (new AjaxRequest(Router.getUrl())).post({
-      install: {
-        action: 'extensionScannerFiles',
-        token: executeToken,
-        extension: extension,
-      },
-    }).then(
-      async (response: AjaxResponse): Promise<void> => {
-        const data: ExtensionScannerFilesResponse = await response.resolve();
-        if (data.success === true && Array.isArray(data.files)) {
-          const numberOfFiles = data.files.length;
-          if (numberOfFiles <= 0) {
-            Notification.warning('No files found', 'The extension ' + extension + ' contains no scannable files');
-            return;
-          }
 
-          this.setStatusMessageForScan(extension, 0, numberOfFiles);
-          $extensionContainer.find('.t3js-extensionScanner-extension-body').text('');
-          $extensionContainer.addClass('panel-has-progress');
-          let doneFiles = 0;
-          data.files.forEach((file: string): void => {
-            AjaxQueue.add({
-              method: 'POST',
-              data: {
-                install: {
-                  action: 'extensionScannerScanFile',
-                  token: this.getModuleContent().data('extension-scanner-scan-file-token'),
-                  extension: extension,
-                  file: file,
-                },
+    try {
+      const response = await (new AjaxRequest(Router.getUrl())).post({
+        install: {
+          action: 'extensionScannerFiles',
+          token: executeToken,
+          extension: extension,
+        },
+      });
+      const data: ExtensionScannerFilesResponse = await response.resolve();
+      if (data.success === true && Array.isArray(data.files)) {
+        const numberOfFiles = data.files.length;
+        if (numberOfFiles <= 0) {
+          Notification.warning('No files found', 'The extension ' + extension + ' contains no scannable files');
+          return;
+        }
+
+        this.setStatusMessageForScan(extension, 0, numberOfFiles);
+        $extensionContainer.find('.t3js-extensionScanner-extension-body').text('');
+        $extensionContainer.addClass('panel-has-progress');
+        let doneFiles = 0;
+        const filePromises = data.files.map((file: string): Promise<void> => new Promise<void>((resolve, reject): void => {
+          AjaxQueue.add({
+            method: 'POST',
+            data: {
+              install: {
+                action: 'extensionScannerScanFile',
+                token: this.getModuleContent().data('extension-scanner-scan-file-token'),
+                extension: extension,
+                file: file,
               },
-              url: Router.getUrl(),
-              onfulfilled: async (response: AjaxResponse): Promise<void> => {
-                const fileData: ExtensionScannerScanFileResponse = await response.resolve();
-                doneFiles++;
-                this.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
-                this.setProgressForScan(extension, doneFiles, numberOfFiles);
-                if (fileData.success && Array.isArray(fileData.matches)) {
-                  fileData.matches.forEach((match: Match): void => {
-                    hitFound = true;
-                    const aMatch: JQuery = modalContent.find(hitTemplate).find('.panel').clone();
-                    aMatch.find('.t3js-extensionScanner-hit-file-panel-head').attr('href', '#collapse' + match.uniqueId);
-                    aMatch.find('.t3js-extensionScanner-hit-file-panel-body').attr('id', 'collapse' + match.uniqueId);
-                    aMatch.find('.t3js-extensionScanner-hit-filename').text(file);
-                    aMatch.find('.t3js-extensionScanner-hit-message').text(match.message);
-                    if (match.indicator === 'strong') {
-                      aMatch.find('.t3js-extensionScanner-hit-file-panel-head .badges')
-                        .append('<span class="badge badge-danger" title="Reliable match, false positive unlikely">strong</span>');
-                    } else {
-                      aMatch.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>');
-                    }
-                    if (match.silenced === true) {
-                      aMatch.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>');
-                    }
-                    aMatch.find('.t3js-extensionScanner-hit-file-lineContent').empty().text(match.lineContent);
-                    aMatch.find('.t3js-extensionScanner-hit-file-line').empty().text(match.line + ': ');
-                    if (Array.isArray(match.restFiles)) {
-                      match.restFiles.forEach((restFile: RestFile): void => {
-                        const aRest = modalContent.find(restTemplate).find('.panel').clone();
-                        aRest.find('.t3js-extensionScanner-hit-rest-panel-head').attr('href', '#collapse' + restFile.uniqueId);
-                        aRest.find('.t3js-extensionScanner-hit-rest-panel-head .badge').empty().text(restFile.version);
-                        aRest.find('.t3js-extensionScanner-hit-rest-panel-body').attr('id', 'collapse' + restFile.uniqueId);
-                        aRest.find('.t3js-extensionScanner-hit-rest-headline').text(restFile.headline);
-                        aRest.find('.t3js-extensionScanner-hit-rest-body').text(restFile.content);
-                        aRest.addClass('panel-' + restFile.class);
-                        aMatch.find('.t3js-extensionScanner-hit-file-rest-container').append(aRest);
-                        this.listOfAffectedRestFileHashes.push(restFile.file_hash);
-                      });
-                    }
-                    const panelClass =
-                      aMatch.find('.panel-breaking, .t3js-extensionScanner-hit-file-rest-container').length > 0
-                        ? 'panel-danger'
-                        : 'panel-warning';
-                    aMatch.addClass(panelClass);
-                    aMatch.removeClass('panel-default');
-                    $extensionContainer.find('.t3js-extensionScanner-extension-body').removeClass('hide').append(aMatch);
-                    $extensionContainer.removeClass('panel-default');
-                    if (panelClass === 'panel-danger') {
-                      $extensionContainer.removeClass('panel-warning');
-                      $extensionContainer.addClass(panelClass);
-                    }
-                    if (panelClass === 'panel-warning' && !$extensionContainer.hasClass('panel-danger')) {
-                      $extensionContainer.addClass(panelClass);
-                    }
-                  });
-                }
-                if (fileData.success) {
-                  const currentLinesOfCode = parseInt($extensionContainer.find('.t3js-extensionScanner-extension-body-loc').text(), 10);
-                  $extensionContainer.find('.t3js-extensionScanner-extension-body-loc').empty()
-                    .text(currentLinesOfCode + fileData.effectiveCodeLines);
-                  if (fileData.isFileIgnored) {
-                    const currentIgnoredFiles = parseInt(
-                      $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').text(),
-                      10,
-                    );
-                    $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').empty().text(currentIgnoredFiles + 1);
+            },
+            url: Router.getUrl(),
+            onfulfilled: async (response: AjaxResponse): Promise<void> => {
+              const fileData: ExtensionScannerScanFileResponse = await response.resolve();
+              doneFiles++;
+              this.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
+              this.setProgressForScan(extension, doneFiles, numberOfFiles);
+              if (fileData.success && Array.isArray(fileData.matches)) {
+                fileData.matches.forEach((match: Match): void => {
+                  hitFound = true;
+                  const aMatch: JQuery = modalContent.find(hitTemplate).find('.panel').clone();
+                  aMatch.find('.t3js-extensionScanner-hit-file-panel-head').attr('href', '#collapse' + match.uniqueId);
+                  aMatch.find('.t3js-extensionScanner-hit-file-panel-body').attr('id', 'collapse' + match.uniqueId);
+                  aMatch.find('.t3js-extensionScanner-hit-filename').text(file);
+                  aMatch.find('.t3js-extensionScanner-hit-message').text(match.message);
+                  if (match.indicator === 'strong') {
+                    aMatch.find('.t3js-extensionScanner-hit-file-panel-head .badges')
+                      .append('<span class="badge badge-danger" title="Reliable match, false positive unlikely">strong</span>');
+                  } else {
+                    aMatch.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>');
+                  }
+                  if (match.silenced === true) {
+                    aMatch.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>');
+                  }
+                  aMatch.find('.t3js-extensionScanner-hit-file-lineContent').empty().text(match.lineContent);
+                  aMatch.find('.t3js-extensionScanner-hit-file-line').empty().text(match.line + ': ');
+                  if (Array.isArray(match.restFiles)) {
+                    match.restFiles.forEach((restFile: RestFile): void => {
+                      const aRest = modalContent.find(restTemplate).find('.panel').clone();
+                      aRest.find('.t3js-extensionScanner-hit-rest-panel-head').attr('href', '#collapse' + restFile.uniqueId);
+                      aRest.find('.t3js-extensionScanner-hit-rest-panel-head .badge').empty().text(restFile.version);
+                      aRest.find('.t3js-extensionScanner-hit-rest-panel-body').attr('id', 'collapse' + restFile.uniqueId);
+                      aRest.find('.t3js-extensionScanner-hit-rest-headline').text(restFile.headline);
+                      aRest.find('.t3js-extensionScanner-hit-rest-body').text(restFile.content);
+                      aRest.addClass('panel-' + restFile.class);
+                      aMatch.find('.t3js-extensionScanner-hit-file-rest-container').append(aRest);
+                      this.listOfAffectedRestFileHashes.push(restFile.file_hash);
+                    });
                   }
-                  const currentIgnoredLines = parseInt(
-                    $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').text(),
+                  const panelClass =
+                    aMatch.find('.panel-breaking, .t3js-extensionScanner-hit-file-rest-container').length > 0
+                      ? 'panel-danger'
+                      : 'panel-warning';
+                  aMatch.addClass(panelClass);
+                  aMatch.removeClass('panel-default');
+                  $extensionContainer.find('.t3js-extensionScanner-extension-body').removeClass('hide').append(aMatch);
+                  $extensionContainer.removeClass('panel-default');
+                  if (panelClass === 'panel-danger') {
+                    $extensionContainer.removeClass('panel-warning');
+                    $extensionContainer.addClass(panelClass);
+                  }
+                  if (panelClass === 'panel-warning' && !$extensionContainer.hasClass('panel-danger')) {
+                    $extensionContainer.addClass(panelClass);
+                  }
+                });
+              }
+              if (fileData.success) {
+                const currentLinesOfCode = parseInt($extensionContainer.find('.t3js-extensionScanner-extension-body-loc').text(), 10);
+                $extensionContainer.find('.t3js-extensionScanner-extension-body-loc').empty()
+                  .text(currentLinesOfCode + fileData.effectiveCodeLines);
+                if (fileData.isFileIgnored) {
+                  const currentIgnoredFiles = parseInt(
+                    $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').text(),
                     10,
                   );
-                  $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').empty()
-                    .text(currentIgnoredLines + fileData.ignoredLines);
+                  $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').empty().text(currentIgnoredFiles + 1);
                 }
-                if (doneFiles === numberOfFiles) {
-                  if (!hitFound) {
-                    $extensionContainer.removeClass('panel-default');
-                    $extensionContainer.addClass('panel-success');
-                  }
-                  $extensionContainer.addClass('t3js-extensionscan-finished');
-                  $extensionContainer.removeClass('panel-has-progress');
-                  this.setProgressForAll();
-                  $extensionContainer.find('.t3js-extensionScanner-scan-single').text('Rescan').attr('disabled', null);
-                }
-              },
-              onrejected: (reason: string): void => {
-                doneFiles = doneFiles + 1;
-                this.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
-                this.setProgressForScan(extension, doneFiles, numberOfFiles);
-                $extensionContainer.removeClass('panel-has-progress');
-                this.setProgressForAll();
-                console.error(reason);
-              },
-            });
+                const currentIgnoredLines = parseInt(
+                  $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').text(),
+                  10,
+                );
+                $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').empty()
+                  .text(currentIgnoredLines + fileData.ignoredLines);
+              }
+              resolve();
+            },
+            onrejected: (reason: string): void => {
+              reject();
+              doneFiles = doneFiles + 1;
+              this.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
+              this.setProgressForScan(extension, doneFiles, numberOfFiles);
+              $extensionContainer.removeClass('panel-has-progress');
+              this.setProgressForAll();
+              console.error(reason);
+            },
           });
-        } else {
-          Notification.error('Oops, an error occurred', 'Please look at the browser console output for details');
-          console.error(data);
+        }));
+
+        await Promise.allSettled(filePromises);
+
+        if (!hitFound) {
+          $extensionContainer.removeClass('panel-default');
+          $extensionContainer.addClass('panel-success');
         }
-      },
-      (error: AjaxResponse): void => {
-        Router.handleAjaxError(error, modalContent);
+        $extensionContainer.addClass('t3js-extensionscan-finished');
+        $extensionContainer.removeClass('panel-has-progress');
+        this.setProgressForAll();
+        $extensionContainer.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(data);
       }
-    );
+    } catch (error: unknown) {
+      Router.handleAjaxError(error as AjaxResponse, modalContent);
+    }
   }
 }
 
diff --git a/Build/Sources/TypeScript/install/module/upgrade/tca-ext-tables-check.ts b/Build/Sources/TypeScript/install/module/upgrade/tca-ext-tables-check.ts
index 2b45ae87ed6beccff9ebc2aa5aa1f3542fdda0f9..8bda7ddd73e3ec7a2b51eb3d5fe62464f4e5a4a4 100644
--- a/Build/Sources/TypeScript/install/module/upgrade/tca-ext-tables-check.ts
+++ b/Build/Sources/TypeScript/install/module/upgrade/tca-ext-tables-check.ts
@@ -81,7 +81,9 @@ class TcaExtTablesCheck extends AbstractInteractableModule {
         (error: AjaxResponse): void => {
           Router.handleAjaxError(error, modalContent);
         }
-      );
+      ).finally((): void => {
+        this.setModalButtonsState(true);
+      });
   }
 }
 
diff --git a/Build/Sources/TypeScript/install/module/upgrade/tca-migrations-check.ts b/Build/Sources/TypeScript/install/module/upgrade/tca-migrations-check.ts
index b49930e3d18e9e7f2b2d250bbf48bfe40035c4a6..c7f9ad9027376817222f23f75cd1ea5a64d00d95 100644
--- a/Build/Sources/TypeScript/install/module/upgrade/tca-migrations-check.ts
+++ b/Build/Sources/TypeScript/install/module/upgrade/tca-migrations-check.ts
@@ -78,12 +78,13 @@ class TcaMigrationsCheck extends AbstractInteractableModule {
             const m4 = FlashMessage.render(Severity.error, 'Something went wrong', 'Use "Check for broken extensions"');
             modalContent.find(this.selectorOutputContainer).append(m4);
           }
-          this.setModalButtonsState(true);
         },
         (error: AjaxResponse): void => {
           Router.handleAjaxError(error, modalContent);
         }
-      );
+      ).finally((): void => {
+        this.setModalButtonsState(true);
+      });
   }
 
 }
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/environment-check.js b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/environment-check.js
index 9c8548ad3e1498231ad1866ce9ab1baf07a9207a..c16d59f56f1addbcc318077e4cb91318ec1c919e 100644
--- a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/environment-check.js
+++ b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/environment-check.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import"bootstrap";import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class EnvironmentCheck extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorGridderBadge=".t3js-environmentCheck-badge",this.selectorExecuteTrigger=".t3js-environmentCheck-execute",this.selectorOutputContainer=".t3js-environmentCheck-output"}initialize(e){this.currentModal=e,this.runTests(),e.on("click",this.selectorExecuteTrigger,(e=>{e.preventDefault(),this.runTests()}))}runTests(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=$(this.selectorGridderBadge);t.text("").hide();const r=ProgressBar.render(Severity.loading,"Loading...","");e.find(this.selectorOutputContainer).empty().append(r),new AjaxRequest(Router.getUrl("environmentCheckGetStatus")).get({cache:"no-cache"}).then((async r=>{const o=await r.resolve();e.empty().append(o.html),Modal.setButtons(o.buttons);let s=0,n=0;if(!0===o.success&&"object"==typeof o.status){for(const t of Object.values(o.status))for(const r of t){1===r.severity&&s++,2===r.severity&&n++;const t=InfoBox.render(r.severity,r.title,r.message);e.find(this.selectorOutputContainer).append(t)}n>0?t.removeClass("badge-warning").addClass("badge-danger").text(n).show():s>0&&t.removeClass("badge-error").addClass("badge-warning").text(s).show()}else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}}export default new EnvironmentCheck;
\ No newline at end of file
+import"bootstrap";import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class EnvironmentCheck extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorGridderBadge=".t3js-environmentCheck-badge",this.selectorExecuteTrigger=".t3js-environmentCheck-execute",this.selectorOutputContainer=".t3js-environmentCheck-output"}initialize(e){this.currentModal=e,this.runTests(),e.on("click",this.selectorExecuteTrigger,(e=>{e.preventDefault(),this.runTests()}))}runTests(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=$(this.selectorGridderBadge);t.text("").hide();const r=ProgressBar.render(Severity.loading,"Loading...","");e.find(this.selectorOutputContainer).empty().append(r),new AjaxRequest(Router.getUrl("environmentCheckGetStatus")).get({cache:"no-cache"}).then((async r=>{const o=await r.resolve();e.empty().append(o.html),Modal.setButtons(o.buttons);let s=0,n=0;if(!0===o.success&&"object"==typeof o.status){for(const t of Object.values(o.status))for(const r of t){1===r.severity&&s++,2===r.severity&&n++;const t=InfoBox.render(r.severity,r.title,r.message);e.find(this.selectorOutputContainer).append(t)}n>0?t.removeClass("badge-warning").addClass("badge-danger").text(n).show():s>0&&t.removeClass("badge-error").addClass("badge-warning").text(s).show()}else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)})).finally((()=>{this.setModalButtonsState(!0)}))}}export default new EnvironmentCheck;
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/folder-structure.js b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/folder-structure.js
index f9c551129b8a7ecdb4e235989385d665017d214e..7268398d45c99365b0ec6edb96e459c149859bd8 100644
--- a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/folder-structure.js
+++ b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/folder-structure.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import"bootstrap";import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class FolderStructure extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorGridderBadge=".t3js-folderStructure-badge",this.selectorOutputContainer=".t3js-folderStructure-output",this.selectorErrorContainer=".t3js-folderStructure-errors",this.selectorErrorList=".t3js-folderStructure-errors-list",this.selectorErrorFixTrigger=".t3js-folderStructure-errors-fix",this.selectorOkContainer=".t3js-folderStructure-ok",this.selectorOkList=".t3js-folderStructure-ok-list",this.selectorPermissionContainer=".t3js-folderStructure-permissions"}static removeLoadingMessage(e){e.find(".alert-loading").remove()}initialize(e){this.currentModal=e,this.getStatus(),e.on("click",this.selectorErrorFixTrigger,(e=>{e.preventDefault(),this.fix()}))}getStatus(){const e=this.getModalBody(),t=$(this.selectorGridderBadge);t.text("").hide(),e.find(this.selectorOutputContainer).empty().append(ProgressBar.render(Severity.loading,"Loading...","")),new AjaxRequest(Router.getUrl("folderStructureGetStatus")).get({cache:"no-cache"}).then((async r=>{const o=await r.resolve();if(e.empty().append(o.html),Modal.setButtons(o.buttons),!0===o.success&&Array.isArray(o.errorStatus)){let r=0;o.errorStatus.length>0?(e.find(this.selectorErrorContainer).show(),e.find(this.selectorErrorList).empty(),o.errorStatus.forEach((o=>{r++,t.text(r).show();const s=InfoBox.render(o.severity,o.title,o.message);e.find(this.selectorErrorList).append(s)}))):e.find(this.selectorErrorContainer).hide()}!0===o.success&&Array.isArray(o.okStatus)&&(o.okStatus.length>0?(e.find(this.selectorOkContainer).show(),e.find(this.selectorOkList).empty(),o.okStatus.forEach((t=>{const r=InfoBox.render(t.severity,t.title,t.message);e.find(this.selectorOkList).append(r)}))):e.find(this.selectorOkContainer).hide());let s=o.folderStructureFilePermissionStatus;e.find(this.selectorPermissionContainer).empty().append(InfoBox.render(s.severity,s.title,s.message)),s=o.folderStructureDirectoryPermissionStatus,e.find(this.selectorPermissionContainer).append(InfoBox.render(s.severity,s.title,s.message))}),(t=>{Router.handleAjaxError(t,e)}))}fix(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.findInModal(this.selectorOutputContainer),r=ProgressBar.render(Severity.loading,"Loading...","");t.empty().append(r),new AjaxRequest(Router.getUrl("folderStructureFix")).get({cache:"no-cache"}).then((async e=>{const r=await e.resolve();FolderStructure.removeLoadingMessage(t),!0===r.success&&Array.isArray(r.fixedStatus)?(r.fixedStatus.length>0?r.fixedStatus.forEach((e=>{t.append(InfoBox.render(e.severity,e.title,e.message))})):t.append(InfoBox.render(Severity.warning,"Nothing fixed","")),this.getStatus()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}}export default new FolderStructure;
\ No newline at end of file
+import"bootstrap";import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class FolderStructure extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorGridderBadge=".t3js-folderStructure-badge",this.selectorOutputContainer=".t3js-folderStructure-output",this.selectorErrorContainer=".t3js-folderStructure-errors",this.selectorErrorList=".t3js-folderStructure-errors-list",this.selectorErrorFixTrigger=".t3js-folderStructure-errors-fix",this.selectorOkContainer=".t3js-folderStructure-ok",this.selectorOkList=".t3js-folderStructure-ok-list",this.selectorPermissionContainer=".t3js-folderStructure-permissions"}static removeLoadingMessage(e){e.find(".alert-loading").remove()}initialize(e){this.currentModal=e,this.getStatus(),e.on("click",this.selectorErrorFixTrigger,(e=>{e.preventDefault(),this.fix()}))}getStatus(){const e=this.getModalBody(),t=$(this.selectorGridderBadge);t.text("").hide(),new AjaxRequest(Router.getUrl("folderStructureGetStatus")).get({cache:"no-cache"}).then((async r=>{const o=await r.resolve();if(e.empty().append(o.html),Modal.setButtons(o.buttons),!0===o.success&&Array.isArray(o.errorStatus)){let r=0;o.errorStatus.length>0?(e.find(this.selectorErrorContainer).show(),e.find(this.selectorErrorList).empty(),o.errorStatus.forEach((o=>{r++,t.text(r).show();const s=InfoBox.render(o.severity,o.title,o.message);e.find(this.selectorErrorList).append(s)}))):e.find(this.selectorErrorContainer).hide()}!0===o.success&&Array.isArray(o.okStatus)&&(o.okStatus.length>0?(e.find(this.selectorOkContainer).show(),e.find(this.selectorOkList).empty(),o.okStatus.forEach((t=>{const r=InfoBox.render(t.severity,t.title,t.message);e.find(this.selectorOkList).append(r)}))):e.find(this.selectorOkContainer).hide());let s=o.folderStructureFilePermissionStatus;e.find(this.selectorPermissionContainer).empty().append(InfoBox.render(s.severity,s.title,s.message)),s=o.folderStructureDirectoryPermissionStatus,e.find(this.selectorPermissionContainer).append(InfoBox.render(s.severity,s.title,s.message))}),(t=>{Router.handleAjaxError(t,e)})).finally((()=>{this.setModalButtonsState(!0)}))}fix(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.findInModal(this.selectorOutputContainer),r=ProgressBar.render(Severity.loading,"Loading...","");t.empty().append(r),new AjaxRequest(Router.getUrl("folderStructureFix")).get({cache:"no-cache"}).then((async e=>{const r=await e.resolve();FolderStructure.removeLoadingMessage(t),!0===r.success&&Array.isArray(r.fixedStatus)?(r.fixedStatus.length>0?r.fixedStatus.forEach((e=>{t.append(InfoBox.render(e.severity,e.title,e.message))})):t.append(InfoBox.render(Severity.warning,"Nothing fixed","")),this.getStatus()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}}export default new FolderStructure;
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/image-processing.js b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/image-processing.js
index 1a319bc58decead3b4a112cb0f309b7f997e383a..6ddbb78900e644515ff66dc0540e97dd3b59f3ed 100644
--- a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/image-processing.js
+++ b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/image-processing.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import"bootstrap";import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class ImageProcessing extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorExecuteTrigger=".t3js-imageProcessing-execute",this.selectorTestContainer=".t3js-imageProcessing-twinContainer",this.selectorTwinImageTemplate=".t3js-imageProcessing-twinImage-template",this.selectorCommandContainer=".t3js-imageProcessing-command",this.selectorCommandText=".t3js-imageProcessing-command-text",this.selectorTwinImages=".t3js-imageProcessing-images"}initialize(e){this.currentModal=e,this.getData(),e.on("click",this.selectorExecuteTrigger,(e=>{e.preventDefault(),this.runTests()}))}getData(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("imageProcessingGetData")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();!0===s.success?(e.empty().append(s.html),Modal.setButtons(s.buttons),this.runTests()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}runTests(){const e=this.getModalBody(),t=this.findInModal(this.selectorExecuteTrigger);this.setModalButtonsState(!1);const s=this.findInModal(this.selectorTwinImageTemplate),o=[];e.find(this.selectorTestContainer).each(((t,r)=>{const a=$(r),n=a.data("test"),i=InfoBox.render(Severity.loading,"Loading...","");a.empty().append(i);const c=new AjaxRequest(Router.getUrl(n)).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();if(!0===t.success){a.empty(),Array.isArray(t.status)&&t.status.forEach((e=>{const t=InfoBox.render(e.severity,e.title,e.message);a.append(t)}));const e=s.clone();if(e.removeClass("t3js-imageProcessing-twinImage-template"),!0===t.fileExists&&(e.find("img.reference").attr("src",t.referenceFile),e.find("img.result").attr("src",t.outputFile),e.find(this.selectorTwinImages).show()),Array.isArray(t.command)&&t.command.length>0){e.find(this.selectorCommandContainer).show();const s=[];t.command.forEach((e=>{s.push("<strong>Command:</strong>\n"+e[1]),3===e.length&&s.push("<strong>Result:</strong>\n"+e[2])})),e.find(this.selectorCommandText).html(s.join("\n"))}a.append(e)}}),(t=>{Router.handleAjaxError(t,e)}));o.push(c)})),Promise.all(o).then((()=>{t.removeClass("disabled").prop("disabled",!1)}))}}export default new ImageProcessing;
\ No newline at end of file
+import"bootstrap";import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class ImageProcessing extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorExecuteTrigger=".t3js-imageProcessing-execute",this.selectorTestContainer=".t3js-imageProcessing-twinContainer",this.selectorTwinImageTemplate=".t3js-imageProcessing-twinImage-template",this.selectorCommandContainer=".t3js-imageProcessing-command",this.selectorCommandText=".t3js-imageProcessing-command-text",this.selectorTwinImages=".t3js-imageProcessing-images"}initialize(e){this.currentModal=e,this.getData(),e.on("click",this.selectorExecuteTrigger,(e=>{e.preventDefault(),this.runTests()}))}getData(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("imageProcessingGetData")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();!0===s.success?(e.empty().append(s.html),Modal.setButtons(s.buttons),this.runTests()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}runTests(){const e=this.getModalBody();this.setModalButtonsState(!1);const t=this.findInModal(this.selectorTwinImageTemplate),s=[];e.find(this.selectorTestContainer).each(((o,r)=>{const n=$(r),a=n.data("test"),i=InfoBox.render(Severity.loading,"Loading...","");n.empty().append(i);const c=new AjaxRequest(Router.getUrl(a)).get({cache:"no-cache"}).then((async e=>{const s=await e.resolve();if(!0===s.success){n.empty(),Array.isArray(s.status)&&s.status.forEach((e=>{const t=InfoBox.render(e.severity,e.title,e.message);n.append(t)}));const e=t.clone();if(e.removeClass("t3js-imageProcessing-twinImage-template"),!0===s.fileExists&&(e.find("img.reference").attr("src",s.referenceFile),e.find("img.result").attr("src",s.outputFile),e.find(this.selectorTwinImages).show()),Array.isArray(s.command)&&s.command.length>0){e.find(this.selectorCommandContainer).show();const t=[];s.command.forEach((e=>{t.push("<strong>Command:</strong>\n"+e[1]),3===e.length&&t.push("<strong>Result:</strong>\n"+e[2])})),e.find(this.selectorCommandText).html(t.join("\n"))}n.append(e)}}),(t=>{Router.handleAjaxError(t,e)}));s.push(c)})),Promise.all(s).then((()=>{this.setModalButtonsState(!0)}))}}export default new ImageProcessing;
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/clear-tables.js b/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/clear-tables.js
index ae7a7b00d75bcba999ac057746676eac3a6bb97e..436470d5a4b10e7116eb23c68b5b779b034bb6eb 100644
--- a/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/clear-tables.js
+++ b/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/clear-tables.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";class ClearTables extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorClearTrigger=".t3js-clearTables-clear",this.selectorStatsTrigger=".t3js-clearTables-stats",this.selectorOutputContainer=".t3js-clearTables-output",this.selectorStatContainer=".t3js-clearTables-stat-container",this.selectorStatTemplate=".t3js-clearTables-stat-template",this.selectorStatDescription=".t3js-clearTables-stat-description",this.selectorStatRows=".t3js-clearTables-stat-rows",this.selectorStatName=".t3js-clearTables-stat-name"}initialize(t){this.currentModal=t,this.getStats(),t.on("click",this.selectorStatsTrigger,(t=>{t.preventDefault(),$(this.selectorOutputContainer).empty(),this.getStats()})),t.on("click",this.selectorClearTrigger,(t=>{const e=$(t.target).closest(this.selectorClearTrigger).data("table");t.preventDefault(),this.clear(e)}))}getStats(){this.setModalButtonsState(!1);const t=this.getModalBody();new AjaxRequest(Router.getUrl("clearTablesStats")).get({cache:"no-cache"}).then((async e=>{const s=await e.resolve();!0===s.success?(t.empty().append(s.html),Modal.setButtons(s.buttons),Array.isArray(s.stats)&&s.stats.length>0&&s.stats.forEach((e=>{if(e.rowCount>0){const s=t.find(this.selectorStatTemplate).clone();s.find(this.selectorStatDescription).text(e.description),s.find(this.selectorStatName).text(e.name),s.find(this.selectorStatRows).text(e.rowCount),s.find(this.selectorClearTrigger).attr("data-table",e.name),t.find(this.selectorStatContainer).append(s.html())}}))):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}))}clear(t){const e=this.getModalBody(),s=this.getModuleContent().data("clear-tables-clear-token");new AjaxRequest(Router.getUrl()).post({install:{action:"clearTablesClear",token:s,table:t}}).then((async t=>{const e=await t.resolve();!0===e.success&&Array.isArray(e.status)?e.status.forEach((t=>{Notification.success(t.title,t.message)})):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log."),this.getStats()}),(t=>{Router.handleAjaxError(t,e)}))}}export default new ClearTables;
\ No newline at end of file
+import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";class ClearTables extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorClearTrigger=".t3js-clearTables-clear",this.selectorStatsTrigger=".t3js-clearTables-stats",this.selectorOutputContainer=".t3js-clearTables-output",this.selectorStatContainer=".t3js-clearTables-stat-container",this.selectorStatTemplate=".t3js-clearTables-stat-template",this.selectorStatDescription=".t3js-clearTables-stat-description",this.selectorStatRows=".t3js-clearTables-stat-rows",this.selectorStatName=".t3js-clearTables-stat-name"}initialize(t){this.currentModal=t,this.getStats(),t.on("click",this.selectorStatsTrigger,(t=>{t.preventDefault(),$(this.selectorOutputContainer).empty(),this.getStats()})),t.on("click",this.selectorClearTrigger,(t=>{const e=$(t.target).closest(this.selectorClearTrigger).data("table");t.preventDefault(),this.clear(e)}))}getStats(){this.setModalButtonsState(!1);const t=this.getModalBody();new AjaxRequest(Router.getUrl("clearTablesStats")).get({cache:"no-cache"}).then((async e=>{const s=await e.resolve();!0===s.success?(t.empty().append(s.html),Modal.setButtons(s.buttons),Array.isArray(s.stats)&&s.stats.length>0&&s.stats.forEach((e=>{if(e.rowCount>0){const s=t.find(this.selectorStatTemplate).clone();s.find(this.selectorStatDescription).text(e.description),s.find(this.selectorStatName).text(e.name),s.find(this.selectorStatRows).text(e.rowCount),s.find(this.selectorClearTrigger).attr("data-table",e.name),t.find(this.selectorStatContainer).append(s.html())}}))):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)})).finally((()=>{this.setModalButtonsState(!0)}))}clear(t){const e=this.getModalBody(),s=this.getModuleContent().data("clear-tables-clear-token");new AjaxRequest(Router.getUrl()).post({install:{action:"clearTablesClear",token:s,table:t}}).then((async t=>{const e=await t.resolve();!0===e.success&&Array.isArray(e.status)?e.status.forEach((t=>{Notification.success(t.title,t.message)})):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log."),this.getStats()}),(t=>{Router.handleAjaxError(t,e)}))}}export default new ClearTables;
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/clear-typo3temp-files.js b/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/clear-typo3temp-files.js
index d49a7f4aff39b41367cdf3fe4984f1d0f4a7372a..3b6d38738826b2695e96d1d4ac7ffd592b22f04e 100644
--- a/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/clear-typo3temp-files.js
+++ b/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/clear-typo3temp-files.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";class ClearTypo3tempFiles extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorDeleteTrigger=".t3js-clearTypo3temp-delete",this.selectorOutputContainer=".t3js-clearTypo3temp-output",this.selectorStatContainer=".t3js-clearTypo3temp-stat-container",this.selectorStatsTrigger=".t3js-clearTypo3temp-stats",this.selectorStatTemplate=".t3js-clearTypo3temp-stat-template",this.selectorStatNumberOfFiles=".t3js-clearTypo3temp-stat-numberOfFiles",this.selectorStatDirectory=".t3js-clearTypo3temp-stat-directory"}initialize(t){this.currentModal=t,this.getStats(),t.on("click",this.selectorStatsTrigger,(t=>{t.preventDefault(),$(this.selectorOutputContainer).empty(),this.getStats()})),t.on("click",this.selectorDeleteTrigger,(t=>{const e=$(t.currentTarget).data("folder"),s=$(t.currentTarget).data("storage-uid");t.preventDefault(),this.delete(e,s)}))}getStats(){this.setModalButtonsState(!1);const t=this.getModalBody();new AjaxRequest(Router.getUrl("clearTypo3tempFilesStats")).get({cache:"no-cache"}).then((async e=>{const s=await e.resolve();!0===s.success?(t.empty().append(s.html),Modal.setButtons(s.buttons),Array.isArray(s.stats)&&s.stats.length>0&&s.stats.forEach((e=>{if(e.numberOfFiles>0){const s=t.find(this.selectorStatTemplate).clone();s.find(this.selectorStatNumberOfFiles).text(e.numberOfFiles),s.find(this.selectorStatDirectory).text(e.directory),s.find(this.selectorDeleteTrigger).attr("data-folder",e.directory),s.find(this.selectorDeleteTrigger).attr("data-storage-uid",e.storageUid),t.find(this.selectorStatContainer).append(s.html())}}))):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}))}delete(t,e){const s=this.getModalBody(),r=this.getModuleContent().data("clear-typo3temp-delete-token");new AjaxRequest(Router.getUrl()).post({install:{action:"clearTypo3tempFiles",token:r,folder:t,storageUid:e}}).then((async t=>{const e=await t.resolve();!0===e.success&&Array.isArray(e.status)?(e.status.forEach((t=>{Notification.success(t.title,t.message)})),this.getStats()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,s)}))}}export default new ClearTypo3tempFiles;
\ No newline at end of file
+import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";class ClearTypo3tempFiles extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorDeleteTrigger=".t3js-clearTypo3temp-delete",this.selectorOutputContainer=".t3js-clearTypo3temp-output",this.selectorStatContainer=".t3js-clearTypo3temp-stat-container",this.selectorStatsTrigger=".t3js-clearTypo3temp-stats",this.selectorStatTemplate=".t3js-clearTypo3temp-stat-template",this.selectorStatNumberOfFiles=".t3js-clearTypo3temp-stat-numberOfFiles",this.selectorStatDirectory=".t3js-clearTypo3temp-stat-directory"}initialize(t){this.currentModal=t,this.getStats(),t.on("click",this.selectorStatsTrigger,(t=>{t.preventDefault(),$(this.selectorOutputContainer).empty(),this.getStats()})),t.on("click",this.selectorDeleteTrigger,(t=>{const e=$(t.currentTarget).data("folder"),s=$(t.currentTarget).data("storage-uid");t.preventDefault(),this.delete(e,s)}))}getStats(){this.setModalButtonsState(!1);const t=this.getModalBody();new AjaxRequest(Router.getUrl("clearTypo3tempFilesStats")).get({cache:"no-cache"}).then((async e=>{const s=await e.resolve();!0===s.success?(t.empty().append(s.html),Modal.setButtons(s.buttons),Array.isArray(s.stats)&&s.stats.length>0&&s.stats.forEach((e=>{if(e.numberOfFiles>0){const s=t.find(this.selectorStatTemplate).clone();s.find(this.selectorStatNumberOfFiles).text(e.numberOfFiles),s.find(this.selectorStatDirectory).text(e.directory),s.find(this.selectorDeleteTrigger).attr("data-folder",e.directory),s.find(this.selectorDeleteTrigger).attr("data-storage-uid",e.storageUid),t.find(this.selectorStatContainer).append(s.html())}}))):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}))}delete(t,e){const s=this.getModalBody(),r=this.getModuleContent().data("clear-typo3temp-delete-token");new AjaxRequest(Router.getUrl()).post({install:{action:"clearTypo3tempFiles",token:r,folder:t,storageUid:e}}).then((async t=>{const e=await t.resolve();!0===e.success&&Array.isArray(e.status)?(e.status.forEach((t=>{Notification.success(t.title,t.message)})),this.getStats()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,s)})).finally((()=>{this.setModalButtonsState(!0)}))}}export default new ClearTypo3tempFiles;
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/database-analyzer.js b/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/database-analyzer.js
index e72cc865be5a3ac3f6b0ef32bfabdc2e0539bade..263ee8ee4501fc6b0fa5ebf92c719b82287b35f2 100644
--- a/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/database-analyzer.js
+++ b/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/database-analyzer.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class DatabaseAnalyzer extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorAnalyzeTrigger=".t3js-databaseAnalyzer-analyze",this.selectorExecuteTrigger=".t3js-databaseAnalyzer-execute",this.selectorOutputContainer=".t3js-databaseAnalyzer-output",this.selectorSuggestionBlock=".t3js-databaseAnalyzer-suggestion-block",this.selectorSuggestionList=".t3js-databaseAnalyzer-suggestion-list",this.selectorSuggestionLineTemplate=".t3js-databaseAnalyzer-suggestion-line-template"}initialize(e){this.currentModal=e,this.getData(),e.on("click",".t3js-databaseAnalyzer-suggestion-block-checkbox",(e=>{const t=$(e.currentTarget);t.closest("fieldset").find(":checkbox").prop("checked",t.get(0).checked)})),e.on("click",this.selectorAnalyzeTrigger,(e=>{e.preventDefault(),this.analyze()})),e.on("click",this.selectorExecuteTrigger,(e=>{e.preventDefault(),this.execute()}))}getData(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("databaseAnalyzer")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success?(e.empty().append(a.html),Modal.setButtons(a.buttons),this.analyze()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}analyze(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.getModalFooter(),a=e.find(this.selectorOutputContainer),s=t.find(this.selectorExecuteTrigger),n=t.find(this.selectorAnalyzeTrigger);a.empty().append(ProgressBar.render(Severity.loading,"Analyzing current database schema...","")),a.on("change",'input[type="checkbox"]',(()=>{const e=a.find(":checked").length>0;this.setModalButtonState(s,e)})),new AjaxRequest(Router.getUrl("databaseAnalyzerAnalyze")).get({cache:"no-cache"}).then((async t=>{const o=await t.resolve();!0===o.success?(Array.isArray(o.status)&&(a.find(".alert-loading").remove(),o.status.forEach((e=>{const t=InfoBox.render(e.severity,e.title,e.message);a.append(t)}))),Array.isArray(o.suggestions)&&(o.suggestions.forEach((t=>{const s=e.find(this.selectorSuggestionBlock).clone();s.removeClass(this.selectorSuggestionBlock.substr(1));const n=t.key;s.find(".t3js-databaseAnalyzer-suggestion-block-legend").text(t.label),s.find(".t3js-databaseAnalyzer-suggestion-block-checkbox").attr("id","t3-install-"+n+"-checkbox"),t.enabled&&s.find(".t3js-databaseAnalyzer-suggestion-block-checkbox").attr("checked","checked"),s.find(".t3js-databaseAnalyzer-suggestion-block-label").attr("for","t3-install-"+n+"-checkbox"),t.children.forEach((a=>{const n=e.find(this.selectorSuggestionLineTemplate).children().clone(),o=a.hash,r=n.find(".t3js-databaseAnalyzer-suggestion-line-checkbox");r.attr("id","t3-install-db-"+o).attr("data-hash",o),t.enabled&&r.attr("checked","checked"),n.find(".t3js-databaseAnalyzer-suggestion-line-label").attr("for","t3-install-db-"+o),n.find(".t3js-databaseAnalyzer-suggestion-line-statement").text(a.statement),void 0!==a.current&&(n.find(".t3js-databaseAnalyzer-suggestion-line-current-value").text(a.current),n.find(".t3js-databaseAnalyzer-suggestion-line-current").show()),void 0!==a.rowCount&&(n.find(".t3js-databaseAnalyzer-suggestion-line-count-value").text(a.rowCount),n.find(".t3js-databaseAnalyzer-suggestion-line-count").show()),s.find(this.selectorSuggestionList).append(n)})),a.append(s.html())})),this.setModalButtonState(n,!0),this.setModalButtonState(s,a.find(":checked").length>0)),0===o.suggestions.length&&0===o.status.length&&a.append(InfoBox.render(Severity.ok,"Database schema is up to date. Good job!",""))):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}execute(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.getModuleContent().data("database-analyzer-execute-token"),a=e.find(this.selectorOutputContainer),s=[];a.find(".t3js-databaseAnalyzer-suggestion-line input:checked").each(((e,t)=>{s.push($(t).data("hash"))})),a.empty().append(ProgressBar.render(Severity.loading,"Executing database updates...","")),new AjaxRequest(Router.getUrl()).post({install:{action:"databaseAnalyzerExecute",token:t,hashes:s}}).then((async e=>{const t=await e.resolve();Array.isArray(t.status)&&t.status.forEach((e=>{Notification.showMessage(e.title,e.message,e.severity)})),this.analyze()}),(t=>{Router.handleAjaxError(t,e)}))}}export default new DatabaseAnalyzer;
\ No newline at end of file
+import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class DatabaseAnalyzer extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorAnalyzeTrigger=".t3js-databaseAnalyzer-analyze",this.selectorExecuteTrigger=".t3js-databaseAnalyzer-execute",this.selectorOutputContainer=".t3js-databaseAnalyzer-output",this.selectorSuggestionBlock=".t3js-databaseAnalyzer-suggestion-block",this.selectorSuggestionList=".t3js-databaseAnalyzer-suggestion-list",this.selectorSuggestionLineTemplate=".t3js-databaseAnalyzer-suggestion-line-template"}initialize(e){this.currentModal=e,this.getData(),e.on("click",".t3js-databaseAnalyzer-suggestion-block-checkbox",(e=>{const t=$(e.currentTarget);t.closest("fieldset").find(":checkbox").prop("checked",t.get(0).checked)})),e.on("click",this.selectorAnalyzeTrigger,(e=>{e.preventDefault(),this.analyze()})),e.on("click",this.selectorExecuteTrigger,(e=>{e.preventDefault(),this.execute()}))}getData(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("databaseAnalyzer")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success?(e.empty().append(a.html),Modal.setButtons(a.buttons),this.analyze()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}analyze(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.getModalFooter(),a=e.find(this.selectorOutputContainer),s=t.find(this.selectorExecuteTrigger),o=t.find(this.selectorAnalyzeTrigger);a.empty().append(ProgressBar.render(Severity.loading,"Analyzing current database schema...","")),a.on("change",'input[type="checkbox"]',(()=>{const e=a.find(":checked").length>0;this.setModalButtonState(s,e)})),new AjaxRequest(Router.getUrl("databaseAnalyzerAnalyze")).get({cache:"no-cache"}).then((async t=>{const n=await t.resolve();!0===n.success?(Array.isArray(n.status)&&(a.find(".alert-loading").remove(),n.status.forEach((e=>{const t=InfoBox.render(e.severity,e.title,e.message);a.append(t)}))),Array.isArray(n.suggestions)&&(n.suggestions.forEach((t=>{const s=e.find(this.selectorSuggestionBlock).clone();s.removeClass(this.selectorSuggestionBlock.substr(1));const o=t.key;s.find(".t3js-databaseAnalyzer-suggestion-block-legend").text(t.label),s.find(".t3js-databaseAnalyzer-suggestion-block-checkbox").attr("id","t3-install-"+o+"-checkbox"),t.enabled&&s.find(".t3js-databaseAnalyzer-suggestion-block-checkbox").attr("checked","checked"),s.find(".t3js-databaseAnalyzer-suggestion-block-label").attr("for","t3-install-"+o+"-checkbox"),t.children.forEach((a=>{const o=e.find(this.selectorSuggestionLineTemplate).children().clone(),n=a.hash,r=o.find(".t3js-databaseAnalyzer-suggestion-line-checkbox");r.attr("id","t3-install-db-"+n).attr("data-hash",n),t.enabled&&r.attr("checked","checked"),o.find(".t3js-databaseAnalyzer-suggestion-line-label").attr("for","t3-install-db-"+n),o.find(".t3js-databaseAnalyzer-suggestion-line-statement").text(a.statement),void 0!==a.current&&(o.find(".t3js-databaseAnalyzer-suggestion-line-current-value").text(a.current),o.find(".t3js-databaseAnalyzer-suggestion-line-current").show()),void 0!==a.rowCount&&(o.find(".t3js-databaseAnalyzer-suggestion-line-count-value").text(a.rowCount),o.find(".t3js-databaseAnalyzer-suggestion-line-count").show()),s.find(this.selectorSuggestionList).append(o)})),a.append(s.html())})),this.setModalButtonState(o,!0),this.setModalButtonState(s,a.find(":checked").length>0)),0===n.suggestions.length&&0===n.status.length&&a.append(InfoBox.render(Severity.ok,"Database schema is up to date. Good job!",""))):(Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log."),this.setModalButtonState(o,!0),this.setModalButtonState(s,!1))}),(t=>{Router.handleAjaxError(t,e),this.setModalButtonState(o,!0),this.setModalButtonState(s,!1)}))}execute(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.getModuleContent().data("database-analyzer-execute-token"),a=e.find(this.selectorOutputContainer),s=[];a.find(".t3js-databaseAnalyzer-suggestion-line input:checked").each(((e,t)=>{s.push($(t).data("hash"))})),a.empty().append(ProgressBar.render(Severity.loading,"Executing database updates...","")),new AjaxRequest(Router.getUrl()).post({install:{action:"databaseAnalyzerExecute",token:t,hashes:s}}).then((async e=>{const t=await e.resolve();Array.isArray(t.status)&&t.status.forEach((e=>{Notification.showMessage(e.title,e.message,e.severity)})),this.analyze()}),(t=>{Router.handleAjaxError(t,e)})).finally((()=>{this.setModalButtonState(this.getModalFooter().find(this.selectorAnalyzeTrigger),!0),this.setModalButtonState(this.getModalFooter().find(this.selectorExecuteTrigger),!1)}))}}export default new DatabaseAnalyzer;
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-compat-tester.js b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-compat-tester.js
index 8a88cc331be68d09ae2f16789c1052c2b1d32c05..3416f111b52a760347a66e05da012876c36e034a 100644
--- a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-compat-tester.js
+++ b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-compat-tester.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import"bootstrap";import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class ExtensionCompatTester extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorCheckTrigger=".t3js-extensionCompatTester-check",this.selectorUninstallTrigger=".t3js-extensionCompatTester-uninstall",this.selectorOutputContainer=".t3js-extensionCompatTester-output"}initialize(e){this.currentModal=e,this.getLoadedExtensionList(),e.on("click",this.selectorCheckTrigger,(()=>{this.findInModal(this.selectorUninstallTrigger).addClass("hidden"),this.findInModal(this.selectorOutputContainer).empty(),this.getLoadedExtensionList()})),e.on("click",this.selectorUninstallTrigger,(e=>{this.uninstallExtension($(e.target).data("extension"))}))}getLoadedExtensionList(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.findInModal(this.selectorOutputContainer);if(t.length){const e=ProgressBar.render(Severity.loading,"Loading...","");t.append(e)}new AjaxRequest(Router.getUrl("extensionCompatTesterLoadedExtensionList")).get({cache:"no-cache"}).then((async t=>{const o=await t.resolve();e.empty().append(o.html),Modal.setButtons(o.buttons);const n=this.findInModal(this.selectorOutputContainer),s=ProgressBar.render(Severity.loading,"Loading...","");n.append(s),!0===o.success?this.loadExtLocalconf().then((()=>{n.append(InfoBox.render(Severity.ok,"ext_localconf.php of all loaded extensions successfully loaded","")),this.loadExtTables().then((()=>{n.append(InfoBox.render(Severity.ok,"ext_tables.php of all loaded extensions successfully loaded",""))}),(async e=>{this.renderFailureMessages("ext_tables.php",(await e.response.json()).brokenExtensions,n)})).finally((()=>{this.unlockModal()}))}),(async e=>{this.renderFailureMessages("ext_localconf.php",(await e.response.json()).brokenExtensions,n),n.append(InfoBox.render(Severity.notice,"Skipped scanning ext_tables.php files due to previous errors","")),this.unlockModal()})):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}unlockModal(){this.findInModal(this.selectorOutputContainer).find(".alert-loading").remove(),this.findInModal(this.selectorCheckTrigger).removeClass("disabled").prop("disabled",!1)}renderFailureMessages(e,t,o){for(const n of t){let t;n.isProtected||(t=$("<button />",{class:"btn btn-danger t3js-extensionCompatTester-uninstall"}).attr("data-extension",n.name).text('Uninstall extension "'+n.name+'"')),o.append(InfoBox.render(Severity.error,"Loading "+e+' of extension "'+n.name+'" failed',n.isProtected?"Extension is mandatory and cannot be uninstalled.":""),t)}this.unlockModal()}loadExtLocalconf(){const e=this.getModuleContent().data("extension-compat-tester-load-ext_localconf-token");return new AjaxRequest(Router.getUrl()).post({install:{action:"extensionCompatTesterLoadExtLocalconf",token:e}})}loadExtTables(){const e=this.getModuleContent().data("extension-compat-tester-load-ext_tables-token");return new AjaxRequest(Router.getUrl()).post({install:{action:"extensionCompatTesterLoadExtTables",token:e}})}uninstallExtension(e){const t=this.getModuleContent().data("extension-compat-tester-uninstall-extension-token"),o=this.getModalBody(),n=$(this.selectorOutputContainer),s=ProgressBar.render(Severity.loading,"Loading...","");n.append(s),new AjaxRequest(Router.getUrl()).post({install:{action:"extensionCompatTesterUninstallExtension",token:t,extension:e}}).then((async e=>{const t=await e.resolve();t.success?(Array.isArray(t.status)&&t.status.forEach((e=>{const t=InfoBox.render(e.severity,e.title,e.message);o.find(this.selectorOutputContainer).empty().append(t)})),this.findInModal(this.selectorUninstallTrigger).addClass("hidden"),this.getLoadedExtensionList()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,o)}))}}export default new ExtensionCompatTester;
\ No newline at end of file
+import"bootstrap";import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class ExtensionCompatTester extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorCheckTrigger=".t3js-extensionCompatTester-check",this.selectorUninstallTrigger=".t3js-extensionCompatTester-uninstall",this.selectorOutputContainer=".t3js-extensionCompatTester-output"}initialize(e){this.currentModal=e,this.getLoadedExtensionList(),e.on("click",this.selectorCheckTrigger,(()=>{this.findInModal(this.selectorUninstallTrigger).addClass("hidden"),this.findInModal(this.selectorOutputContainer).empty(),this.getLoadedExtensionList()})),e.on("click",this.selectorUninstallTrigger,(e=>{this.uninstallExtension($(e.target).data("extension"))}))}getLoadedExtensionList(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.findInModal(this.selectorOutputContainer);if(t.length){const e=ProgressBar.render(Severity.loading,"Loading...","");t.append(e)}new AjaxRequest(Router.getUrl("extensionCompatTesterLoadedExtensionList")).get({cache:"no-cache"}).then((async t=>{const o=await t.resolve();e.empty().append(o.html),Modal.setButtons(o.buttons);const n=this.findInModal(this.selectorOutputContainer),s=ProgressBar.render(Severity.loading,"Loading...","");n.append(s),!0===o.success?this.loadExtLocalconf().then((()=>{n.append(InfoBox.render(Severity.ok,"ext_localconf.php of all loaded extensions successfully loaded","")),this.loadExtTables().then((()=>{n.append(InfoBox.render(Severity.ok,"ext_tables.php of all loaded extensions successfully loaded",""))}),(async e=>{this.renderFailureMessages("ext_tables.php",(await e.response.json()).brokenExtensions,n)})).finally((()=>{this.unlockModal()}))}),(async e=>{this.renderFailureMessages("ext_localconf.php",(await e.response.json()).brokenExtensions,n),n.append(InfoBox.render(Severity.notice,"Skipped scanning ext_tables.php files due to previous errors","")),this.unlockModal()})):(Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log."),this.unlockModal())}),(t=>{Router.handleAjaxError(t,e),this.unlockModal()}))}unlockModal(){this.findInModal(this.selectorOutputContainer).find(".alert-loading").remove(),this.setModalButtonsState(!0)}renderFailureMessages(e,t,o){for(const n of t){let t;n.isProtected||(t=$("<button />",{class:"btn btn-danger t3js-extensionCompatTester-uninstall"}).attr("data-extension",n.name).text('Uninstall extension "'+n.name+'"')),o.append(InfoBox.render(Severity.error,"Loading "+e+' of extension "'+n.name+'" failed',n.isProtected?"Extension is mandatory and cannot be uninstalled.":""),t)}this.unlockModal()}loadExtLocalconf(){const e=this.getModuleContent().data("extension-compat-tester-load-ext_localconf-token");return new AjaxRequest(Router.getUrl()).post({install:{action:"extensionCompatTesterLoadExtLocalconf",token:e}})}loadExtTables(){const e=this.getModuleContent().data("extension-compat-tester-load-ext_tables-token");return new AjaxRequest(Router.getUrl()).post({install:{action:"extensionCompatTesterLoadExtTables",token:e}})}uninstallExtension(e){const t=this.getModuleContent().data("extension-compat-tester-uninstall-extension-token"),o=this.getModalBody(),n=$(this.selectorOutputContainer),s=ProgressBar.render(Severity.loading,"Loading...","");n.append(s),new AjaxRequest(Router.getUrl()).post({install:{action:"extensionCompatTesterUninstallExtension",token:t,extension:e}}).then((async e=>{const t=await e.resolve();t.success?(Array.isArray(t.status)&&t.status.forEach((e=>{const t=InfoBox.render(e.severity,e.title,e.message);o.find(this.selectorOutputContainer).empty().append(t)})),this.findInModal(this.selectorUninstallTrigger).addClass("hidden"),this.getLoadedExtensionList()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,o)}))}}export default new ExtensionCompatTester;
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-scanner.js b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-scanner.js
index f7f59bba8e34807c9c04675638c5a33696170d7d..8e6268ccb84ee22130be4d61d189b41eac9ae649 100644
--- a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-scanner.js
+++ b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-scanner.js
@@ -10,4 +10,4 @@
  *
  * 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}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(),e.each(((e,n)=>{const t=$(n),s=t.data("extension");this.scanSingleExtension(s),t.data("scanned",!0)}))}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=this.getModalBody();this.findInModal(".t3js-extensionScanner-progress-all-extension .progress-bar").css("width",t+"%").attr("aria-valuenow",t).find("span").text(n+" of "+e+" scanned"),n===e&&(this.findInModal(this.selectorExtensionScanButton).removeClass("disabled").prop("disabled",!1),Notification.success("Scan finished","All extensions have been scanned."),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))}}).then((async e=>{const n=await e.resolve();!0===n.success&&Notification.success("Marked not affected files","Marked "+n.markedAsNotAffected+" ReST files as not affected.")}),(e=>{Router.handleAjaxError(e,s)})))}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(),new AjaxRequest(Router.getUrl()).post({install:{action:"extensionScannerFiles",token:n,extension:e}}).then((async n=>{const i=await n.resolve();if(!0===i.success&&Array.isArray(i.files)){const n=i.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 o=0;i.files.forEach((i=>{AjaxQueue.add({method:"POST",data:{install:{action:"extensionScannerScanFile",token:this.getModuleContent().data("extension-scanner-scan-file-token"),extension:e,file:i}},url:Router.getUrl(),onfulfilled:async r=>{const l=await r.resolve();if(o++,this.setStatusMessageForScan(e,o,n),this.setProgressForScan(e,o,n),l.success&&Array.isArray(l.matches)&&l.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(i),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 o=n.find(".panel-breaking, .t3js-extensionScanner-hit-file-rest-container").length>0?"panel-danger":"panel-warning";n.addClass(o),n.removeClass("panel-default"),s.find(".t3js-extensionScanner-extension-body").removeClass("hide").append(n),s.removeClass("panel-default"),"panel-danger"===o&&(s.removeClass("panel-warning"),s.addClass(o)),"panel-warning"!==o||s.hasClass("panel-danger")||s.addClass(o)})),l.success){const e=parseInt(s.find(".t3js-extensionScanner-extension-body-loc").text(),10);if(s.find(".t3js-extensionScanner-extension-body-loc").empty().text(e+l.effectiveCodeLines),l.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+l.ignoredLines)}o===n&&(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))},onrejected:t=>{o+=1,this.setStatusMessageForScan(e,o,n),this.setProgressForScan(e,o,n),s.removeClass("panel-has-progress"),this.setProgressForAll(),console.error(t)}})}))}else Notification.error("Oops, an error occurred","Please look at the browser console output for details"),console.error(i)}),(e=>{Router.handleAjaxError(e,t)}))}}export default new ExtensionScanner;
\ No newline at end of file
+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;this.findInModal(".t3js-extensionScanner-progress-all-extension .progress-bar").css("width",t+"%").attr("aria-valuenow",t).find("span").text(n+" of "+e+" scanned")}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;
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-ext-tables-check.js b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-ext-tables-check.js
index a1def5de7ed5d1203b46127b5af62fa4dc3609f9..2fa1a9d48f1077069ad6f329c6fe46b81cc9a6cc 100644
--- a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-ext-tables-check.js
+++ b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-ext-tables-check.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class TcaExtTablesCheck extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorCheckTrigger=".t3js-tcaExtTablesCheck-check",this.selectorOutputContainer=".t3js-tcaExtTablesCheck-output"}initialize(e){this.currentModal=e,this.check(),e.on("click",this.selectorCheckTrigger,(e=>{e.preventDefault(),this.check()}))}check(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=$(this.selectorOutputContainer),o=ProgressBar.render(Severity.loading,"Loading...","");t.empty().append(o),new AjaxRequest(Router.getUrl("tcaExtTablesCheck")).get({cache:"no-cache"}).then((async o=>{const r=await o.resolve();if(e.empty().append(r.html),Modal.setButtons(r.buttons),!0===r.success&&Array.isArray(r.status))if(r.status.length>0){const o=InfoBox.render(Severity.warning,"Following extensions change TCA in ext_tables.php","Check ext_tables.php files, look for ExtensionManagementUtility calls and $GLOBALS['TCA'] modifications");e.find(this.selectorOutputContainer).append(o),r.status.forEach((o=>{const r=InfoBox.render(o.severity,o.title,o.message);t.append(r),e.append(r)}))}else{const t=InfoBox.render(Severity.ok,"No TCA changes in ext_tables.php files. Good job!","");e.find(this.selectorOutputContainer).append(t)}else Notification.error("Something went wrong",'Please use the module "Check for broken extensions" to find a possible extension causing this issue.')}),(t=>{Router.handleAjaxError(t,e)}))}}export default new TcaExtTablesCheck;
\ No newline at end of file
+import $ from"jquery";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 AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class TcaExtTablesCheck extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorCheckTrigger=".t3js-tcaExtTablesCheck-check",this.selectorOutputContainer=".t3js-tcaExtTablesCheck-output"}initialize(e){this.currentModal=e,this.check(),e.on("click",this.selectorCheckTrigger,(e=>{e.preventDefault(),this.check()}))}check(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=$(this.selectorOutputContainer),o=ProgressBar.render(Severity.loading,"Loading...","");t.empty().append(o),new AjaxRequest(Router.getUrl("tcaExtTablesCheck")).get({cache:"no-cache"}).then((async o=>{const r=await o.resolve();if(e.empty().append(r.html),Modal.setButtons(r.buttons),!0===r.success&&Array.isArray(r.status))if(r.status.length>0){const o=InfoBox.render(Severity.warning,"Following extensions change TCA in ext_tables.php","Check ext_tables.php files, look for ExtensionManagementUtility calls and $GLOBALS['TCA'] modifications");e.find(this.selectorOutputContainer).append(o),r.status.forEach((o=>{const r=InfoBox.render(o.severity,o.title,o.message);t.append(r),e.append(r)}))}else{const t=InfoBox.render(Severity.ok,"No TCA changes in ext_tables.php files. Good job!","");e.find(this.selectorOutputContainer).append(t)}else Notification.error("Something went wrong",'Please use the module "Check for broken extensions" to find a possible extension causing this issue.')}),(t=>{Router.handleAjaxError(t,e)})).finally((()=>{this.setModalButtonsState(!0)}))}}export default new TcaExtTablesCheck;
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-migrations-check.js b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-migrations-check.js
index 63008b32e7d9e877063b2f81660f7a7d4f9f5ce3..8770aff87bbd1efd89a757c2fbacfb2b8eb0f499 100644
--- a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-migrations-check.js
+++ b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-migrations-check.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import FlashMessage from"@typo3/install/renderable/flash-message.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class TcaMigrationsCheck extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorCheckTrigger=".t3js-tcaMigrationsCheck-check",this.selectorOutputContainer=".t3js-tcaMigrationsCheck-output"}initialize(e){this.currentModal=e,this.check(),e.on("click",this.selectorCheckTrigger,(e=>{e.preventDefault(),this.check()}))}check(){this.setModalButtonsState(!1);const e=$(this.selectorOutputContainer),t=this.getModalBody(),r=ProgressBar.render(Severity.loading,"Loading...","");e.empty().append(r),new AjaxRequest(Router.getUrl("tcaMigrationsCheck")).get({cache:"no-cache"}).then((async e=>{const r=await e.resolve();if(t.empty().append(r.html),Modal.setButtons(r.buttons),!0===r.success&&Array.isArray(r.status))if(r.status.length>0){const e=InfoBox.render(Severity.warning,"TCA migrations need to be applied","Check the following list and apply needed changes.");t.find(this.selectorOutputContainer).empty(),t.find(this.selectorOutputContainer).append(e),r.status.forEach((e=>{const r=InfoBox.render(e.severity,e.title,e.message);t.find(this.selectorOutputContainer).append(r)}))}else{const e=InfoBox.render(Severity.ok,"No TCA migrations need to be applied","Your TCA looks good.");t.find(this.selectorOutputContainer).append(e)}else{const e=FlashMessage.render(Severity.error,"Something went wrong",'Use "Check for broken extensions"');t.find(this.selectorOutputContainer).append(e)}this.setModalButtonsState(!0)}),(e=>{Router.handleAjaxError(e,t)}))}}export default new TcaMigrationsCheck;
\ No newline at end of file
+import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import FlashMessage from"@typo3/install/renderable/flash-message.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class TcaMigrationsCheck extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorCheckTrigger=".t3js-tcaMigrationsCheck-check",this.selectorOutputContainer=".t3js-tcaMigrationsCheck-output"}initialize(e){this.currentModal=e,this.check(),e.on("click",this.selectorCheckTrigger,(e=>{e.preventDefault(),this.check()}))}check(){this.setModalButtonsState(!1);const e=$(this.selectorOutputContainer),t=this.getModalBody(),r=ProgressBar.render(Severity.loading,"Loading...","");e.empty().append(r),new AjaxRequest(Router.getUrl("tcaMigrationsCheck")).get({cache:"no-cache"}).then((async e=>{const r=await e.resolve();if(t.empty().append(r.html),Modal.setButtons(r.buttons),!0===r.success&&Array.isArray(r.status))if(r.status.length>0){const e=InfoBox.render(Severity.warning,"TCA migrations need to be applied","Check the following list and apply needed changes.");t.find(this.selectorOutputContainer).empty(),t.find(this.selectorOutputContainer).append(e),r.status.forEach((e=>{const r=InfoBox.render(e.severity,e.title,e.message);t.find(this.selectorOutputContainer).append(r)}))}else{const e=InfoBox.render(Severity.ok,"No TCA migrations need to be applied","Your TCA looks good.");t.find(this.selectorOutputContainer).append(e)}else{const e=FlashMessage.render(Severity.error,"Something went wrong",'Use "Check for broken extensions"');t.find(this.selectorOutputContainer).append(e)}}),(e=>{Router.handleAjaxError(e,t)})).finally((()=>{this.setModalButtonsState(!0)}))}}export default new TcaMigrationsCheck;
\ No newline at end of file