[TASK] Organize Install Tool JavaScript modules into directories 97/61397/2
authorAndreas Fernandez <a.fernandez@scripting-base.de>
Thu, 1 Aug 2019 12:48:09 +0000 (14:48 +0200)
committerSusanne Moog <look@susi.dev>
Thu, 1 Aug 2019 15:45:47 +0000 (17:45 +0200)
For organizational purposes the Install Tool JavaScript modules are now
grouped into directories matching their parent module.

Resolves: #88880
Releases: master
Change-Id: Ifc9844f6e044e1b1ea6ebd394a433d7bf1ae014e
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/61397
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Susanne Moog <look@susi.dev>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Susanne Moog <look@susi.dev>
112 files changed:
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Cache.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ChangeInstallToolPassword.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ClearTables.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ClearTypo3tempFiles.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/CoreUpdate.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/CreateAdmin.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/DatabaseAnalyzer.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/DumpAutoload.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/EnvironmentCheck.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/FolderStructure.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/ImageProcessing.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/MailTest.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/PhpInfo.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/SystemInformation.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/EnvironmentCheck.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ExtensionCompatTester.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ExtensionConfiguration.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ExtensionScanner.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Features.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/FolderStructure.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ImageProcessing.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/LanguagePacks.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/LocalConfiguration.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/MailTest.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/Cache.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/ClearTables.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/ClearTypo3tempFiles.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/CreateAdmin.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/DatabaseAnalyzer.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/DumpAutoload.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/LanguagePacks.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/ResetBackendUserUc.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/PhpInfo.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Presets.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ResetBackendUserUc.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/ChangeInstallToolPassword.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/ExtensionConfiguration.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/Features.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/LocalConfiguration.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/Presets.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/SystemMaintainer.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/SystemInformation.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/SystemMaintainer.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/TcaExtTablesCheck.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/TcaMigrationsCheck.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/CoreUpdate.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/ExtensionCompatTester.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/ExtensionScanner.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/TcaExtTablesCheck.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/TcaMigrationsCheck.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/UpgradeDocs.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/UpgradeWizards.ts [new file with mode: 0644]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/UpgradeDocs.ts [deleted file]
Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/UpgradeWizards.ts [deleted file]
typo3/sysext/install/Resources/Private/Templates/Environment/Cards.html
typo3/sysext/install/Resources/Private/Templates/Maintenance/Cards.html
typo3/sysext/install/Resources/Private/Templates/Settings/Cards.html
typo3/sysext/install/Resources/Private/Templates/Upgrade/Cards.html
typo3/sysext/install/Resources/Public/JavaScript/Module/Cache.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/ChangeInstallToolPassword.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/ClearTables.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/ClearTypo3tempFiles.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/CoreUpdate.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/CreateAdmin.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/DatabaseAnalyzer.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/DumpAutoload.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/Environment/EnvironmentCheck.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Environment/FolderStructure.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Environment/ImageProcessing.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Environment/MailTest.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Environment/PhpInfo.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Environment/SystemInformation.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/EnvironmentCheck.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/ExtensionCompatTester.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/ExtensionConfiguration.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/ExtensionScanner.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/Features.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/FolderStructure.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/ImageProcessing.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/LanguagePacks.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/LocalConfiguration.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/MailTest.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/Maintenance/Cache.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Maintenance/ClearTables.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Maintenance/ClearTypo3tempFiles.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Maintenance/CreateAdmin.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Maintenance/DatabaseAnalyzer.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Maintenance/DumpAutoload.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Maintenance/LanguagePacks.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Maintenance/ResetBackendUserUc.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/PhpInfo.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/Presets.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/ResetBackendUserUc.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/Settings/ChangeInstallToolPassword.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Settings/ExtensionConfiguration.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Settings/Features.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Settings/LocalConfiguration.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Settings/Presets.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Settings/SystemMaintainer.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/SystemInformation.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/SystemMaintainer.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/TcaExtTablesCheck.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/TcaMigrationsCheck.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/Upgrade/CoreUpdate.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Upgrade/ExtensionCompatTester.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Upgrade/ExtensionScanner.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Upgrade/TcaExtTablesCheck.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Upgrade/TcaMigrationsCheck.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Upgrade/UpgradeDocs.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Upgrade/UpgradeWizards.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/UpgradeDocs.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Module/UpgradeWizards.js [deleted file]

diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Cache.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Cache.ts
deleted file mode 100644 (file)
index 14f9da5..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {InlineModuleInterface} from './InlineModuleInterface';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/Cache
- */
-class Cache implements InlineModuleInterface {
-  public initialize($trigger: JQuery): void {
-    $.ajax({
-      url: Router.getUrl('cacheClearAll', 'maintenance'),
-      cache: false,
-      beforeSend: (): void => {
-        $trigger.addClass('disabled').prop('disabled', true);
-      },
-      success: (data: any): void => {
-        if (data.success === true && Array.isArray(data.status)) {
-          if (data.status.length > 0) {
-            data.status.forEach(((element: any): void => {
-              Notification.success(element.title, element.message);
-            }));
-          }
-        } else {
-          Notification.error('Something went wrong clearing caches');
-        }
-      },
-      error: (): void => {
-        // In case the clear cache action fails (typically 500 from server), do not kill the entire
-        // install tool, instead show a notification that something went wrong.
-        Notification.error(
-          'Clearing caches went wrong on the server side. Check the system for broken extensions or missing database tables and try again',
-        );
-      },
-      complete: (): void => {
-        $trigger.removeClass('disabled').prop('disabled', false);
-      },
-    });
-  }
-}
-
-export = new Cache();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ChangeInstallToolPassword.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ChangeInstallToolPassword.ts
deleted file mode 100644 (file)
index b385945..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import PasswordStrength = require('./PasswordStrength');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/ChangeInstallToolPassword
- */
-class ChangeInstallToolPassword extends AbstractInteractableModule {
-  private selectorChangeButton: string = '.t3js-changeInstallToolPassword-change';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.getData();
-
-    currentModal.on('click', this.selectorChangeButton, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.change();
-    });
-    currentModal.on('click', '.t3-install-form-password-strength', (e: JQueryEventObject): void => {
-      PasswordStrength.initialize('.t3-install-form-password-strength');
-    });
-  }
-
-  private getData(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('changeInstallToolPasswordGetData'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          modalContent.empty().append(data.html);
-          Modal.setButtons(data.buttons);
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private change(): void {
-    const modalContent = this.getModalBody();
-    const executeToken = this.getModuleContent().data('install-tool-token');
-    $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      data: {
-        'install': {
-          'action': 'changeInstallToolPassword',
-          'token': executeToken,
-          'password': this.findInModal('.t3js-changeInstallToolPassword-password').val(),
-          'passwordCheck': this.findInModal('.t3js-changeInstallToolPassword-password-check').val(),
-        },
-      },
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true && Array.isArray(data.status)) {
-          data.status.forEach((element: any): void => {
-            Notification.showMessage('', element.message, element.severity);
-          });
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-      complete: (): void => {
-        this.findInModal('.t3js-changeInstallToolPassword-password,.t3js-changeInstallToolPassword-password-check').val('');
-      },
-    });
-  }
-}
-
-export = new ChangeInstallToolPassword();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ClearTables.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ClearTables.ts
deleted file mode 100644 (file)
index d7aad56..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/ClearTables
- */
-class ClearTables extends AbstractInteractableModule {
-  private selectorClearTrigger: string = '.t3js-clearTables-clear';
-  private selectorStatsTrigger: string = '.t3js-clearTables-stats';
-  private selectorOutputContainer: string = '.t3js-clearTables-output';
-  private selectorStatContainer: string = '.t3js-clearTables-stat-container';
-  private selectorStatTemplate: string = '.t3js-clearTables-stat-template';
-  private selectorStatDescription: string = '.t3js-clearTables-stat-description';
-  private selectorStatRows: string = '.t3js-clearTables-stat-rows';
-  private selectorStatName: string = '.t3js-clearTables-stat-name';
-
-  public initialize(currentModal: any): void {
-    this.currentModal = currentModal;
-    this.getStats();
-
-    currentModal.on('click', this.selectorStatsTrigger, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      $(this.selectorOutputContainer).empty();
-      this.getStats();
-    });
-
-    currentModal.on('click', this.selectorClearTrigger, (e: JQueryEventObject): void => {
-      const table = $(e.target).closest(this.selectorClearTrigger).data('table');
-      e.preventDefault();
-      this.clear(table);
-    });
-  }
-
-  private getStats(): void {
-    const modalContent: JQuery = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('clearTablesStats'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          modalContent.empty().append(data.html);
-          Modal.setButtons(data.buttons);
-          if (Array.isArray(data.stats) && data.stats.length > 0) {
-            data.stats.forEach((element: any): void => {
-              if (element.rowCount > 0) {
-                const aStat = modalContent.find(this.selectorStatTemplate).clone();
-                aStat.find(this.selectorStatDescription).text(element.description);
-                aStat.find(this.selectorStatName).text(element.name);
-                aStat.find(this.selectorStatRows).text(element.rowCount);
-                aStat.find(this.selectorClearTrigger).attr('data-table', element.name);
-                modalContent.find(this.selectorStatContainer).append(aStat.html());
-              }
-            });
-          }
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private clear(table: string): void {
-    const modalContent = this.getModalBody();
-    const executeToken = this.getModuleContent().data('clear-tables-clear-token');
-    $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      context: this,
-      data: {
-        'install': {
-          'action': 'clearTablesClear',
-          'token': executeToken,
-          'table': table,
-        },
-      },
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true && Array.isArray(data.status)) {
-          data.status.forEach((element: any): void => {
-            Notification.success(element.message);
-          });
-        } else {
-          Notification.error('Something went wrong');
-        }
-        this.getStats();
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new ClearTables();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ClearTypo3tempFiles.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ClearTypo3tempFiles.ts
deleted file mode 100644 (file)
index c6018b4..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/ClearTypo3tempFiles
- */
-class ClearTypo3tempFiles extends AbstractInteractableModule {
-  private selectorDeleteTrigger: string = '.t3js-clearTypo3temp-delete';
-  private selectorOutputContainer: string = '.t3js-clearTypo3temp-output';
-  private selectorStatContainer: string = '.t3js-clearTypo3temp-stat-container';
-  private selectorStatsTrigger: string = '.t3js-clearTypo3temp-stats';
-  private selectorStatTemplate: string = '.t3js-clearTypo3temp-stat-template';
-  private selectorStatNumberOfFiles: string = '.t3js-clearTypo3temp-stat-numberOfFiles';
-  private selectorStatDirectory: string = '.t3js-clearTypo3temp-stat-directory';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.getStats();
-
-    currentModal.on('click', this.selectorStatsTrigger, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      $(this.selectorOutputContainer).empty();
-      this.getStats();
-    });
-    currentModal.on('click', this.selectorDeleteTrigger, (e: JQueryEventObject): void => {
-      const folder = $(e.currentTarget).data('folder');
-      const storageUid = $(e.currentTarget).data('storage-uid');
-      e.preventDefault();
-      this.delete(folder, storageUid);
-    });
-  }
-
-  private getStats(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('clearTypo3tempFilesStats'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          modalContent.empty().append(data.html);
-          Modal.setButtons(data.buttons);
-          if (Array.isArray(data.stats) && data.stats.length > 0) {
-            data.stats.forEach((element: any): void => {
-              if (element.numberOfFiles > 0) {
-                const aStat = modalContent.find(this.selectorStatTemplate).clone();
-                aStat.find(this.selectorStatNumberOfFiles).text(element.numberOfFiles);
-                aStat.find(this.selectorStatDirectory).text(element.directory);
-                aStat.find(this.selectorDeleteTrigger).attr('data-folder', element.directory);
-                aStat.find(this.selectorDeleteTrigger).attr('data-storage-uid', element.storageUid);
-                modalContent.find(this.selectorStatContainer).append(aStat.html());
-              }
-            });
-          }
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private delete(folder: string, storageUid: number): void {
-    const modalContent = this.getModalBody();
-    const executeToken = this.getModuleContent().data('clear-typo3temp-delete-token');
-    $.ajax({
-      method: 'POST',
-      url: Router.getUrl(),
-      context: this,
-      data: {
-        'install': {
-          'action': 'clearTypo3tempFiles',
-          'token': executeToken,
-          'folder': folder,
-          'storageUid': storageUid,
-        },
-      },
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true && Array.isArray(data.status)) {
-          data.status.forEach((element: any): void => {
-            Notification.success(element.message);
-          });
-          this.getStats();
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new ClearTypo3tempFiles();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/CoreUpdate.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/CoreUpdate.ts
deleted file mode 100644 (file)
index 005c16b..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import FlashMessage = require('../Renderable/FlashMessage');
-import Severity = require('../Renderable/Severity');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-interface ActionItem {
-  loadingMessage: string;
-  finishMessage: string;
-  nextActionName: string;
-}
-
-interface ActionQueue {
-  [k: string]: ActionItem;
-}
-
-class CoreUpdate extends AbstractInteractableModule {
-  private actionQueue: ActionQueue = {
-    coreUpdateIsUpdateAvailable: {
-      loadingMessage: 'Checking for possible regular or security update',
-      finishMessage: undefined,
-      nextActionName: undefined,
-    },
-    coreUpdateCheckPreConditions: {
-      loadingMessage: 'Checking if update is possible',
-      finishMessage: 'System can be updated',
-      nextActionName: 'coreUpdateDownload',
-    },
-    coreUpdateDownload: {
-      loadingMessage: 'Downloading new core',
-      finishMessage: undefined,
-      nextActionName: 'coreUpdateVerifyChecksum',
-    },
-    coreUpdateVerifyChecksum: {
-      loadingMessage: 'Verifying checksum of downloaded core',
-      finishMessage: undefined,
-      nextActionName: 'coreUpdateUnpack',
-    },
-    coreUpdateUnpack: {
-      loadingMessage: 'Unpacking core',
-      finishMessage: undefined,
-      nextActionName: 'coreUpdateMove',
-    },
-    coreUpdateMove: {
-      loadingMessage: 'Moving core',
-      finishMessage: undefined,
-      nextActionName: 'coreUpdateActivate',
-    },
-    coreUpdateActivate: {
-      loadingMessage: 'Activating core',
-      finishMessage: 'Core updated - please reload your browser',
-      nextActionName: undefined,
-    },
-  };
-
-  private selectorOutput: string = '.t3js-coreUpdate-output';
-  private updateButton: string = '.t3js-coreUpdate-button';
-
-  /**
-   * Clone of a DOM object acts as button template
-   */
-  private buttonTemplate: any = null;
-
-  /**
-   * Fetching the templates out of the DOM
-   */
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.getData().done((): void => {
-      this.buttonTemplate = this.findInModal(this.updateButton).clone();
-    });
-
-    currentModal.on('click', '.t3js-coreUpdate-init', (e: JQueryEventObject): void => {
-      e.preventDefault();
-      // Don't use jQuery's data() function, as the DOM is re-rendered and any set data attribute gets lost.
-      // See showActionButton()
-      const action = $(e.currentTarget).attr('data-action');
-
-      this.findInModal(this.selectorOutput).empty();
-      switch (action) {
-        case 'checkForUpdate':
-          this.callAction('coreUpdateIsUpdateAvailable');
-          break;
-        case 'updateDevelopment':
-          this.update('development');
-          break;
-        case 'updateRegular':
-          this.update('regular');
-          break;
-        default:
-          throw 'Unknown update action "' + action + '"';
-      }
-    });
-  }
-
-  private getData(): JQueryXHR {
-    const modalContent = this.getModalBody();
-    return $.ajax({
-      url: Router.getUrl('coreUpdateGetData'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          modalContent.empty().append(data.html);
-          Modal.setButtons(data.buttons);
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  /**
-   * Execute core update.
-   *
-   * @param type Either 'development' or 'regular'
-   */
-  private update(type: string): void {
-    if (type !== 'development') {
-      type = 'regular';
-    }
-    this.callAction('coreUpdateCheckPreConditions', type);
-  }
-
-  /**
-   * Generic method to call actions from the queue
-   *
-   * @param actionName Name of the action to be called
-   * @param type Update type (optional)
-   */
-  private callAction(actionName: string, type?: string): void {
-    const data: any = {
-      install: {
-        action: actionName,
-      },
-    };
-    if (type !== undefined) {
-      data.install.type = type;
-    }
-    this.addLoadingMessage(this.actionQueue[actionName].loadingMessage);
-    $.ajax({
-      url: Router.getUrl(),
-      data: data,
-      cache: false,
-      success: (result: any): void => {
-        const canContinue = this.handleResult(result, this.actionQueue[actionName].finishMessage);
-        if (canContinue === true && (this.actionQueue[actionName].nextActionName !== undefined)) {
-          this.callAction(this.actionQueue[actionName].nextActionName, type);
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, this.getModalBody());
-      },
-    });
-  }
-
-  /**
-   * Handle ajax result of core update step.
-   */
-  private handleResult(data: any, successMessage: string): boolean {
-    const canContinue: boolean = data.success;
-    this.removeLoadingMessage();
-
-    if (data.status && typeof(data.status) === 'object') {
-      this.showStatusMessages(data.status);
-    }
-    if (data.action && typeof(data.action) === 'object') {
-      this.showActionButton(data.action);
-    }
-    if (successMessage) {
-      this.addMessage(Severity.ok, successMessage);
-    }
-    return canContinue;
-  }
-
-  /**
-   * Add a loading message with some text.
-   *
-   * @param messageTitle
-   */
-  private addLoadingMessage(messageTitle: string): void {
-    const domMessage = FlashMessage.render(Severity.loading, messageTitle);
-    this.findInModal(this.selectorOutput).append(domMessage);
-  }
-
-  /**
-   * Remove an enabled loading message
-   */
-  private removeLoadingMessage(): void {
-    this.findInModal(this.selectorOutput).find('.alert-loading').remove();
-  }
-
-  /**
-   * Show a list of status messages
-   *
-   * @param messages
-   */
-  private showStatusMessages(messages: any): void {
-    $.each(messages, (index: number, element: any): void => {
-      let title: string = '';
-      let message: string = '';
-      const severity: number = element.severity;
-      if (element.title) {
-        title = element.title;
-      }
-      if (element.message) {
-        message = element.message;
-      }
-      this.addMessage(severity, title, message);
-    });
-  }
-
-  /**
-   * Show an action button
-   *
-   * @param button
-   */
-  private showActionButton(button: any): void {
-    let title = false;
-    let action = false;
-    if (button.title) {
-      title = button.title;
-    }
-    if (button.action) {
-      action = button.action;
-    }
-    const domButton = this.buttonTemplate;
-    if (action) {
-      domButton.attr('data-action', action);
-    }
-    if (title) {
-      domButton.text(title);
-    }
-    this.findInModal(this.updateButton).replaceWith(domButton);
-  }
-
-  /**
-   * Show a status message
-   */
-  private addMessage(severity: number, title: string, message?: string): void {
-    const domMessage = FlashMessage.render(severity, title, message);
-    this.findInModal(this.selectorOutput).append(domMessage);
-  }
-}
-
-export = new CoreUpdate();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/CreateAdmin.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/CreateAdmin.ts
deleted file mode 100644 (file)
index 6b904d7..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import PasswordStrength = require('./PasswordStrength');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/CreateAdmin
- */
-class CreateAdmin extends AbstractInteractableModule {
-  private selectorAdminCreateButton: string = '.t3js-createAdmin-create';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.getData();
-
-    currentModal.on('click', this.selectorAdminCreateButton, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.create();
-    });
-
-    currentModal.on('click', '.t3-install-form-password-strength', (e: JQueryEventObject): void => {
-      PasswordStrength.initialize('.t3-install-form-password-strength');
-    });
-  }
-
-  private getData(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('createAdminGetData'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          modalContent.empty().append(data.html);
-          Modal.setButtons(data.buttons);
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private create(): void {
-    const modalContent = this.getModalBody();
-    const executeToken = this.getModuleContent().data('create-admin-token');
-    $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      data: {
-        'install': {
-          'action': 'createAdmin',
-          'token': executeToken,
-          'userName': this.findInModal('.t3js-createAdmin-user').val(),
-          'userPassword': this.findInModal('.t3js-createAdmin-password').val(),
-          'userPasswordCheck': this.findInModal('.t3js-createAdmin-password-check').val(),
-          'userSystemMaintainer': (this.findInModal('.t3js-createAdmin-system-maintainer').is(':checked')) ? 1 : 0,
-        },
-      },
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true && Array.isArray(data.status)) {
-          data.status.forEach((element: any): void => {
-            if (element.severity === 2) {
-              Notification.error(element.message);
-            } else {
-              Notification.success(element.title);
-            }
-          });
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-    this.findInModal('.t3js-createAdmin-user').val('');
-    this.findInModal('.t3js-createAdmin-password').val('');
-    this.findInModal('.t3js-createAdmin-password-check').val('');
-    this.findInModal('.t3js-createAdmin-system-maintainer').prop('checked', false);
-  }
-}
-
-export = new CreateAdmin();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/DatabaseAnalyzer.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/DatabaseAnalyzer.ts
deleted file mode 100644 (file)
index 1a24d4a..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import ProgressBar = require('../Renderable/ProgressBar');
-import InfoBox = require('../Renderable/InfoBox');
-import Severity = require('../Renderable/Severity');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/DatabaseAnalyzer
- */
-class DatabaseAnalyzer extends AbstractInteractableModule {
-  private selectorAnalyzeTrigger: string = '.t3js-databaseAnalyzer-analyze';
-  private selectorExecuteTrigger: string = '.t3js-databaseAnalyzer-execute';
-  private selectorOutputContainer: string = '.t3js-databaseAnalyzer-output';
-  private selectorSuggestionBlock: string = '.t3js-databaseAnalyzer-suggestion-block';
-  private selectorSuggestionList: string = '.t3js-databaseAnalyzer-suggestion-list';
-  private selectorSuggestionLineTemplate: string = '.t3js-databaseAnalyzer-suggestion-line-template';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.getData();
-
-    // Select / deselect all checkboxes
-    currentModal.on('click', '.t3js-databaseAnalyzer-suggestion-block-checkbox', (e: JQueryEventObject): void => {
-      const $element = $(e.currentTarget);
-      $element.closest('fieldset').find(':checkbox').prop('checked', (<HTMLInputElement>$element.get(0)).checked);
-    });
-    currentModal.on('click', this.selectorAnalyzeTrigger, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.analyze();
-    });
-    currentModal.on('click', this.selectorExecuteTrigger, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.execute();
-    });
-  }
-
-  private getData(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('databaseAnalyzer'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          modalContent.empty().append(data.html);
-          Modal.setButtons(data.buttons);
-          this.analyze();
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private analyze(): void {
-    const modalContent = this.getModalBody();
-    const modalFooter = this.getModalFooter();
-    const outputContainer = modalContent.find(this.selectorOutputContainer);
-    const executeTrigger = modalFooter.find(this.selectorExecuteTrigger);
-    const analyzeTrigger = modalFooter.find(this.selectorAnalyzeTrigger);
-
-    outputContainer.empty().append(ProgressBar.render(Severity.loading, 'Analyzing current database schema...', ''));
-
-    analyzeTrigger.prop('disabled', true);
-    executeTrigger.prop('disabled', true);
-
-    outputContainer.on('change', 'input[type="checkbox"]', (): void => {
-      const hasCheckedCheckboxes = outputContainer.find(':checked').length > 0;
-      executeTrigger.prop('disabled', !hasCheckedCheckboxes);
-    });
-
-    $.ajax({
-      url: Router.getUrl('databaseAnalyzerAnalyze'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          if (Array.isArray(data.status)) {
-            outputContainer.find('.alert-loading').remove();
-            data.status.forEach((element: any): void => {
-              const message = InfoBox.render(element.severity, element.title, element.message);
-              outputContainer.append(message);
-            });
-          }
-          if (Array.isArray(data.suggestions)) {
-            data.suggestions.forEach((element: any): void => {
-              const aBlock = modalContent.find(this.selectorSuggestionBlock).clone();
-              aBlock.removeClass(this.selectorSuggestionBlock.substr(1));
-              const key = element.key;
-              aBlock.find('.t3js-databaseAnalyzer-suggestion-block-legend').text(element.label);
-              aBlock.find('.t3js-databaseAnalyzer-suggestion-block-checkbox').attr('id', 't3-install-' + key + '-checkbox');
-              if (element.enabled) {
-                aBlock.find('.t3js-databaseAnalyzer-suggestion-block-checkbox').attr('checked', 'checked');
-              }
-              aBlock.find('.t3js-databaseAnalyzer-suggestion-block-label').attr('for', 't3-install-' + key + '-checkbox');
-              element.children.forEach((line: any): void => {
-                const aLine = modalContent.find(this.selectorSuggestionLineTemplate).children().clone();
-                const hash = line.hash;
-                const $checkbox = aLine.find('.t3js-databaseAnalyzer-suggestion-line-checkbox');
-                $checkbox.attr('id', 't3-install-db-' + hash).attr('data-hash', hash);
-                if (element.enabled) {
-                  $checkbox.attr('checked', 'checked');
-                }
-                aLine.find('.t3js-databaseAnalyzer-suggestion-line-label').attr('for', 't3-install-db-' + hash);
-                aLine.find('.t3js-databaseAnalyzer-suggestion-line-statement').text(line.statement);
-                if (typeof line.current !== 'undefined') {
-                  aLine.find('.t3js-databaseAnalyzer-suggestion-line-current-value').text(line.current);
-                  aLine.find('.t3js-databaseAnalyzer-suggestion-line-current').show();
-                }
-                if (typeof line.rowCount !== 'undefined') {
-                  aLine.find('.t3js-databaseAnalyzer-suggestion-line-count-value').text(line.rowCount);
-                  aLine.find('.t3js-databaseAnalyzer-suggestion-line-count').show();
-                }
-                aBlock.find(this.selectorSuggestionList).append(aLine);
-              });
-              outputContainer.append(aBlock.html());
-            });
-
-            const isInitiallyDisabled = outputContainer.find(':checked').length === 0;
-            analyzeTrigger.prop('disabled', false);
-            executeTrigger.prop('disabled', isInitiallyDisabled);
-          }
-          if (data.suggestions.length === 0 && data.status.length === 0) {
-            outputContainer.append(InfoBox.render(Severity.ok, 'Database schema is up to date. Good job!', ''));
-          }
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private execute(): void {
-    const modalContent = this.getModalBody();
-    const executeToken = this.getModuleContent().data('database-analyzer-execute-token');
-    const outputContainer = modalContent.find(this.selectorOutputContainer);
-    const selectedHashes: Array<any> = [];
-
-    outputContainer.find('.t3js-databaseAnalyzer-suggestion-line input:checked').each((index: number, element: any): void => {
-      selectedHashes.push($(element).data('hash'));
-    });
-    outputContainer.empty().append(ProgressBar.render(Severity.loading, 'Executing database updates...', ''));
-    modalContent.find(this.selectorExecuteTrigger).prop('disabled', true);
-    modalContent.find(this.selectorAnalyzeTrigger).prop('disabled', true);
-
-    $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      data: {
-        'install': {
-          'action': 'databaseAnalyzerExecute',
-          'token': executeToken,
-          'hashes': selectedHashes,
-        },
-      },
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          if (Array.isArray(data.status)) {
-            data.status.forEach((element: any): void => {
-              Notification.showMessage(element.title, element.message, element.severity);
-            });
-          }
-        }
-        this.analyze();
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new DatabaseAnalyzer();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/DumpAutoload.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/DumpAutoload.ts
deleted file mode 100644 (file)
index 3298ab0..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {InlineModuleInterface} from './InlineModuleInterface';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/DumpAutoload
- */
-class DumpAutoload implements InlineModuleInterface {
-  public initialize($trigger: JQuery): void {
-    $.ajax({
-      url: Router.getUrl('dumpAutoload'),
-      cache: false,
-      beforeSend: (): void => {
-        $trigger.addClass('disabled').prop('disabled', true);
-      },
-      success: (data: any): void => {
-        if (data.success === true && Array.isArray(data.status)) {
-          if (data.status.length > 0) {
-            data.status.forEach((element: any): void => {
-              Notification.success(element.message);
-            });
-          }
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (): void => {
-        // In case the dump action fails (typically 500 from server), do not kill the entire
-        // install tool, instead show a notification that something went wrong.
-        Notification.error('Dumping autoload files went wrong on the server side. Check the system for broken extensions and try again');
-      },
-      complete: (): void => {
-        $trigger.removeClass('disabled').prop('disabled', false);
-      },
-    });
-  }
-}
-
-export = new DumpAutoload();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/EnvironmentCheck.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/EnvironmentCheck.ts
new file mode 100644 (file)
index 0000000..390dd17
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../../Router');
+import ProgressBar = require('../../Renderable/ProgressBar');
+import InfoBox = require('../../Renderable/InfoBox');
+import Severity = require('../../Renderable/Severity');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/EnvironmentCheck
+ */
+class EnvironmentCheck extends AbstractInteractableModule {
+  private selectorGridderBadge: string = '.t3js-environmentCheck-badge';
+  private selectorExecuteTrigger: string = '.t3js-environmentCheck-execute';
+  private selectorOutputContainer: string = '.t3js-environmentCheck-output';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+
+    // Get status on initialize to have the badge and content ready
+    this.runTests();
+
+    currentModal.on('click', this.selectorExecuteTrigger, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.runTests();
+    });
+  }
+
+  private runTests(): void {
+    const modalContent = this.getModalBody();
+    const $errorBadge = $(this.selectorGridderBadge);
+    $errorBadge.text('').hide();
+    const message = ProgressBar.render(Severity.loading, 'Loading...', '');
+    modalContent.find(this.selectorOutputContainer).empty().append(message);
+    this.findInModal(this.selectorExecuteTrigger).addClass('disabled').prop('disabled', true);
+
+    $.ajax({
+      url: Router.getUrl('environmentCheckGetStatus'),
+      cache: false,
+      success: (data: any): void => {
+        modalContent.empty().append(data.html);
+        Modal.setButtons(data.buttons);
+        let warningCount = 0;
+        let errorCount = 0;
+        if (data.success === true && typeof(data.status) === 'object') {
+          $.each(data.status, (i: number, element: any): void => {
+            if (Array.isArray(element) && element.length > 0) {
+              element.forEach((aStatus: any): void => {
+                if (aStatus.severity === 1) {
+                  warningCount++;
+                }
+                if (aStatus.severity === 2) {
+                  errorCount++;
+                }
+                const aMessage = InfoBox.render(aStatus.severity, aStatus.title, aStatus.message);
+                modalContent.find(this.selectorOutputContainer).append(aMessage);
+              });
+            }
+          });
+          if (errorCount > 0) {
+            $errorBadge.removeClass('label-warning').addClass('label-danger').text(errorCount).show();
+          } else if (warningCount > 0) {
+            $errorBadge.removeClass('label-error').addClass('label-warning').text(warningCount).show();
+          }
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new EnvironmentCheck();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/FolderStructure.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/FolderStructure.ts
new file mode 100644 (file)
index 0000000..7680614
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../../Router');
+import ProgressBar = require('../../Renderable/ProgressBar');
+import InfoBox = require('../../Renderable/InfoBox');
+import Severity = require('../../Renderable/Severity');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/FolderStructure
+ */
+class FolderStructure extends AbstractInteractableModule {
+  private selectorGridderBadge: string = '.t3js-folderStructure-badge';
+  private selectorOutputContainer: string = '.t3js-folderStructure-output';
+  private selectorErrorContainer: string = '.t3js-folderStructure-errors';
+  private selectorErrorList: string = '.t3js-folderStructure-errors-list';
+  private selectorErrorFixTrigger: string = '.t3js-folderStructure-errors-fix';
+  private selectorOkContainer: string = '.t3js-folderStructure-ok';
+  private selectorOkList: string = '.t3js-folderStructure-ok-list';
+  private selectorPermissionContainer: string = '.t3js-folderStructure-permissions';
+
+  private static removeLoadingMessage($container: JQuery): void {
+    $container.find('.alert-loading').remove();
+  }
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+
+    // Get status on initialize to have the badge and content ready
+    this.getStatus();
+
+    currentModal.on('click', this.selectorErrorFixTrigger, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      $(e.currentTarget).addClass('disabled').prop('disabled', true);
+      this.fix();
+    });
+  }
+
+  private getStatus(): void {
+    const modalContent = this.getModalBody();
+    const $errorBadge = $(this.selectorGridderBadge);
+    $errorBadge.text('').hide();
+    modalContent.find(this.selectorOutputContainer).empty().append(
+      ProgressBar.render(Severity.loading, 'Loading...', ''),
+    );
+    $.ajax({
+      url: Router.getUrl('folderStructureGetStatus'),
+      cache: false,
+      success: (data: any): void => {
+        modalContent.empty().append(data.html);
+        Modal.setButtons(data.buttons);
+        if (data.success === true && Array.isArray(data.errorStatus)) {
+          let errorCount = 0;
+          if (data.errorStatus.length > 0) {
+            modalContent.find(this.selectorErrorContainer).show();
+            modalContent.find(this.selectorErrorList).empty();
+            data.errorStatus.forEach(((aElement: any): void => {
+              errorCount++;
+              $errorBadge.text(errorCount).show();
+              const aMessage = InfoBox.render(aElement.severity, aElement.title, aElement.message);
+              modalContent.find(this.selectorErrorList).append(aMessage);
+            }));
+          } else {
+            modalContent.find(this.selectorErrorContainer).hide();
+          }
+        }
+        if (data.success === true && Array.isArray(data.okStatus)) {
+          if (data.okStatus.length > 0) {
+            modalContent.find(this.selectorOkContainer).show();
+            modalContent.find(this.selectorOkList).empty();
+            data.okStatus.forEach(((aElement: any): void => {
+              const aMessage = InfoBox.render(aElement.severity, aElement.title, aElement.message);
+              modalContent.find(this.selectorOkList).append(aMessage);
+            }));
+          } else {
+            modalContent.find(this.selectorOkContainer).hide();
+          }
+        }
+        let element = data.folderStructureFilePermissionStatus;
+        modalContent.find(this.selectorPermissionContainer).empty().append(
+          InfoBox.render(element.severity, element.title, element.message),
+        );
+        element = data.folderStructureDirectoryPermissionStatus;
+        modalContent.find(this.selectorPermissionContainer).append(
+          InfoBox.render(element.severity, element.title, element.message),
+        );
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private fix(): void {
+    const modalContent: JQuery = this.getModalBody();
+    const $outputContainer: JQuery = this.findInModal(this.selectorOutputContainer);
+    const message: any = ProgressBar.render(Severity.loading, 'Loading...', '');
+    $outputContainer.empty().html(message);
+    $.ajax({
+      url: Router.getUrl('folderStructureFix'),
+      cache: false,
+      success: (data: any): void => {
+        FolderStructure.removeLoadingMessage($outputContainer);
+        if (data.success === true && Array.isArray(data.fixedStatus)) {
+          if (data.fixedStatus.length > 0) {
+            data.fixedStatus.forEach((element: any): void => {
+              $outputContainer.append(
+                InfoBox.render(element.severity, element.title, element.message),
+              );
+            });
+          } else {
+            $outputContainer.append(
+              InfoBox.render(Severity.warning, 'Nothing fixed', ''),
+            );
+          }
+          this.getStatus();
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new FolderStructure();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/ImageProcessing.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/ImageProcessing.ts
new file mode 100644 (file)
index 0000000..804541b
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../../Router');
+import InfoBox = require('../../Renderable/InfoBox');
+import Severity = require('../../Renderable/Severity');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/ImageProcessing
+ */
+class ImageProcessing extends AbstractInteractableModule {
+  private selectorExecuteTrigger: string = '.t3js-imageProcessing-execute';
+  private selectorTestContainer: string = '.t3js-imageProcessing-twinContainer';
+  private selectorTwinImageTemplate: string = '.t3js-imageProcessing-twinImage-template';
+  private selectorCommandContainer: string = '.t3js-imageProcessing-command';
+  private selectorCommandText: string = '.t3js-imageProcessing-command-text';
+  private selectorTwinImages: string = '.t3js-imageProcessing-images';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getData();
+
+    currentModal.on('click', this.selectorExecuteTrigger, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.runTests();
+    });
+  }
+
+  private getData(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('imageProcessingGetData'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          Modal.setButtons(data.buttons);
+          this.runTests();
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private runTests(): void {
+    const modalContent = this.getModalBody();
+    const $triggerButton = this.findInModal(this.selectorExecuteTrigger);
+    $triggerButton.addClass('disabled').prop('disabled', true);
+
+    const $twinImageTemplate = this.findInModal(this.selectorTwinImageTemplate);
+    const promises: Array<JQueryXHR> = [];
+    modalContent.find(this.selectorTestContainer).each((index: number, element: any): void => {
+      const $container: JQuery = $(element);
+      const testType: string = $container.data('test');
+      const message: any = InfoBox.render(Severity.loading, 'Loading...', '');
+      $container.empty().html(message);
+      promises.push($.ajax({
+        url: Router.getUrl(testType),
+        cache: false,
+        success: (data: any): void => {
+          if (data.success === true) {
+            $container.empty();
+            if (Array.isArray(data.status)) {
+              data.status.forEach((aElement: any): void => {
+                const aMessage = InfoBox.render(element.severity, element.title, element.message);
+                $container.append(aMessage);
+              });
+            }
+            const $aTwin = $twinImageTemplate.clone();
+            $aTwin.removeClass('t3js-imageProcessing-twinImage-template');
+            if (data.fileExists === true) {
+              $aTwin.find('img.reference').attr('src', data.referenceFile);
+              $aTwin.find('img.result').attr('src', data.outputFile);
+              $aTwin.find(this.selectorTwinImages).show();
+            }
+            if (Array.isArray(data.command) && data.command.length > 0) {
+              $aTwin.find(this.selectorCommandContainer).show();
+              const commandText: Array<string> = [];
+              data.command.forEach((aElement: any): void => {
+                commandText.push('<strong>Command:</strong>\n' + aElement[1]);
+                if (aElement.length === 3) {
+                  commandText.push('<strong>Result:</strong>\n' + aElement[2]);
+                }
+              });
+              $aTwin.find(this.selectorCommandText).html(commandText.join('\n'));
+            }
+            $container.append($aTwin);
+          }
+        },
+        error: (xhr: XMLHttpRequest): void => {
+          Router.handleAjaxError(xhr, modalContent);
+        },
+      }));
+    });
+
+    $.when.apply($, promises).done((): void => {
+      $triggerButton.removeClass('disabled').prop('disabled', false);
+    });
+  }
+}
+
+export = new ImageProcessing();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/MailTest.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/MailTest.ts
new file mode 100644 (file)
index 0000000..deb3bfd
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../../Router');
+import ProgressBar = require('../../Renderable/ProgressBar');
+import Severity = require('../../Renderable/Severity');
+import InfoBox = require('../../Renderable/InfoBox');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/CreateAdmin
+ */
+class MailTest extends AbstractInteractableModule {
+  private selectorOutputContainer: string = '.t3js-mailTest-output';
+  private selectorMailTestButton: string = '.t3js-mailTest-execute';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getData();
+    currentModal.on('click', this.selectorMailTestButton, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.send();
+    });
+  }
+
+  private getData(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('mailTestGetData'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          Modal.setButtons(data.buttons);
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private send(): void {
+    const executeToken: string = this.getModuleContent().data('mail-test-token');
+    const $outputContainer: JQuery = this.findInModal(this.selectorOutputContainer);
+    const message: any = ProgressBar.render(Severity.loading, 'Loading...', '');
+    $outputContainer.empty().html(message);
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: {
+        'install': {
+          'action': 'mailTest',
+          'token': executeToken,
+          'email': this.findInModal('.t3js-mailTest-email').val(),
+        },
+      },
+      cache: false,
+      success: (data: any): void => {
+        $outputContainer.empty();
+        if (data.success === true && Array.isArray(data.status)) {
+          data.status.forEach((element: any): void => {
+            const aMessage: any = InfoBox.render(element.severity, element.title, element.message);
+            $outputContainer.html(aMessage);
+          });
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (): void => {
+        // 500 can happen here if the mail configuration is broken
+        Notification.error('Something went wrong');
+      },
+    });
+  }
+}
+
+export = new MailTest();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/PhpInfo.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/PhpInfo.ts
new file mode 100644 (file)
index 0000000..e85c59d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/PhpInfo
+ */
+class PhpInfo extends AbstractInteractableModule {
+  public initialize(currentModal: any): void {
+    this.currentModal = currentModal;
+    this.getData();
+  }
+
+  private getData(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('phpInfoGetData'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new PhpInfo();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/SystemInformation.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Environment/SystemInformation.ts
new file mode 100644 (file)
index 0000000..6e30a38
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/SystemInformation
+ */
+class SystemInformation extends AbstractInteractableModule {
+  public initialize(currentModal: any): void {
+    this.currentModal = currentModal;
+    this.getData();
+  }
+
+  private getData(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('systemInformationGetData'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new SystemInformation();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/EnvironmentCheck.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/EnvironmentCheck.ts
deleted file mode 100644 (file)
index 59caa8b..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import 'bootstrap';
-import Router = require('../Router');
-import ProgressBar = require('../Renderable/ProgressBar');
-import InfoBox = require('../Renderable/InfoBox');
-import Severity = require('../Renderable/Severity');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/EnvironmentCheck
- */
-class EnvironmentCheck extends AbstractInteractableModule {
-  private selectorGridderBadge: string = '.t3js-environmentCheck-badge';
-  private selectorExecuteTrigger: string = '.t3js-environmentCheck-execute';
-  private selectorOutputContainer: string = '.t3js-environmentCheck-output';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-
-    // Get status on initialize to have the badge and content ready
-    this.runTests();
-
-    currentModal.on('click', this.selectorExecuteTrigger, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.runTests();
-    });
-  }
-
-  private runTests(): void {
-    const modalContent = this.getModalBody();
-    const $errorBadge = $(this.selectorGridderBadge);
-    $errorBadge.text('').hide();
-    const message = ProgressBar.render(Severity.loading, 'Loading...', '');
-    modalContent.find(this.selectorOutputContainer).empty().append(message);
-    this.findInModal(this.selectorExecuteTrigger).addClass('disabled').prop('disabled', true);
-
-    $.ajax({
-      url: Router.getUrl('environmentCheckGetStatus'),
-      cache: false,
-      success: (data: any): void => {
-        modalContent.empty().append(data.html);
-        Modal.setButtons(data.buttons);
-        let warningCount = 0;
-        let errorCount = 0;
-        if (data.success === true && typeof(data.status) === 'object') {
-          $.each(data.status, (i: number, element: any): void => {
-            if (Array.isArray(element) && element.length > 0) {
-              element.forEach((aStatus: any): void => {
-                if (aStatus.severity === 1) {
-                  warningCount++;
-                }
-                if (aStatus.severity === 2) {
-                  errorCount++;
-                }
-                const aMessage = InfoBox.render(aStatus.severity, aStatus.title, aStatus.message);
-                modalContent.find(this.selectorOutputContainer).append(aMessage);
-              });
-            }
-          });
-          if (errorCount > 0) {
-            $errorBadge.removeClass('label-warning').addClass('label-danger').text(errorCount).show();
-          } else if (warningCount > 0) {
-            $errorBadge.removeClass('label-error').addClass('label-warning').text(warningCount).show();
-          }
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new EnvironmentCheck();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ExtensionCompatTester.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ExtensionCompatTester.ts
deleted file mode 100644 (file)
index d055945..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import 'bootstrap';
-import Router = require('../Router');
-import ProgressBar = require('../Renderable/ProgressBar');
-import InfoBox = require('../Renderable/InfoBox');
-import Severity = require('../Renderable/Severity');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/ExtensionCompatTester
- */
-class ExtensionCompatTester extends AbstractInteractableModule {
-  private selectorCheckTrigger: string = '.t3js-extensionCompatTester-check';
-  private selectorUninstallTrigger: string = '.t3js-extensionCompatTester-uninstall';
-  private selectorOutputContainer: string = '.t3js-extensionCompatTester-output';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.getLoadedExtensionList();
-
-    currentModal.on('click', this.selectorCheckTrigger, (e: JQueryEventObject): void => {
-      this.findInModal(this.selectorUninstallTrigger).addClass('hidden');
-      this.findInModal(this.selectorOutputContainer).empty();
-      this.getLoadedExtensionList();
-    });
-    currentModal.on('click', this.selectorUninstallTrigger, (e: JQueryEventObject): void => {
-      this.uninstallExtension($(e.target).data('extension'));
-    });
-  }
-
-  private getLoadedExtensionList(): void {
-    this.findInModal(this.selectorCheckTrigger).addClass('disabled').prop('disabled', true);
-    this.findInModal('.modal-loading').hide();
-    const modalContent = this.getModalBody();
-    const modalFooter = this.getModalFooter();
-    const $outputContainer = this.findInModal(this.selectorOutputContainer);
-    const message = ProgressBar.render(Severity.loading, 'Loading...', '');
-    $outputContainer.append(message);
-
-    $.ajax({
-      url: Router.getUrl('extensionCompatTesterLoadedExtensionList'),
-      cache: false,
-      success: (data: any): void => {
-        modalContent.empty().append(data.html);
-        Modal.setButtons(data.buttons);
-        const $innerOutputContainer: JQuery = this.findInModal(this.selectorOutputContainer);
-        const progressBar = ProgressBar.render(Severity.loading, 'Loading...', '');
-        $innerOutputContainer.append(progressBar);
-
-        if (data.success === true && Array.isArray(data.extensions)) {
-          const loadExtLocalconf = (): void => {
-            const promises: Array<any> = [];
-            data.extensions.forEach((extension: any): void => {
-              promises.push(this.loadExtLocalconf(extension));
-            });
-            return $.when.apply($, promises).done((): void => {
-              const aMessage = InfoBox.render(Severity.ok, 'ext_localconf.php of all loaded extensions successfully loaded', '');
-              $innerOutputContainer.append(aMessage);
-            });
-          };
-
-          const loadExtTables = (): void => {
-            const promises: Array<any> = [];
-            data.extensions.forEach((extension: any): void => {
-              promises.push(this.loadExtTables(extension));
-            });
-            return $.when.apply($, promises).done((): void => {
-              const aMessage = InfoBox.render(Severity.ok, 'ext_tables.php of all loaded extensions successfully loaded', '');
-              $innerOutputContainer.append(aMessage);
-            });
-          };
-
-          $.when(loadExtLocalconf(), loadExtTables()).fail((response: any): void => {
-            const aMessage = InfoBox.render(
-              Severity.error,
-              'Loading ' + response.scope + ' of extension "' + response.extension + '" failed',
-            );
-            $innerOutputContainer.append(aMessage);
-            modalFooter.find(this.selectorUninstallTrigger)
-              .text('Unload extension "' + response.extension + '"')
-              .attr('data-extension', response.extension)
-              .removeClass('hidden');
-          }).always((): void => {
-            $innerOutputContainer.find('.alert-loading').remove();
-            this.findInModal(this.selectorCheckTrigger).removeClass('disabled').prop('disabled', false);
-          });
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private loadExtLocalconf(extension: string): JQueryPromise<{}> {
-    const executeToken = this.getModuleContent().data('extension-compat-tester-load-ext_localconf-token');
-    const $ajax = $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      cache: false,
-      data: {
-        'install': {
-          'action': 'extensionCompatTesterLoadExtLocalconf',
-          'token': executeToken,
-          'extension': extension,
-        },
-      },
-    });
-
-    return $ajax.promise().then(null, (): any => {
-      throw {
-        scope: 'ext_localconf.php',
-        extension: extension,
-      };
-    });
-  }
-
-  private loadExtTables(extension: string): JQueryPromise<{}> {
-    const executeToken = this.getModuleContent().data('extension-compat-tester-load-ext_tables-token');
-    const $ajax = $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      cache: false,
-      data: {
-        'install': {
-          'action': 'extensionCompatTesterLoadExtTables',
-          'token': executeToken,
-          'extension': extension,
-        },
-      },
-    });
-
-    return $ajax.promise().then(null, (): any => {
-      throw {
-        scope: 'ext_tables.php',
-        extension: extension,
-      };
-    });
-  }
-
-  /**
-   * Send an ajax request to uninstall an extension (or multiple extensions)
-   *
-   * @param extension string of extension(s) - may be comma separated
-   */
-  private uninstallExtension(extension: string): void {
-    const executeToken = this.getModuleContent().data('extension-compat-tester-uninstall-extension-token');
-    const modalContent = this.getModalBody();
-    const $outputContainer = $(this.selectorOutputContainer);
-    const message = ProgressBar.render(Severity.loading, 'Loading...', '');
-    $outputContainer.append(message);
-    $.ajax({
-      url: Router.getUrl(),
-      cache: false,
-      method: 'POST',
-      data: {
-        'install': {
-          'action': 'extensionCompatTesterUninstallExtension',
-          'token': executeToken,
-          'extension': extension,
-        },
-      },
-      success: (data: any): void => {
-        if (data.success) {
-          if (Array.isArray(data.status)) {
-            data.status.forEach((element: any): void => {
-              const aMessage = InfoBox.render(element.severity, element.title, element.message);
-              modalContent.find(this.selectorOutputContainer).empty().append(aMessage);
-            });
-          }
-          this.findInModal(this.selectorUninstallTrigger).addClass('hidden');
-          this.getLoadedExtensionList();
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new ExtensionCompatTester();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ExtensionConfiguration.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ExtensionConfiguration.ts
deleted file mode 100644 (file)
index 6071c94..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import 'bootstrap';
-import Router = require('../Router');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/ExtensionConfiguration
- */
-class ExtensionConfiguration extends AbstractInteractableModule {
-  private selectorFormListener: string = '.t3js-extensionConfiguration-form';
-  private selectorSearchInput: string = '.t3js-extensionConfiguration-search';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.getContent();
-
-    // Focus search field on certain user interactions
-    currentModal.on('keydown', (e: JQueryEventObject): void => {
-      const $searchInput = currentModal.find(this.selectorSearchInput);
-      if (e.ctrlKey || e.metaKey) {
-        // Focus search field on ctrl-f
-        if (String.fromCharCode(e.which).toLowerCase() === 'f') {
-          e.preventDefault();
-          $searchInput.focus();
-        }
-      } else if (e.keyCode === 27) {
-        // Clear search on ESC key
-        e.preventDefault();
-        $searchInput.val('').focus();
-      }
-    });
-
-    // Perform expand collapse on search matches
-    currentModal.on('keyup', this.selectorSearchInput, (e: JQueryEventObject): void => {
-      const typedQuery = $(e.target).val();
-      const $searchInput = currentModal.find(this.selectorSearchInput);
-      currentModal.find('.search-item').each((index: number, element: any): void => {
-        const $item = $(element);
-        if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) {
-          $item.removeClass('hidden').addClass('searchhit');
-        } else {
-          $item.removeClass('searchhit').addClass('hidden');
-        }
-      });
-      currentModal.find('.searchhit').collapse('show');
-      // Make search field clearable
-      require(['jquery.clearable'], (): void => {
-        $searchInput.clearable().focus();
-      });
-    });
-
-    currentModal.on('submit', this.selectorFormListener, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.write($(e.currentTarget));
-    });
-  }
-
-  private getContent(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('extensionConfigurationGetContent'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          if (Array.isArray(data.status)) {
-            data.status.forEach((element: any): void => {
-              Notification.success(element.title, element.message);
-            });
-          }
-          modalContent.html(data.html);
-          this.initializeWrap();
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  /**
-   * Submit the form and show the result message
-   *
-   * @param {JQuery} $form The form of the current extension
-   */
-  private write($form: JQuery): void {
-    const modalContent = this.getModalBody();
-    const executeToken = this.getModuleContent().data('extension-configuration-write-token');
-    const extensionConfiguration: any = {};
-    $.each($form.serializeArray(), (index: number, element: any): void => {
-      extensionConfiguration[element.name] = element.value;
-    });
-
-    $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      data: {
-        'install': {
-          'token': executeToken,
-          'action': 'extensionConfigurationWrite',
-          'extensionKey': $form.attr('data-extensionKey'),
-          'extensionConfiguration': extensionConfiguration,
-        },
-      },
-      success: (data: any): void => {
-        if (data.success === true && Array.isArray(data.status)) {
-          data.status.forEach((element: any): void => {
-            Notification.showMessage(element.title, element.message, element.severity);
-          });
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    }).always((): void => {
-      // empty method? why? I guess there is a reason, so let's keep it for the time being.
-    });
-  }
-
-  /**
-   * configuration properties
-   */
-  private initializeWrap(): void {
-    this.findInModal('.t3js-emconf-offset').each((index: number, element: any): void => {
-      const $me = $(element);
-      const $parent = $me.parent();
-      const id = $me.attr('id');
-      const val = $me.attr('value');
-      const valArr = val.split(',');
-
-      $me
-        .attr('data-offsetfield-x', '#' + id + '_offset_x')
-        .attr('data-offsetfield-y', '#' + id + '_offset_y')
-        .wrap('<div class="hidden"></div>');
-
-      const elementX = $('<div>', {'class': 'form-multigroup-item'}).append(
-        $('<div>', {'class': 'input-group'}).append(
-          $('<div>', {'class': 'input-group-addon'}).text('x'),
-          $('<input>', {
-            'id': id + '_offset_x',
-            'class': 'form-control t3js-emconf-offsetfield',
-            'data-target': '#' + id,
-            'value': $.trim(valArr[0]),
-          }),
-        ),
-      );
-      const elementY = $('<div>', {'class': 'form-multigroup-item'}).append(
-        $('<div>', {'class': 'input-group'}).append(
-          $('<div>', {'class': 'input-group-addon'}).text('y'),
-          $('<input>', {
-            'id': id + '_offset_y',
-            'class': 'form-control t3js-emconf-offsetfield',
-            'data-target': '#' + id,
-            'value': $.trim(valArr[1]),
-          }),
-        ),
-      );
-
-      const offsetGroup = $('<div>', {'class': 'form-multigroup-wrap'}).append(elementX, elementY);
-      $parent.append(offsetGroup);
-      $parent.find('.t3js-emconf-offsetfield').keyup((evt: JQueryEventObject): void => {
-        const $target = $parent.find($(evt.currentTarget).data('target'));
-        $target.val($parent.find($target.data('offsetfield-x')).val() + ',' + $parent.find($target.data('offsetfield-y')).val());
-      });
-    });
-
-    this.findInModal('.t3js-emconf-wrap').each((index: number, element: any): void => {
-      const $me = $(element);
-      const $parent = $me.parent();
-      const id = $me.attr('id');
-      const val = $me.attr('value');
-      const valArr = val.split('|');
-
-      $me.attr('data-wrapfield-start', '#' + id + '_wrap_start')
-        .attr('data-wrapfield-end', '#' + id + '_wrap_end')
-        .wrap('<div class="hidden"></div>');
-
-      const wrapGroup = $('<div>', {'class': 'form-multigroup-wrap'}).append(
-        $('<div>', {'class': 'form-multigroup-item'}).append(
-          $('<input>', {
-            'id': id + '_wrap_start',
-            'class': 'form-control t3js-emconf-wrapfield',
-            'data-target': '#' + id,
-            'value': $.trim(valArr[0]),
-          }),
-        ),
-        $('<div>', {'class': 'form-multigroup-item'}).append(
-          $('<input>', {
-            'id': id + '_wrap_end',
-            'class': 'form-control t3js-emconf-wrapfield',
-            'data-target': '#' + id,
-            'value': $.trim(valArr[1]),
-          }),
-        ),
-      );
-      $parent.append(wrapGroup);
-      $parent.find('.t3js-emconf-wrapfield').keyup((evt: JQueryEventObject): void => {
-        const $target = $parent.find($(evt.currentTarget).data('target'));
-        $target.val($parent.find($target.data('wrapfield-start')).val() + '|' + $parent.find($target.data('wrapfield-end')).val());
-      });
-    });
-  }
-}
-
-export = new ExtensionConfiguration();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ExtensionScanner.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ExtensionScanner.ts
deleted file mode 100644 (file)
index 6dff7f4..0000000
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import 'bootstrap';
-import AjaxQueue = require('../Ajax/AjaxQueue');
-import Router = require('../Router');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-interface FileData {
-  success: boolean;
-  matches: Array<Match>;
-  isFileIgnored: boolean;
-  effectiveCodeLines: number;
-  ignoredLines: number;
-}
-
-interface Match {
-  uniqueId: string;
-  message: string;
-  indicator: string;
-  silenced: boolean;
-  lineContent: string;
-  line: number;
-  restFiles: Array<RestFile>;
-}
-
-interface RestFile {
-  uniqueId: string;
-  version: string;
-  headline: string;
-  content: string;
-  class: string;
-  file_hash: string;
-}
-
-class ExtensionScanner extends AbstractInteractableModule {
-  private listOfAffectedRestFileHashes: Array<any> = [];
-  private selectorExtensionContainer: string = '.t3js-extensionScanner-extension';
-  private selectorNumberOfFiles: string = '.t3js-extensionScanner-number-of-files';
-  private selectorScanSingleTrigger: string = '.t3js-extensionScanner-scan-single';
-  private selectorExtensionScanButton: string = '.t3js-extensionScanner-scan-all';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.getData();
-
-    currentModal.on('show.bs.collapse', this.selectorExtensionContainer, (e: JQueryEventObject): void => {
-      // Scan a single extension by opening the panel
-      const $me = $(e.currentTarget);
-      if (typeof $me.data('scanned') === 'undefined') {
-        const extension = $me.data('extension');
-        this.scanSingleExtension(extension);
-        $me.data('scanned', true);
-      }
-    }).on('click', this.selectorScanSingleTrigger, (e: JQueryEventObject): void => {
-      // Scan a single extension by clicking "Rescan"
-      e.preventDefault();
-      const extension = $(e.currentTarget).closest(this.selectorExtensionContainer).data('extension');
-      this.scanSingleExtension(extension);
-    }).on('click', this.selectorExtensionScanButton, (e: JQueryEventObject): void => {
-      // Scan all button
-      e.preventDefault();
-      $(e.currentTarget).addClass('disabled').prop('disabled', true);
-      const $extensions = currentModal.find(this.selectorExtensionContainer);
-      this.scanAll($extensions);
-    });
-  }
-
-  private getData(): void {
-    const modalContent = this.getModalBody();
-    AjaxQueue.add({
-      url: Router.getUrl('extensionScannerGetData'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          modalContent.empty().append(data.html);
-          Modal.setButtons(data.buttons);
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private getExtensionSelector(extension: string): string {
-    return this.selectorExtensionContainer + '-' + extension;
-  }
-
-  private scanAll($extensions: JQuery): void {
-    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();
-    $extensions.each((index: number, element: any): void => {
-      const $me: JQuery = $(element);
-      const extension = $me.data('extension');
-      this.scanSingleExtension(extension);
-      $me.data('scanned', true);
-    });
-  }
-
-  private setStatusMessageForScan(extension: string, doneFiles: number, numberOfFiles: number): void {
-    this.findInModal(this.getExtensionSelector(extension))
-      .find(this.selectorNumberOfFiles)
-      .text('Checked ' + doneFiles + ' of ' + numberOfFiles + ' files');
-  }
-
-  private setProgressForScan(extension: string, doneFiles: number, numberOfFiles: number): void {
-    const percent = (doneFiles / numberOfFiles) * 100;
-    this.findInModal(this.getExtensionSelector(extension))
-      .find('.panel-progress-bar')
-      .css('width', percent + '%')
-      .attr('aria-valuenow', percent)
-      .find('span')
-      .text(percent + '%');
-  }
-
-  private setProgressForAll(): void {
-    const numberOfExtensions: number = this.findInModal(this.selectorExtensionContainer).length;
-    const numberOfSuccess: number = this.findInModal(this.selectorExtensionContainer
-      + '.t3js-extensionscan-finished.panel-success').length;
-    const numberOfWarning: number = this.findInModal(this.selectorExtensionContainer
-      + '.t3js-extensionscan-finished.panel-warning').length;
-    const numberOfError: number = this.findInModal(this.selectorExtensionContainer
-      + '.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');
-      AjaxQueue.add({
-        url: Router.getUrl(),
-        method: 'POST',
-        data: {
-          'install': {
-            'action': 'extensionScannerMarkFullyScannedRestFiles',
-            'token': this.getModuleContent().data('extension-scanner-mark-fully-scanned-rest-files-token'),
-            'hashes': this.uniqueArray(this.listOfAffectedRestFileHashes),
-          },
-        },
-        cache: false,
-        success: (data: any): void => {
-          if (data.success === true) {
-            Notification.success('Marked not affected files', 'Marked ' + data.markedAsNotAffected + ' ReST files as not affected.');
-          }
-        },
-        error: (xhr: XMLHttpRequest): void => {
-          Router.handleAjaxError(xhr, modalContent);
-        },
-      });
-    }
-  }
-
-  /**
-   * Helper method removing duplicate entries from an array
-   */
-  private uniqueArray(anArray: Array<any>): Array<any> {
-    return anArray.filter((value, index, self): boolean => {
-      return self.indexOf(value) === index;
-    });
-  }
-
-  /**
-   * Handle a single extension scan
-   */
-  private scanSingleExtension(extension: string): void {
-    const executeToken = this.getModuleContent().data('extension-scanner-files-token');
-    const modalContent = this.getModalBody();
-    const $extensionContainer = this.findInModal(this.getExtensionSelector(extension));
-    const hitTemplate = '#t3js-extensionScanner-file-hit-template';
-    const restTemplate = '#t3js-extensionScanner-file-hit-rest-template';
-    let hitFound = false;
-    $extensionContainer.removeClass('panel-danger panel-warning panel-success t3js-extensionscan-finished');
-    $extensionContainer.data('hasRun', 'true');
-    $extensionContainer.find('.t3js-extensionScanner-scan-single').text('Scanning...').attr('disabled', 'disabled');
-    $extensionContainer.find('.t3js-extensionScanner-extension-body-loc').empty().text('0');
-    $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').empty().text('0');
-    $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').empty().text('0');
-    this.setProgressForAll();
-    AjaxQueue.add({
-      url: Router.getUrl(),
-      method: 'POST',
-      data: {
-        'install': {
-          'action': 'extensionScannerFiles',
-          'token': executeToken,
-          'extension': extension,
-        },
-      },
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true && Array.isArray(data.files)) {
-          const numberOfFiles = data.files.length;
-          if (numberOfFiles > 0) {
-            this.setStatusMessageForScan(extension, 0, numberOfFiles);
-            $extensionContainer.find('.t3js-extensionScanner-extension-body').text('');
-            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,
-                  },
-                },
-                url: Router.getUrl(),
-                cache: false,
-                success: (fileData: FileData): void => {
-                  doneFiles++;
-                  this.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
-                  this.setProgressForScan(extension, doneFiles, numberOfFiles);
-                  if (fileData.success && $.isArray(fileData.matches)) {
-                    fileData.matches.forEach((match: Match): void => {
-                      hitFound = true;
-                      const aMatch: any = modalContent.find(hitTemplate).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" title="Reliable match, false positive unlikely">strong</span>');
-                      } else {
-                        aMatch.find('.t3js-extensionScanner-hit-file-panel-head .badges')
-                          .append('<span class="badge" 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" 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 ($.isArray(match.restFiles)) {
-                        match.restFiles.forEach((restFile: RestFile): void => {
-                          const aRest = modalContent.find(restTemplate).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);
-                      $extensionContainer.find('.t3js-extensionScanner-extension-body').removeClass('hide').append(aMatch);
-                      if (panelClass === 'panel-danger') {
-                        $extensionContainer.removeClass('panel-warning').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);
-                    }
-                    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);
-                  }
-                  if (doneFiles === numberOfFiles) {
-                    if (!hitFound) {
-                      $extensionContainer.addClass('panel-success');
-                    }
-                    $extensionContainer.addClass('t3js-extensionscan-finished');
-                    this.setProgressForAll();
-                    $extensionContainer.find('.t3js-extensionScanner-scan-single').text('Rescan').attr('disabled', null);
-                  }
-                },
-                error: (xhr: XMLHttpRequest): void => {
-                  doneFiles = doneFiles + 1;
-                  this.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
-                  this.setProgressForScan(extension, doneFiles, numberOfFiles);
-                  this.setProgressForAll();
-                  Notification.error('Oops, an error occurred', 'Please look at the console output for details');
-                  console.error(xhr);
-                },
-                });
-              });
-            } else {
-              Notification.warning('No files found', 'The extension EXT:' + extension + ' contains no files we can scan');
-            }
-          } else {
-            Notification.error('Oops, an error occurred', 'Please look at the console output for details');
-            console.error(data);
-          }
-        },
-        error: (xhr: XMLHttpRequest): void => {
-          Router.handleAjaxError(xhr, modalContent);
-        },
-      },
-    );
-  }
-}
-
-export = new ExtensionScanner();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Features.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Features.ts
deleted file mode 100644 (file)
index b71f5bc..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/Features
- */
-class Features extends AbstractInteractableModule {
-  private selectorSaveTrigger: string = '.t3js-features-save';
-
-  public initialize(currentModal: any): void {
-    this.currentModal = currentModal;
-    this.getContent();
-
-    currentModal.on('click', this.selectorSaveTrigger, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.save();
-    });
-  }
-
-  private getContent(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('featuresGetContent'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
-          modalContent.empty().append(data.html);
-          Modal.setButtons(data.buttons);
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private save(): void {
-    const modalContent = this.getModalBody();
-    const executeToken = this.getModuleContent().data('features-save-token');
-    const postData: any = {};
-    $(this.findInModal('form').serializeArray()).each((index: number, element: any): void => {
-      postData[element.name] = element.value;
-    });
-    postData['install[action]'] = 'featuresSave';
-    postData['install[token]'] = executeToken;
-    $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      data: postData,
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true && Array.isArray(data.status)) {
-          data.status.forEach((element: any): void => {
-            Notification.showMessage(element.title, element.message, element.severity);
-          });
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new Features();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/FolderStructure.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/FolderStructure.ts
deleted file mode 100644 (file)
index eb5acd6..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import 'bootstrap';
-import Router = require('../Router');
-import ProgressBar = require('../Renderable/ProgressBar');
-import InfoBox = require('../Renderable/InfoBox');
-import Severity = require('../Renderable/Severity');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/FolderStructure
- */
-class FolderStructure extends AbstractInteractableModule {
-  private selectorGridderBadge: string = '.t3js-folderStructure-badge';
-  private selectorOutputContainer: string = '.t3js-folderStructure-output';
-  private selectorErrorContainer: string = '.t3js-folderStructure-errors';
-  private selectorErrorList: string = '.t3js-folderStructure-errors-list';
-  private selectorErrorFixTrigger: string = '.t3js-folderStructure-errors-fix';
-  private selectorOkContainer: string = '.t3js-folderStructure-ok';
-  private selectorOkList: string = '.t3js-folderStructure-ok-list';
-  private selectorPermissionContainer: string = '.t3js-folderStructure-permissions';
-
-  private static removeLoadingMessage($container: JQuery): void {
-    $container.find('.alert-loading').remove();
-  }
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-
-    // Get status on initialize to have the badge and content ready
-    this.getStatus();
-
-    currentModal.on('click', this.selectorErrorFixTrigger, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      $(e.currentTarget).addClass('disabled').prop('disabled', true);
-      this.fix();
-    });
-  }
-
-  private getStatus(): void {
-    const modalContent = this.getModalBody();
-    const $errorBadge = $(this.selectorGridderBadge);
-    $errorBadge.text('').hide();
-    modalContent.find(this.selectorOutputContainer).empty().append(
-      ProgressBar.render(Severity.loading, 'Loading...', ''),
-    );
-    $.ajax({
-      url: Router.getUrl('folderStructureGetStatus'),
-      cache: false,
-      success: (data: any): void => {
-        modalContent.empty().append(data.html);
-        Modal.setButtons(data.buttons);
-        if (data.success === true && Array.isArray(data.errorStatus)) {
-          let errorCount = 0;
-          if (data.errorStatus.length > 0) {
-            modalContent.find(this.selectorErrorContainer).show();
-            modalContent.find(this.selectorErrorList).empty();
-            data.errorStatus.forEach(((aElement: any): void => {
-              errorCount++;
-              $errorBadge.text(errorCount).show();
-              const aMessage = InfoBox.render(aElement.severity, aElement.title, aElement.message);
-              modalContent.find(this.selectorErrorList).append(aMessage);
-            }));
-          } else {
-            modalContent.find(this.selectorErrorContainer).hide();
-          }
-        }
-        if (data.success === true && Array.isArray(data.okStatus)) {
-          if (data.okStatus.length > 0) {
-            modalContent.find(this.selectorOkContainer).show();
-            modalContent.find(this.selectorOkList).empty();
-            data.okStatus.forEach(((aElement: any): void => {
-              const aMessage = InfoBox.render(aElement.severity, aElement.title, aElement.message);
-              modalContent.find(this.selectorOkList).append(aMessage);
-            }));
-          } else {
-            modalContent.find(this.selectorOkContainer).hide();
-          }
-        }
-        let element = data.folderStructureFilePermissionStatus;
-        modalContent.find(this.selectorPermissionContainer).empty().append(
-          InfoBox.render(element.severity, element.title, element.message),
-        );
-        element = data.folderStructureDirectoryPermissionStatus;
-        modalContent.find(this.selectorPermissionContainer).append(
-          InfoBox.render(element.severity, element.title, element.message),
-        );
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private fix(): void {
-    const modalContent: JQuery = this.getModalBody();
-    const $outputContainer: JQuery = this.findInModal(this.selectorOutputContainer);
-    const message: any = ProgressBar.render(Severity.loading, 'Loading...', '');
-    $outputContainer.empty().html(message);
-    $.ajax({
-      url: Router.getUrl('folderStructureFix'),
-      cache: false,
-      success: (data: any): void => {
-        FolderStructure.removeLoadingMessage($outputContainer);
-        if (data.success === true && Array.isArray(data.fixedStatus)) {
-          if (data.fixedStatus.length > 0) {
-            data.fixedStatus.forEach((element: any): void => {
-              $outputContainer.append(
-                InfoBox.render(element.severity, element.title, element.message),
-              );
-            });
-          } else {
-            $outputContainer.append(
-              InfoBox.render(Severity.warning, 'Nothing fixed', ''),
-            );
-          }
-          this.getStatus();
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new FolderStructure();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ImageProcessing.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ImageProcessing.ts
deleted file mode 100644 (file)
index fadb18e..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import 'bootstrap';
-import Router = require('../Router');
-import InfoBox = require('../Renderable/InfoBox');
-import Severity = require('../Renderable/Severity');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/ImageProcessing
- */
-class ImageProcessing extends AbstractInteractableModule {
-  private selectorExecuteTrigger: string = '.t3js-imageProcessing-execute';
-  private selectorTestContainer: string = '.t3js-imageProcessing-twinContainer';
-  private selectorTwinImageTemplate: string = '.t3js-imageProcessing-twinImage-template';
-  private selectorCommandContainer: string = '.t3js-imageProcessing-command';
-  private selectorCommandText: string = '.t3js-imageProcessing-command-text';
-  private selectorTwinImages: string = '.t3js-imageProcessing-images';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.getData();
-
-    currentModal.on('click', this.selectorExecuteTrigger, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.runTests();
-    });
-  }
-
-  private getData(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('imageProcessingGetData'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          modalContent.empty().append(data.html);
-          Modal.setButtons(data.buttons);
-          this.runTests();
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private runTests(): void {
-    const modalContent = this.getModalBody();
-    const $triggerButton = this.findInModal(this.selectorExecuteTrigger);
-    $triggerButton.addClass('disabled').prop('disabled', true);
-
-    const $twinImageTemplate = this.findInModal(this.selectorTwinImageTemplate);
-    const promises: Array<JQueryXHR> = [];
-    modalContent.find(this.selectorTestContainer).each((index: number, element: any): void => {
-      const $container: JQuery = $(element);
-      const testType: string = $container.data('test');
-      const message: any = InfoBox.render(Severity.loading, 'Loading...', '');
-      $container.empty().html(message);
-      promises.push($.ajax({
-        url: Router.getUrl(testType),
-        cache: false,
-        success: (data: any): void => {
-          if (data.success === true) {
-            $container.empty();
-            if (Array.isArray(data.status)) {
-              data.status.forEach((aElement: any): void => {
-                const aMessage = InfoBox.render(element.severity, element.title, element.message);
-                $container.append(aMessage);
-              });
-            }
-            const $aTwin = $twinImageTemplate.clone();
-            $aTwin.removeClass('t3js-imageProcessing-twinImage-template');
-            if (data.fileExists === true) {
-              $aTwin.find('img.reference').attr('src', data.referenceFile);
-              $aTwin.find('img.result').attr('src', data.outputFile);
-              $aTwin.find(this.selectorTwinImages).show();
-            }
-            if (Array.isArray(data.command) && data.command.length > 0) {
-              $aTwin.find(this.selectorCommandContainer).show();
-              const commandText: Array<string> = [];
-              data.command.forEach((aElement: any): void => {
-                commandText.push('<strong>Command:</strong>\n' + aElement[1]);
-                if (aElement.length === 3) {
-                  commandText.push('<strong>Result:</strong>\n' + aElement[2]);
-                }
-              });
-              $aTwin.find(this.selectorCommandText).html(commandText.join('\n'));
-            }
-            $container.append($aTwin);
-          }
-        },
-        error: (xhr: XMLHttpRequest): void => {
-          Router.handleAjaxError(xhr, modalContent);
-        },
-      }));
-    });
-
-    $.when.apply($, promises).done((): void => {
-      $triggerButton.removeClass('disabled').prop('disabled', false);
-    });
-  }
-}
-
-export = new ImageProcessing();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/LanguagePacks.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/LanguagePacks.ts
deleted file mode 100644 (file)
index a83dd72..0000000
+++ /dev/null
@@ -1,515 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import 'bootstrap';
-import Router = require('../Router');
-import FlashMessage = require('../Renderable/FlashMessage');
-import ProgressBar = require('../Renderable/ProgressBar');
-import InfoBox = require('../Renderable/InfoBox');
-import Severity = require('../Renderable/Severity');
-
-/**
- * Module: TYPO3/CMS/Install/Module/LanguagePacks
- */
-class LanguagePacks extends AbstractInteractableModule {
-  private selectorOutputContainer: string = '.t3js-languagePacks-output';
-  private selectorContentContainer: string = '.t3js-languagePacks-mainContent';
-  private selectorActivateLanguage: string = '.t3js-languagePacks-activateLanguage';
-  private selectorActivateLanguageIcon: string = '#t3js-languagePacks-activate-icon';
-  private selectorAddLanguageToggle: string = '.t3js-languagePacks-addLanguage-toggle';
-  private selectorLanguageInactive: string = '.t3js-languagePacks-inactive';
-  private selectorDeactivateLanguage: string = '.t3js-languagePacks-deactivateLanguage';
-  private selectorDeactivateLanguageIcon: string = '#t3js-languagePacks-deactivate-icon';
-  private selectorUpdate: string = '.t3js-languagePacks-update';
-  private selectorLanguageUpdateIcon: string = '#t3js-languagePacks-languageUpdate-icon';
-  private selectorExtensionPackMissesIcon: string = '#t3js-languagePacks-extensionPack-misses-icon';
-  private selectorNotifications: string = '.t3js-languagePacks-notifications';
-
-  private activeLanguages: Array<any> = [];
-  private activeExtensions: Array<any> = [];
-
-  private packsUpdateDetails: { [id: string]: number } = {
-    toHandle: 0,
-    handled: 0,
-    updated: 0,
-    new: 0,
-    failed: 0,
-  };
-
-  private notifications: Array<any> = [];
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-
-    // Get configuration list on modal open
-    this.getData();
-
-    currentModal.on('click', this.selectorAddLanguageToggle, (): void => {
-      currentModal.find(this.selectorContentContainer + ' ' + this.selectorLanguageInactive).toggle();
-    });
-
-    currentModal.on('click', this.selectorActivateLanguage, (e: JQueryEventObject): void => {
-      const iso = $(e.target).closest(this.selectorActivateLanguage).data('iso');
-      e.preventDefault();
-      this.activateLanguage(iso);
-    });
-
-    currentModal.on('click', this.selectorDeactivateLanguage, (e: JQueryEventObject): void => {
-      const iso = $(e.target).closest(this.selectorDeactivateLanguage).data('iso');
-      e.preventDefault();
-      this.deactivateLanguage(iso);
-    });
-
-    currentModal.on('click', this.selectorUpdate, (e: JQueryEventObject): void => {
-      const iso = $(e.target).closest(this.selectorUpdate).data('iso');
-      const extension = $(e.target).closest(this.selectorUpdate).data('extension');
-      e.preventDefault();
-      this.updatePacks(iso, extension);
-    });
-  }
-
-  private getData(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('languagePacksGetData'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          this.activeLanguages = data.activeLanguages;
-          this.activeExtensions = data.activeExtensions;
-          modalContent.empty().append(data.html);
-          const contentContainer: JQuery = modalContent.parent().find(this.selectorContentContainer);
-          contentContainer.empty();
-          contentContainer.append(this.languageMatrixHtml(data));
-          contentContainer.append(this.extensionMatrixHtml(data));
-          $('[data-toggle="tooltip"]').tooltip(<any>({container: contentContainer}));
-        } else {
-          const message = InfoBox.render(Severity.error, 'Something went wrong', '');
-          this.addNotification(message);
-        }
-
-        this.renderNotifications();
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private activateLanguage(iso: string): void {
-    const modalContent = this.getModalBody();
-    const $outputContainer = this.findInModal(this.selectorOutputContainer);
-    const message = ProgressBar.render(Severity.loading, 'Loading...', '');
-    $outputContainer.empty().append(message);
-
-    $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      context: this,
-      data: {
-        'install': {
-          'action': 'languagePacksActivateLanguage',
-          'token': this.getModuleContent().data('language-packs-activate-language-token'),
-          'iso': iso,
-        },
-      },
-      cache: false,
-      beforeSend: (): void => {
-        this.getNotificationBox().empty();
-      },
-      success: (data: any): void => {
-        $outputContainer.empty();
-        if (data.success === true && Array.isArray(data.status)) {
-          data.status.forEach((element: any): void => {
-            const m: any = InfoBox.render(element.severity, element.title, element.message);
-            this.addNotification(m);
-          });
-        } else {
-          const m2: any = FlashMessage.render(Severity.error, 'Something went wrong', '');
-          this.addNotification(m2);
-        }
-        this.getData();
-      },
-      error: (xhr: XMLHttpRequest): any => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private deactivateLanguage(iso: string): void {
-    const modalContent = this.getModalBody();
-    const $outputContainer = this.findInModal(this.selectorOutputContainer);
-    const message = ProgressBar.render(Severity.loading, 'Loading...', '');
-    $outputContainer.empty().append(message);
-    $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      context: this,
-      data: {
-        'install': {
-          'action': 'languagePacksDeactivateLanguage',
-          'token': this.getModuleContent().data('language-packs-deactivate-language-token'),
-          'iso': iso,
-        },
-      },
-      cache: false,
-      beforeSend: (): void => {
-        this.getNotificationBox().empty();
-      },
-      success: (data: any): void => {
-        $outputContainer.empty();
-        if (data.success === true && Array.isArray(data.status)) {
-          data.status.forEach((element: any): void => {
-            const m: any = InfoBox.render(element.severity, element.title, element.message);
-            this.addNotification(m);
-          });
-        } else {
-          const m2: any = FlashMessage.render(Severity.error, 'Something went wrong', '');
-          this.addNotification(m2);
-        }
-        this.getData();
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private updatePacks(iso: string, extension: string): void {
-    const $outputContainer = this.findInModal(this.selectorOutputContainer);
-    const $contentContainer = this.findInModal(this.selectorContentContainer);
-    const isos = iso === undefined ? this.activeLanguages : [ iso ];
-    let updateIsoTimes = true;
-    let extensions = this.activeExtensions;
-    if (extension !== undefined) {
-      extensions = [ extension ];
-      updateIsoTimes = false;
-    }
-
-    this.packsUpdateDetails = {
-      toHandle: isos.length * extensions.length,
-      handled: 0,
-      updated: 0,
-      new: 0,
-      failed: 0,
-    };
-
-    $outputContainer.empty().append(
-      $('<div>', {'class': 'progress'}).append(
-        $('<div>', {
-          'class': 'progress-bar progress-bar-info',
-          'role': 'progressbar',
-          'aria-valuenow': 0,
-          'aria-valuemin': 0,
-          'aria-valuemax': 100,
-          'style': 'width: 0;',
-        }).append(
-          $('<span>', {'class': 'text-nowrap'}).text('0 of ' + this.packsUpdateDetails.toHandle + ' language packs updated'),
-        ),
-      ));
-    $contentContainer.empty();
-
-    isos.forEach((isoCode: string): void => {
-      extensions.forEach((extensionKey: string): void => {
-        $.ajax({
-          url: Router.getUrl(),
-          method: 'POST',
-          context: this,
-          data: {
-            'install': {
-              'action': 'languagePacksUpdatePack',
-              'token': this.getModuleContent().data('language-packs-update-pack-token'),
-              'iso': isoCode,
-              'extension': extensionKey,
-            },
-          },
-          cache: false,
-          beforeSend: (): void => {
-            this.getNotificationBox().empty();
-          },
-          success: (data: any): void => {
-            if (data.success === true) {
-              this.packsUpdateDetails.handled++;
-              if (data.packResult === 'new') {
-                this.packsUpdateDetails.new++;
-              } else if (data.packResult === 'update') {
-                this.packsUpdateDetails.updated++;
-              } else {
-                this.packsUpdateDetails.failed++;
-              }
-              this.packUpdateDone(updateIsoTimes, isos);
-            } else {
-              this.packsUpdateDetails.handled++;
-              this.packsUpdateDetails.failed++;
-              this.packUpdateDone(updateIsoTimes, isos);
-            }
-          },
-          error: (): void => {
-            this.packsUpdateDetails.handled++;
-            this.packsUpdateDetails.failed++;
-            this.packUpdateDone(updateIsoTimes, isos);
-          },
-        });
-      });
-    });
-  }
-
-  private packUpdateDone(updateIsoTimes: boolean, isos: Array<any>): void {
-    const modalContent = this.getModalBody();
-    const $outputContainer = this.findInModal(this.selectorOutputContainer);
-    if (this.packsUpdateDetails.handled === this.packsUpdateDetails.toHandle) {
-      // All done - create summary, update 'last update' of iso list, render main view
-      const message = InfoBox.render(
-        Severity.ok,
-        'Language packs updated',
-        this.packsUpdateDetails.new + ' new language packs downloaded, ' +
-        this.packsUpdateDetails.updated + ' language packs updated, ' +
-        this.packsUpdateDetails.failed + ' language packs not available',
-      );
-      this.addNotification(message);
-      if (updateIsoTimes === true) {
-        $.ajax({
-          url: Router.getUrl(),
-          method: 'POST',
-          context: this,
-          data: {
-            'install': {
-              'action': 'languagePacksUpdateIsoTimes',
-              'token': this.getModuleContent().data('language-packs-update-iso-times-token'),
-              'isos': isos,
-            },
-          },
-          cache: false,
-          success: (data: any): void => {
-            if (data.success === true) {
-              this.getData();
-            } else {
-              const m: any = FlashMessage.render(Severity.error, 'Something went wrong', '');
-              this.addNotification(m);
-            }
-          },
-          error: (xhr: XMLHttpRequest): void => {
-            Router.handleAjaxError(xhr, modalContent);
-          },
-        });
-      } else {
-        this.getData();
-      }
-    } else {
-      // Update progress bar
-      const percent = (this.packsUpdateDetails.handled / this.packsUpdateDetails.toHandle) * 100;
-      $outputContainer.find('.progress-bar')
-        .css('width', percent + '%')
-        .attr('aria-valuenow', percent)
-        .find('span')
-        .text(this.packsUpdateDetails.handled + ' of ' + this.packsUpdateDetails.toHandle + ' language packs updated');
-    }
-  }
-
-  private languageMatrixHtml(data: any): string {
-    const activateIcon = this.findInModal(this.selectorActivateLanguageIcon).html();
-    const deactivateIcon = this.findInModal(this.selectorDeactivateLanguageIcon).html();
-    const updateIcon = this.findInModal(this.selectorLanguageUpdateIcon).html();
-    const $markupContainer = $('<div>');
-
-    const $tbody = $('<tbody>');
-    data.languages.forEach((language: any): void => {
-      const active = language.active;
-      const $tr = $('<tr>');
-      if (active) {
-        $tbody.append(
-          $tr.append(
-            $('<td>').append(
-              $('<a>', {
-                'class': 'btn btn-default t3js-languagePacks-deactivateLanguage',
-                'data-iso': language.iso,
-                'data-toggle': 'tooltip',
-                'title': 'Deactivate',
-              }).append(deactivateIcon),
-              $('<a>', {
-                'class': 'btn btn-default t3js-languagePacks-update',
-                'data-iso': language.iso,
-                'data-toggle': 'tooltip',
-                'title': 'Download language packs',
-              }).append(updateIcon),
-            ),
-          ),
-        );
-      } else {
-        $tbody.append(
-          $tr.addClass('t3-languagePacks-inactive t3js-languagePacks-inactive').css({'display': 'none'}).append(
-            $('<td>').append(
-              $('<a>', {
-                'class': 'btn btn-default t3js-languagePacks-activateLanguage',
-                'data-iso': language.iso,
-                'data-toggle': 'tooltip',
-                'title': 'Activate',
-              }).append(activateIcon),
-            ),
-          ),
-        );
-      }
-      $tr.append(
-        $('<td>').text(language.name),
-        $('<td>').text(language.iso),
-        $('<td>').text(language.dependencies.join(', ')),
-        $('<td>').text(language.lastUpdate === null ? '' : language.lastUpdate),
-      );
-      $tbody.append($tr);
-    });
-    $markupContainer.append(
-      $('<h3>').text('Active languages'),
-      $('<table>', {'class': 'table table-striped table-bordered'}).append(
-        $('<thead>').append(
-          $('<tr>').append(
-            $('<th>').append(
-              $('<button>', {'class': 'btn btn-default t3js-languagePacks-addLanguage-toggle', 'type': 'button'}).append(
-                $('<span>').append(activateIcon),
-                ' Add language',
-              ),
-              $('<button>', {'class': 'btn btn-default t3js-languagePacks-update', 'type': 'button'}).append(
-                $('<span>').append(updateIcon),
-                ' Update all',
-              ),
-            ),
-            $('<th>').text('Language'),
-            $('<th>').text('Locale'),
-            $('<th>').text('Dependencies'),
-            $('<th>').text('Last update'),
-          ),
-        ),
-        $tbody,
-      ),
-    );
-    return $markupContainer.html();
-  }
-
-  private extensionMatrixHtml(data: any): any {
-    const packMissesIcon: string = this.findInModal(this.selectorExtensionPackMissesIcon).html();
-    const updateIcon: string = this.findInModal(this.selectorLanguageUpdateIcon).html();
-    let tooltip: string = '';
-    let extensionTitle: JQuery;
-    let allPackagesExist: boolean = true;
-    let rowCount: number = 0;
-    const $markupContainer: JQuery = $('<div>');
-
-    const $headerRow: JQuery = $('<tr>');
-    $headerRow.append(
-      $('<th>').text('Extension'),
-      $('<th>').text('Key'),
-    );
-    data.activeLanguages.forEach((language: string): void => {
-      $headerRow.append(
-        $('<th>').append(
-          $('<a>', {
-            'class': 'btn btn-default t3js-languagePacks-update',
-            'data-iso': language,
-            'data-toggle': 'tooltip',
-            'title': 'Download and update all language packs',
-          }).append(
-            $('<span>').append(updateIcon),
-            ' ' + language,
-          ),
-        ),
-      );
-    });
-
-    const $tbody = $('<tbody>');
-    data.extensions.forEach((extension: any): void => {
-      allPackagesExist = true;
-      extension.packs.forEach((pack: any): void => {
-        if (pack.exists === false) {
-          allPackagesExist = false;
-        }
-      });
-      if (allPackagesExist === true) {
-        return;
-      }
-      rowCount++;
-      if (extension.icon !== '') {
-        extensionTitle = $('<span>').append(
-          $('<img>', {
-            'style': 'max-height: 16px; max-width: 16px;',
-            'src': '../' + extension.icon,
-            'alt': extension.title,
-          }),
-          $('<span>').text(extension.title),
-        );
-      } else {
-        extensionTitle = $('<span>').text(extension.title);
-      }
-      const $tr = $('<tr>');
-      $tr.append(
-        $('<td>').html(extensionTitle.html()),
-        $('<td>').text(extension.key),
-      );
-      extension.packs.forEach((pack: any): void => {
-        if (pack.exists !== true) {
-          if (pack.lastUpdate !== null) {
-            tooltip = 'No language pack available when tried at ' + pack.lastUpdate + '. Click to re-try.';
-          } else {
-            tooltip = 'Language pack not downloaded. Click to download';
-          }
-          $tr.append(
-            $('<td>').append(
-              $('<a>', {
-                'class': 'btn btn-default t3js-languagePacks-update',
-                'data-extension': extension.key,
-                'data-iso': pack.iso,
-                'data-toggle': 'tooltip',
-                'title': tooltip,
-              }).append(packMissesIcon),
-            ),
-          );
-        }
-      });
-      $tbody.append($tr);
-    });
-
-    $markupContainer.append(
-      $('<h3>').text('Translation status'),
-      $('<table>', {'class': 'table table-striped table-bordered'}).append(
-        $('<thead>').append($headerRow),
-        $tbody,
-      ),
-    );
-    if (rowCount === 0) {
-      return InfoBox.render(
-        Severity.ok,
-        'Language packs have been found for every installed extension.',
-        'To download the latest changes, use the refresh button in the list above.',
-      );
-    }
-    return $markupContainer.html();
-  }
-
-  private getNotificationBox(): JQuery {
-    return this.findInModal(this.selectorNotifications);
-  }
-
-  private addNotification(notification: any): void {
-    this.notifications.push(notification);
-  }
-
-  private renderNotifications(): void {
-    const $notificationBox: JQuery = this.getNotificationBox();
-    for (let i = 0; i < this.notifications.length; ++i) {
-      $notificationBox.append(this.notifications[i]);
-    }
-    this.notifications = [];
-  }
-}
-
-export = new LanguagePacks();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/LocalConfiguration.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/LocalConfiguration.ts
deleted file mode 100644 (file)
index eb65daa..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import 'bootstrap';
-import Router = require('../Router');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/LocalConfiguration
- */
-class LocalConfiguration extends AbstractInteractableModule {
-  private selectorToggleAllTrigger: string = '.t3js-localConfiguration-toggleAll';
-  private selectorWriteTrigger: string = '.t3js-localConfiguration-write';
-  private selectorSearchTrigger: string = '.t3js-localConfiguration-search';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.getContent();
-
-    // Write out new settings
-    currentModal.on('click', this.selectorWriteTrigger, (): void => {
-      this.write();
-    });
-
-    // Expand / collapse "Toggle all" button
-    currentModal.on('click', this.selectorToggleAllTrigger, (): void => {
-      const modalContent = this.getModalBody();
-      const panels = modalContent.find('.panel-collapse');
-      const action = (panels.eq(0).hasClass('in')) ? 'hide' : 'show';
-      panels.collapse(action);
-    });
-
-    // Make jquerys "contains" work case-insensitive
-    jQuery.expr[':'].contains = jQuery.expr.createPseudo((arg: any): Function => {
-      return (elem: any): boolean => {
-        return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
-      };
-    });
-
-    // Focus search field on certain user interactions
-    currentModal.on('keydown', (e: JQueryEventObject): void => {
-      const $searchInput = currentModal.find(this.selectorSearchTrigger);
-      if (e.ctrlKey || e.metaKey) {
-        // Focus search field on ctrl-f
-        if (String.fromCharCode(e.which).toLowerCase() === 'f') {
-            e.preventDefault();
-            $searchInput.focus();
-        }
-      } else if (e.keyCode === 27) {
-        // Clear search on ESC key
-        e.preventDefault();
-        $searchInput.val('').focus();
-      }
-    });
-
-    // Perform expand collapse on search matches
-    currentModal.on('keyup', this.selectorSearchTrigger, (e: JQueryEventObject): void => {
-      const typedQuery = $(e.target).val();
-      const $searchInput = currentModal.find((this.selectorSearchTrigger));
-      currentModal.find('div.item').each((index: number, element: any): void => {
-        const $item = $(element);
-        if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) {
-          $item.removeClass('hidden').addClass('searchhit');
-        } else {
-          $item.removeClass('searchhit').addClass('hidden');
-        }
-      });
-      currentModal.find('.searchhit').parent().collapse('show');
-      // Make search field clearable
-      require(['jquery.clearable'], (): void => {
-        $searchInput.clearable().focus();
-      });
-    });
-  }
-
-  private getContent(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('localConfigurationGetContent'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          if (Array.isArray(data.status)) {
-            data.status.forEach((element: any): void => {
-              Notification.success(element.title, element.message);
-            });
-          }
-          modalContent.html(data.html);
-          Modal.setButtons(data.buttons);
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private write(): void {
-    const modalContent: JQuery = this.getModalBody();
-    const executeToken: JQuery = this.getModuleContent().data('local-configuration-write-token');
-    const configurationValues: any = {};
-    this.findInModal('.t3js-localConfiguration-pathValue').each((i: number, element: any): void => {
-      const $element: JQuery = $(element);
-      if ($element.attr('type') === 'checkbox') {
-        if (element.checked) {
-          configurationValues[$element.data('path')] = '1';
-        } else {
-          configurationValues[$element.data('path')] = '0';
-        }
-      } else {
-        configurationValues[$element.data('path')] = $element.val();
-      }
-    });
-    $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      data: {
-        'install': {
-          'action': 'localConfigurationWrite',
-          'token': executeToken,
-          'configurationValues': configurationValues,
-        },
-      },
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true && Array.isArray(data.status)) {
-          data.status.forEach((element: any): void => {
-            Notification.showMessage(element.title, element.message, element.severity);
-          });
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new LocalConfiguration();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/MailTest.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/MailTest.ts
deleted file mode 100644 (file)
index 87655a3..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import 'bootstrap';
-import Router = require('../Router');
-import ProgressBar = require('../Renderable/ProgressBar');
-import Severity = require('../Renderable/Severity');
-import InfoBox = require('../Renderable/InfoBox');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/CreateAdmin
- */
-class MailTest extends AbstractInteractableModule {
-  private selectorOutputContainer: string = '.t3js-mailTest-output';
-  private selectorMailTestButton: string = '.t3js-mailTest-execute';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.getData();
-    currentModal.on('click', this.selectorMailTestButton, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.send();
-    });
-  }
-
-  private getData(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('mailTestGetData'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          modalContent.empty().append(data.html);
-          Modal.setButtons(data.buttons);
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private send(): void {
-    const executeToken: string = this.getModuleContent().data('mail-test-token');
-    const $outputContainer: JQuery = this.findInModal(this.selectorOutputContainer);
-    const message: any = ProgressBar.render(Severity.loading, 'Loading...', '');
-    $outputContainer.empty().html(message);
-    $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      data: {
-        'install': {
-          'action': 'mailTest',
-          'token': executeToken,
-          'email': this.findInModal('.t3js-mailTest-email').val(),
-        },
-      },
-      cache: false,
-      success: (data: any): void => {
-        $outputContainer.empty();
-        if (data.success === true && Array.isArray(data.status)) {
-          data.status.forEach((element: any): void => {
-            const aMessage: any = InfoBox.render(element.severity, element.title, element.message);
-            $outputContainer.html(aMessage);
-          });
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (): void => {
-        // 500 can happen here if the mail configuration is broken
-        Notification.error('Something went wrong');
-      },
-    });
-  }
-}
-
-export = new MailTest();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/Cache.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/Cache.ts
new file mode 100644 (file)
index 0000000..4f27786
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {InlineModuleInterface} from './../InlineModuleInterface';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/Cache
+ */
+class Cache implements InlineModuleInterface {
+  public initialize($trigger: JQuery): void {
+    $.ajax({
+      url: Router.getUrl('cacheClearAll', 'maintenance'),
+      cache: false,
+      beforeSend: (): void => {
+        $trigger.addClass('disabled').prop('disabled', true);
+      },
+      success: (data: any): void => {
+        if (data.success === true && Array.isArray(data.status)) {
+          if (data.status.length > 0) {
+            data.status.forEach(((element: any): void => {
+              Notification.success(element.title, element.message);
+            }));
+          }
+        } else {
+          Notification.error('Something went wrong clearing caches');
+        }
+      },
+      error: (): void => {
+        // In case the clear cache action fails (typically 500 from server), do not kill the entire
+        // install tool, instead show a notification that something went wrong.
+        Notification.error(
+          'Clearing caches went wrong on the server side. Check the system for broken extensions or missing database tables and try again',
+        );
+      },
+      complete: (): void => {
+        $trigger.removeClass('disabled').prop('disabled', false);
+      },
+    });
+  }
+}
+
+export = new Cache();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/ClearTables.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/ClearTables.ts
new file mode 100644 (file)
index 0000000..1bc4a8a
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/ClearTables
+ */
+class ClearTables extends AbstractInteractableModule {
+  private selectorClearTrigger: string = '.t3js-clearTables-clear';
+  private selectorStatsTrigger: string = '.t3js-clearTables-stats';
+  private selectorOutputContainer: string = '.t3js-clearTables-output';
+  private selectorStatContainer: string = '.t3js-clearTables-stat-container';
+  private selectorStatTemplate: string = '.t3js-clearTables-stat-template';
+  private selectorStatDescription: string = '.t3js-clearTables-stat-description';
+  private selectorStatRows: string = '.t3js-clearTables-stat-rows';
+  private selectorStatName: string = '.t3js-clearTables-stat-name';
+
+  public initialize(currentModal: any): void {
+    this.currentModal = currentModal;
+    this.getStats();
+
+    currentModal.on('click', this.selectorStatsTrigger, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      $(this.selectorOutputContainer).empty();
+      this.getStats();
+    });
+
+    currentModal.on('click', this.selectorClearTrigger, (e: JQueryEventObject): void => {
+      const table = $(e.target).closest(this.selectorClearTrigger).data('table');
+      e.preventDefault();
+      this.clear(table);
+    });
+  }
+
+  private getStats(): void {
+    const modalContent: JQuery = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('clearTablesStats'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          Modal.setButtons(data.buttons);
+          if (Array.isArray(data.stats) && data.stats.length > 0) {
+            data.stats.forEach((element: any): void => {
+              if (element.rowCount > 0) {
+                const aStat = modalContent.find(this.selectorStatTemplate).clone();
+                aStat.find(this.selectorStatDescription).text(element.description);
+                aStat.find(this.selectorStatName).text(element.name);
+                aStat.find(this.selectorStatRows).text(element.rowCount);
+                aStat.find(this.selectorClearTrigger).attr('data-table', element.name);
+                modalContent.find(this.selectorStatContainer).append(aStat.html());
+              }
+            });
+          }
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private clear(table: string): void {
+    const modalContent = this.getModalBody();
+    const executeToken = this.getModuleContent().data('clear-tables-clear-token');
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      context: this,
+      data: {
+        'install': {
+          'action': 'clearTablesClear',
+          'token': executeToken,
+          'table': table,
+        },
+      },
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true && Array.isArray(data.status)) {
+          data.status.forEach((element: any): void => {
+            Notification.success(element.message);
+          });
+        } else {
+          Notification.error('Something went wrong');
+        }
+        this.getStats();
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new ClearTables();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/ClearTypo3tempFiles.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/ClearTypo3tempFiles.ts
new file mode 100644 (file)
index 0000000..51ea519
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/ClearTypo3tempFiles
+ */
+class ClearTypo3tempFiles extends AbstractInteractableModule {
+  private selectorDeleteTrigger: string = '.t3js-clearTypo3temp-delete';
+  private selectorOutputContainer: string = '.t3js-clearTypo3temp-output';
+  private selectorStatContainer: string = '.t3js-clearTypo3temp-stat-container';
+  private selectorStatsTrigger: string = '.t3js-clearTypo3temp-stats';
+  private selectorStatTemplate: string = '.t3js-clearTypo3temp-stat-template';
+  private selectorStatNumberOfFiles: string = '.t3js-clearTypo3temp-stat-numberOfFiles';
+  private selectorStatDirectory: string = '.t3js-clearTypo3temp-stat-directory';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getStats();
+
+    currentModal.on('click', this.selectorStatsTrigger, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      $(this.selectorOutputContainer).empty();
+      this.getStats();
+    });
+    currentModal.on('click', this.selectorDeleteTrigger, (e: JQueryEventObject): void => {
+      const folder = $(e.currentTarget).data('folder');
+      const storageUid = $(e.currentTarget).data('storage-uid');
+      e.preventDefault();
+      this.delete(folder, storageUid);
+    });
+  }
+
+  private getStats(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('clearTypo3tempFilesStats'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          Modal.setButtons(data.buttons);
+          if (Array.isArray(data.stats) && data.stats.length > 0) {
+            data.stats.forEach((element: any): void => {
+              if (element.numberOfFiles > 0) {
+                const aStat = modalContent.find(this.selectorStatTemplate).clone();
+                aStat.find(this.selectorStatNumberOfFiles).text(element.numberOfFiles);
+                aStat.find(this.selectorStatDirectory).text(element.directory);
+                aStat.find(this.selectorDeleteTrigger).attr('data-folder', element.directory);
+                aStat.find(this.selectorDeleteTrigger).attr('data-storage-uid', element.storageUid);
+                modalContent.find(this.selectorStatContainer).append(aStat.html());
+              }
+            });
+          }
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private delete(folder: string, storageUid: number): void {
+    const modalContent = this.getModalBody();
+    const executeToken = this.getModuleContent().data('clear-typo3temp-delete-token');
+    $.ajax({
+      method: 'POST',
+      url: Router.getUrl(),
+      context: this,
+      data: {
+        'install': {
+          'action': 'clearTypo3tempFiles',
+          'token': executeToken,
+          'folder': folder,
+          'storageUid': storageUid,
+        },
+      },
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true && Array.isArray(data.status)) {
+          data.status.forEach((element: any): void => {
+            Notification.success(element.message);
+          });
+          this.getStats();
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new ClearTypo3tempFiles();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/CreateAdmin.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/CreateAdmin.ts
new file mode 100644 (file)
index 0000000..71d4dda
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import PasswordStrength = require('../PasswordStrength');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/CreateAdmin
+ */
+class CreateAdmin extends AbstractInteractableModule {
+  private selectorAdminCreateButton: string = '.t3js-createAdmin-create';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getData();
+
+    currentModal.on('click', this.selectorAdminCreateButton, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.create();
+    });
+
+    currentModal.on('click', '.t3-install-form-password-strength', (e: JQueryEventObject): void => {
+      PasswordStrength.initialize('.t3-install-form-password-strength');
+    });
+  }
+
+  private getData(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('createAdminGetData'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          Modal.setButtons(data.buttons);
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private create(): void {
+    const modalContent = this.getModalBody();
+    const executeToken = this.getModuleContent().data('create-admin-token');
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: {
+        'install': {
+          'action': 'createAdmin',
+          'token': executeToken,
+          'userName': this.findInModal('.t3js-createAdmin-user').val(),
+          'userPassword': this.findInModal('.t3js-createAdmin-password').val(),
+          'userPasswordCheck': this.findInModal('.t3js-createAdmin-password-check').val(),
+          'userSystemMaintainer': (this.findInModal('.t3js-createAdmin-system-maintainer').is(':checked')) ? 1 : 0,
+        },
+      },
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true && Array.isArray(data.status)) {
+          data.status.forEach((element: any): void => {
+            if (element.severity === 2) {
+              Notification.error(element.message);
+            } else {
+              Notification.success(element.title);
+            }
+          });
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+    this.findInModal('.t3js-createAdmin-user').val('');
+    this.findInModal('.t3js-createAdmin-password').val('');
+    this.findInModal('.t3js-createAdmin-password-check').val('');
+    this.findInModal('.t3js-createAdmin-system-maintainer').prop('checked', false);
+  }
+}
+
+export = new CreateAdmin();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/DatabaseAnalyzer.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/DatabaseAnalyzer.ts
new file mode 100644 (file)
index 0000000..686719a
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import ProgressBar = require('../../Renderable/ProgressBar');
+import InfoBox = require('../../Renderable/InfoBox');
+import Severity = require('../../Renderable/Severity');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/DatabaseAnalyzer
+ */
+class DatabaseAnalyzer extends AbstractInteractableModule {
+  private selectorAnalyzeTrigger: string = '.t3js-databaseAnalyzer-analyze';
+  private selectorExecuteTrigger: string = '.t3js-databaseAnalyzer-execute';
+  private selectorOutputContainer: string = '.t3js-databaseAnalyzer-output';
+  private selectorSuggestionBlock: string = '.t3js-databaseAnalyzer-suggestion-block';
+  private selectorSuggestionList: string = '.t3js-databaseAnalyzer-suggestion-list';
+  private selectorSuggestionLineTemplate: string = '.t3js-databaseAnalyzer-suggestion-line-template';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getData();
+
+    // Select / deselect all checkboxes
+    currentModal.on('click', '.t3js-databaseAnalyzer-suggestion-block-checkbox', (e: JQueryEventObject): void => {
+      const $element = $(e.currentTarget);
+      $element.closest('fieldset').find(':checkbox').prop('checked', (<HTMLInputElement>$element.get(0)).checked);
+    });
+    currentModal.on('click', this.selectorAnalyzeTrigger, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.analyze();
+    });
+    currentModal.on('click', this.selectorExecuteTrigger, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.execute();
+    });
+  }
+
+  private getData(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('databaseAnalyzer'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          Modal.setButtons(data.buttons);
+          this.analyze();
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private analyze(): void {
+    const modalContent = this.getModalBody();
+    const modalFooter = this.getModalFooter();
+    const outputContainer = modalContent.find(this.selectorOutputContainer);
+    const executeTrigger = modalFooter.find(this.selectorExecuteTrigger);
+    const analyzeTrigger = modalFooter.find(this.selectorAnalyzeTrigger);
+
+    outputContainer.empty().append(ProgressBar.render(Severity.loading, 'Analyzing current database schema...', ''));
+
+    analyzeTrigger.prop('disabled', true);
+    executeTrigger.prop('disabled', true);
+
+    outputContainer.on('change', 'input[type="checkbox"]', (): void => {
+      const hasCheckedCheckboxes = outputContainer.find(':checked').length > 0;
+      executeTrigger.prop('disabled', !hasCheckedCheckboxes);
+    });
+
+    $.ajax({
+      url: Router.getUrl('databaseAnalyzerAnalyze'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          if (Array.isArray(data.status)) {
+            outputContainer.find('.alert-loading').remove();
+            data.status.forEach((element: any): void => {
+              const message = InfoBox.render(element.severity, element.title, element.message);
+              outputContainer.append(message);
+            });
+          }
+          if (Array.isArray(data.suggestions)) {
+            data.suggestions.forEach((element: any): void => {
+              const aBlock = modalContent.find(this.selectorSuggestionBlock).clone();
+              aBlock.removeClass(this.selectorSuggestionBlock.substr(1));
+              const key = element.key;
+              aBlock.find('.t3js-databaseAnalyzer-suggestion-block-legend').text(element.label);
+              aBlock.find('.t3js-databaseAnalyzer-suggestion-block-checkbox').attr('id', 't3-install-' + key + '-checkbox');
+              if (element.enabled) {
+                aBlock.find('.t3js-databaseAnalyzer-suggestion-block-checkbox').attr('checked', 'checked');
+              }
+              aBlock.find('.t3js-databaseAnalyzer-suggestion-block-label').attr('for', 't3-install-' + key + '-checkbox');
+              element.children.forEach((line: any): void => {
+                const aLine = modalContent.find(this.selectorSuggestionLineTemplate).children().clone();
+                const hash = line.hash;
+                const $checkbox = aLine.find('.t3js-databaseAnalyzer-suggestion-line-checkbox');
+                $checkbox.attr('id', 't3-install-db-' + hash).attr('data-hash', hash);
+                if (element.enabled) {
+                  $checkbox.attr('checked', 'checked');
+                }
+                aLine.find('.t3js-databaseAnalyzer-suggestion-line-label').attr('for', 't3-install-db-' + hash);
+                aLine.find('.t3js-databaseAnalyzer-suggestion-line-statement').text(line.statement);
+                if (typeof line.current !== 'undefined') {
+                  aLine.find('.t3js-databaseAnalyzer-suggestion-line-current-value').text(line.current);
+                  aLine.find('.t3js-databaseAnalyzer-suggestion-line-current').show();
+                }
+                if (typeof line.rowCount !== 'undefined') {
+                  aLine.find('.t3js-databaseAnalyzer-suggestion-line-count-value').text(line.rowCount);
+                  aLine.find('.t3js-databaseAnalyzer-suggestion-line-count').show();
+                }
+                aBlock.find(this.selectorSuggestionList).append(aLine);
+              });
+              outputContainer.append(aBlock.html());
+            });
+
+            const isInitiallyDisabled = outputContainer.find(':checked').length === 0;
+            analyzeTrigger.prop('disabled', false);
+            executeTrigger.prop('disabled', isInitiallyDisabled);
+          }
+          if (data.suggestions.length === 0 && data.status.length === 0) {
+            outputContainer.append(InfoBox.render(Severity.ok, 'Database schema is up to date. Good job!', ''));
+          }
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private execute(): void {
+    const modalContent = this.getModalBody();
+    const executeToken = this.getModuleContent().data('database-analyzer-execute-token');
+    const outputContainer = modalContent.find(this.selectorOutputContainer);
+    const selectedHashes: Array<any> = [];
+
+    outputContainer.find('.t3js-databaseAnalyzer-suggestion-line input:checked').each((index: number, element: any): void => {
+      selectedHashes.push($(element).data('hash'));
+    });
+    outputContainer.empty().append(ProgressBar.render(Severity.loading, 'Executing database updates...', ''));
+    modalContent.find(this.selectorExecuteTrigger).prop('disabled', true);
+    modalContent.find(this.selectorAnalyzeTrigger).prop('disabled', true);
+
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: {
+        'install': {
+          'action': 'databaseAnalyzerExecute',
+          'token': executeToken,
+          'hashes': selectedHashes,
+        },
+      },
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          if (Array.isArray(data.status)) {
+            data.status.forEach((element: any): void => {
+              Notification.showMessage(element.title, element.message, element.severity);
+            });
+          }
+        }
+        this.analyze();
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new DatabaseAnalyzer();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/DumpAutoload.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/DumpAutoload.ts
new file mode 100644 (file)
index 0000000..4780c54
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {InlineModuleInterface} from './../InlineModuleInterface';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/DumpAutoload
+ */
+class DumpAutoload implements InlineModuleInterface {
+  public initialize($trigger: JQuery): void {
+    $.ajax({
+      url: Router.getUrl('dumpAutoload'),
+      cache: false,
+      beforeSend: (): void => {
+        $trigger.addClass('disabled').prop('disabled', true);
+      },
+      success: (data: any): void => {
+        if (data.success === true && Array.isArray(data.status)) {
+          if (data.status.length > 0) {
+            data.status.forEach((element: any): void => {
+              Notification.success(element.message);
+            });
+          }
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (): void => {
+        // In case the dump action fails (typically 500 from server), do not kill the entire
+        // install tool, instead show a notification that something went wrong.
+        Notification.error('Dumping autoload files went wrong on the server side. Check the system for broken extensions and try again');
+      },
+      complete: (): void => {
+        $trigger.removeClass('disabled').prop('disabled', false);
+      },
+    });
+  }
+}
+
+export = new DumpAutoload();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/LanguagePacks.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/LanguagePacks.ts
new file mode 100644 (file)
index 0000000..c492f5f
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../../Router');
+import FlashMessage = require('../../Renderable/FlashMessage');
+import ProgressBar = require('../../Renderable/ProgressBar');
+import InfoBox = require('../../Renderable/InfoBox');
+import Severity = require('../../Renderable/Severity');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/LanguagePacks
+ */
+class LanguagePacks extends AbstractInteractableModule {
+  private selectorOutputContainer: string = '.t3js-languagePacks-output';
+  private selectorContentContainer: string = '.t3js-languagePacks-mainContent';
+  private selectorActivateLanguage: string = '.t3js-languagePacks-activateLanguage';
+  private selectorActivateLanguageIcon: string = '#t3js-languagePacks-activate-icon';
+  private selectorAddLanguageToggle: string = '.t3js-languagePacks-addLanguage-toggle';
+  private selectorLanguageInactive: string = '.t3js-languagePacks-inactive';
+  private selectorDeactivateLanguage: string = '.t3js-languagePacks-deactivateLanguage';
+  private selectorDeactivateLanguageIcon: string = '#t3js-languagePacks-deactivate-icon';
+  private selectorUpdate: string = '.t3js-languagePacks-update';
+  private selectorLanguageUpdateIcon: string = '#t3js-languagePacks-languageUpdate-icon';
+  private selectorExtensionPackMissesIcon: string = '#t3js-languagePacks-extensionPack-misses-icon';
+  private selectorNotifications: string = '.t3js-languagePacks-notifications';
+
+  private activeLanguages: Array<any> = [];
+  private activeExtensions: Array<any> = [];
+
+  private packsUpdateDetails: { [id: string]: number } = {
+    toHandle: 0,
+    handled: 0,
+    updated: 0,
+    new: 0,
+    failed: 0,
+  };
+
+  private notifications: Array<any> = [];
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+
+    // Get configuration list on modal open
+    this.getData();
+
+    currentModal.on('click', this.selectorAddLanguageToggle, (): void => {
+      currentModal.find(this.selectorContentContainer + ' ' + this.selectorLanguageInactive).toggle();
+    });
+
+    currentModal.on('click', this.selectorActivateLanguage, (e: JQueryEventObject): void => {
+      const iso = $(e.target).closest(this.selectorActivateLanguage).data('iso');
+      e.preventDefault();
+      this.activateLanguage(iso);
+    });
+
+    currentModal.on('click', this.selectorDeactivateLanguage, (e: JQueryEventObject): void => {
+      const iso = $(e.target).closest(this.selectorDeactivateLanguage).data('iso');
+      e.preventDefault();
+      this.deactivateLanguage(iso);
+    });
+
+    currentModal.on('click', this.selectorUpdate, (e: JQueryEventObject): void => {
+      const iso = $(e.target).closest(this.selectorUpdate).data('iso');
+      const extension = $(e.target).closest(this.selectorUpdate).data('extension');
+      e.preventDefault();
+      this.updatePacks(iso, extension);
+    });
+  }
+
+  private getData(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('languagePacksGetData'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          this.activeLanguages = data.activeLanguages;
+          this.activeExtensions = data.activeExtensions;
+          modalContent.empty().append(data.html);
+          const contentContainer: JQuery = modalContent.parent().find(this.selectorContentContainer);
+          contentContainer.empty();
+          contentContainer.append(this.languageMatrixHtml(data));
+          contentContainer.append(this.extensionMatrixHtml(data));
+          $('[data-toggle="tooltip"]').tooltip(<any>({container: contentContainer}));
+        } else {
+          const message = InfoBox.render(Severity.error, 'Something went wrong', '');
+          this.addNotification(message);
+        }
+
+        this.renderNotifications();
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private activateLanguage(iso: string): void {
+    const modalContent = this.getModalBody();
+    const $outputContainer = this.findInModal(this.selectorOutputContainer);
+    const message = ProgressBar.render(Severity.loading, 'Loading...', '');
+    $outputContainer.empty().append(message);
+
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      context: this,
+      data: {
+        'install': {
+          'action': 'languagePacksActivateLanguage',
+          'token': this.getModuleContent().data('language-packs-activate-language-token'),
+          'iso': iso,
+        },
+      },
+      cache: false,
+      beforeSend: (): void => {
+        this.getNotificationBox().empty();
+      },
+      success: (data: any): void => {
+        $outputContainer.empty();
+        if (data.success === true && Array.isArray(data.status)) {
+          data.status.forEach((element: any): void => {
+            const m: any = InfoBox.render(element.severity, element.title, element.message);
+            this.addNotification(m);
+          });
+        } else {
+          const m2: any = FlashMessage.render(Severity.error, 'Something went wrong', '');
+          this.addNotification(m2);
+        }
+        this.getData();
+      },
+      error: (xhr: XMLHttpRequest): any => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private deactivateLanguage(iso: string): void {
+    const modalContent = this.getModalBody();
+    const $outputContainer = this.findInModal(this.selectorOutputContainer);
+    const message = ProgressBar.render(Severity.loading, 'Loading...', '');
+    $outputContainer.empty().append(message);
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      context: this,
+      data: {
+        'install': {
+          'action': 'languagePacksDeactivateLanguage',
+          'token': this.getModuleContent().data('language-packs-deactivate-language-token'),
+          'iso': iso,
+        },
+      },
+      cache: false,
+      beforeSend: (): void => {
+        this.getNotificationBox().empty();
+      },
+      success: (data: any): void => {
+        $outputContainer.empty();
+        if (data.success === true && Array.isArray(data.status)) {
+          data.status.forEach((element: any): void => {
+            const m: any = InfoBox.render(element.severity, element.title, element.message);
+            this.addNotification(m);
+          });
+        } else {
+          const m2: any = FlashMessage.render(Severity.error, 'Something went wrong', '');
+          this.addNotification(m2);
+        }
+        this.getData();
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private updatePacks(iso: string, extension: string): void {
+    const $outputContainer = this.findInModal(this.selectorOutputContainer);
+    const $contentContainer = this.findInModal(this.selectorContentContainer);
+    const isos = iso === undefined ? this.activeLanguages : [ iso ];
+    let updateIsoTimes = true;
+    let extensions = this.activeExtensions;
+    if (extension !== undefined) {
+      extensions = [ extension ];
+      updateIsoTimes = false;
+    }
+
+    this.packsUpdateDetails = {
+      toHandle: isos.length * extensions.length,
+      handled: 0,
+      updated: 0,
+      new: 0,
+      failed: 0,
+    };
+
+    $outputContainer.empty().append(
+      $('<div>', {'class': 'progress'}).append(
+        $('<div>', {
+          'class': 'progress-bar progress-bar-info',
+          'role': 'progressbar',
+          'aria-valuenow': 0,
+          'aria-valuemin': 0,
+          'aria-valuemax': 100,
+          'style': 'width: 0;',
+        }).append(
+          $('<span>', {'class': 'text-nowrap'}).text('0 of ' + this.packsUpdateDetails.toHandle + ' language packs updated'),
+        ),
+      ));
+    $contentContainer.empty();
+
+    isos.forEach((isoCode: string): void => {
+      extensions.forEach((extensionKey: string): void => {
+        $.ajax({
+          url: Router.getUrl(),
+          method: 'POST',
+          context: this,
+          data: {
+            'install': {
+              'action': 'languagePacksUpdatePack',
+              'token': this.getModuleContent().data('language-packs-update-pack-token'),
+              'iso': isoCode,
+              'extension': extensionKey,
+            },
+          },
+          cache: false,
+          beforeSend: (): void => {
+            this.getNotificationBox().empty();
+          },
+          success: (data: any): void => {
+            if (data.success === true) {
+              this.packsUpdateDetails.handled++;
+              if (data.packResult === 'new') {
+                this.packsUpdateDetails.new++;
+              } else if (data.packResult === 'update') {
+                this.packsUpdateDetails.updated++;
+              } else {
+                this.packsUpdateDetails.failed++;
+              }
+              this.packUpdateDone(updateIsoTimes, isos);
+            } else {
+              this.packsUpdateDetails.handled++;
+              this.packsUpdateDetails.failed++;
+              this.packUpdateDone(updateIsoTimes, isos);
+            }
+          },
+          error: (): void => {
+            this.packsUpdateDetails.handled++;
+            this.packsUpdateDetails.failed++;
+            this.packUpdateDone(updateIsoTimes, isos);
+          },
+        });
+      });
+    });
+  }
+
+  private packUpdateDone(updateIsoTimes: boolean, isos: Array<any>): void {
+    const modalContent = this.getModalBody();
+    const $outputContainer = this.findInModal(this.selectorOutputContainer);
+    if (this.packsUpdateDetails.handled === this.packsUpdateDetails.toHandle) {
+      // All done - create summary, update 'last update' of iso list, render main view
+      const message = InfoBox.render(
+        Severity.ok,
+        'Language packs updated',
+        this.packsUpdateDetails.new + ' new language packs downloaded, ' +
+        this.packsUpdateDetails.updated + ' language packs updated, ' +
+        this.packsUpdateDetails.failed + ' language packs not available',
+      );
+      this.addNotification(message);
+      if (updateIsoTimes === true) {
+        $.ajax({
+          url: Router.getUrl(),
+          method: 'POST',
+          context: this,
+          data: {
+            'install': {
+              'action': 'languagePacksUpdateIsoTimes',
+              'token': this.getModuleContent().data('language-packs-update-iso-times-token'),
+              'isos': isos,
+            },
+          },
+          cache: false,
+          success: (data: any): void => {
+            if (data.success === true) {
+              this.getData();
+            } else {
+              const m: any = FlashMessage.render(Severity.error, 'Something went wrong', '');
+              this.addNotification(m);
+            }
+          },
+          error: (xhr: XMLHttpRequest): void => {
+            Router.handleAjaxError(xhr, modalContent);
+          },
+        });
+      } else {
+        this.getData();
+      }
+    } else {
+      // Update progress bar
+      const percent = (this.packsUpdateDetails.handled / this.packsUpdateDetails.toHandle) * 100;
+      $outputContainer.find('.progress-bar')
+        .css('width', percent + '%')
+        .attr('aria-valuenow', percent)
+        .find('span')
+        .text(this.packsUpdateDetails.handled + ' of ' + this.packsUpdateDetails.toHandle + ' language packs updated');
+    }
+  }
+
+  private languageMatrixHtml(data: any): string {
+    const activateIcon = this.findInModal(this.selectorActivateLanguageIcon).html();
+    const deactivateIcon = this.findInModal(this.selectorDeactivateLanguageIcon).html();
+    const updateIcon = this.findInModal(this.selectorLanguageUpdateIcon).html();
+    const $markupContainer = $('<div>');
+
+    const $tbody = $('<tbody>');
+    data.languages.forEach((language: any): void => {
+      const active = language.active;
+      const $tr = $('<tr>');
+      if (active) {
+        $tbody.append(
+          $tr.append(
+            $('<td>').append(
+              $('<a>', {
+                'class': 'btn btn-default t3js-languagePacks-deactivateLanguage',
+                'data-iso': language.iso,
+                'data-toggle': 'tooltip',
+                'title': 'Deactivate',
+              }).append(deactivateIcon),
+              $('<a>', {
+                'class': 'btn btn-default t3js-languagePacks-update',
+                'data-iso': language.iso,
+                'data-toggle': 'tooltip',
+                'title': 'Download language packs',
+              }).append(updateIcon),
+            ),
+          ),
+        );
+      } else {
+        $tbody.append(
+          $tr.addClass('t3-languagePacks-inactive t3js-languagePacks-inactive').css({'display': 'none'}).append(
+            $('<td>').append(
+              $('<a>', {
+                'class': 'btn btn-default t3js-languagePacks-activateLanguage',
+                'data-iso': language.iso,
+                'data-toggle': 'tooltip',
+                'title': 'Activate',
+              }).append(activateIcon),
+            ),
+          ),
+        );
+      }
+      $tr.append(
+        $('<td>').text(language.name),
+        $('<td>').text(language.iso),
+        $('<td>').text(language.dependencies.join(', ')),
+        $('<td>').text(language.lastUpdate === null ? '' : language.lastUpdate),
+      );
+      $tbody.append($tr);
+    });
+    $markupContainer.append(
+      $('<h3>').text('Active languages'),
+      $('<table>', {'class': 'table table-striped table-bordered'}).append(
+        $('<thead>').append(
+          $('<tr>').append(
+            $('<th>').append(
+              $('<button>', {'class': 'btn btn-default t3js-languagePacks-addLanguage-toggle', 'type': 'button'}).append(
+                $('<span>').append(activateIcon),
+                ' Add language',
+              ),
+              $('<button>', {'class': 'btn btn-default t3js-languagePacks-update', 'type': 'button'}).append(
+                $('<span>').append(updateIcon),
+                ' Update all',
+              ),
+            ),
+            $('<th>').text('Language'),
+            $('<th>').text('Locale'),
+            $('<th>').text('Dependencies'),
+            $('<th>').text('Last update'),
+          ),
+        ),
+        $tbody,
+      ),
+    );
+    return $markupContainer.html();
+  }
+
+  private extensionMatrixHtml(data: any): any {
+    const packMissesIcon: string = this.findInModal(this.selectorExtensionPackMissesIcon).html();
+    const updateIcon: string = this.findInModal(this.selectorLanguageUpdateIcon).html();
+    let tooltip: string = '';
+    let extensionTitle: JQuery;
+    let allPackagesExist: boolean = true;
+    let rowCount: number = 0;
+    const $markupContainer: JQuery = $('<div>');
+
+    const $headerRow: JQuery = $('<tr>');
+    $headerRow.append(
+      $('<th>').text('Extension'),
+      $('<th>').text('Key'),
+    );
+    data.activeLanguages.forEach((language: string): void => {
+      $headerRow.append(
+        $('<th>').append(
+          $('<a>', {
+            'class': 'btn btn-default t3js-languagePacks-update',
+            'data-iso': language,
+            'data-toggle': 'tooltip',
+            'title': 'Download and update all language packs',
+          }).append(
+            $('<span>').append(updateIcon),
+            ' ' + language,
+          ),
+        ),
+      );
+    });
+
+    const $tbody = $('<tbody>');
+    data.extensions.forEach((extension: any): void => {
+      allPackagesExist = true;
+      extension.packs.forEach((pack: any): void => {
+        if (pack.exists === false) {
+          allPackagesExist = false;
+        }
+      });
+      if (allPackagesExist === true) {
+        return;
+      }
+      rowCount++;
+      if (extension.icon !== '') {
+        extensionTitle = $('<span>').append(
+          $('<img>', {
+            'style': 'max-height: 16px; max-width: 16px;',
+            'src': '../' + extension.icon,
+            'alt': extension.title,
+          }),
+          $('<span>').text(extension.title),
+        );
+      } else {
+        extensionTitle = $('<span>').text(extension.title);
+      }
+      const $tr = $('<tr>');
+      $tr.append(
+        $('<td>').html(extensionTitle.html()),
+        $('<td>').text(extension.key),
+      );
+      extension.packs.forEach((pack: any): void => {
+        if (pack.exists !== true) {
+          if (pack.lastUpdate !== null) {
+            tooltip = 'No language pack available when tried at ' + pack.lastUpdate + '. Click to re-try.';
+          } else {
+            tooltip = 'Language pack not downloaded. Click to download';
+          }
+          $tr.append(
+            $('<td>').append(
+              $('<a>', {
+                'class': 'btn btn-default t3js-languagePacks-update',
+                'data-extension': extension.key,
+                'data-iso': pack.iso,
+                'data-toggle': 'tooltip',
+                'title': tooltip,
+              }).append(packMissesIcon),
+            ),
+          );
+        }
+      });
+      $tbody.append($tr);
+    });
+
+    $markupContainer.append(
+      $('<h3>').text('Translation status'),
+      $('<table>', {'class': 'table table-striped table-bordered'}).append(
+        $('<thead>').append($headerRow),
+        $tbody,
+      ),
+    );
+    if (rowCount === 0) {
+      return InfoBox.render(
+        Severity.ok,
+        'Language packs have been found for every installed extension.',
+        'To download the latest changes, use the refresh button in the list above.',
+      );
+    }
+    return $markupContainer.html();
+  }
+
+  private getNotificationBox(): JQuery {
+    return this.findInModal(this.selectorNotifications);
+  }
+
+  private addNotification(notification: any): void {
+    this.notifications.push(notification);
+  }
+
+  private renderNotifications(): void {
+    const $notificationBox: JQuery = this.getNotificationBox();
+    for (let i = 0; i < this.notifications.length; ++i) {
+      $notificationBox.append(this.notifications[i]);
+    }
+    this.notifications = [];
+  }
+}
+
+export = new LanguagePacks();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/ResetBackendUserUc.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Maintenance/ResetBackendUserUc.ts
new file mode 100644 (file)
index 0000000..2b1a0ba
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {InlineModuleInterface} from './../InlineModuleInterface';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/ResetBackendUserUc
+ */
+class ResetBackendUserUc implements InlineModuleInterface {
+  public initialize($trigger: JQuery): void {
+    $.ajax({
+      url: Router.getUrl('resetBackendUserUc'),
+      cache: false,
+      beforeSend: (): void => {
+        $trigger.addClass('disabled').prop('disabled', true);
+      },
+      success: (data: any): void => {
+        if (data.success === true && Array.isArray(data.status)) {
+          if (data.status.length > 0) {
+            data.status.forEach((element: any): void => {
+              Notification.success(element.message);
+            });
+          }
+        } else {
+          Notification.error('Something went wrong ...');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        // If reset fails on server side (typically a 500), do not crash entire install tool
+        // but render an error notification instead.
+        Notification.error('Resetting backend user uc failed. Please check the system for missing database fields and try again.');
+      },
+      complete: (): void => {
+        $trigger.removeClass('disabled').prop('disabled', false);
+      },
+    });
+  }
+}
+
+export = new ResetBackendUserUc();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/PhpInfo.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/PhpInfo.ts
deleted file mode 100644 (file)
index 537b852..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/PhpInfo
- */
-class PhpInfo extends AbstractInteractableModule {
-  public initialize(currentModal: any): void {
-    this.currentModal = currentModal;
-    this.getData();
-  }
-
-  private getData(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('phpInfoGetData'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          modalContent.empty().append(data.html);
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new PhpInfo();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Presets.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Presets.ts
deleted file mode 100644 (file)
index 1ad8af5..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import 'bootstrap';
-import Router = require('../Router');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/Presets
- */
-class Presets extends AbstractInteractableModule {
-  private selectorActivateTrigger: string = '.t3js-presets-activate';
-  private selectorImageExecutable: string = '.t3js-presets-image-executable';
-  private selectorImageExecutableTrigger: string = '.t3js-presets-image-executable-trigger';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.getContent();
-
-    // Load content with post data on click 'custom image executable path'
-    currentModal.on('click', this.selectorImageExecutableTrigger, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.getCustomImagePathContent();
-    });
-
-    // Write out selected preset
-    currentModal.on('click', this.selectorActivateTrigger, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.activate();
-    });
-
-    // Automatically select the custom preset if a value in one of its input fields is changed
-    currentModal.find('.t3js-custom-preset').on('input', '.t3js-custom-preset', (e: JQueryEventObject): void => {
-      $('#' + $(e.currentTarget).data('radio')).prop('checked', true);
-    });
-  }
-
-  private getContent(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('presetsGetContent'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
-          modalContent.empty().append(data.html);
-          Modal.setButtons(data.buttons);
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private getCustomImagePathContent(): void {
-    const modalContent = this.getModalBody();
-    const presetsContentToken = this.getModuleContent().data('presets-content-token');
-    $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      data: {
-        'install': {
-          'token': presetsContentToken,
-          'action': 'presetsGetContent',
-          'values': {
-            'Image': {
-              'additionalSearchPath': this.findInModal(this.selectorImageExecutable).val(),
-            },
-          },
-        },
-      },
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
-          modalContent.empty().append(data.html);
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private activate(): void {
-    const modalContent: JQuery = this.getModalBody();
-    const executeToken: string = this.getModuleContent().data('presets-activate-token');
-    const postData: any = {};
-    $(this.findInModal('form').serializeArray()).each((index: number, element: any): void => {
-      postData[element.name] = element.value;
-    });
-    postData['install[action]'] = 'presetsActivate';
-    postData['install[token]'] = executeToken;
-    $.ajax({
-      url: Router.getUrl(),
-      method: 'POST',
-      data: postData,
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true && Array.isArray(data.status)) {
-          data.status.forEach((element: any): void => {
-            Notification.showMessage(element.title, element.message, element.severity);
-          });
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new Presets();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ResetBackendUserUc.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/ResetBackendUserUc.ts
deleted file mode 100644 (file)
index 7d054a3..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {InlineModuleInterface} from './InlineModuleInterface';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/ResetBackendUserUc
- */
-class ResetBackendUserUc implements InlineModuleInterface {
-  public initialize($trigger: JQuery): void {
-    $.ajax({
-      url: Router.getUrl('resetBackendUserUc'),
-      cache: false,
-      beforeSend: (): void => {
-        $trigger.addClass('disabled').prop('disabled', true);
-      },
-      success: (data: any): void => {
-        if (data.success === true && Array.isArray(data.status)) {
-          if (data.status.length > 0) {
-            data.status.forEach((element: any): void => {
-              Notification.success(element.message);
-            });
-          }
-        } else {
-          Notification.error('Something went wrong ...');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        // If reset fails on server side (typically a 500), do not crash entire install tool
-        // but render an error notification instead.
-        Notification.error('Resetting backend user uc failed. Please check the system for missing database fields and try again.');
-      },
-      complete: (): void => {
-        $trigger.removeClass('disabled').prop('disabled', false);
-      },
-    });
-  }
-}
-
-export = new ResetBackendUserUc();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/ChangeInstallToolPassword.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/ChangeInstallToolPassword.ts
new file mode 100644 (file)
index 0000000..f2cb5b0
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import PasswordStrength = require('../PasswordStrength');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/ChangeInstallToolPassword
+ */
+class ChangeInstallToolPassword extends AbstractInteractableModule {
+  private selectorChangeButton: string = '.t3js-changeInstallToolPassword-change';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getData();
+
+    currentModal.on('click', this.selectorChangeButton, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.change();
+    });
+    currentModal.on('click', '.t3-install-form-password-strength', (e: JQueryEventObject): void => {
+      PasswordStrength.initialize('.t3-install-form-password-strength');
+    });
+  }
+
+  private getData(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('changeInstallToolPasswordGetData'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          Modal.setButtons(data.buttons);
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private change(): void {
+    const modalContent = this.getModalBody();
+    const executeToken = this.getModuleContent().data('install-tool-token');
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: {
+        'install': {
+          'action': 'changeInstallToolPassword',
+          'token': executeToken,
+          'password': this.findInModal('.t3js-changeInstallToolPassword-password').val(),
+          'passwordCheck': this.findInModal('.t3js-changeInstallToolPassword-password-check').val(),
+        },
+      },
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true && Array.isArray(data.status)) {
+          data.status.forEach((element: any): void => {
+            Notification.showMessage('', element.message, element.severity);
+          });
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+      complete: (): void => {
+        this.findInModal('.t3js-changeInstallToolPassword-password,.t3js-changeInstallToolPassword-password-check').val('');
+      },
+    });
+  }
+}
+
+export = new ChangeInstallToolPassword();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/ExtensionConfiguration.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/ExtensionConfiguration.ts
new file mode 100644 (file)
index 0000000..eb1afa2
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/ExtensionConfiguration
+ */
+class ExtensionConfiguration extends AbstractInteractableModule {
+  private selectorFormListener: string = '.t3js-extensionConfiguration-form';
+  private selectorSearchInput: string = '.t3js-extensionConfiguration-search';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getContent();
+
+    // Focus search field on certain user interactions
+    currentModal.on('keydown', (e: JQueryEventObject): void => {
+      const $searchInput = currentModal.find(this.selectorSearchInput);
+      if (e.ctrlKey || e.metaKey) {
+        // Focus search field on ctrl-f
+        if (String.fromCharCode(e.which).toLowerCase() === 'f') {
+          e.preventDefault();
+          $searchInput.focus();
+        }
+      } else if (e.keyCode === 27) {
+        // Clear search on ESC key
+        e.preventDefault();
+        $searchInput.val('').focus();
+      }
+    });
+
+    // Perform expand collapse on search matches
+    currentModal.on('keyup', this.selectorSearchInput, (e: JQueryEventObject): void => {
+      const typedQuery = $(e.target).val();
+      const $searchInput = currentModal.find(this.selectorSearchInput);
+      currentModal.find('.search-item').each((index: number, element: any): void => {
+        const $item = $(element);
+        if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) {
+          $item.removeClass('hidden').addClass('searchhit');
+        } else {
+          $item.removeClass('searchhit').addClass('hidden');
+        }
+      });
+      currentModal.find('.searchhit').collapse('show');
+      // Make search field clearable
+      require(['jquery.clearable'], (): void => {
+        $searchInput.clearable().focus();
+      });
+    });
+
+    currentModal.on('submit', this.selectorFormListener, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.write($(e.currentTarget));
+    });
+  }
+
+  private getContent(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('extensionConfigurationGetContent'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          if (Array.isArray(data.status)) {
+            data.status.forEach((element: any): void => {
+              Notification.success(element.title, element.message);
+            });
+          }
+          modalContent.html(data.html);
+          this.initializeWrap();
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  /**
+   * Submit the form and show the result message
+   *
+   * @param {JQuery} $form The form of the current extension
+   */
+  private write($form: JQuery): void {
+    const modalContent = this.getModalBody();
+    const executeToken = this.getModuleContent().data('extension-configuration-write-token');
+    const extensionConfiguration: any = {};
+    $.each($form.serializeArray(), (index: number, element: any): void => {
+      extensionConfiguration[element.name] = element.value;
+    });
+
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: {
+        'install': {
+          'token': executeToken,
+          'action': 'extensionConfigurationWrite',
+          'extensionKey': $form.attr('data-extensionKey'),
+          'extensionConfiguration': extensionConfiguration,
+        },
+      },
+      success: (data: any): void => {
+        if (data.success === true && Array.isArray(data.status)) {
+          data.status.forEach((element: any): void => {
+            Notification.showMessage(element.title, element.message, element.severity);
+          });
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    }).always((): void => {
+      // empty method? why? I guess there is a reason, so let's keep it for the time being.
+    });
+  }
+
+  /**
+   * configuration properties
+   */
+  private initializeWrap(): void {
+    this.findInModal('.t3js-emconf-offset').each((index: number, element: any): void => {
+      const $me = $(element);
+      const $parent = $me.parent();
+      const id = $me.attr('id');
+      const val = $me.attr('value');
+      const valArr = val.split(',');
+
+      $me
+        .attr('data-offsetfield-x', '#' + id + '_offset_x')
+        .attr('data-offsetfield-y', '#' + id + '_offset_y')
+        .wrap('<div class="hidden"></div>');
+
+      const elementX = $('<div>', {'class': 'form-multigroup-item'}).append(
+        $('<div>', {'class': 'input-group'}).append(
+          $('<div>', {'class': 'input-group-addon'}).text('x'),
+          $('<input>', {
+            'id': id + '_offset_x',
+            'class': 'form-control t3js-emconf-offsetfield',
+            'data-target': '#' + id,
+            'value': $.trim(valArr[0]),
+          }),
+        ),
+      );
+      const elementY = $('<div>', {'class': 'form-multigroup-item'}).append(
+        $('<div>', {'class': 'input-group'}).append(
+          $('<div>', {'class': 'input-group-addon'}).text('y'),
+          $('<input>', {
+            'id': id + '_offset_y',
+            'class': 'form-control t3js-emconf-offsetfield',
+            'data-target': '#' + id,
+            'value': $.trim(valArr[1]),
+          }),
+        ),
+      );
+
+      const offsetGroup = $('<div>', {'class': 'form-multigroup-wrap'}).append(elementX, elementY);
+      $parent.append(offsetGroup);
+      $parent.find('.t3js-emconf-offsetfield').keyup((evt: JQueryEventObject): void => {
+        const $target = $parent.find($(evt.currentTarget).data('target'));
+        $target.val($parent.find($target.data('offsetfield-x')).val() + ',' + $parent.find($target.data('offsetfield-y')).val());
+      });
+    });
+
+    this.findInModal('.t3js-emconf-wrap').each((index: number, element: any): void => {
+      const $me = $(element);
+      const $parent = $me.parent();
+      const id = $me.attr('id');
+      const val = $me.attr('value');
+      const valArr = val.split('|');
+
+      $me.attr('data-wrapfield-start', '#' + id + '_wrap_start')
+        .attr('data-wrapfield-end', '#' + id + '_wrap_end')
+        .wrap('<div class="hidden"></div>');
+
+      const wrapGroup = $('<div>', {'class': 'form-multigroup-wrap'}).append(
+        $('<div>', {'class': 'form-multigroup-item'}).append(
+          $('<input>', {
+            'id': id + '_wrap_start',
+            'class': 'form-control t3js-emconf-wrapfield',
+            'data-target': '#' + id,
+            'value': $.trim(valArr[0]),
+          }),
+        ),
+        $('<div>', {'class': 'form-multigroup-item'}).append(
+          $('<input>', {
+            'id': id + '_wrap_end',
+            'class': 'form-control t3js-emconf-wrapfield',
+            'data-target': '#' + id,
+            'value': $.trim(valArr[1]),
+          }),
+        ),
+      );
+      $parent.append(wrapGroup);
+      $parent.find('.t3js-emconf-wrapfield').keyup((evt: JQueryEventObject): void => {
+        const $target = $parent.find($(evt.currentTarget).data('target'));
+        $target.val($parent.find($target.data('wrapfield-start')).val() + '|' + $parent.find($target.data('wrapfield-end')).val());
+      });
+    });
+  }
+}
+
+export = new ExtensionConfiguration();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/Features.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/Features.ts
new file mode 100644 (file)
index 0000000..1cadecf
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/Features
+ */
+class Features extends AbstractInteractableModule {
+  private selectorSaveTrigger: string = '.t3js-features-save';
+
+  public initialize(currentModal: any): void {
+    this.currentModal = currentModal;
+    this.getContent();
+
+    currentModal.on('click', this.selectorSaveTrigger, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.save();
+    });
+  }
+
+  private getContent(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('featuresGetContent'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
+          modalContent.empty().append(data.html);
+          Modal.setButtons(data.buttons);
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private save(): void {
+    const modalContent = this.getModalBody();
+    const executeToken = this.getModuleContent().data('features-save-token');
+    const postData: any = {};
+    $(this.findInModal('form').serializeArray()).each((index: number, element: any): void => {
+      postData[element.name] = element.value;
+    });
+    postData['install[action]'] = 'featuresSave';
+    postData['install[token]'] = executeToken;
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: postData,
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true && Array.isArray(data.status)) {
+          data.status.forEach((element: any): void => {
+            Notification.showMessage(element.title, element.message, element.severity);
+          });
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new Features();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/LocalConfiguration.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/LocalConfiguration.ts
new file mode 100644 (file)
index 0000000..43e518d
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../../Router');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/LocalConfiguration
+ */
+class LocalConfiguration extends AbstractInteractableModule {
+  private selectorToggleAllTrigger: string = '.t3js-localConfiguration-toggleAll';
+  private selectorWriteTrigger: string = '.t3js-localConfiguration-write';
+  private selectorSearchTrigger: string = '.t3js-localConfiguration-search';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getContent();
+
+    // Write out new settings
+    currentModal.on('click', this.selectorWriteTrigger, (): void => {
+      this.write();
+    });
+
+    // Expand / collapse "Toggle all" button
+    currentModal.on('click', this.selectorToggleAllTrigger, (): void => {
+      const modalContent = this.getModalBody();
+      const panels = modalContent.find('.panel-collapse');
+      const action = (panels.eq(0).hasClass('in')) ? 'hide' : 'show';
+      panels.collapse(action);
+    });
+
+    // Make jquerys "contains" work case-insensitive
+    jQuery.expr[':'].contains = jQuery.expr.createPseudo((arg: any): Function => {
+      return (elem: any): boolean => {
+        return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
+      };
+    });
+
+    // Focus search field on certain user interactions
+    currentModal.on('keydown', (e: JQueryEventObject): void => {
+      const $searchInput = currentModal.find(this.selectorSearchTrigger);
+      if (e.ctrlKey || e.metaKey) {
+        // Focus search field on ctrl-f
+        if (String.fromCharCode(e.which).toLowerCase() === 'f') {
+            e.preventDefault();
+            $searchInput.focus();
+        }
+      } else if (e.keyCode === 27) {
+        // Clear search on ESC key
+        e.preventDefault();
+        $searchInput.val('').focus();
+      }
+    });
+
+    // Perform expand collapse on search matches
+    currentModal.on('keyup', this.selectorSearchTrigger, (e: JQueryEventObject): void => {
+      const typedQuery = $(e.target).val();
+      const $searchInput = currentModal.find((this.selectorSearchTrigger));
+      currentModal.find('div.item').each((index: number, element: any): void => {
+        const $item = $(element);
+        if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) {
+          $item.removeClass('hidden').addClass('searchhit');
+        } else {
+          $item.removeClass('searchhit').addClass('hidden');
+        }
+      });
+      currentModal.find('.searchhit').parent().collapse('show');
+      // Make search field clearable
+      require(['jquery.clearable'], (): void => {
+        $searchInput.clearable().focus();
+      });
+    });
+  }
+
+  private getContent(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('localConfigurationGetContent'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          if (Array.isArray(data.status)) {
+            data.status.forEach((element: any): void => {
+              Notification.success(element.title, element.message);
+            });
+          }
+          modalContent.html(data.html);
+          Modal.setButtons(data.buttons);
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private write(): void {
+    const modalContent: JQuery = this.getModalBody();
+    const executeToken: JQuery = this.getModuleContent().data('local-configuration-write-token');
+    const configurationValues: any = {};
+    this.findInModal('.t3js-localConfiguration-pathValue').each((i: number, element: any): void => {
+      const $element: JQuery = $(element);
+      if ($element.attr('type') === 'checkbox') {
+        if (element.checked) {
+          configurationValues[$element.data('path')] = '1';
+        } else {
+          configurationValues[$element.data('path')] = '0';
+        }
+      } else {
+        configurationValues[$element.data('path')] = $element.val();
+      }
+    });
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: {
+        'install': {
+          'action': 'localConfigurationWrite',
+          'token': executeToken,
+          'configurationValues': configurationValues,
+        },
+      },
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true && Array.isArray(data.status)) {
+          data.status.forEach((element: any): void => {
+            Notification.showMessage(element.title, element.message, element.severity);
+          });
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new LocalConfiguration();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/Presets.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/Presets.ts
new file mode 100644 (file)
index 0000000..d278f7e
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../../Router');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/Presets
+ */
+class Presets extends AbstractInteractableModule {
+  private selectorActivateTrigger: string = '.t3js-presets-activate';
+  private selectorImageExecutable: string = '.t3js-presets-image-executable';
+  private selectorImageExecutableTrigger: string = '.t3js-presets-image-executable-trigger';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getContent();
+
+    // Load content with post data on click 'custom image executable path'
+    currentModal.on('click', this.selectorImageExecutableTrigger, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.getCustomImagePathContent();
+    });
+
+    // Write out selected preset
+    currentModal.on('click', this.selectorActivateTrigger, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.activate();
+    });
+
+    // Automatically select the custom preset if a value in one of its input fields is changed
+    currentModal.find('.t3js-custom-preset').on('input', '.t3js-custom-preset', (e: JQueryEventObject): void => {
+      $('#' + $(e.currentTarget).data('radio')).prop('checked', true);
+    });
+  }
+
+  private getContent(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('presetsGetContent'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
+          modalContent.empty().append(data.html);
+          Modal.setButtons(data.buttons);
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private getCustomImagePathContent(): void {
+    const modalContent = this.getModalBody();
+    const presetsContentToken = this.getModuleContent().data('presets-content-token');
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: {
+        'install': {
+          'token': presetsContentToken,
+          'action': 'presetsGetContent',
+          'values': {
+            'Image': {
+              'additionalSearchPath': this.findInModal(this.selectorImageExecutable).val(),
+            },
+          },
+        },
+      },
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
+          modalContent.empty().append(data.html);
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private activate(): void {
+    const modalContent: JQuery = this.getModalBody();
+    const executeToken: string = this.getModuleContent().data('presets-activate-token');
+    const postData: any = {};
+    $(this.findInModal('form').serializeArray()).each((index: number, element: any): void => {
+      postData[element.name] = element.value;
+    });
+    postData['install[action]'] = 'presetsActivate';
+    postData['install[token]'] = executeToken;
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: postData,
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true && Array.isArray(data.status)) {
+          data.status.forEach((element: any): void => {
+            Notification.showMessage(element.title, element.message, element.severity);
+          });
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new Presets();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/SystemMaintainer.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Settings/SystemMaintainer.ts
new file mode 100644 (file)
index 0000000..bd31801
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../../Router');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/SystemMaintainer
+ */
+class SystemMaintainer extends AbstractInteractableModule {
+  private selectorWriteTrigger: string = '.t3js-systemMaintainer-write';
+  private selectorChosenContainer: string = '.t3js-systemMaintainer-chosen';
+  private selectorChosenField: string = '.t3js-systemMaintainer-chosen-select';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    const isInIframe = window.location !== window.parent.location;
+    if (isInIframe) {
+      top.require(['TYPO3/CMS/Install/chosen.jquery.min'], (): void => {
+        this.getList();
+      });
+    } else {
+      require(['TYPO3/CMS/Install/chosen.jquery.min'], (): void => {
+        this.getList();
+      });
+    }
+
+    currentModal.on('click', this.selectorWriteTrigger, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.write();
+    });
+  }
+
+  private getList(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('systemMaintainerGetList'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          if (Array.isArray(data.status)) {
+            data.status.forEach((element: any): void => {
+              Notification.success(element.title, element.message);
+            });
+          }
+          modalContent.html(data.html);
+          Modal.setButtons(data.buttons);
+          if (Array.isArray(data.users)) {
+            data.users.forEach((element: any): void => {
+              let name = element.username;
+              if (element.disable) {
+                name = '[DISABLED] ' + name;
+              }
+              const $option = $('<option>', {'value': element.uid}).text(name);
+              if (element.isSystemMaintainer) {
+                $option.attr('selected', 'selected');
+              }
+              modalContent.find(this.selectorChosenField).append($option);
+            });
+          }
+          const config: any = {
+            '.t3js-systemMaintainer-chosen-select': {
+              width: '100%',
+              placeholder_text_multiple: 'users',
+            },
+          };
+
+          for (const selector in config) {
+            if (config.hasOwnProperty(selector)) {
+              modalContent.find(selector).chosen(config[selector]);
+            }
+          }
+          modalContent.find(this.selectorChosenContainer).show();
+          modalContent.find(this.selectorChosenField).trigger('chosen:updated');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private write(): void {
+    const modalContent = this.getModalBody();
+    const executeToken = this.getModuleContent().data('system-maintainer-write-token');
+    const selectedUsers = this.findInModal(this.selectorChosenField).val();
+    $.ajax({
+      method: 'POST',
+      url: Router.getUrl(),
+      data: {
+        'install': {
+          'users': selectedUsers,
+          'token': executeToken,
+          'action': 'systemMaintainerWrite',
+        },
+      },
+      success: (data: any): void => {
+        if (data.success === true) {
+          if (Array.isArray(data.status)) {
+            data.status.forEach((element: any): void => {
+              Notification.success(element.title, element.message);
+            });
+          }
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new SystemMaintainer();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/SystemInformation.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/SystemInformation.ts
deleted file mode 100644 (file)
index 508291e..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/SystemInformation
- */
-class SystemInformation extends AbstractInteractableModule {
-  public initialize(currentModal: any): void {
-    this.currentModal = currentModal;
-    this.getData();
-  }
-
-  private getData(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('systemInformationGetData'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          modalContent.empty().append(data.html);
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new SystemInformation();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/SystemMaintainer.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/SystemMaintainer.ts
deleted file mode 100644 (file)
index 521c6b6..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import 'bootstrap';
-import Router = require('../Router');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/SystemMaintainer
- */
-class SystemMaintainer extends AbstractInteractableModule {
-  private selectorWriteTrigger: string = '.t3js-systemMaintainer-write';
-  private selectorChosenContainer: string = '.t3js-systemMaintainer-chosen';
-  private selectorChosenField: string = '.t3js-systemMaintainer-chosen-select';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    const isInIframe = window.location !== window.parent.location;
-    if (isInIframe) {
-      top.require(['TYPO3/CMS/Install/chosen.jquery.min'], (): void => {
-        this.getList();
-      });
-    } else {
-      require(['TYPO3/CMS/Install/chosen.jquery.min'], (): void => {
-        this.getList();
-      });
-    }
-
-    currentModal.on('click', this.selectorWriteTrigger, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.write();
-    });
-  }
-
-  private getList(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('systemMaintainerGetList'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          if (Array.isArray(data.status)) {
-            data.status.forEach((element: any): void => {
-              Notification.success(element.title, element.message);
-            });
-          }
-          modalContent.html(data.html);
-          Modal.setButtons(data.buttons);
-          if (Array.isArray(data.users)) {
-            data.users.forEach((element: any): void => {
-              let name = element.username;
-              if (element.disable) {
-                name = '[DISABLED] ' + name;
-              }
-              const $option = $('<option>', {'value': element.uid}).text(name);
-              if (element.isSystemMaintainer) {
-                $option.attr('selected', 'selected');
-              }
-              modalContent.find(this.selectorChosenField).append($option);
-            });
-          }
-          const config: any = {
-            '.t3js-systemMaintainer-chosen-select': {
-              width: '100%',
-              placeholder_text_multiple: 'users',
-            },
-          };
-
-          for (const selector in config) {
-            if (config.hasOwnProperty(selector)) {
-              modalContent.find(selector).chosen(config[selector]);
-            }
-          }
-          modalContent.find(this.selectorChosenContainer).show();
-          modalContent.find(this.selectorChosenField).trigger('chosen:updated');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private write(): void {
-    const modalContent = this.getModalBody();
-    const executeToken = this.getModuleContent().data('system-maintainer-write-token');
-    const selectedUsers = this.findInModal(this.selectorChosenField).val();
-    $.ajax({
-      method: 'POST',
-      url: Router.getUrl(),
-      data: {
-        'install': {
-          'users': selectedUsers,
-          'token': executeToken,
-          'action': 'systemMaintainerWrite',
-        },
-      },
-      success: (data: any): void => {
-        if (data.success === true) {
-          if (Array.isArray(data.status)) {
-            data.status.forEach((element: any): void => {
-              Notification.success(element.title, element.message);
-            });
-          }
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new SystemMaintainer();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/TcaExtTablesCheck.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/TcaExtTablesCheck.ts
deleted file mode 100644 (file)
index ee6771b..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import ProgressBar = require('../Renderable/ProgressBar');
-import Severity = require('../Renderable/Severity');
-import InfoBox = require('../Renderable/InfoBox');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/TcaExtTablesCheck
- */
-class TcaExtTablesCheck extends AbstractInteractableModule {
-  private selectorCheckTrigger: string = '.t3js-tcaExtTablesCheck-check';
-  private selectorOutputContainer: string = '.t3js-tcaExtTablesCheck-output';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.check();
-    currentModal.on('click',  this.selectorCheckTrigger, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.check();
-    });
-  }
-
-  private check(): void {
-    const modalContent = this.getModalBody();
-    const $outputContainer = $(this.selectorOutputContainer);
-    const m: any = ProgressBar.render(Severity.loading, 'Loading...', '');
-    $outputContainer.empty().html(m);
-    $.ajax({
-      url: Router.getUrl('tcaExtTablesCheck'),
-      cache: false,
-      success: (data: any): void => {
-        modalContent.empty().append(data.html);
-        Modal.setButtons(data.buttons);
-        if (data.success === true && Array.isArray(data.status)) {
-          if (data.status.length > 0) {
-            const aMessage: any = InfoBox.render(
-              Severity.warning,
-              'Extensions change TCA in ext_tables.php',
-              'Check for ExtensionManagementUtility and $GLOBALS["TCA"]',
-            );
-            modalContent.find(this.selectorOutputContainer).append(aMessage);
-            data.status.forEach((element: any): void => {
-              const m2: any = InfoBox.render(element.severity, element.title, element.message);
-              $outputContainer.append(m2);
-              modalContent.append(m2);
-            });
-          } else {
-            const aMessage: any = InfoBox.render(Severity.ok, 'No TCA changes in ext_tables.php files. Good job!', '');
-            modalContent.find(this.selectorOutputContainer).append(aMessage);
-          }
-        } else {
-          Notification.error('Something went wrong', 'Use "Check for broken extensions"');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new TcaExtTablesCheck();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/TcaMigrationsCheck.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/TcaMigrationsCheck.ts
deleted file mode 100644 (file)
index 51af969..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import Router = require('../Router');
-import ProgressBar = require('../Renderable/ProgressBar');
-import FlashMessage = require('../Renderable/FlashMessage');
-import Severity = require('../Renderable/Severity');
-import InfoBox = require('../Renderable/InfoBox');
-import Modal = require('TYPO3/CMS/Backend/Modal');
-
-/**
- * Module: TYPO3/CMS/Install/Module/TcaMigrationsCheck
- */
-class TcaMigrationsCheck extends AbstractInteractableModule {
-  private selectorCheckTrigger: string = '.t3js-tcaMigrationsCheck-check';
-  private selectorOutputContainer: string = '.t3js-tcaMigrationsCheck-output';
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    this.check();
-    currentModal.on('click',  this.selectorCheckTrigger, (e: JQueryEventObject): void => {
-      e.preventDefault();
-      this.check();
-    });
-  }
-
-  private check(): void {
-    const $outputContainer: JQuery = $(this.selectorOutputContainer);
-    const modalContent: JQuery = this.getModalBody();
-    const message: any = ProgressBar.render(Severity.loading, 'Loading...', '');
-    $outputContainer.empty().html(message);
-    $.ajax({
-      url: Router.getUrl('tcaMigrationsCheck'),
-      cache: false,
-      success: (data: any): void => {
-        modalContent.empty().append(data.html);
-        Modal.setButtons(data.buttons);
-        if (data.success === true && Array.isArray(data.status)) {
-          if (data.status.length > 0) {
-            const m: any = InfoBox.render(
-              Severity.warning,
-              'TCA migrations need to be applied',
-              'Check the following list and apply needed changes.',
-            );
-            modalContent.find(this.selectorOutputContainer).empty();
-            modalContent.find(this.selectorOutputContainer).append(m);
-            data.status.forEach((element: any): void => {
-              const m2 = InfoBox.render(element.severity, element.title, element.message);
-              modalContent.find(this.selectorOutputContainer).append(m2);
-            });
-          } else {
-            const m3 = InfoBox.render(Severity.ok, 'No TCA migrations need to be applied', 'Your TCA looks good.');
-            modalContent.find(this.selectorOutputContainer).append(m3);
-          }
-        } else {
-          const m4 = FlashMessage.render(Severity.error, 'Something went wrong', 'Use "Check for broken extensions"');
-          modalContent.find(this.selectorOutputContainer).append(m4);
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-}
-
-export = new TcaMigrationsCheck();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/CoreUpdate.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/CoreUpdate.ts
new file mode 100644 (file)
index 0000000..9b31422
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import FlashMessage = require('../../Renderable/FlashMessage');
+import Severity = require('../../Renderable/Severity');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+interface ActionItem {
+  loadingMessage: string;
+  finishMessage: string;
+  nextActionName: string;
+}
+
+interface ActionQueue {
+  [k: string]: ActionItem;
+}
+
+class CoreUpdate extends AbstractInteractableModule {
+  private actionQueue: ActionQueue = {
+    coreUpdateIsUpdateAvailable: {
+      loadingMessage: 'Checking for possible regular or security update',
+      finishMessage: undefined,
+      nextActionName: undefined,
+    },
+    coreUpdateCheckPreConditions: {
+      loadingMessage: 'Checking if update is possible',
+      finishMessage: 'System can be updated',
+      nextActionName: 'coreUpdateDownload',
+    },
+    coreUpdateDownload: {
+      loadingMessage: 'Downloading new core',
+      finishMessage: undefined,
+      nextActionName: 'coreUpdateVerifyChecksum',
+    },
+    coreUpdateVerifyChecksum: {
+      loadingMessage: 'Verifying checksum of downloaded core',
+      finishMessage: undefined,
+      nextActionName: 'coreUpdateUnpack',
+    },
+    coreUpdateUnpack: {
+      loadingMessage: 'Unpacking core',
+      finishMessage: undefined,
+      nextActionName: 'coreUpdateMove',
+    },
+    coreUpdateMove: {
+      loadingMessage: 'Moving core',
+      finishMessage: undefined,
+      nextActionName: 'coreUpdateActivate',
+    },
+    coreUpdateActivate: {
+      loadingMessage: 'Activating core',
+      finishMessage: 'Core updated - please reload your browser',
+      nextActionName: undefined,
+    },
+  };
+
+  private selectorOutput: string = '.t3js-coreUpdate-output';
+  private updateButton: string = '.t3js-coreUpdate-button';
+
+  /**
+   * Clone of a DOM object acts as button template
+   */
+  private buttonTemplate: any = null;
+
+  /**
+   * Fetching the templates out of the DOM
+   */
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getData().done((): void => {
+      this.buttonTemplate = this.findInModal(this.updateButton).clone();
+    });
+
+    currentModal.on('click', '.t3js-coreUpdate-init', (e: JQueryEventObject): void => {
+      e.preventDefault();
+      // Don't use jQuery's data() function, as the DOM is re-rendered and any set data attribute gets lost.
+      // See showActionButton()
+      const action = $(e.currentTarget).attr('data-action');
+
+      this.findInModal(this.selectorOutput).empty();
+      switch (action) {
+        case 'checkForUpdate':
+          this.callAction('coreUpdateIsUpdateAvailable');
+          break;
+        case 'updateDevelopment':
+          this.update('development');
+          break;
+        case 'updateRegular':
+          this.update('regular');
+          break;
+        default:
+          throw 'Unknown update action "' + action + '"';
+      }
+    });
+  }
+
+  private getData(): JQueryXHR {
+    const modalContent = this.getModalBody();
+    return $.ajax({
+      url: Router.getUrl('coreUpdateGetData'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          Modal.setButtons(data.buttons);
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  /**
+   * Execute core update.
+   *
+   * @param type Either 'development' or 'regular'
+   */
+  private update(type: string): void {
+    if (type !== 'development') {
+      type = 'regular';
+    }
+    this.callAction('coreUpdateCheckPreConditions', type);
+  }
+
+  /**
+   * Generic method to call actions from the queue
+   *
+   * @param actionName Name of the action to be called
+   * @param type Update type (optional)
+   */
+  private callAction(actionName: string, type?: string): void {
+    const data: any = {
+      install: {
+        action: actionName,
+      },
+    };
+    if (type !== undefined) {
+      data.install.type = type;
+    }
+    this.addLoadingMessage(this.actionQueue[actionName].loadingMessage);
+    $.ajax({
+      url: Router.getUrl(),
+      data: data,
+      cache: false,
+      success: (result: any): void => {
+        const canContinue = this.handleResult(result, this.actionQueue[actionName].finishMessage);
+        if (canContinue === true && (this.actionQueue[actionName].nextActionName !== undefined)) {
+          this.callAction(this.actionQueue[actionName].nextActionName, type);
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, this.getModalBody());
+      },
+    });
+  }
+
+  /**
+   * Handle ajax result of core update step.
+   */
+  private handleResult(data: any, successMessage: string): boolean {
+    const canContinue: boolean = data.success;
+    this.removeLoadingMessage();
+
+    if (data.status && typeof(data.status) === 'object') {
+      this.showStatusMessages(data.status);
+    }
+    if (data.action && typeof(data.action) === 'object') {
+      this.showActionButton(data.action);
+    }
+    if (successMessage) {
+      this.addMessage(Severity.ok, successMessage);
+    }
+    return canContinue;
+  }
+
+  /**
+   * Add a loading message with some text.
+   *
+   * @param messageTitle
+   */
+  private addLoadingMessage(messageTitle: string): void {
+    const domMessage = FlashMessage.render(Severity.loading, messageTitle);
+    this.findInModal(this.selectorOutput).append(domMessage);
+  }
+
+  /**
+   * Remove an enabled loading message
+   */
+  private removeLoadingMessage(): void {
+    this.findInModal(this.selectorOutput).find('.alert-loading').remove();
+  }
+
+  /**
+   * Show a list of status messages
+   *
+   * @param messages
+   */
+  private showStatusMessages(messages: any): void {
+    $.each(messages, (index: number, element: any): void => {
+      let title: string = '';
+      let message: string = '';
+      const severity: number = element.severity;
+      if (element.title) {
+        title = element.title;
+      }
+      if (element.message) {
+        message = element.message;
+      }
+      this.addMessage(severity, title, message);
+    });
+  }
+
+  /**
+   * Show an action button
+   *
+   * @param button
+   */
+  private showActionButton(button: any): void {
+    let title = false;
+    let action = false;
+    if (button.title) {
+      title = button.title;
+    }
+    if (button.action) {
+      action = button.action;
+    }
+    const domButton = this.buttonTemplate;
+    if (action) {
+      domButton.attr('data-action', action);
+    }
+    if (title) {
+      domButton.text(title);
+    }
+    this.findInModal(this.updateButton).replaceWith(domButton);
+  }
+
+  /**
+   * Show a status message
+   */
+  private addMessage(severity: number, title: string, message?: string): void {
+    const domMessage = FlashMessage.render(severity, title, message);
+    this.findInModal(this.selectorOutput).append(domMessage);
+  }
+}
+
+export = new CoreUpdate();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/ExtensionCompatTester.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/ExtensionCompatTester.ts
new file mode 100644 (file)
index 0000000..0ba53ce
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../../Router');
+import ProgressBar = require('../../Renderable/ProgressBar');
+import InfoBox = require('../../Renderable/InfoBox');
+import Severity = require('../../Renderable/Severity');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/ExtensionCompatTester
+ */
+class ExtensionCompatTester extends AbstractInteractableModule {
+  private selectorCheckTrigger: string = '.t3js-extensionCompatTester-check';
+  private selectorUninstallTrigger: string = '.t3js-extensionCompatTester-uninstall';
+  private selectorOutputContainer: string = '.t3js-extensionCompatTester-output';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getLoadedExtensionList();
+
+    currentModal.on('click', this.selectorCheckTrigger, (e: JQueryEventObject): void => {
+      this.findInModal(this.selectorUninstallTrigger).addClass('hidden');
+      this.findInModal(this.selectorOutputContainer).empty();
+      this.getLoadedExtensionList();
+    });
+    currentModal.on('click', this.selectorUninstallTrigger, (e: JQueryEventObject): void => {
+      this.uninstallExtension($(e.target).data('extension'));
+    });
+  }
+
+  private getLoadedExtensionList(): void {
+    this.findInModal(this.selectorCheckTrigger).addClass('disabled').prop('disabled', true);
+    this.findInModal('.modal-loading').hide();
+    const modalContent = this.getModalBody();
+    const modalFooter = this.getModalFooter();
+    const $outputContainer = this.findInModal(this.selectorOutputContainer);
+    const message = ProgressBar.render(Severity.loading, 'Loading...', '');
+    $outputContainer.append(message);
+
+    $.ajax({
+      url: Router.getUrl('extensionCompatTesterLoadedExtensionList'),
+      cache: false,
+      success: (data: any): void => {
+        modalContent.empty().append(data.html);
+        Modal.setButtons(data.buttons);
+        const $innerOutputContainer: JQuery = this.findInModal(this.selectorOutputContainer);
+        const progressBar = ProgressBar.render(Severity.loading, 'Loading...', '');
+        $innerOutputContainer.append(progressBar);
+
+        if (data.success === true && Array.isArray(data.extensions)) {
+          const loadExtLocalconf = (): void => {
+            const promises: Array<any> = [];
+            data.extensions.forEach((extension: any): void => {
+              promises.push(this.loadExtLocalconf(extension));
+            });
+            return $.when.apply($, promises).done((): void => {
+              const aMessage = InfoBox.render(Severity.ok, 'ext_localconf.php of all loaded extensions successfully loaded', '');
+              $innerOutputContainer.append(aMessage);
+            });
+          };
+
+          const loadExtTables = (): void => {
+            const promises: Array<any> = [];
+            data.extensions.forEach((extension: any): void => {
+              promises.push(this.loadExtTables(extension));
+            });
+            return $.when.apply($, promises).done((): void => {
+              const aMessage = InfoBox.render(Severity.ok, 'ext_tables.php of all loaded extensions successfully loaded', '');
+              $innerOutputContainer.append(aMessage);
+            });
+          };
+
+          $.when(loadExtLocalconf(), loadExtTables()).fail((response: any): void => {
+            const aMessage = InfoBox.render(
+              Severity.error,
+              'Loading ' + response.scope + ' of extension "' + response.extension + '" failed',
+            );
+            $innerOutputContainer.append(aMessage);
+            modalFooter.find(this.selectorUninstallTrigger)
+              .text('Unload extension "' + response.extension + '"')
+              .attr('data-extension', response.extension)
+              .removeClass('hidden');
+          }).always((): void => {
+            $innerOutputContainer.find('.alert-loading').remove();
+            this.findInModal(this.selectorCheckTrigger).removeClass('disabled').prop('disabled', false);
+          });
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private loadExtLocalconf(extension: string): JQueryPromise<{}> {
+    const executeToken = this.getModuleContent().data('extension-compat-tester-load-ext_localconf-token');
+    const $ajax = $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      cache: false,
+      data: {
+        'install': {
+          'action': 'extensionCompatTesterLoadExtLocalconf',
+          'token': executeToken,
+          'extension': extension,
+        },
+      },
+    });
+
+    return $ajax.promise().then(null, (): any => {
+      throw {
+        scope: 'ext_localconf.php',
+        extension: extension,
+      };
+    });
+  }
+
+  private loadExtTables(extension: string): JQueryPromise<{}> {
+    const executeToken = this.getModuleContent().data('extension-compat-tester-load-ext_tables-token');
+    const $ajax = $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      cache: false,
+      data: {
+        'install': {
+          'action': 'extensionCompatTesterLoadExtTables',
+          'token': executeToken,
+          'extension': extension,
+        },
+      },
+    });
+
+    return $ajax.promise().then(null, (): any => {
+      throw {
+        scope: 'ext_tables.php',
+        extension: extension,
+      };
+    });
+  }
+
+  /**
+   * Send an ajax request to uninstall an extension (or multiple extensions)
+   *
+   * @param extension string of extension(s) - may be comma separated
+   */
+  private uninstallExtension(extension: string): void {
+    const executeToken = this.getModuleContent().data('extension-compat-tester-uninstall-extension-token');
+    const modalContent = this.getModalBody();
+    const $outputContainer = $(this.selectorOutputContainer);
+    const message = ProgressBar.render(Severity.loading, 'Loading...', '');
+    $outputContainer.append(message);
+    $.ajax({
+      url: Router.getUrl(),
+      cache: false,
+      method: 'POST',
+      data: {
+        'install': {
+          'action': 'extensionCompatTesterUninstallExtension',
+          'token': executeToken,
+          'extension': extension,
+        },
+      },
+      success: (data: any): void => {
+        if (data.success) {
+          if (Array.isArray(data.status)) {
+            data.status.forEach((element: any): void => {
+              const aMessage = InfoBox.render(element.severity, element.title, element.message);
+              modalContent.find(this.selectorOutputContainer).empty().append(aMessage);
+            });
+          }
+          this.findInModal(this.selectorUninstallTrigger).addClass('hidden');
+          this.getLoadedExtensionList();
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new ExtensionCompatTester();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/ExtensionScanner.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/ExtensionScanner.ts
new file mode 100644 (file)
index 0000000..e0200bd
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import 'bootstrap';
+import AjaxQueue = require('../../Ajax/AjaxQueue');
+import Router = require('../../Router');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+interface FileData {
+  success: boolean;
+  matches: Array<Match>;
+  isFileIgnored: boolean;
+  effectiveCodeLines: number;
+  ignoredLines: number;
+}
+
+interface Match {
+  uniqueId: string;
+  message: string;
+  indicator: string;
+  silenced: boolean;
+  lineContent: string;
+  line: number;
+  restFiles: Array<RestFile>;
+}
+
+interface RestFile {
+  uniqueId: string;
+  version: string;
+  headline: string;
+  content: string;
+  class: string;
+  file_hash: string;
+}
+
+class ExtensionScanner extends AbstractInteractableModule {
+  private listOfAffectedRestFileHashes: Array<any> = [];
+  private selectorExtensionContainer: string = '.t3js-extensionScanner-extension';
+  private selectorNumberOfFiles: string = '.t3js-extensionScanner-number-of-files';
+  private selectorScanSingleTrigger: string = '.t3js-extensionScanner-scan-single';
+  private selectorExtensionScanButton: string = '.t3js-extensionScanner-scan-all';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getData();
+
+    currentModal.on('show.bs.collapse', this.selectorExtensionContainer, (e: JQueryEventObject): void => {
+      // Scan a single extension by opening the panel
+      const $me = $(e.currentTarget);
+      if (typeof $me.data('scanned') === 'undefined') {
+        const extension = $me.data('extension');
+        this.scanSingleExtension(extension);
+        $me.data('scanned', true);
+      }
+    }).on('click', this.selectorScanSingleTrigger, (e: JQueryEventObject): void => {
+      // Scan a single extension by clicking "Rescan"
+      e.preventDefault();
+      const extension = $(e.currentTarget).closest(this.selectorExtensionContainer).data('extension');
+      this.scanSingleExtension(extension);
+    }).on('click', this.selectorExtensionScanButton, (e: JQueryEventObject): void => {
+      // Scan all button
+      e.preventDefault();
+      $(e.currentTarget).addClass('disabled').prop('disabled', true);
+      const $extensions = currentModal.find(this.selectorExtensionContainer);
+      this.scanAll($extensions);
+    });
+  }
+
+  private getData(): void {
+    const modalContent = this.getModalBody();
+    AjaxQueue.add({
+      url: Router.getUrl('extensionScannerGetData'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          Modal.setButtons(data.buttons);
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private getExtensionSelector(extension: string): string {
+    return this.selectorExtensionContainer + '-' + extension;
+  }
+
+  private scanAll($extensions: JQuery): void {
+    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();
+    $extensions.each((index: number, element: any): void => {
+      const $me: JQuery = $(element);
+      const extension = $me.data('extension');
+      this.scanSingleExtension(extension);
+      $me.data('scanned', true);
+    });
+  }
+
+  private setStatusMessageForScan(extension: string, doneFiles: number, numberOfFiles: number): void {
+    this.findInModal(this.getExtensionSelector(extension))
+      .find(this.selectorNumberOfFiles)
+      .text('Checked ' + doneFiles + ' of ' + numberOfFiles + ' files');
+  }
+
+  private setProgressForScan(extension: string, doneFiles: number, numberOfFiles: number): void {
+    const percent = (doneFiles / numberOfFiles) * 100;
+    this.findInModal(this.getExtensionSelector(extension))
+      .find('.panel-progress-bar')
+      .css('width', percent + '%')
+      .attr('aria-valuenow', percent)
+      .find('span')
+      .text(percent + '%');
+  }
+
+  private setProgressForAll(): void {
+    const numberOfExtensions: number = this.findInModal(this.selectorExtensionContainer).length;
+    const numberOfSuccess: number = this.findInModal(this.selectorExtensionContainer
+      + '.t3js-extensionscan-finished.panel-success').length;
+    const numberOfWarning: number = this.findInModal(this.selectorExtensionContainer
+      + '.t3js-extensionscan-finished.panel-warning').length;
+    const numberOfError: number = this.findInModal(this.selectorExtensionContainer
+      + '.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');
+      AjaxQueue.add({
+        url: Router.getUrl(),
+        method: 'POST',
+        data: {
+          'install': {
+            'action': 'extensionScannerMarkFullyScannedRestFiles',
+            'token': this.getModuleContent().data('extension-scanner-mark-fully-scanned-rest-files-token'),
+            'hashes': this.uniqueArray(this.listOfAffectedRestFileHashes),
+          },
+        },
+        cache: false,
+        success: (data: any): void => {
+          if (data.success === true) {
+            Notification.success('Marked not affected files', 'Marked ' + data.markedAsNotAffected + ' ReST files as not affected.');
+          }
+        },
+        error: (xhr: XMLHttpRequest): void => {
+          Router.handleAjaxError(xhr, modalContent);
+        },
+      });
+    }
+  }
+
+  /**
+   * Helper method removing duplicate entries from an array
+   */
+  private uniqueArray(anArray: Array<any>): Array<any> {
+    return anArray.filter((value, index, self): boolean => {
+      return self.indexOf(value) === index;
+    });
+  }
+
+  /**
+   * Handle a single extension scan
+   */
+  private scanSingleExtension(extension: string): void {
+    const executeToken = this.getModuleContent().data('extension-scanner-files-token');
+    const modalContent = this.getModalBody();
+    const $extensionContainer = this.findInModal(this.getExtensionSelector(extension));
+    const hitTemplate = '#t3js-extensionScanner-file-hit-template';
+    const restTemplate = '#t3js-extensionScanner-file-hit-rest-template';
+    let hitFound = false;
+    $extensionContainer.removeClass('panel-danger panel-warning panel-success t3js-extensionscan-finished');
+    $extensionContainer.data('hasRun', 'true');
+    $extensionContainer.find('.t3js-extensionScanner-scan-single').text('Scanning...').attr('disabled', 'disabled');
+    $extensionContainer.find('.t3js-extensionScanner-extension-body-loc').empty().text('0');
+    $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').empty().text('0');
+    $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').empty().text('0');
+    this.setProgressForAll();
+    AjaxQueue.add({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: {
+        'install': {
+          'action': 'extensionScannerFiles',
+          'token': executeToken,
+          'extension': extension,
+        },
+      },
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true && Array.isArray(data.files)) {
+          const numberOfFiles = data.files.length;
+          if (numberOfFiles > 0) {
+            this.setStatusMessageForScan(extension, 0, numberOfFiles);
+            $extensionContainer.find('.t3js-extensionScanner-extension-body').text('');
+            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,
+                  },
+                },
+                url: Router.getUrl(),
+                cache: false,
+                success: (fileData: FileData): void => {
+                  doneFiles++;
+                  this.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
+                  this.setProgressForScan(extension, doneFiles, numberOfFiles);
+                  if (fileData.success && $.isArray(fileData.matches)) {
+                    fileData.matches.forEach((match: Match): void => {
+                      hitFound = true;
+                      const aMatch: any = modalContent.find(hitTemplate).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" title="Reliable match, false positive unlikely">strong</span>');
+                      } else {
+                        aMatch.find('.t3js-extensionScanner-hit-file-panel-head .badges')
+                          .append('<span class="badge" 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" 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 ($.isArray(match.restFiles)) {
+                        match.restFiles.forEach((restFile: RestFile): void => {
+                          const aRest = modalContent.find(restTemplate).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);
+                      $extensionContainer.find('.t3js-extensionScanner-extension-body').removeClass('hide').append(aMatch);
+                      if (panelClass === 'panel-danger') {
+                        $extensionContainer.removeClass('panel-warning').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);
+                    }
+                    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);
+                  }
+                  if (doneFiles === numberOfFiles) {
+                    if (!hitFound) {
+                      $extensionContainer.addClass('panel-success');
+                    }
+                    $extensionContainer.addClass('t3js-extensionscan-finished');
+                    this.setProgressForAll();
+                    $extensionContainer.find('.t3js-extensionScanner-scan-single').text('Rescan').attr('disabled', null);
+                  }
+                },
+                error: (xhr: XMLHttpRequest): void => {
+                  doneFiles = doneFiles + 1;
+                  this.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
+                  this.setProgressForScan(extension, doneFiles, numberOfFiles);
+                  this.setProgressForAll();
+                  Notification.error('Oops, an error occurred', 'Please look at the console output for details');
+                  console.error(xhr);
+                },
+                });
+              });
+            } else {
+              Notification.warning('No files found', 'The extension EXT:' + extension + ' contains no files we can scan');
+            }
+          } else {
+            Notification.error('Oops, an error occurred', 'Please look at the console output for details');
+            console.error(data);
+          }
+        },
+        error: (xhr: XMLHttpRequest): void => {
+          Router.handleAjaxError(xhr, modalContent);
+        },
+      },
+    );
+  }
+}
+
+export = new ExtensionScanner();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/TcaExtTablesCheck.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/TcaExtTablesCheck.ts
new file mode 100644 (file)
index 0000000..3af4c80
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import ProgressBar = require('../../Renderable/ProgressBar');
+import Severity = require('../../Renderable/Severity');
+import InfoBox = require('../../Renderable/InfoBox');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/TcaExtTablesCheck
+ */
+class TcaExtTablesCheck extends AbstractInteractableModule {
+  private selectorCheckTrigger: string = '.t3js-tcaExtTablesCheck-check';
+  private selectorOutputContainer: string = '.t3js-tcaExtTablesCheck-output';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.check();
+    currentModal.on('click',  this.selectorCheckTrigger, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.check();
+    });
+  }
+
+  private check(): void {
+    const modalContent = this.getModalBody();
+    const $outputContainer = $(this.selectorOutputContainer);
+    const m: any = ProgressBar.render(Severity.loading, 'Loading...', '');
+    $outputContainer.empty().html(m);
+    $.ajax({
+      url: Router.getUrl('tcaExtTablesCheck'),
+      cache: false,
+      success: (data: any): void => {
+        modalContent.empty().append(data.html);
+        Modal.setButtons(data.buttons);
+        if (data.success === true && Array.isArray(data.status)) {
+          if (data.status.length > 0) {
+            const aMessage: any = InfoBox.render(
+              Severity.warning,
+              'Extensions change TCA in ext_tables.php',
+              'Check for ExtensionManagementUtility and $GLOBALS["TCA"]',
+            );
+            modalContent.find(this.selectorOutputContainer).append(aMessage);
+            data.status.forEach((element: any): void => {
+              const m2: any = InfoBox.render(element.severity, element.title, element.message);
+              $outputContainer.append(m2);
+              modalContent.append(m2);
+            });
+          } else {
+            const aMessage: any = InfoBox.render(Severity.ok, 'No TCA changes in ext_tables.php files. Good job!', '');
+            modalContent.find(this.selectorOutputContainer).append(aMessage);
+          }
+        } else {
+          Notification.error('Something went wrong', 'Use "Check for broken extensions"');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new TcaExtTablesCheck();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/TcaMigrationsCheck.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/TcaMigrationsCheck.ts
new file mode 100644 (file)
index 0000000..109e126
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import Router = require('../../Router');
+import ProgressBar = require('../../Renderable/ProgressBar');
+import FlashMessage = require('../../Renderable/FlashMessage');
+import Severity = require('../../Renderable/Severity');
+import InfoBox = require('../../Renderable/InfoBox');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/TcaMigrationsCheck
+ */
+class TcaMigrationsCheck extends AbstractInteractableModule {
+  private selectorCheckTrigger: string = '.t3js-tcaMigrationsCheck-check';
+  private selectorOutputContainer: string = '.t3js-tcaMigrationsCheck-output';
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.check();
+    currentModal.on('click',  this.selectorCheckTrigger, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.check();
+    });
+  }
+
+  private check(): void {
+    const $outputContainer: JQuery = $(this.selectorOutputContainer);
+    const modalContent: JQuery = this.getModalBody();
+    const message: any = ProgressBar.render(Severity.loading, 'Loading...', '');
+    $outputContainer.empty().html(message);
+    $.ajax({
+      url: Router.getUrl('tcaMigrationsCheck'),
+      cache: false,
+      success: (data: any): void => {
+        modalContent.empty().append(data.html);
+        Modal.setButtons(data.buttons);
+        if (data.success === true && Array.isArray(data.status)) {
+          if (data.status.length > 0) {
+            const m: any = InfoBox.render(
+              Severity.warning,
+              'TCA migrations need to be applied',
+              'Check the following list and apply needed changes.',
+            );
+            modalContent.find(this.selectorOutputContainer).empty();
+            modalContent.find(this.selectorOutputContainer).append(m);
+            data.status.forEach((element: any): void => {
+              const m2 = InfoBox.render(element.severity, element.title, element.message);
+              modalContent.find(this.selectorOutputContainer).append(m2);
+            });
+          } else {
+            const m3 = InfoBox.render(Severity.ok, 'No TCA migrations need to be applied', 'Your TCA looks good.');
+            modalContent.find(this.selectorOutputContainer).append(m3);
+          }
+        } else {
+          const m4 = FlashMessage.render(Severity.error, 'Something went wrong', 'Use "Check for broken extensions"');
+          modalContent.find(this.selectorOutputContainer).append(m4);
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+}
+
+export = new TcaMigrationsCheck();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/UpgradeDocs.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/UpgradeDocs.ts
new file mode 100644 (file)
index 0000000..7df0b8d
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/UpgradeDocs
+ */
+class UpgradeDocs extends AbstractInteractableModule {
+  private selectorRestFileItem: string = '.upgrade_analysis_item_to_filter';
+  private selectorFulltextSearch: string = '.t3js-upgradeDocs-fulltext-search';
+  private selectorChosenField: string = '.t3js-upgradeDocs-chosen-select';
+  private selectorChangeLogsForVersionContainer: string = '.t3js-version-changes';
+  private selectorChangeLogsForVersion: string = '.t3js-changelog-list';
+
+  private chosenField: JQuery;
+  private fulltextSearchField: JQuery;
+
+  private static trimExplodeAndUnique(delimiter: string, string: string): Array<string> {
+    const result: Array<string> = [];
+    const items = string.split(delimiter);
+    for (let i = 0; i < items.length; i++) {
+      const item = items[i].trim();
+      if (item.length > 0) {
+        if ($.inArray(item, result) === -1) {
+          result.push(item);
+        }
+      }
+    }
+    return result;
+  }
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    const isInIframe = (window.location !== window.parent.location);
+    if (isInIframe) {
+      top.require(['TYPO3/CMS/Install/chosen.jquery.min'], (): void => {
+        this.getContent();
+      });
+    } else {
+      require(['TYPO3/CMS/Install/chosen.jquery.min'], (): void => {
+        this.getContent();
+      });
+    }
+
+    // Mark a file as read
+    currentModal.on('click',  '.t3js-upgradeDocs-markRead', (e: JQueryEventObject): void => {
+      this.markRead(e.target);
+    });
+    currentModal.on('click',  '.t3js-upgradeDocs-unmarkRead', (e: JQueryEventObject): void => {
+      this.unmarkRead(e.target);
+    });
+
+    // Make jquerys "contains" work case-insensitive
+    jQuery.expr[':'].contains = jQuery.expr.createPseudo((arg: any): Function => {
+      return (elem: any): boolean => {
+        return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
+      };
+    });
+
+    require(['jquery.clearable'], (): void => {
+      currentModal.find(this.selectorFulltextSearch).clearable().focus();
+    });
+  }
+
+  private getContent(): void {
+    const modalContent = this.getModalBody();
+    $.ajax({
+      url: Router.getUrl('upgradeDocsGetContent'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
+          modalContent.empty().append(data.html);
+
+          this.initializeFullTextSearch();
+          this.initializeChosenSelector();
+          this.loadChangelogs();
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private loadChangelogs(): void {
+    const promises: Array<any> = [];
+    const modalContent = this.getModalBody();
+    this.findInModal(this.selectorChangeLogsForVersionContainer).each((index: number, el: any): void => {
+      const $request = $.ajax({
+        url: Router.getUrl('upgradeDocsGetChangelogForVersion'),
+        cache: false,
+        data: {
+          install: {
+            version: el.dataset.version,
+          },
+        },
+        success: (data: any): void => {
+          if (data.success === true) {
+            const $panelGroup = $(el);
+            const $container = $panelGroup.find(this.selectorChangeLogsForVersion);
+            $container.html(data.html);
+            this.renderTags($container);
+            this.moveNotRelevantDocuments($container);
+
+            // Remove loading spinner form panel
+            $panelGroup.find('.t3js-panel-loading').remove();
+          } else {
+            Notification.error('Something went wrong');
+          }
+        },
+        error: (xhr: XMLHttpRequest): void => {
+          Router.handleAjaxError(xhr, modalContent);
+        },
+      });
+
+      promises.push($request);
+    });
+
+    $.when.apply($, promises).done((): void => {
+      this.fulltextSearchField.prop('disabled', false);
+      this.appendItemsToChosenSelector();
+    });
+  }
+
+  private initializeFullTextSearch(): void {
+    this.fulltextSearchField = this.findInModal(this.selectorFulltextSearch);
+    this.fulltextSearchField.clearable().focus();
+    this.initializeChosenSelector();
+    this.fulltextSearchField.on('keyup', (): void => {
+      this.combinedFilterSearch();
+    });
+  }
+
+  private initializeChosenSelector(): void {
+    this.chosenField = this.getModalBody().find(this.selectorChosenField);
+
+    const config: any = {
+      '.chosen-select': {width: '100%', placeholder_text_multiple: 'tags'},
+      '.chosen-select-deselect': {allow_single_deselect: true},
+      '.chosen-select-no-single': {disable_search_threshold: 10},
+      '.chosen-select-no-results': {no_results_text: 'Oops, nothing found!'},
+      '.chosen-select-width': {width: '100%'},
+    };
+    for (const selector in config) {
+      if (config.hasOwnProperty(selector)) {
+        this.findInModal(selector).chosen(config[selector]);
+      }
+    }
+    this.chosenField.on('change', (): void => {
+      this.combinedFilterSearch();
+    });
+  }
+
+  /**
+   * Appends tags to the chosen selector
+   */
+  private appendItemsToChosenSelector(): void {
+    let tagString = '';
+    $(this.findInModal(this.selectorRestFileItem)).each((index: number, element: any): void => {
+      tagString += $(element).data('item-tags') + ',';
+    });
+    const tagArray = UpgradeDocs.trimExplodeAndUnique(',', tagString).sort((a: string, b: string): number => {
+      // Sort case-insensitive by name
+      return a.toLowerCase().localeCompare(b.toLowerCase());
+    });
+    this.chosenField.prop('disabled', false);
+    $.each(tagArray, (i: number, tag: any): void => {
+      this.chosenField.append($('<option>').text(tag));
+    });
+    this.chosenField.trigger('chosen:updated');
+  }
+
+  private combinedFilterSearch(): boolean {
+    const modalContent = this.getModalBody();
+    const $items = modalContent.find('div.item');
+    if (this.chosenField.val().length < 1 && this.fulltextSearchField.val().length < 1) {
+      $('.panel-version:not(:first) > .panel-collapse').collapse('hide');
+      $items.removeClass('hidden searchhit filterhit');
+      return false;
+    }
+    $items.addClass('hidden').removeClass('searchhit filterhit');
+
+    // apply tags
+    if (this.chosenField.val().length > 0) {
+      $items
+        .addClass('hidden')
+        .removeClass('filterhit');
+      const orTags: Array<string> = [];
+      const andTags: Array<string> = [];
+      $.each(this.chosenField.val(), (index: number, item: any): void => {
+        const tagFilter = '[data-item-tags*="' + item + '"]';
+        if (item.indexOf(':') > 0) {
+          orTags.push(tagFilter);
+        } else {
+          andTags.push(tagFilter);
+        }
+      });
+      const andString = andTags.join('');
+      const tags = [];
+      if (orTags.length) {
+        for (let i = 0; i < orTags.length; i++) {
+          tags.push(andString + orTags[i]);
+        }
+      } else {
+        tags.push(andString);
+      }
+      const tagSelection = tags.join(',');
+      modalContent.find(tagSelection)
+        .removeClass('hidden')
+        .addClass('searchhit filterhit');
+    } else {
+      $items
+        .addClass('filterhit')
+        .removeClass('hidden');
+    }
+    // apply fulltext search
+    const typedQuery = this.fulltextSearchField.val();
+    modalContent.find('div.item.filterhit').each((index: number, element: any): void => {
+      const $item = $(element);
+      if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) {
+        $item.removeClass('hidden').addClass('searchhit');
+      } else {
+        $item.removeClass('searchhit').addClass('hidden');
+      }
+    });
+
+    modalContent.find('.searchhit').closest('.panel-collapse').collapse('show');
+
+    // Check for empty panels
+    modalContent.find('.panel-version').each((index: number, element: any): void => {
+      const $element: any = $(element);
+      if ($element.find('.searchhit', '.filterhit').length < 1) {
+        $element.find(' > .panel-collapse').collapse('hide');
+      }
+    });
+
+    return true;
+  }
+
+  private renderTags($container: any): void {
+    $.each($container.find(this.selectorRestFileItem), (index: number, element: any): void => {
+      const $me = $(element);
+      const tags = $me.data('item-tags').split(',');
+      const $tagContainer = $me.find('.t3js-tags');
+      tags.forEach((value: string): void => {
+        $tagContainer.append($('<span />', {'class': 'label'}).text(value));
+      });
+    });
+  }
+
+  /**
+   * Moves all documents that are either read or not affected
+   */
+  private moveNotRelevantDocuments($container: JQuery): void {
+    $container.find('[data-item-state="read"]').appendTo(this.findInModal('.panel-body-read'));
+    $container.find('[data-item-state="notAffected"]').appendTo(this.findInModal('.panel-body-not-affected'));
+  }
+
+  private markRead(element: any): void {
+    const modalContent = this.getModalBody();
+    const executeToken = this.getModuleContent().data('upgrade-docs-mark-read-token');
+    const $button = $(element).closest('a');
+    $button.toggleClass('t3js-upgradeDocs-unmarkRead t3js-upgradeDocs-markRead');
+    $button.find('i').toggleClass('fa-check fa-ban');
+    $button.closest('.panel').appendTo(this.findInModal('.panel-body-read'));
+    $.ajax({
+      method: 'POST',
+      url: Router.getUrl(),
+      data: {
+        'install': {
+          'ignoreFile': $button.data('filepath'),
+          'token': executeToken,
+          'action': 'upgradeDocsMarkRead',
+        },
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private unmarkRead(element: any): void {
+    const modalContent = this.getModalBody();
+    const executeToken = this.getModuleContent().data('upgrade-docs-unmark-read-token');
+    const $button = $(element).closest('a');
+    const version = $button.closest('.panel').data('item-version');
+    $button.toggleClass('t3js-upgradeDocs-markRead t3js-upgradeDocs-unmarkRead');
+    $button.find('i').toggleClass('fa-check fa-ban');
+    $button.closest('.panel').appendTo(this.findInModal('*[data-group-version="' + version + '"] .panel-body'));
+    $.ajax({
+      method: 'POST',
+      url: Router.getUrl(),
+      data: {
+        'install': {
+          'ignoreFile': $button.data('filepath'),
+          'token': executeToken,
+          action: 'upgradeDocsUnmarkRead',
+        },
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new UpgradeDocs();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/UpgradeWizards.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/UpgradeWizards.ts
new file mode 100644 (file)
index 0000000..dfe3d04
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import {AbstractInteractableModule} from '../AbstractInteractableModule';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../../Router');
+import Severity = require('../../Renderable/Severity');
+import ProgressBar = require('../../Renderable/ProgressBar');
+import InfoBox = require('../../Renderable/InfoBox');
+import FlashMessage = require('../../Renderable/FlashMessage');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+import SecurityUtility = require('TYPO3/CMS/Core/SecurityUtility');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/UpgradeWizards
+ */
+class UpgradeWizards extends AbstractInteractableModule {
+  private selectorOutputWizardsContainer: string = '.t3js-upgradeWizards-wizards-output';
+  private selectorOutputDoneContainer: string = '.t3js-upgradeWizards-done-output';
+  private selectorWizardsBlockingAddsTemplate: string = '.t3js-upgradeWizards-blocking-adds-template';
+  private selectorWizardsBlockingAddsRows: string = '.t3js-upgradeWizards-blocking-adds-rows';
+  private selectorWizardsBlockingAddsExecute: string = '.t3js-upgradeWizards-blocking-adds-execute';
+  private selectorWizardsBlockingCharsetTemplate: string = '.t3js-upgradeWizards-blocking-charset-template';
+  private selectorWizardsBlockingCharsetFix: string = '.t3js-upgradeWizards-blocking-charset-fix';
+  private selectorWizardsDoneBodyTemplate: string = '.t3js-upgradeWizards-done-body-template';
+  private selectorWizardsDoneRows: string = '.t3js-upgradeWizards-done-rows';
+  private selectorWizardsDoneRowTemplate: string = '.t3js-upgradeWizards-done-row-template table tr';
+  private selectorWizardsDoneRowMarkUndone: string = '.t3js-upgradeWizards-done-markUndone';
+  private selectorWizardsDoneRowTitle: string = '.t3js-upgradeWizards-done-title';
+  private selectorWizardsListTemplate: string = '.t3js-upgradeWizards-list-template';
+  private selectorWizardsListRows: string = '.t3js-upgradeWizards-list-rows';
+  private selectorWizardsListRowTemplate: string = '.t3js-upgradeWizards-list-row-template';
+  private selectorWizardsListRowTitle: string = '.t3js-upgradeWizards-list-row-title';
+  private selectorWizardsListRowExplanation: string = '.t3js-upgradeWizards-list-row-explanation';
+  private selectorWizardsListRowExecute: string = '.t3js-upgradeWizards-list-row-execute';
+  private selectorWizardsInputTemplate: string = '.t3js-upgradeWizards-input';
+  private selectorWizardsInputTitle: string = '.t3js-upgradeWizards-input-title';
+  private selectorWizardsInputHtml: string = '.t3js-upgradeWizards-input-html';
+  private selectorWizardsInputPerform: string = '.t3js-upgradeWizards-input-perform';
+  private securityUtility: SecurityUtility;
+
+  constructor() {
+    super();
+    this.securityUtility = new SecurityUtility();
+  }
+
+  private static removeLoadingMessage($container: JQuery): void {
+    $container.find('.alert-loading').remove();
+  }
+
+  private static renderProgressBar(title: string): any {
+    return ProgressBar.render(Severity.loading, title, '');
+  }
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+
+    this.getData().done((): void => {
+      this.doneUpgrades();
+    });
+
+    // Mark a done wizard undone
+    currentModal.on('click', this.selectorWizardsDoneRowMarkUndone, (e: JQueryEventObject): void => {
+      this.markUndone((<HTMLElement>e.target).dataset.identifier);
+    });
+
+    // Execute "fix default mysql connection db charset" blocking wizard
+    currentModal.on('click', this.selectorWizardsBlockingCharsetFix, (e: JQueryEventObject): void => {
+      this.blockingUpgradesDatabaseCharsetFix();
+    });
+
+    // Execute "add required fields + tables" blocking wizard
+    currentModal.on('click', this.selectorWizardsBlockingAddsExecute, (e: JQueryEventObject): void => {
+      this.blockingUpgradesDatabaseAddsExecute();
+    });
+
+    // Get user input of a single upgrade wizard
+    currentModal.on('click', this.selectorWizardsListRowExecute, (e: JQueryEventObject): void => {
+      this.wizardInput((<HTMLElement>e.target).dataset.identifier, (<HTMLElement>e.target).dataset.title);
+    });
+
+    // Execute one upgrade wizard
+    currentModal.on('click', this.selectorWizardsInputPerform, (e: JQueryEventObject): void => {
+      this.wizardExecute((<HTMLElement>e.target).dataset.identifier, (<HTMLElement>e.target).dataset.title);
+    });
+  }
+
+  private getData(): JQueryPromise<any> {
+    const modalContent = this.getModalBody();
+    return $.ajax({
+      url: Router.getUrl('upgradeWizardsGetData'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          this.blockingUpgradesDatabaseCharsetTest();
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr);
+      },
+    });
+  }
+
+  private blockingUpgradesDatabaseCharsetTest(): void {
+    const modalContent = this.getModalBody();
+    const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer);
+    $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Checking database charset...'));
+    $.ajax({
+      url: Router.getUrl('upgradeWizardsBlockingDatabaseCharsetTest'),
+      cache: false,
+      success: (data: any): void => {
+        UpgradeWizards.removeLoadingMessage($outputContainer);
+        if (data.success === true) {
+          if (data.needsUpdate === true) {
+            modalContent.find(this.selectorOutputWizardsContainer)
+              .append(modalContent.find(this.selectorWizardsBlockingCharsetTemplate)).clone();
+          } else {
+            this.blockingUpgradesDatabaseAdds();
+          }
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, $outputContainer);
+      },
+    });
+  }
+
+  private blockingUpgradesDatabaseCharsetFix(): void {
+    const $outputContainer = $(this.selectorOutputWizardsContainer);
+    $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Setting database charset to UTF-8...'));
+    $.ajax({
+      url: Router.getUrl('upgradeWizardsBlockingDatabaseCharsetFix'),
+      cache: false,
+      success: (data: any): void => {
+        UpgradeWizards.removeLoadingMessage($outputContainer);
+        if (data.success === true) {
+          if (Array.isArray(data.status) && data.status.length > 0) {
+            data.status.forEach((element: any): void => {
+              const message: any = InfoBox.render(element.severity, element.title, element.message);
+              $outputContainer.append(message);
+            });
+          }
+        } else {
+          const message = FlashMessage.render(Severity.error, 'Something went wrong', '');
+          UpgradeWizards.removeLoadingMessage($outputContainer);
+          $outputContainer.append(message);
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, $outputContainer);
+      },
+    });
+  }
+
+  private blockingUpgradesDatabaseAdds(): void {
+    const modalContent = this.getModalBody();
+    const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer);
+    $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Check for missing mandatory database tables and fields...'));
+    $.ajax({
+      url: Router.getUrl('upgradeWizardsBlockingDatabaseAdds'),
+      cache: false,
+      success: (data: any): void => {
+        UpgradeWizards.removeLoadingMessage($outputContainer);
+        if (data.success === true) {
+          if (data.needsUpdate === true) {
+            const adds = modalContent.find(this.selectorWizardsBlockingAddsTemplate).clone();
+            if (typeof(data.adds.tables) === 'object') {
+              data.adds.tables.forEach((element: any): void => {
+                const title = 'Table: ' + this.securityUtility.encodeHtml(element.table);
+                adds.find(this.selectorWizardsBlockingAddsRows).append(title, '<br>');
+              });
+            }
+            if (typeof(data.adds.columns) === 'object') {
+              data.adds.columns.forEach((element: any): void => {
+                const title = 'Table: ' + this.securityUtility.encodeHtml(element.table)
+                  + ', Field: ' + this.securityUtility.encodeHtml(element.field);
+                adds.find(this.selectorWizardsBlockingAddsRows).append(title, '<br>');
+              });
+            }
+            if (typeof(data.adds.indexes) === 'object') {
+              data.adds.indexes.forEach((element: any): void => {
+                const title = 'Table: ' + this.securityUtility.encodeHtml(element.table)
+                  + ', Index: ' + this.securityUtility.encodeHtml(element.index);
+                adds.find(this.selectorWizardsBlockingAddsRows).append(title, '<br>');
+              });
+            }
+            modalContent.find(this.selectorOutputWizardsContainer).append(adds);
+          } else {
+            this.wizardsList();
+          }
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, $outputContainer);
+      },
+    });
+  }
+
+  private blockingUpgradesDatabaseAddsExecute(): void {
+    const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer);
+    $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Adding database tables and fields...'));
+    $.ajax({
+      url: Router.getUrl('upgradeWizardsBlockingDatabaseExecute'),
+      cache: false,
+      success: (data: any): void => {
+        UpgradeWizards.removeLoadingMessage($outputContainer);
+        if (data.success === true) {
+          if (Array.isArray(data.status) && data.status.length > 0) {
+            data.status.forEach((element: any): void => {
+              const message = InfoBox.render(element.severity, element.title, element.message);
+              $outputContainer.append(message);
+            });
+            this.wizardsList();
+          }
+        } else {
+          const message = FlashMessage.render(Severity.error, 'Something went wrong', '');
+          UpgradeWizards.removeLoadingMessage($outputContainer);
+          $outputContainer.append(message);
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, $outputContainer);
+      },
+    });
+  }
+
+  private wizardsList(): void {
+    const modalContent = this.getModalBody();
+    const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer);
+    $outputContainer.append(UpgradeWizards.renderProgressBar('Loading upgrade wizards...'));
+
+    $.ajax({
+      url: Router.getUrl('upgradeWizardsList'),
+      cache: false,
+      success: (data: any): void => {
+        UpgradeWizards.removeLoadingMessage($outputContainer);
+        const list = modalContent.find(this.selectorWizardsListTemplate).clone();
+        list.removeClass('t3js-upgradeWizards-list-template');
+        if (data.success === true) {
+          let numberOfWizardsTodo = 0;
+          let numberOfWizards = 0;
+          if (Array.isArray(data.wizards) && data.wizards.length > 0) {
+            numberOfWizards = data.wizards.length;
+            data.wizards.forEach((element: any): void => {
+              if (element.shouldRenderWizard === true) {
+                const aRow = modalContent.find(this.selectorWizardsListRowTemplate).clone();
+                numberOfWizardsTodo = numberOfWizardsTodo + 1;
+                aRow.removeClass('t3js-upgradeWizards-list-row-template');
+                aRow.find(this.selectorWizardsListRowTitle).empty().text(element.title);
+                aRow.find(this.selectorWizardsListRowExplanation).empty().text(element.explanation);
+                aRow.find(this.selectorWizardsListRowExecute).attr('data-identifier', element.identifier).attr('data-title', element.title);
+                list.find(this.selectorWizardsListRows).append(aRow);
+              }
+            });
+            list.find(this.selectorWizardsListRows + ' hr:last').remove();
+          }
+          let percent: number = 100;
+          const $progressBar = list.find('.progress-bar');
+          if (numberOfWizardsTodo > 0) {
+            percent = Math.round((numberOfWizards - numberOfWizardsTodo) / data.wizards.length * 100);
+          } else {
+            $progressBar
+              .removeClass('progress-bar-info')
+              .addClass('progress-bar-success');
+          }
+          $progressBar
+            .removeClass('progress-bar-striped')
+            .css('width', percent + '%')
+            .attr('aria-valuenow', percent)
+            .find('span')
+            .text(percent + '%');
+          modalContent.find(this.selectorOutputWizardsContainer).append(list);
+          this.findInModal(this.selectorWizardsDoneRowMarkUndone).prop('disabled', false);
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, $outputContainer);
+      },
+    });
+  }
+
+  private wizardInput(identifier: string, title: string): void {
+    const executeToken = this.getModuleContent().data('upgrade-wizards-input-token');
+    const modalContent = this.getModalBody();
+    const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer);
+    $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Loading "' + title + '"...'));
+
+    modalContent.animate(
+      {
+        scrollTop: modalContent.scrollTop() - Math.abs(modalContent.find('.t3js-upgrade-status-section').position().top),
+      },
+      250,
+    );
+
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: {
+        'install': {
+          'action': 'upgradeWizardsInput',
+          'token': executeToken,
+          'identifier': identifier,
+        },
+      },
+      cache: false,
+      success: (data: any): void => {
+        $outputContainer.empty();
+        const input = modalContent.find(this.selectorWizardsInputTemplate).clone();
+        input.removeClass('t3js-upgradeWizards-input');
+        if (data.success === true) {
+          if (Array.isArray(data.status)) {
+            data.status.forEach((element: any): void => {
+              const message = FlashMessage.render(element.severity, element.title, element.message);
+              $outputContainer.append(message);
+            });
+          }
+          if (data.userInput.wizardHtml.length > 0) {
+            input.find(this.selectorWizardsInputHtml).html(data.userInput.wizardHtml);
+          }
+          input.find(this.selectorWizardsInputTitle).text(data.userInput.title);
+          input.find(this.selectorWizardsInputPerform)
+            .attr('data-identifier', data.userInput.identifier)
+            .attr('data-title', data.userInput.title);
+        }
+        modalContent.find(this.selectorOutputWizardsContainer).append(input);
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, $outputContainer);
+      },
+    });
+  }
+
+  private wizardExecute(identifier: string, title: string): void {
+    const executeToken = this.getModuleContent().data('upgrade-wizards-execute-token');
+    const modalContent = this.getModalBody();
+    const postData: any = {
+      'install[action]': 'upgradeWizardsExecute',
+      'install[token]': executeToken,
+      'install[identifier]': identifier,
+    };
+    $(this.findInModal(this.selectorOutputWizardsContainer + ' form').serializeArray()).each((index: number, element: any): void => {
+      postData[element.name] = element.value;
+    });
+    const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer);
+    // modalContent.find(this.selectorOutputWizardsContainer).empty();
+    $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Executing "' + title + '"...'));
+    this.findInModal(this.selectorWizardsDoneRowMarkUndone).prop('disabled', true);
+    $.ajax({
+      method: 'POST',
+      data: postData,
+      url: Router.getUrl(),
+      cache: false,
+      success: (data: any): void => {
+        $outputContainer.empty();
+        if (data.success === true) {
+          if (Array.isArray(data.status)) {
+            data.status.forEach((element: any): void => {
+              const message = InfoBox.render(element.severity, element.title, element.message);
+              $outputContainer.append(message);
+            });
+          }
+          this.wizardsList();
+          modalContent.find(this.selectorOutputDoneContainer).empty();
+          this.doneUpgrades();
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, $outputContainer);
+      },
+    });
+  }
+
+  private doneUpgrades(): void {
+    const modalContent = this.getModalBody();
+    const $outputContainer = modalContent.find(this.selectorOutputDoneContainer);
+    $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Loading executed upgrade wizards...'));
+
+    $.ajax({
+      url: Router.getUrl('upgradeWizardsDoneUpgrades'),
+      cache: false,
+      success: (data: any): void => {
+        UpgradeWizards.removeLoadingMessage($outputContainer);
+        if (data.success === true) {
+          if (Array.isArray(data.status) && data.status.length > 0) {
+            data.status.forEach((element: any): void => {
+              const message = InfoBox.render(element.severity, element.title, element.message);
+              $outputContainer.append(message);
+            });
+          }
+          const body = modalContent.find(this.selectorWizardsDoneBodyTemplate).clone();
+          const $wizardsDoneContainer = body.find(this.selectorWizardsDoneRows);
+          let hasBodyContent: boolean = false;
+          if (Array.isArray(data.wizardsDone) && data.wizardsDone.length > 0) {
+            data.wizardsDone.forEach((element: any): void => {
+              hasBodyContent = true;
+              const aRow = modalContent.find(this.selectorWizardsDoneRowTemplate).clone();
+              aRow.find(this.selectorWizardsDoneRowMarkUndone).attr('data-identifier', element.identifier);
+              aRow.find(this.selectorWizardsDoneRowTitle).text(element.title);
+              $wizardsDoneContainer.append(aRow);
+            });
+          }
+          if (Array.isArray(data.rowUpdatersDone) && data.rowUpdatersDone.length > 0) {
+            data.rowUpdatersDone.forEach((element: any): void => {
+              hasBodyContent = true;
+              const aRow = modalContent.find(this.selectorWizardsDoneRowTemplate).clone();
+              aRow.find(this.selectorWizardsDoneRowMarkUndone).attr('data-identifier', element.identifier);
+              aRow.find(this.selectorWizardsDoneRowTitle).text(element.title);
+              $wizardsDoneContainer.append(aRow);
+            });
+          }
+          if (hasBodyContent) {
+            modalContent.find(this.selectorOutputDoneContainer).append(body);
+            this.findInModal(this.selectorWizardsDoneRowMarkUndone).prop('disabled', true);
+          }
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, $outputContainer);
+      },
+    });
+  }
+
+  private markUndone(identifier: string): void {
+    const executeToken = this.getModuleContent().data('upgrade-wizards-mark-undone-token');
+    const modalContent = this.getModalBody();
+    const $outputContainer = this.findInModal(this.selectorOutputDoneContainer);
+    $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Marking upgrade wizard as undone...'));
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: {
+        'install': {
+          'action': 'upgradeWizardsMarkUndone',
+          'token': executeToken,
+          'identifier': identifier,
+        },
+      },
+      cache: false,
+      success: (data: any): void => {
+        $outputContainer.empty();
+        modalContent.find(this.selectorOutputDoneContainer).empty();
+        if (data.success === true && Array.isArray(data.status)) {
+          data.status.forEach((element: any): void => {
+            Notification.success(element.message);
+            this.doneUpgrades();
+            this.blockingUpgradesDatabaseCharsetTest();
+          });
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, $outputContainer);
+      },
+    });
+  }
+}
+
+export = new UpgradeWizards();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/UpgradeDocs.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/UpgradeDocs.ts
deleted file mode 100644 (file)
index f52c32a..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import 'bootstrap';
-import Router = require('../Router');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-
-/**
- * Module: TYPO3/CMS/Install/Module/UpgradeDocs
- */
-class UpgradeDocs extends AbstractInteractableModule {
-  private selectorRestFileItem: string = '.upgrade_analysis_item_to_filter';
-  private selectorFulltextSearch: string = '.t3js-upgradeDocs-fulltext-search';
-  private selectorChosenField: string = '.t3js-upgradeDocs-chosen-select';
-  private selectorChangeLogsForVersionContainer: string = '.t3js-version-changes';
-  private selectorChangeLogsForVersion: string = '.t3js-changelog-list';
-
-  private chosenField: JQuery;
-  private fulltextSearchField: JQuery;
-
-  private static trimExplodeAndUnique(delimiter: string, string: string): Array<string> {
-    const result: Array<string> = [];
-    const items = string.split(delimiter);
-    for (let i = 0; i < items.length; i++) {
-      const item = items[i].trim();
-      if (item.length > 0) {
-        if ($.inArray(item, result) === -1) {
-          result.push(item);
-        }
-      }
-    }
-    return result;
-  }
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-    const isInIframe = (window.location !== window.parent.location);
-    if (isInIframe) {
-      top.require(['TYPO3/CMS/Install/chosen.jquery.min'], (): void => {
-        this.getContent();
-      });
-    } else {
-      require(['TYPO3/CMS/Install/chosen.jquery.min'], (): void => {
-        this.getContent();
-      });
-    }
-
-    // Mark a file as read
-    currentModal.on('click',  '.t3js-upgradeDocs-markRead', (e: JQueryEventObject): void => {
-      this.markRead(e.target);
-    });
-    currentModal.on('click',  '.t3js-upgradeDocs-unmarkRead', (e: JQueryEventObject): void => {
-      this.unmarkRead(e.target);
-    });
-
-    // Make jquerys "contains" work case-insensitive
-    jQuery.expr[':'].contains = jQuery.expr.createPseudo((arg: any): Function => {
-      return (elem: any): boolean => {
-        return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
-      };
-    });
-
-    require(['jquery.clearable'], (): void => {
-      currentModal.find(this.selectorFulltextSearch).clearable().focus();
-    });
-  }
-
-  private getContent(): void {
-    const modalContent = this.getModalBody();
-    $.ajax({
-      url: Router.getUrl('upgradeDocsGetContent'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
-          modalContent.empty().append(data.html);
-
-          this.initializeFullTextSearch();
-          this.initializeChosenSelector();
-          this.loadChangelogs();
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private loadChangelogs(): void {
-    const promises: Array<any> = [];
-    const modalContent = this.getModalBody();
-    this.findInModal(this.selectorChangeLogsForVersionContainer).each((index: number, el: any): void => {
-      const $request = $.ajax({
-        url: Router.getUrl('upgradeDocsGetChangelogForVersion'),
-        cache: false,
-        data: {
-          install: {
-            version: el.dataset.version,
-          },
-        },
-        success: (data: any): void => {
-          if (data.success === true) {
-            const $panelGroup = $(el);
-            const $container = $panelGroup.find(this.selectorChangeLogsForVersion);
-            $container.html(data.html);
-            this.renderTags($container);
-            this.moveNotRelevantDocuments($container);
-
-            // Remove loading spinner form panel
-            $panelGroup.find('.t3js-panel-loading').remove();
-          } else {
-            Notification.error('Something went wrong');
-          }
-        },
-        error: (xhr: XMLHttpRequest): void => {
-          Router.handleAjaxError(xhr, modalContent);
-        },
-      });
-
-      promises.push($request);
-    });
-
-    $.when.apply($, promises).done((): void => {
-      this.fulltextSearchField.prop('disabled', false);
-      this.appendItemsToChosenSelector();
-    });
-  }
-
-  private initializeFullTextSearch(): void {
-    this.fulltextSearchField = this.findInModal(this.selectorFulltextSearch);
-    this.fulltextSearchField.clearable().focus();
-    this.initializeChosenSelector();
-    this.fulltextSearchField.on('keyup', (): void => {
-      this.combinedFilterSearch();
-    });
-  }
-
-  private initializeChosenSelector(): void {
-    this.chosenField = this.getModalBody().find(this.selectorChosenField);
-
-    const config: any = {
-      '.chosen-select': {width: '100%', placeholder_text_multiple: 'tags'},
-      '.chosen-select-deselect': {allow_single_deselect: true},
-      '.chosen-select-no-single': {disable_search_threshold: 10},
-      '.chosen-select-no-results': {no_results_text: 'Oops, nothing found!'},
-      '.chosen-select-width': {width: '100%'},
-    };
-    for (const selector in config) {
-      if (config.hasOwnProperty(selector)) {
-        this.findInModal(selector).chosen(config[selector]);
-      }
-    }
-    this.chosenField.on('change', (): void => {
-      this.combinedFilterSearch();
-    });
-  }
-
-  /**
-   * Appends tags to the chosen selector
-   */
-  private appendItemsToChosenSelector(): void {
-    let tagString = '';
-    $(this.findInModal(this.selectorRestFileItem)).each((index: number, element: any): void => {
-      tagString += $(element).data('item-tags') + ',';
-    });
-    const tagArray = UpgradeDocs.trimExplodeAndUnique(',', tagString).sort((a: string, b: string): number => {
-      // Sort case-insensitive by name
-      return a.toLowerCase().localeCompare(b.toLowerCase());
-    });
-    this.chosenField.prop('disabled', false);
-    $.each(tagArray, (i: number, tag: any): void => {
-      this.chosenField.append($('<option>').text(tag));
-    });
-    this.chosenField.trigger('chosen:updated');
-  }
-
-  private combinedFilterSearch(): boolean {
-    const modalContent = this.getModalBody();
-    const $items = modalContent.find('div.item');
-    if (this.chosenField.val().length < 1 && this.fulltextSearchField.val().length < 1) {
-      $('.panel-version:not(:first) > .panel-collapse').collapse('hide');
-      $items.removeClass('hidden searchhit filterhit');
-      return false;
-    }
-    $items.addClass('hidden').removeClass('searchhit filterhit');
-
-    // apply tags
-    if (this.chosenField.val().length > 0) {
-      $items
-        .addClass('hidden')
-        .removeClass('filterhit');
-      const orTags: Array<string> = [];
-      const andTags: Array<string> = [];
-      $.each(this.chosenField.val(), (index: number, item: any): void => {
-        const tagFilter = '[data-item-tags*="' + item + '"]';
-        if (item.indexOf(':') > 0) {
-          orTags.push(tagFilter);
-        } else {
-          andTags.push(tagFilter);
-        }
-      });
-      const andString = andTags.join('');
-      const tags = [];
-      if (orTags.length) {
-        for (let i = 0; i < orTags.length; i++) {
-          tags.push(andString + orTags[i]);
-        }
-      } else {
-        tags.push(andString);
-      }
-      const tagSelection = tags.join(',');
-      modalContent.find(tagSelection)
-        .removeClass('hidden')
-        .addClass('searchhit filterhit');
-    } else {
-      $items
-        .addClass('filterhit')
-        .removeClass('hidden');
-    }
-    // apply fulltext search
-    const typedQuery = this.fulltextSearchField.val();
-    modalContent.find('div.item.filterhit').each((index: number, element: any): void => {
-      const $item = $(element);
-      if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) {
-        $item.removeClass('hidden').addClass('searchhit');
-      } else {
-        $item.removeClass('searchhit').addClass('hidden');
-      }
-    });
-
-    modalContent.find('.searchhit').closest('.panel-collapse').collapse('show');
-
-    // Check for empty panels
-    modalContent.find('.panel-version').each((index: number, element: any): void => {
-      const $element: any = $(element);
-      if ($element.find('.searchhit', '.filterhit').length < 1) {
-        $element.find(' > .panel-collapse').collapse('hide');
-      }
-    });
-
-    return true;
-  }
-
-  private renderTags($container: any): void {
-    $.each($container.find(this.selectorRestFileItem), (index: number, element: any): void => {
-      const $me = $(element);
-      const tags = $me.data('item-tags').split(',');
-      const $tagContainer = $me.find('.t3js-tags');
-      tags.forEach((value: string): void => {
-        $tagContainer.append($('<span />', {'class': 'label'}).text(value));
-      });
-    });
-  }
-
-  /**
-   * Moves all documents that are either read or not affected
-   */
-  private moveNotRelevantDocuments($container: JQuery): void {
-    $container.find('[data-item-state="read"]').appendTo(this.findInModal('.panel-body-read'));
-    $container.find('[data-item-state="notAffected"]').appendTo(this.findInModal('.panel-body-not-affected'));
-  }
-
-  private markRead(element: any): void {
-    const modalContent = this.getModalBody();
-    const executeToken = this.getModuleContent().data('upgrade-docs-mark-read-token');
-    const $button = $(element).closest('a');
-    $button.toggleClass('t3js-upgradeDocs-unmarkRead t3js-upgradeDocs-markRead');
-    $button.find('i').toggleClass('fa-check fa-ban');
-    $button.closest('.panel').appendTo(this.findInModal('.panel-body-read'));
-    $.ajax({
-      method: 'POST',
-      url: Router.getUrl(),
-      data: {
-        'install': {
-          'ignoreFile': $button.data('filepath'),
-          'token': executeToken,
-          'action': 'upgradeDocsMarkRead',
-        },
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-
-  private unmarkRead(element: any): void {
-    const modalContent = this.getModalBody();
-    const executeToken = this.getModuleContent().data('upgrade-docs-unmark-read-token');
-    const $button = $(element).closest('a');
-    const version = $button.closest('.panel').data('item-version');
-    $button.toggleClass('t3js-upgradeDocs-markRead t3js-upgradeDocs-unmarkRead');
-    $button.find('i').toggleClass('fa-check fa-ban');
-    $button.closest('.panel').appendTo(this.findInModal('*[data-group-version="' + version + '"] .panel-body'));
-    $.ajax({
-      method: 'POST',
-      url: Router.getUrl(),
-      data: {
-        'install': {
-          'ignoreFile': $button.data('filepath'),
-          'token': executeToken,
-          action: 'upgradeDocsUnmarkRead',
-        },
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, modalContent);
-      },
-    });
-  }
-}
-
-export = new UpgradeDocs();
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/UpgradeWizards.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/UpgradeWizards.ts
deleted file mode 100644 (file)
index 0ba7bad..0000000
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-import {AbstractInteractableModule} from './AbstractInteractableModule';
-import * as $ from 'jquery';
-import 'bootstrap';
-import Router = require('../Router');
-import Severity = require('../Renderable/Severity');
-import ProgressBar = require('../Renderable/ProgressBar');
-import InfoBox = require('../Renderable/InfoBox');
-import FlashMessage = require('../Renderable/FlashMessage');
-import Notification = require('TYPO3/CMS/Backend/Notification');
-import SecurityUtility = require('TYPO3/CMS/Core/SecurityUtility');
-
-/**
- * Module: TYPO3/CMS/Install/Module/UpgradeWizards
- */
-class UpgradeWizards extends AbstractInteractableModule {
-  private selectorOutputWizardsContainer: string = '.t3js-upgradeWizards-wizards-output';
-  private selectorOutputDoneContainer: string = '.t3js-upgradeWizards-done-output';
-  private selectorWizardsBlockingAddsTemplate: string = '.t3js-upgradeWizards-blocking-adds-template';
-  private selectorWizardsBlockingAddsRows: string = '.t3js-upgradeWizards-blocking-adds-rows';
-  private selectorWizardsBlockingAddsExecute: string = '.t3js-upgradeWizards-blocking-adds-execute';
-  private selectorWizardsBlockingCharsetTemplate: string = '.t3js-upgradeWizards-blocking-charset-template';
-  private selectorWizardsBlockingCharsetFix: string = '.t3js-upgradeWizards-blocking-charset-fix';
-  private selectorWizardsDoneBodyTemplate: string = '.t3js-upgradeWizards-done-body-template';
-  private selectorWizardsDoneRows: string = '.t3js-upgradeWizards-done-rows';
-  private selectorWizardsDoneRowTemplate: string = '.t3js-upgradeWizards-done-row-template table tr';
-  private selectorWizardsDoneRowMarkUndone: string = '.t3js-upgradeWizards-done-markUndone';
-  private selectorWizardsDoneRowTitle: string = '.t3js-upgradeWizards-done-title';
-  private selectorWizardsListTemplate: string = '.t3js-upgradeWizards-list-template';
-  private selectorWizardsListRows: string = '.t3js-upgradeWizards-list-rows';
-  private selectorWizardsListRowTemplate: string = '.t3js-upgradeWizards-list-row-template';
-  private selectorWizardsListRowTitle: string = '.t3js-upgradeWizards-list-row-title';
-  private selectorWizardsListRowExplanation: string = '.t3js-upgradeWizards-list-row-explanation';
-  private selectorWizardsListRowExecute: string = '.t3js-upgradeWizards-list-row-execute';
-  private selectorWizardsInputTemplate: string = '.t3js-upgradeWizards-input';
-  private selectorWizardsInputTitle: string = '.t3js-upgradeWizards-input-title';
-  private selectorWizardsInputHtml: string = '.t3js-upgradeWizards-input-html';
-  private selectorWizardsInputPerform: string = '.t3js-upgradeWizards-input-perform';
-  private securityUtility: SecurityUtility;
-
-  constructor() {
-    super();
-    this.securityUtility = new SecurityUtility();
-  }
-
-  private static removeLoadingMessage($container: JQuery): void {
-    $container.find('.alert-loading').remove();
-  }
-
-  private static renderProgressBar(title: string): any {
-    return ProgressBar.render(Severity.loading, title, '');
-  }
-
-  public initialize(currentModal: JQuery): void {
-    this.currentModal = currentModal;
-
-    this.getData().done((): void => {
-      this.doneUpgrades();
-    });
-
-    // Mark a done wizard undone
-    currentModal.on('click', this.selectorWizardsDoneRowMarkUndone, (e: JQueryEventObject): void => {
-      this.markUndone((<HTMLElement>e.target).dataset.identifier);
-    });
-
-    // Execute "fix default mysql connection db charset" blocking wizard
-    currentModal.on('click', this.selectorWizardsBlockingCharsetFix, (e: JQueryEventObject): void => {
-      this.blockingUpgradesDatabaseCharsetFix();
-    });
-
-    // Execute "add required fields + tables" blocking wizard
-    currentModal.on('click', this.selectorWizardsBlockingAddsExecute, (e: JQueryEventObject): void => {
-      this.blockingUpgradesDatabaseAddsExecute();
-    });
-
-    // Get user input of a single upgrade wizard
-    currentModal.on('click', this.selectorWizardsListRowExecute, (e: JQueryEventObject): void => {
-      this.wizardInput((<HTMLElement>e.target).dataset.identifier, (<HTMLElement>e.target).dataset.title);
-    });
-
-    // Execute one upgrade wizard
-    currentModal.on('click', this.selectorWizardsInputPerform, (e: JQueryEventObject): void => {
-      this.wizardExecute((<HTMLElement>e.target).dataset.identifier, (<HTMLElement>e.target).dataset.title);
-    });
-  }
-
-  private getData(): JQueryPromise<any> {
-    const modalContent = this.getModalBody();
-    return $.ajax({
-      url: Router.getUrl('upgradeWizardsGetData'),
-      cache: false,
-      success: (data: any): void => {
-        if (data.success === true) {
-          modalContent.empty().append(data.html);
-          this.blockingUpgradesDatabaseCharsetTest();
-        } else {
-          Notification.error('Something went wrong');
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr);
-      },
-    });
-  }
-
-  private blockingUpgradesDatabaseCharsetTest(): void {
-    const modalContent = this.getModalBody();
-    const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer);
-    $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Checking database charset...'));
-    $.ajax({
-      url: Router.getUrl('upgradeWizardsBlockingDatabaseCharsetTest'),
-      cache: false,
-      success: (data: any): void => {
-        UpgradeWizards.removeLoadingMessage($outputContainer);
-        if (data.success === true) {
-          if (data.needsUpdate === true) {
-            modalContent.find(this.selectorOutputWizardsContainer)
-              .append(modalContent.find(this.selectorWizardsBlockingCharsetTemplate)).clone();
-          } else {
-            this.blockingUpgradesDatabaseAdds();
-          }
-        }
-      },
-      error: (xhr: XMLHttpRequest): void => {
-        Router.handleAjaxError(xhr, $outputContainer);
-      },
-    });
-  }
-
-  private blockingUpgradesDat