[TASK] Migrate TYPO3/CMS/Install/* to TypeScript 09/59709/13
authorFrank Naegler <frank.naegler@typo3.org>
Fri, 15 Feb 2019 17:09:36 +0000 (18:09 +0100)
committerAndreas Fernandez <a.fernandez@scripting-base.de>
Thu, 14 Mar 2019 13:56:58 +0000 (14:56 +0100)
This patch migrates all install tool JS files to TypeScript.
It also improves the performance by a better caching and
with less request for modules.

Resolves: #87724
Releases: master
Change-Id: I555924008bf19eb4394268acc231eca91d8b16c6
Reviewed-on: https://review.typo3.org/c/59709
Tested-by: Markus Klein <markus.klein@typo3.org>
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
121 files changed:
Build/types/TYPO3/index.d.ts
typo3/sysext/install/Classes/Controller/InstallerController.php
typo3/sysext/install/Classes/Controller/LayoutController.php
typo3/sysext/install/Resources/Private/Templates/Environment/Cards.html
typo3/sysext/install/Resources/Private/Templates/Installer/Init.html
typo3/sysext/install/Resources/Private/Templates/Layout/Init.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/Private/TypeScript/Ajax/AjaxQueue.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Install.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Installer.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/Cache.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/ChangeInstallToolPassword.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/ClearTables.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/ClearTypo3tempFiles.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/CoreUpdate.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/CreateAdmin.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/DatabaseAnalyzer.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/DumpAutoload.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/EnvironmentCheck.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/ExtensionCompatTester.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/ExtensionConfiguration.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/ExtensionScanner.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/Features.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/FolderStructure.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/ImageProcessing.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/InlineModuleInterface.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/InteractableModuleInterface.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/LanguagePacks.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/LocalConfiguration.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/MailTest.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/PasswordStrength.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/PhpInfo.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/Presets.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/ResetBackendUserUc.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/SystemInformation.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/SystemMaintainer.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/TcaExtTablesCheck.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/TcaMigrationsCheck.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/UpgradeDocs.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Module/UpgradeWizards.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Renderable/FlashMessage.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Renderable/InfoBox.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Renderable/ProgressBar.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Renderable/Severity.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Private/TypeScript/Router.ts [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Ajax/AjaxQueue.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Install.js
typo3/sysext/install/Resources/Public/JavaScript/Installer.js
typo3/sysext/install/Resources/Public/JavaScript/Module/Cache.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/ChangeInstallToolPassword.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/ClearTables.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/ClearTypo3tempFiles.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/CoreUpdate.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/CreateAdmin.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/DatabaseAnalyzer.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/DumpAutoload.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/EnvironmentCheck.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/ExtensionCompatTester.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/ExtensionConfiguration.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/ExtensionScanner.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Features.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/FolderStructure.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/ImageProcessing.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/InlineModuleInterface.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/InteractableModuleInterface.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/LanguagePacks.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/LocalConfiguration.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/MailTest.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/PasswordStrength.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/PhpInfo.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/Presets.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/ResetBackendUserUc.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/SystemInformation.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/SystemMaintainer.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/TcaExtTablesCheck.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/TcaMigrationsCheck.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/UpgradeDocs.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Module/UpgradeWizards.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Modules/AjaxQueue.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/Cache.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/ChangeInstallToolPassword.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/ClearTables.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/ClearTypo3tempFiles.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/CoreUpdate.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/CreateAdmin.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/DatabaseAnalyzer.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/DumpAutoload.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/EnvironmentCheck.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/ExtensionCompatTester.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/ExtensionConfiguration.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/ExtensionScanner.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/Features.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/FlashMessage.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/FolderStructure.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/ImageProcessing.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/InfoBox.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/LanguagePacks.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/LocalConfiguration.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/MailTest.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/PasswordStrength.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/PhpInfo.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/Presets.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/ProgressBar.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/ResetBackendUserUc.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/Router.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/Severity.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/SystemInformation.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/SystemMaintainer.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/TcaExtTablesCheck.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/TcaMigrationsCheck.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/UpgradeDocs.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Modules/UpgradeWizards.js [deleted file]
typo3/sysext/install/Resources/Public/JavaScript/Renderable/FlashMessage.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Renderable/InfoBox.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Renderable/ProgressBar.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/Renderable/Severity.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/RequireJSConfig.js
typo3/sysext/install/Resources/Public/JavaScript/Router.js [new file with mode: 0644]
typo3/sysext/install/Resources/Public/JavaScript/jquery.clearable.js [new file with mode: 0644]

index 278b244..a5a8f66 100644 (file)
@@ -22,8 +22,8 @@ declare namespace TYPO3 {
   export let Tooltip: any;
   export let Utility: any;
   export let Wizard: any;
+  export let settings: any;
   export const lang: { [key: string]: string };
-  export const settings: any;
   export const configuration: any;
   export namespace CMS {
     export namespace Backend {
@@ -92,6 +92,7 @@ interface Window {
     delayedImportElement: (objectId: number, table: string, uid: number, type: string) => void,
   };
   rawurlencode: Function;
+  require: Function;
   list_frame: Window;
   jump: Function;
   currentSubScript: string;
@@ -130,6 +131,8 @@ interface JQueryTypedEvent<T extends Event> extends JQueryEventObject {
 interface JQuery {
   clearable(options?: any): JQuery;
 
+  chosen(options?: any): JQuery;
+
   datetimepicker(options?: any): JQuery;
 
   dragUploader(options?: any): JQuery;
index 1d8c1f2..bdbf7cb 100644 (file)
@@ -68,7 +68,12 @@ class InstallerController
      */
     public function initAction(): ResponseInterface
     {
+        $bust = $GLOBALS['EXEC_TIME'];
+        if (!GeneralUtility::getApplicationContext()->isDevelopment()) {
+            $bust = GeneralUtility::hmac(TYPO3_version . Environment::getProjectPath());
+        }
         $view = $this->initializeStandaloneView('Installer/Init.html');
+        $view->assign('bust', $bust);
         return new HtmlResponse(
             $view->render(),
             200,
index dcab92a..21161e6 100644 (file)
@@ -18,8 +18,10 @@ namespace TYPO3\CMS\Install\Controller;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
+use TYPO3\CMS\Core\Core\Environment;
 use TYPO3\CMS\Core\Http\HtmlResponse;
 use TYPO3\CMS\Core\Http\JsonResponse;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Install\Service\Exception\ConfigurationChangedException;
 use TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService;
 
@@ -41,10 +43,14 @@ class LayoutController extends AbstractController
      */
     public function initAction(ServerRequestInterface $request): ResponseInterface
     {
+        $bust = $GLOBALS['EXEC_TIME'];
+        if (!GeneralUtility::getApplicationContext()->isDevelopment()) {
+            $bust = GeneralUtility::hmac(TYPO3_version . Environment::getProjectPath());
+        }
         $view = $this->initializeStandaloneView($request, 'Layout/Init.html');
         $view->assignMultiple([
             // time is used as cache bust for js and css resources
-            'time' => time(),
+            'bust' => $bust,
             'siteName' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'],
         ]);
         return new HtmlResponse(
index 8cabab0..f9e9097 100644 (file)
@@ -9,7 +9,7 @@
                        <p class="card-text">Gives an overview of your host environment including its web server, PHP version and selected database.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-modal-size="small" data-require="TYPO3/CMS/Install/SystemInformation">Show System Information</a>
+                       <a href="#" class="btn btn-default" data-modal-size="small" data-require="TYPO3/CMS/Install/Module/SystemInformation">Show System Information</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -18,7 +18,7 @@
                        <p class="card-text">Analyzes your host environment, identifying any issues that may prevent TYPO3 from running correctly.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/EnvironmentCheck">Check Environment</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/EnvironmentCheck">Check Environment</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -27,7 +27,7 @@
                        <p class="card-text">Analyzes your folder structure, checking files and directories for correct permissions and identifying any files or directories that may be missing from your installation.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/FolderStructure">Check Directory Status</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/FolderStructure">Check Directory Status</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -36,7 +36,7 @@
                        <p class="card-text">Outputs detailed information about your installation of PHP. Including version details and enabled PHP extensions.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/PhpInfo">View PHP Info</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/PhpInfo">View PHP Info</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -45,7 +45,7 @@
                        <p class="card-text">Test your mail configuration by sending out a dummy email via TYPO3.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/MailTest">Test Mail Setup</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/MailTest">Test Mail Setup</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -54,7 +54,7 @@
                        <p class="card-text">Creates test images and compares them against a set of reference images to help ensure that image processing is working correctly within your environment.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/ImageProcessing">Test Images</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/ImageProcessing">Test Images</a>
                </div>
        </div>
 </div>
index 39b5135..07a4997 100644 (file)
@@ -4,11 +4,16 @@
        <title>Installing TYPO3 CMS</title>
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-       <link rel="stylesheet" type="text/css" href="{f:uri.resource(path: 'Css/backend.css', extensionName: 'backend')}?{time}" />
-       <link rel="icon" type="image/vnd.microsoft.icon" href="{f:uri.resource(path: 'Icons/favicon.ico')}?{time}" />
-       <script type="text/javascript" src="{f:uri.resource(path: 'JavaScript/RequireJSConfig.js')}?{time}"></script>
-       <script type="text/javascript" src="{f:uri.resource(path: 'JavaScript/Contrib/require.js', extensionName: 'Core')}?{time}"></script>
-       <script type="text/javascript" src="{f:uri.resource(path: 'JavaScript/Installer.js')}?{time}"></script>
+       <link rel="stylesheet" type="text/css" href="{f:uri.resource(path: 'Css/backend.css', extensionName: 'backend')}?{bust}" />
+       <link rel="icon" type="image/vnd.microsoft.icon" href="{f:uri.resource(path: 'Icons/favicon.ico')}?{bust}" />
+       <script>
+               var __bust = '{bust}';
+       </script>
+       <script type="text/javascript" src="{f:uri.resource(path: 'JavaScript/RequireJSConfig.js')}?{bust}"></script>
+       <script type="text/javascript" src="{f:uri.resource(path: 'JavaScript/Contrib/require.js', extensionName: 'Core')}?{bust}"></script>
+       <script type="text/javascript">
+               require(['TYPO3/CMS/Install/Installer']);
+       </script>
 </head>
 <body class="backend t3js-body install-tool-installer typo3-install"></body>
 </html>
index c329d9f..7bc55fc 100644 (file)
@@ -4,14 +4,17 @@
        <title>Install tool on site {siteName}</title>
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-       <link rel="stylesheet" type="text/css" href="{f:uri.resource(path: 'Css/backend.css', extensionName: 'backend')}?{time}" />
-       <link rel="icon" type="image/vnd.microsoft.icon" href="{f:uri.resource(path: 'Icons/favicon.ico')}?{time}" />
+       <link rel="stylesheet" type="text/css" href="{f:uri.resource(path: 'Css/backend.css', extensionName: 'backend')}?{bust}" />
+       <link rel="icon" type="image/vnd.microsoft.icon" href="{f:uri.resource(path: 'Icons/favicon.ico')}?{bust}" />
        <script>
                var TYPO3 = TYPO3 || {};
+               var __bust = '{bust}';
+       </script>
+       <script type="text/javascript" src="{f:uri.resource(path: 'JavaScript/RequireJSConfig.js')}?{bust}"></script>
+       <script type="text/javascript" src="{f:uri.resource(path: 'JavaScript/Contrib/require.js', extensionName: 'Core')}?{bust}"></script>
+       <script type="text/javascript">
+               require(['TYPO3/CMS/Install/Install']);
        </script>
-       <script type="text/javascript" src="{f:uri.resource(path: 'JavaScript/RequireJSConfig.js')}?{time}"></script>
-       <script type="text/javascript" src="{f:uri.resource(path: 'JavaScript/Contrib/require.js', extensionName: 'Core')}?{time}"></script>
-       <script type="text/javascript" src="{f:uri.resource(path: 'JavaScript/Install.js')}?{time}"></script>
 </head>
 <body class="backend install-tool-init t3js-body" data-controller="{controller}" data-context="{context}">
 <div>
index c72f4c9..0caed74 100644 (file)
@@ -12,7 +12,7 @@
                        </p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-primary " data-inline="1"  data-require="TYPO3/CMS/Install/Cache">Flush cache</a>
+                       <a href="#" class="btn btn-primary " data-inline="1"  data-require="TYPO3/CMS/Install/Module/Cache">Flush cache</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -21,7 +21,7 @@
                        <p class="card-text">Compare and update the database table and field definitions of your installation against the specification defined for every activated extension.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/DatabaseAnalyzer">Analyze database</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/DatabaseAnalyzer">Analyze database</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -30,7 +30,7 @@
                        <p class="card-text">Clears temporary files including concatenated JS/CSS files and processed images.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/ClearTypo3tempFiles">Scan temporary files</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/ClearTypo3tempFiles">Scan temporary files</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -44,7 +44,7 @@
                        </f:then>
                        <f:else>
                                <div class="card-footer">
-                                       <a href="#" class="btn btn-primary " data-inline="1"  data-require="TYPO3/CMS/Install/DumpAutoload">Dump autoload</a>
+                                       <a href="#" class="btn btn-primary " data-inline="1"  data-require="TYPO3/CMS/Install/Module/DumpAutoload">Dump autoload</a>
                                </div>
                        </f:else>
                </f:if>
@@ -55,7 +55,7 @@
                        <p class="card-text">Truncates persistent database tables, which are not related to caching.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/ClearTables">Scan tables</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/ClearTables">Scan tables</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -64,7 +64,7 @@
                        <p class="card-text">Create new administrative users and grant them system maintainer privileges (optional).</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-modal-size="small" data-require="TYPO3/CMS/Install/CreateAdmin">Create Administrator</a>
+                       <a href="#" class="btn btn-default" data-modal-size="small" data-require="TYPO3/CMS/Install/Module/CreateAdmin">Create Administrator</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -73,7 +73,7 @@
                        <p class="card-text">Clears preferences and settings for all backend users. The <code>uc</code> field is then set to an empty string.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-primary " data-inline="1"  data-require="TYPO3/CMS/Install/ResetBackendUserUc">Reset backend user preferences</a>
+                       <a href="#" class="btn btn-primary " data-inline="1"  data-require="TYPO3/CMS/Install/Module/ResetBackendUserUc">Reset backend user preferences</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -82,7 +82,7 @@
                        <p class="card-text">Download and update language packs for installed extensions.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/LanguagePacks">Manage languages</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/LanguagePacks">Manage languages</a>
                </div>
        </div>
 </div>
index b9f2c67..5510eda 100644 (file)
@@ -9,7 +9,7 @@
                        <p class="card-text">Configure settings for all enabled extensions.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/ExtensionConfiguration">Configure extensions</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/ExtensionConfiguration">Configure extensions</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -18,7 +18,7 @@
                        <p class="card-text">Set a new password for the Install Tool when accessed in Standalone mode.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-modal-size="small" data-require="TYPO3/CMS/Install/ChangeInstallToolPassword">Change Install Tool Password</a>
+                       <a href="#" class="btn btn-default" data-modal-size="small" data-require="TYPO3/CMS/Install/Module/ChangeInstallToolPassword">Change Install Tool Password</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -27,7 +27,7 @@
                        <p class="card-text">Specify which backend administrators have access to the Admin Tools module and Install Tool when accessed in Standalone Mode.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-modal-size="medium" data-require="TYPO3/CMS/Install/SystemMaintainer">Manage System Maintainers</a>
+                       <a href="#" class="btn btn-default" data-modal-size="medium" data-require="TYPO3/CMS/Install/Module/SystemMaintainer">Manage System Maintainers</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -36,7 +36,7 @@
                        <p class="card-text">Configure image processing, debug/live mode and mail settings.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Presets">Choose Preset</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/Presets">Choose Preset</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -45,7 +45,7 @@
                        <p class="card-text">Enable and disable core features.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-modal-size="medium" data-require="TYPO3/CMS/Install/Features">Configure Features</a>
+                       <a href="#" class="btn btn-default" data-modal-size="medium" data-require="TYPO3/CMS/Install/Module/Features">Configure Features</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -54,7 +54,7 @@
                        <p class="card-text">Modify settings written to LocalConfiguration.php.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/LocalConfiguration">Configure options</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/LocalConfiguration">Configure options</a>
                </div>
        </div>
 </div>
index fd3e42f..6d5369a 100644 (file)
@@ -9,7 +9,7 @@
                        <p class="card-text">Update your TYPO3 installation (support for symbolic links required).</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/CoreUpdate">Update Core</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/CoreUpdate">Update Core</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -18,7 +18,7 @@
                        <p class="card-text">Finalises the upgrade process when moving to a major release.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/UpgradeWizards">Run Upgrade Wizard</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/UpgradeWizards">Run Upgrade Wizard</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -27,7 +27,7 @@
                        <p class="card-text">View and search for important changes that have been made for every major and minor release of TYPO3.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/UpgradeDocs">View Upgrade Documentation</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/UpgradeDocs">View Upgrade Documentation</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -37,7 +37,7 @@
                        </p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/TcaExtTablesCheck">Check TCA</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/TcaExtTablesCheck">Check TCA</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -46,7 +46,7 @@
                        <p class="card-text">Checks the compatibility of all active extensions against the current (installed) version of TYPO3.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-modal-size="small" data-require="TYPO3/CMS/Install/ExtensionCompatTester">Check Extension Compatibility</a>
+                       <a href="#" class="btn btn-default" data-modal-size="small" data-require="TYPO3/CMS/Install/Module/ExtensionCompatTester">Check Extension Compatibility</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -55,7 +55,7 @@
                        <p class="card-text">Identifies any third-party extensions that contain an outdated TCA configuration which should be adapted for the current (installed) version of TYPO3.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/TcaMigrationsCheck">Check TCA Migrations</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/TcaMigrationsCheck">Check TCA Migrations</a>
                </div>
        </div>
        <div class="card card-size-fixed-small">
@@ -64,7 +64,7 @@
                        <p class="card-text">Scan extensions for usage of deprecated or outdated TYPO3 API calls.</p>
                </div>
                <div class="card-footer">
-                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/ExtensionScanner">Scan Extension Files</a>
+                       <a href="#" class="btn btn-default" data-require="TYPO3/CMS/Install/Module/ExtensionScanner">Scan Extension Files</a>
                </div>
        </div>
 </div>
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Ajax/AjaxQueue.ts b/typo3/sysext/install/Resources/Private/TypeScript/Ajax/AjaxQueue.ts
new file mode 100644 (file)
index 0000000..2639917
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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 * as $ from 'jquery';
+
+/**
+ * Module: TYPO3/CMS/Install/Module/AjaxQueue
+ */
+class AjaxQueue {
+  private requestCount: number = 0;
+  private threshold: number = 10;
+  private queue: Array<any> = [];
+
+  public add(payload: JQueryAjaxSettings): void {
+    const oldComplete = payload.complete;
+    payload.complete = (jqXHR: JQueryXHR, textStatus: string): void => {
+      if (this.queue.length > 0 && this.requestCount <= this.threshold) {
+        $.ajax(this.queue.shift()).always((): void => {
+          this.decrementRequestCount();
+        });
+      } else {
+        this.decrementRequestCount();
+      }
+
+      if (oldComplete) {
+        oldComplete(jqXHR, textStatus);
+      }
+    };
+
+    if (this.requestCount >= this.threshold) {
+      this.queue.push(payload);
+    } else {
+      this.incrementRequestCount();
+      $.ajax(payload);
+    }
+  }
+
+  private incrementRequestCount(): void {
+    this.requestCount++;
+  }
+
+  private decrementRequestCount(): void {
+    if (this.requestCount > 0) {
+      this.requestCount--;
+    }
+  }
+}
+
+export = new AjaxQueue();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Install.ts b/typo3/sysext/install/Resources/Private/TypeScript/Install.ts
new file mode 100644 (file)
index 0000000..d5c8de8
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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 * as $ from 'jquery';
+import Router = require('./Router');
+
+/**
+ * Walk through the installation process of TYPO3
+ */
+class Install {
+  constructor() {
+    $((): void => {
+      Router.initialize();
+    });
+  }
+}
+
+export = new Install();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Installer.ts b/typo3/sysext/install/Resources/Private/TypeScript/Installer.ts
new file mode 100644 (file)
index 0000000..a970b99
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * 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 * as $ from 'jquery';
+import InfoBox = require('./Renderable/InfoBox');
+import Severity = require('./Renderable/Severity');
+import ProgressBar = require('./Renderable/ProgressBar');
+import PasswordStrength = require('./Module/PasswordStrength');
+
+/**
+ * Walk through the installation process of TYPO3
+ */
+class Installer {
+  private selectorBody: string = '.t3js-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  private selectorMainContent: string = '.t3js-installer-content';
+  private selectorProgressBar: string = '.t3js-installer-progress';
+  private selectorDatabaseConnectOutput: string = '.t3js-installer-databaseConnect-output';
+  private selectorDatabaseSelectOutput: string = '.t3js-installer-databaseSelect-output';
+  private selectorDatabaseDataOutput: string = '.t3js-installer-databaseData-output';
+
+  constructor() {
+    this.initializeEvents();
+    $((): void => {
+      this.initialize();
+    });
+  }
+
+  private initializeEvents(): void {
+    $(document).on('click', '.t3js-installer-environmentFolders-retry', (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.showEnvironmentAndFolders();
+    });
+    $(document).on('click', '.t3js-installer-environmentFolders-execute', (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.executeEnvironmentAndFolders();
+    });
+    $(document).on('click', '.t3js-installer-databaseConnect-execute', (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.executeDatabaseConnect();
+    });
+    $(document).on('click', '.t3js-installer-databaseSelect-execute', (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.executeDatabaseSelect();
+    });
+    $(document).on('click', '.t3js-installer-databaseData-execute', (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.executeDatabaseData();
+    });
+    $(document).on('click', '.t3js-installer-defaultConfiguration-execute', (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.executeDefaultConfiguration();
+    });
+    $(document).on('keyup', '.t3-install-form-password-strength', (): void => {
+      PasswordStrength.initialize('.t3-install-form-password-strength');
+    });
+
+    // Database connect db driver selection
+    $(document).on('change', '#t3js-connect-database-driver', (e: JQueryEventObject): void => {
+      let driver: string = $(e.currentTarget).val();
+      $('.t3-install-driver-data').hide();
+      $('.t3-install-driver-data input').attr('disabled', 'disabled');
+      $('#' + driver + ' input').attr('disabled', null);
+      $('#' + driver).show();
+    });
+  }
+
+  private initialize(): void {
+    this.setProgress(0);
+    this.getMainLayout();
+  }
+
+  private getUrl(action?: string): string {
+    let url: string = location.href;
+    url = url.replace(location.search, '');
+    if (action !== undefined) {
+      url = url + '?install[action]=' + action;
+    }
+    return url;
+  }
+
+  private setProgress(done: number): void {
+    let $progressBar: JQuery = $(this.selectorProgressBar);
+    let percent: number = 0;
+    if (done !== 0) {
+      percent = (done / 5) * 100;
+      $progressBar.find('.progress-bar').empty().text(done + ' / 5 - ' + percent + '% Complete');
+    }
+    $progressBar
+      .find('.progress-bar')
+      .css('width', percent + '%')
+      .attr('aria-valuenow', percent);
+  }
+
+  private getMainLayout(): void {
+    $.ajax({
+      url: this.getUrl('mainLayout'),
+      cache: false,
+      success: (data: any): void => {
+        $(this.selectorBody).empty().append(data.html);
+        this.checkInstallerAvailable();
+      },
+    });
+  }
+
+  private checkInstallerAvailable(): void {
+    $.ajax({
+      url: this.getUrl('checkInstallerAvailable'),
+      cache: false,
+      success: (data: any): void => {
+        data.success
+          ? this.checkEnvironmentAndFolders()
+          : this.showInstallerNotAvailable();
+      },
+    });
+  }
+
+  private showInstallerNotAvailable(): void {
+    let $outputContainer: JQuery = $(this.selectorMainContent);
+    $.ajax({
+      url: this.getUrl('showInstallerNotAvailable'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          $outputContainer.empty().append(data.html);
+        }
+      },
+    });
+  }
+
+  private checkEnvironmentAndFolders(): void {
+    this.setProgress(1);
+    $.ajax({
+      url: this.getUrl('checkEnvironmentAndFolders'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          this.checkTrustedHostsPattern();
+        } else {
+          this.showEnvironmentAndFolders();
+        }
+      },
+    });
+  }
+
+  private showEnvironmentAndFolders(): void {
+    let $outputContainer: JQuery = $(this.selectorMainContent);
+    $.ajax({
+      url: this.getUrl('showEnvironmentAndFolders'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          $outputContainer.empty().html(data.html);
+          let $detailContainer: JQuery = $('.t3js-installer-environment-details');
+          let hasMessage: boolean = false;
+          if (Array.isArray(data.environmentStatusErrors)) {
+            data.environmentStatusErrors.forEach((element: any): void => {
+              hasMessage = true;
+              let message: any = InfoBox.render(element.severity, element.title, element.message);
+              $detailContainer.append(message);
+            });
+          }
+          if (Array.isArray(data.environmentStatusWarnings)) {
+            data.environmentStatusWarnings.forEach((element: any): void => {
+              hasMessage = true;
+              let message: any = InfoBox.render(element.severity, element.title, element.message);
+              $detailContainer.append(message);
+            });
+          }
+          if (Array.isArray(data.structureErrors)) {
+            data.structureErrors.forEach((element: any): void => {
+              hasMessage = true;
+              let message: any = InfoBox.render(element.severity, element.title, element.message);
+              $detailContainer.append(message);
+            });
+          }
+          if (hasMessage) {
+            $detailContainer.show();
+            $('.t3js-installer-environmentFolders-bad').show();
+          } else {
+            $('.t3js-installer-environmentFolders-good').show();
+          }
+        }
+      },
+    });
+  }
+
+  private executeEnvironmentAndFolders(): void {
+    $.ajax({
+      url: this.getUrl('executeEnvironmentAndFolders'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          this.checkTrustedHostsPattern();
+        } else {
+          // @todo message output handling
+        }
+      },
+    });
+  }
+
+  private checkTrustedHostsPattern(): void {
+    $.ajax({
+      url: this.getUrl('checkTrustedHostsPattern'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          this.executeSilentConfigurationUpdate();
+        } else {
+          this.executeAdjustTrustedHostsPattern();
+        }
+      },
+    });
+  }
+
+  private executeAdjustTrustedHostsPattern(): void {
+    $.ajax({
+      url: this.getUrl('executeAdjustTrustedHostsPattern'),
+      cache: false,
+      success: (): void => {
+        this.executeSilentConfigurationUpdate();
+      },
+    });
+  }
+
+  private executeSilentConfigurationUpdate(): void {
+    $.ajax({
+      url: this.getUrl('executeSilentConfigurationUpdate'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          this.checkDatabaseConnect();
+        } else {
+          this.executeSilentConfigurationUpdate();
+        }
+      },
+    });
+  }
+
+  private checkDatabaseConnect(): void {
+    this.setProgress(2);
+    $.ajax({
+      url: this.getUrl('checkDatabaseConnect'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          this.checkDatabaseSelect();
+        } else {
+          this.showDatabaseConnect();
+        }
+      },
+    });
+  }
+
+  private showDatabaseConnect(): void {
+    let $outputContainer: JQuery = $(this.selectorMainContent);
+    $.ajax({
+      url: this.getUrl('showDatabaseConnect'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          $outputContainer.empty().html(data.html);
+          $('#t3js-connect-database-driver').trigger('change');
+        }
+      },
+    });
+  }
+
+  private executeDatabaseConnect(): void {
+    let $outputContainer: JQuery = $(this.selectorDatabaseConnectOutput);
+    let postData: any = {
+      'install[action]': 'executeDatabaseConnect',
+      'install[token]': $(this.selectorModuleContent).data('installer-database-connect-execute-token'),
+    };
+    $($(this.selectorBody + ' form').serializeArray()).each((index: number, element: any): void => {
+      postData[element.name] = element.value;
+    });
+    $.ajax({
+      url: this.getUrl(),
+      cache: false,
+      method: 'POST',
+      data: postData,
+      success: (data: any): void => {
+        if (data.success === true) {
+          this.checkDatabaseSelect();
+        } else {
+          if (Array.isArray(data.status)) {
+            data.status.forEach((element: any): void => {
+              let message: any = InfoBox.render(element.severity, element.title, element.message);
+              $outputContainer.empty().append(message);
+            });
+          }
+        }
+      },
+    });
+  }
+
+  private checkDatabaseSelect(): void {
+    this.setProgress(3);
+    $.ajax({
+      url: this.getUrl('checkDatabaseSelect'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          this.checkDatabaseData();
+        } else {
+          this.showDatabaseSelect();
+        }
+      },
+    });
+  }
+
+  private showDatabaseSelect(): void {
+    let $outputContainer: JQuery = $(this.selectorMainContent);
+    $.ajax({
+      url: this.getUrl('showDatabaseSelect'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          $outputContainer.empty().html(data.html);
+        }
+      },
+    });
+  }
+
+  private executeDatabaseSelect(): void {
+    let $outputContainer: JQuery = $(this.selectorDatabaseSelectOutput);
+    let postData: { [id: string]: string } = {
+      'install[action]': 'executeDatabaseSelect',
+      'install[token]': $(this.selectorModuleContent).data('installer-database-select-execute-token'),
+    };
+    $($(this.selectorBody + ' form').serializeArray()).each((index: number, element: any): void => {
+      postData[element.name] = element.value;
+    });
+    $.ajax({
+      url: this.getUrl(),
+      cache: false,
+      method: 'POST',
+      data: postData,
+      success: (data: any): void => {
+        if (data.success === true) {
+          this.checkDatabaseData();
+        } else {
+          if (Array.isArray(data.status)) {
+            data.status.forEach((element: any): void => {
+              let message: any = InfoBox.render(element.severity, element.title, element.message);
+              $outputContainer.empty().append(message);
+            });
+          }
+        }
+      },
+    });
+  }
+
+  private checkDatabaseData(): void {
+    this.setProgress(4);
+    $.ajax({
+      url: this.getUrl('checkDatabaseData'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          this.showDefaultConfiguration();
+        } else {
+          this.showDatabaseData();
+        }
+      },
+    });
+  }
+
+  private showDatabaseData(): void {
+    let $outputContainer: JQuery = $(this.selectorMainContent);
+    $.ajax({
+      url: this.getUrl('showDatabaseData'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          $outputContainer.empty().html(data.html);
+        }
+      },
+    });
+  }
+
+  private executeDatabaseData(): void {
+    let $outputContainer: JQuery = $(this.selectorDatabaseDataOutput);
+    let postData: any = {
+      'install[action]': 'executeDatabaseData',
+      'install[token]': $(this.selectorModuleContent).data('installer-database-data-execute-token'),
+    };
+    $($(this.selectorBody + ' form').serializeArray()).each((index: number, element: any): void => {
+      postData[element.name] = element.value;
+    });
+    let message: any = ProgressBar.render(Severity.loading, 'Loading...', '');
+    $outputContainer.empty().html(message);
+    $.ajax({
+      url: this.getUrl(),
+      cache: false,
+      method: 'POST',
+      data: postData,
+      success: (data: any): void => {
+        if (data.success === true) {
+          this.showDefaultConfiguration();
+        } else {
+          if (Array.isArray(data.status)) {
+            data.status.forEach((element: any): void => {
+              let m: any = InfoBox.render(element.severity, element.title, element.message);
+              $outputContainer.empty().append(m);
+            });
+          }
+        }
+      },
+    });
+  }
+
+  private showDefaultConfiguration(): void {
+    let $outputContainer: JQuery = $(this.selectorMainContent);
+    this.setProgress(5);
+    $.ajax({
+      url: this.getUrl('showDefaultConfiguration'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          $outputContainer.empty().html(data.html);
+        }
+      },
+    });
+  }
+
+  private executeDefaultConfiguration(): void {
+    let postData: any = {
+      'install[action]': 'executeDefaultConfiguration',
+      'install[token]': $(this.selectorModuleContent).data('installer-default-configuration-execute-token'),
+    };
+    $($(this.selectorBody + ' form').serializeArray()).each((index: number, element: any): void => {
+      postData[element.name] = element.value;
+    });
+    $.ajax({
+      url: this.getUrl(),
+      cache: false,
+      method: 'POST',
+      data: postData,
+      success: (data: any): void => {
+        top.location.href = data.redirect;
+      },
+    });
+  }
+}
+
+export = new Installer();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Module/Cache.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/Cache.ts
new file mode 100644 (file)
index 0000000..4d228b8
--- /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');
+      },
+      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');
+      },
+    });
+  }
+}
+
+export = new Cache();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Module/ChangeInstallToolPassword.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/ChangeInstallToolPassword.ts
new file mode 100644 (file)
index 0000000..f873ca6
--- /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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import Router = require('../Router');
+import PasswordStrength = require('./PasswordStrength');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/ChangeInstallToolPassword
+ */
+class ChangeInstallToolPassword implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  private selectorChangeForm: string = '#t3js-changeInstallToolPassword-form';
+  private currentModal: any = {};
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getData();
+
+    currentModal.on('submit', this.selectorChangeForm, (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.currentModal.find(this.selectorModalBody);
+    $.ajax({
+      url: Router.getUrl('changeInstallToolPasswordGetData'),
+      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);
+      },
+    });
+  }
+
+  private change(): void {
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    const executeToken = this.currentModal.find(this.selectorModuleContent).data('install-tool-token');
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: {
+        'install': {
+          'action': 'changeInstallToolPassword',
+          'token': executeToken,
+          'password': this.currentModal.find('.t3js-changeInstallToolPassword-password').val(),
+          'passwordCheck': this.currentModal.find('.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.currentModal.find('.t3js-changeInstallToolPassword-password,.t3js-changeInstallToolPassword-password-check').val('');
+      },
+    });
+  }
+}
+
+export = new ChangeInstallToolPassword();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Module/ClearTables.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/ClearTables.ts
new file mode 100644 (file)
index 0000000..8575e0b
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import Router = require('../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/ClearTables
+ */
+class ClearTables implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  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';
+  private currentModal: any = {};
+
+  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.currentModal.find(this.selectorModalBody);
+    $.ajax({
+      url: Router.getUrl('clearTablesStats'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          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.currentModal.find(this.selectorModalBody);
+    const executeToken = this.currentModal.find(this.selectorModuleContent).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/typo3/sysext/install/Resources/Private/TypeScript/Module/ClearTypo3tempFiles.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/ClearTypo3tempFiles.ts
new file mode 100644 (file)
index 0000000..0c95aae
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import Router = require('../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/ClearTypo3tempFiles
+ */
+class ClearTypo3tempFiles implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  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';
+  private currentModal: JQuery;
+
+  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.currentModal.find(this.selectorModalBody);
+    $.ajax({
+      url: Router.getUrl('clearTypo3tempFilesStats'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          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.currentModal.find(this.selectorModalBody);
+    const executeToken = this.currentModal.find(this.selectorModuleContent).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/typo3/sysext/install/Resources/Private/TypeScript/Module/CoreUpdate.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/CoreUpdate.ts
new file mode 100644 (file)
index 0000000..e3e4d3f
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import Router = require('../Router');
+import FlashMessage = require('../Renderable/FlashMessage');
+import Severity = require('../Renderable/Severity');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+interface ActionItem {
+  loadingMessage: string;
+  finishMessage: string;
+  nextActionName: string;
+}
+
+interface ActionQueue {
+  [k: string]: ActionItem;
+}
+
+class CoreUpdate implements InteractableModuleInterface {
+  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 selectorModalBody: string = '.t3js-modal-body';
+  private selectorOutput: string = '.t3js-coreUpdate-output';
+  private selectorTemplate: string = '.t3js-coreUpdate-buttonTemplate';
+
+  /**
+   * Clone of a DOM object acts as button template
+   */
+  private buttonTemplate: any = null;
+
+  private currentModal: JQuery = null;
+
+  /**
+   * Fetching the templates out of the DOM
+   */
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getData().done((): void => {
+      const buttonTemplateSection = currentModal.find(this.selectorTemplate);
+      this.buttonTemplate = buttonTemplateSection.children().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.target).attr('data-action');
+
+      currentModal.find(this.selectorOutput).empty();
+      CoreUpdate.call(action);
+    });
+  }
+
+  private getData(): JQueryXHR {
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    return $.ajax({
+      url: Router.getUrl('coreUpdateGetData'),
+      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);
+      },
+    });
+  }
+
+  /**
+   * Internal action called by callAction()
+   */
+  private checkForUpdate(): void {
+    this.callAction('coreUpdateIsUpdateAvailable');
+  }
+
+  /**
+   * Internal action called by callAction()
+   */
+  private updateDevelopment(): void {
+    this.update('development');
+  }
+
+  /**
+   * Internal action called by callAction()
+   */
+  private updateRegular(): void {
+    this.update('regular');
+  }
+
+  /**
+   * 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.currentModal.find(this.selectorModalBody));
+      },
+    });
+  }
+
+  /**
+   * 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.currentModal.find(this.selectorOutput).append(domMessage);
+  }
+
+  /**
+   * Remove an enabled loading message
+   */
+  private removeLoadingMessage(): void {
+    this.currentModal.find(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.currentModal.find(this.selectorOutput).append(domButton);
+  }
+
+  /**
+   * Show a status message
+   */
+  private addMessage(severity: number, title: string, message?: string): void {
+    const domMessage = FlashMessage.render(severity, title, message);
+    this.currentModal.find(this.selectorOutput).append(domMessage);
+  }
+}
+
+export = new CoreUpdate();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Module/CreateAdmin.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/CreateAdmin.ts
new file mode 100644 (file)
index 0000000..1d4768f
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import Router = require('../Router');
+import PasswordStrength = require('./PasswordStrength');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/CreateAdmin
+ */
+class CreateAdmin implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  private selectorCreateForm: string = '#t3js-createAdmin-form';
+  private currentModal: JQuery;
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getData();
+
+    currentModal.on('submit', this.selectorCreateForm, (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.currentModal.find(this.selectorModalBody);
+    $.ajax({
+      url: Router.getUrl('createAdminGetData'),
+      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);
+      },
+    });
+  }
+
+  private create(): void {
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    const executeToken = this.currentModal.find(this.selectorModuleContent).data('create-admin-token');
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: {
+        'install': {
+          'action': 'createAdmin',
+          'token': executeToken,
+          'userName': this.currentModal.find('.t3js-createAdmin-user').val(),
+          'userPassword': this.currentModal.find('.t3js-createAdmin-password').val(),
+          'userPasswordCheck': this.currentModal.find('.t3js-createAdmin-password-check').val(),
+          'userSystemMaintainer': (this.currentModal.find('.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.currentModal.find('.t3js-createAdmin-user').val('');
+    this.currentModal.find('.t3js-createAdmin-password').val('');
+    this.currentModal.find('.t3js-createAdmin-password-check').val('');
+    this.currentModal.find('.t3js-createAdmin-system-maintainer').prop('checked', false);
+  }
+}
+
+export = new CreateAdmin();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Module/DatabaseAnalyzer.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/DatabaseAnalyzer.ts
new file mode 100644 (file)
index 0000000..ec1dd6d
--- /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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import Router = require('../Router');
+import ProgressBar = require('../Renderable/ProgressBar');
+import InfoBox = require('../Renderable/InfoBox');
+import Severity = require('../Renderable/Severity');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/DatabaseAnalyzer
+ */
+class DatabaseAnalyzer implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  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';
+  private currentModal: JQuery;
+
+  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.currentModal.find(this.selectorModalBody);
+    $.ajax({
+      url: Router.getUrl('databaseAnalyzer'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          this.analyze();
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private analyze(): void {
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    const outputContainer = modalContent.find(this.selectorOutputContainer);
+    const executeTrigger = modalContent.find(this.selectorExecuteTrigger);
+    const analyzeTrigger = modalContent.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.currentModal.find(this.selectorModalBody);
+    const executeToken = this.currentModal.find(this.selectorModuleContent).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/typo3/sysext/install/Resources/Private/TypeScript/Module/DumpAutoload.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/DumpAutoload.ts
new file mode 100644 (file)
index 0000000..28f41a6
--- /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');
+      },
+      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');
+      },
+    });
+  }
+}
+
+export = new DumpAutoload();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Module/EnvironmentCheck.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/EnvironmentCheck.ts
new file mode 100644 (file)
index 0000000..37dad42
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+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 Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/EnvironmentCheck
+ */
+class EnvironmentCheck implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorGridderBadge: string = '.t3js-environmentCheck-badge';
+  private selectorExecuteTrigger: string = '.t3js-environmentCheck-execute';
+  private selectorOutputContainer: string = '.t3js-environmentCheck-output';
+  private currentModal: JQuery;
+
+  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.currentModal.find(this.selectorModalBody);
+    const $errorBadge = $(this.selectorGridderBadge);
+    $errorBadge.text('').hide();
+    const message = ProgressBar.render(Severity.loading, 'Loading...', '');
+    modalContent.find(this.selectorOutputContainer).empty().append(message);
+    $.ajax({
+      url: Router.getUrl('environmentCheckGetStatus'),
+      cache: false,
+      success: (data: any): void => {
+        modalContent.empty().append(data.html);
+        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/typo3/sysext/install/Resources/Private/TypeScript/Module/ExtensionCompatTester.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/ExtensionCompatTester.ts
new file mode 100644 (file)
index 0000000..9fbe2cd
--- /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 {InteractableModuleInterface} from './InteractableModuleInterface';
+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 Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/ExtensionCompatTester
+ */
+class ExtensionCompatTester implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  private selectorCheckTrigger: string = '.t3js-extensionCompatTester-check';
+  private selectorUninstallTrigger: string = '.t3js-extensionCompatTester-uninstall';
+  private selectorOutputContainer: string = '.t3js-extensionCompatTester-output';
+  private currentModal: JQuery;
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getLoadedExtensionList();
+
+    currentModal.on('click', this.selectorCheckTrigger, (e: JQueryEventObject): void => {
+      currentModal.find(this.selectorUninstallTrigger).hide();
+      currentModal.find(this.selectorOutputContainer).empty();
+      this.getLoadedExtensionList();
+    });
+    currentModal.on('click', this.selectorUninstallTrigger, (e: JQueryEventObject): void => {
+      this.uninstallExtension($(e.target).data('extension'));
+    });
+  }
+
+  private getLoadedExtensionList(): void {
+    this.currentModal.find(this.selectorCheckTrigger).prop('disabled', true);
+    this.currentModal.find('.modal-loading').hide();
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    const $outputContainer = this.currentModal.find(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);
+        const $innerOutputContainer: JQuery = this.currentModal.find(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);
+            modalContent.find(this.selectorUninstallTrigger)
+              .text('Unload extension "' + response.extension + '"')
+              .attr('data-extension', response.extension)
+              .show();
+          }).always((): void => {
+            $innerOutputContainer.find('.alert-loading').remove();
+            this.currentModal.find(this.selectorCheckTrigger).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.currentModal.find(this.selectorModuleContent).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.currentModal.find(this.selectorModuleContent).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.currentModal.find(this.selectorModuleContent).data('extension-compat-tester-uninstall-extension-token');
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    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.selectorUninstallTrigger).hide();
+          this.getLoadedExtensionList();
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+}
+
+export = new ExtensionCompatTester();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Module/ExtensionConfiguration.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/ExtensionConfiguration.ts
new file mode 100644 (file)
index 0000000..f0c4da5
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+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 implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  private selectorFormListener: string = '.t3js-extensionConfiguration-form';
+  private selectorSearchInput: string = '.t3js-extensionConfiguration-search';
+  private currentModal: JQuery;
+
+  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.currentModal.find(this.selectorModalBody);
+    $.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.currentModal.find(this.selectorModalBody);
+    const executeToken = this.currentModal.find(this.selectorModuleContent).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.currentModal.find('.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.currentModal.find('.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/typo3/sysext/install/Resources/Private/TypeScript/Module/ExtensionScanner.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/ExtensionScanner.ts
new file mode 100644 (file)
index 0000000..722ef90
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import 'bootstrap';
+import AjaxQueue = require('../Ajax/AjaxQueue');
+import Router = require('../Router');
+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 implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  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 currentModal: JQuery;
+
+  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', '.t3js-extensionScanner-scan-all', (e: JQueryEventObject): void => {
+      // Scan all button
+      e.preventDefault();
+      const $extensions = currentModal.find(this.selectorExtensionContainer);
+      this.scanAll($extensions);
+    });
+  }
+
+  private getData(): void {
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    AjaxQueue.add({
+      url: Router.getUrl('extensionScannerGetData'),
+      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);
+      },
+    });
+  }
+
+  private getExtensionSelector(extension: string): string {
+    return this.selectorExtensionContainer + '-' + extension;
+  }
+
+  private scanAll($extensions: JQuery): void {
+    this.currentModal.find(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.currentModal.find(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.currentModal.find(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.currentModal.find(this.selectorExtensionContainer).length;
+    const numberOfSuccess: number = this.currentModal.find(this.selectorExtensionContainer
+      + '.t3js-extensionscan-finished.panel-success').length;
+    const numberOfWarning: number = this.currentModal.find(this.selectorExtensionContainer
+      + '.t3js-extensionscan-finished.panel-warning').length;
+    const numberOfError: number = this.currentModal.find(this.selectorExtensionContainer
+      + '.t3js-extensionscan-finished.panel-danger').length;
+    const numberOfScannedExtensions: number = numberOfSuccess + numberOfWarning + numberOfError;
+    const percent: number = (numberOfScannedExtensions / numberOfExtensions) * 100;
+    const modalContent: JQuery = this.currentModal.find(this.selectorModalBody);
+    this.currentModal.find('.t3js-extensionScanner-progress-all-extension .progress-bar')
+      .css('width', percent + '%')
+      .attr('aria-valuenow', percent)
+      .find('span')
+      .text(numberOfScannedExtensions + ' of ' + numberOfExtensions + ' scanned');
+
+    if (numberOfScannedExtensions === numberOfExtensions) {
+      Notification.success('Scan finished', 'All extensions have been scanned');
+      AjaxQueue.add({
+        url: Router.getUrl(),
+        method: 'POST',
+        data: {
+          'install': {
+            'action': 'extensionScannerMarkFullyScannedRestFiles',
+            'token': this.currentModal.find(this.selectorModuleContent).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.currentModal.find(this.selectorModuleContent).data('extension-scanner-files-token');
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    const $extensionContainer = this.currentModal.find(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.currentModal.find(this.selectorModuleContent).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/typo3/sysext/install/Resources/Private/TypeScript/Module/Features.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/Features.ts
new file mode 100644 (file)
index 0000000..74eb35d
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import Router = require('../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/Features
+ */
+class Features implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-features-content';
+  private selectorSaveTrigger: string = '.t3js-features-save';
+  private currentModal: any;
+
+  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.currentModal.find(this.selectorModalBody);
+    $.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);
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private save(): void {
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    const executeToken = this.currentModal.find(this.selectorModuleContent).data('features-save-token');
+    const postData: any = {};
+    $(this.currentModal.find(this.selectorModuleContent + ' 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/typo3/sysext/install/Resources/Private/TypeScript/Module/FolderStructure.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/FolderStructure.ts
new file mode 100644 (file)
index 0000000..13a5d54
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+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 Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/FolderStructure
+ */
+class FolderStructure implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  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 currentModal: JQuery;
+
+  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();
+      this.fix();
+    });
+  }
+
+  private getStatus(): void {
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    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);
+        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.currentModal.find(this.selectorModalBody);
+    const $outputContainer: JQuery = this.currentModal.find(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/typo3/sysext/install/Resources/Private/TypeScript/Module/ImageProcessing.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/ImageProcessing.ts
new file mode 100644 (file)
index 0000000..4c5e289
--- /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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../Router');
+import InfoBox = require('../Renderable/InfoBox');
+import Severity = require('../Renderable/Severity');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/ImageProcessing
+ */
+class ImageProcessing implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  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';
+  private currentModal: JQuery;
+
+  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.currentModal.find(this.selectorModalBody);
+    $.ajax({
+      url: Router.getUrl('imageProcessingGetData'),
+      cache: false,
+      success: (data: any): void => {
+        if (data.success === true) {
+          modalContent.empty().append(data.html);
+          this.runTests();
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private runTests(): void {
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    const $twinImageTemplate = this.currentModal.find(this.selectorTwinImageTemplate);
+    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);
+      $.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);
+        },
+      });
+    });
+  }
+}
+
+export = new ImageProcessing();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Module/InlineModuleInterface.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/InlineModuleInterface.ts
new file mode 100644 (file)
index 0000000..fd207fd
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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!
+ */
+
+export interface InlineModuleInterface {
+  initialize($trigger: JQuery): void;
+}
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Module/InteractableModuleInterface.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/InteractableModuleInterface.ts
new file mode 100644 (file)
index 0000000..6e3a790
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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!
+ */
+
+export interface InteractableModuleInterface {
+  initialize(currentModal: JQuery): void;
+}
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Module/LanguagePacks.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/LanguagePacks.ts
new file mode 100644 (file)
index 0000000..1d7a38b
--- /dev/null
@@ -0,0 +1,519 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+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 implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  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 currentModal: JQuery;
+
+  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.currentModal.find(this.selectorModalBody);
+    $.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.currentModal.find(this.selectorModalBody);
+    const $outputContainer = this.currentModal.find(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.currentModal.find(this.selectorModuleContent).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.currentModal.find(this.selectorModalBody);
+    const $outputContainer = this.currentModal.find(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.currentModal.find(this.selectorModuleContent).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.currentModal.find(this.selectorOutputContainer);
+    const $contentContainer = this.currentModal.find(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.currentModal.find(this.selectorModuleContent).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.currentModal.find(this.selectorModalBody);
+    const $outputContainer = this.currentModal.find(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.currentModal.find(this.selectorModuleContent).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.currentModal.find(this.selectorActivateLanguageIcon).html();
+    const deactivateIcon = this.currentModal.find(this.selectorDeactivateLanguageIcon).html();
+    const updateIcon = this.currentModal.find(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.currentModal.find(this.selectorExtensionPackMissesIcon).html();
+    const updateIcon: string = this.currentModal.find(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.currentModal.find(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/typo3/sysext/install/Resources/Private/TypeScript/Module/LocalConfiguration.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/LocalConfiguration.ts
new file mode 100644 (file)
index 0000000..a5d8382
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/LocalConfiguration
+ */
+class LocalConfiguration implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  private selectorToggleAllTrigger: string = '.t3js-localConfiguration-toggleAll';
+  private selectorWriteTrigger: string = '.t3js-localConfiguration-write';
+  private selectorSearchTrigger: string = '.t3js-localConfiguration-search';
+  private currentModal: JQuery;
+
+  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.currentModal.find(this.selectorModalBody);
+      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.currentModal.find(this.selectorModalBody);
+    $.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);
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private write(): void {
+    const modalContent: JQuery = this.currentModal.find(this.selectorModalBody);
+    const executeToken: JQuery = this.currentModal.find(this.selectorModuleContent).data('local-configuration-write-token');
+    const configurationValues: any = {};
+    this.currentModal.find('.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/typo3/sysext/install/Resources/Private/TypeScript/Module/MailTest.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/MailTest.ts
new file mode 100644 (file)
index 0000000..18a5f69
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+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 Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/CreateAdmin
+ */
+class MailTest implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  private selectorForm: string = '#t3js-mailTest-form';
+  private selectorOutputContainer: string = '.t3js-mailTest-output';
+  private currentModal: JQuery;
+
+  public initialize(currentModal: JQuery): void {
+    this.currentModal = currentModal;
+    this.getData();
+    currentModal.on('submit', this.selectorForm, (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.send();
+    });
+  }
+
+  private getData(): void {
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    $.ajax({
+      url: Router.getUrl('mailTestGetData'),
+      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);
+      },
+    });
+  }
+
+  private send(): void {
+    const executeToken: string = this.currentModal.find(this.selectorModuleContent).data('mail-test-token');
+    const $outputContainer: JQuery = this.currentModal.find(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.currentModal.find('.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/typo3/sysext/install/Resources/Private/TypeScript/Module/PasswordStrength.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/PasswordStrength.ts
new file mode 100644 (file)
index 0000000..7b41ca7
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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 * as $ from 'jquery';
+
+class PasswordStrength {
+  public initialize(field: string): void {
+    // Simple password strength indicator
+    $(document).on('keyup', field, (event: JQueryEventObject): void => {
+      const $this = $(event.currentTarget);
+      const value = $this.val();
+      const strongRegex = new RegExp('^(?=.{8,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*\\W).*$', 'g');
+      const mediumRegex = new RegExp('^(?=.{8,})(((?=.*[A-Z])(?=.*[a-z]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[0-9]))).*$', 'g');
+      const enoughRegex = new RegExp('(?=.{8,}).*', 'g');
+
+      if (value.length === 0) {
+        $this.attr('style', 'background-color:#FBB19B; border:1px solid #DC4C42');
+      } else if (!enoughRegex.test(value)) {
+        $this.attr('style', 'background-color:#FBB19B; border:1px solid #DC4C42');
+      } else if (strongRegex.test(value)) {
+        $this.attr('style', 'background-color:#CDEACA; border:1px solid #58B548');
+      } else if (mediumRegex.test(value)) {
+        $this.attr('style', 'background-color:#FBFFB3; border:1px solid #C4B70D');
+      } else {
+        $this.attr('style', 'background-color:#FBFFB3; border:1px solid #C4B70D');
+      }
+    });
+  }
+}
+
+export = new PasswordStrength();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Module/PhpInfo.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/PhpInfo.ts
new file mode 100644 (file)
index 0000000..a08fb90
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import Router = require('../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/PhpInfo
+ */
+class PhpInfo implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private currentModal: any = {};
+
+  public initialize(currentModal: any): void {
+    this.currentModal = currentModal;
+    this.getData();
+  }
+
+  private getData(): void {
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    $.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/typo3/sysext/install/Resources/Private/TypeScript/Module/Presets.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/Presets.ts
new file mode 100644 (file)
index 0000000..23c8b9f
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/Presets
+ */
+class Presets implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-presets-content';
+  private selectorActivateTrigger: string = '.t3js-presets-activate';
+  private selectorImageExecutable: string = '.t3js-presets-image-executable';
+  private selectorImageExecutableTrigger: string = '.t3js-presets-image-executable-trigger';
+  private currentModal: JQuery;
+
+  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.currentModal.find(this.selectorModalBody);
+    $.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);
+        } else {
+          Notification.error('Something went wrong');
+        }
+      },
+      error: (xhr: XMLHttpRequest): void => {
+        Router.handleAjaxError(xhr, modalContent);
+      },
+    });
+  }
+
+  private getCustomImagePathContent(): void {
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    const presetsContentToken = this.currentModal.find(this.selectorModuleContent).data('presets-content-token');
+    $.ajax({
+      url: Router.getUrl(),
+      method: 'POST',
+      data: {
+        'install': {
+          'token': presetsContentToken,
+          'action': 'presetsGetContent',
+          'values': {
+            'Image': {
+              'additionalSearchPath': this.currentModal.find(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.currentModal.find(this.selectorModalBody);
+    const executeToken: string = this.currentModal.find(this.selectorModuleContent).data('presets-activate-token');
+    const postData: any = {};
+    $(this.currentModal.find(this.selectorModuleContent + ' 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/typo3/sysext/install/Resources/Private/TypeScript/Module/ResetBackendUserUc.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/ResetBackendUserUc.ts
new file mode 100644 (file)
index 0000000..f50e0c8
--- /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');
+      },
+      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');
+      },
+    });
+  }
+}
+
+export = new ResetBackendUserUc();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Module/SystemInformation.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/SystemInformation.ts
new file mode 100644 (file)
index 0000000..4e8560b
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import Router = require('../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/SystemInformation
+ */
+class SystemInformation implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private currentModal: any = {};
+
+  public initialize(currentModal: any): void {
+    this.currentModal = currentModal;
+    this.getData();
+  }
+
+  private getData(): void {
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    $.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/typo3/sysext/install/Resources/Private/TypeScript/Module/SystemMaintainer.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/SystemMaintainer.ts
new file mode 100644 (file)
index 0000000..c02a293
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import 'bootstrap';
+import Router = require('../Router');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/SystemMaintainer
+ */
+class SystemMaintainer implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  private selectorWriteTrigger: string = '.t3js-systemMaintainer-write';
+  private selectorChosenContainer: string = '.t3js-systemMaintainer-chosen';
+  private selectorChosenField: string = '.t3js-systemMaintainer-chosen-select';
+  private currentModal: JQuery;
+
+  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.currentModal.find(this.selectorModalBody);
+    $.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));
+          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.currentModal.find(this.selectorModalBody);
+    const executeToken = this.currentModal.find(this.selectorModuleContent).data('system-maintainer-write-token');
+    const selectedUsers = this.currentModal.find(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/typo3/sysext/install/Resources/Private/TypeScript/Module/TcaExtTablesCheck.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/TcaExtTablesCheck.ts
new file mode 100644 (file)
index 0000000..afbbf64
--- /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 {InteractableModuleInterface} from './InteractableModuleInterface';
+import * as $ from 'jquery';
+import Router = require('../Router');
+import ProgressBar = require('../Renderable/ProgressBar');
+import Severity = require('../Renderable/Severity');
+import InfoBox = require('../Renderable/InfoBox');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/TcaExtTablesCheck
+ */
+class TcaExtTablesCheck implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorCheckTrigger: string = '.t3js-tcaExtTablesCheck-check';
+  private selectorOutputContainer: string = '.t3js-tcaExtTablesCheck-output';
+  private currentModal: JQuery;
+
+  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.currentModal.find(this.selectorModalBody);
+    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);
+        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/typo3/sysext/install/Resources/Private/TypeScript/Module/TcaMigrationsCheck.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/TcaMigrationsCheck.ts
new file mode 100644 (file)
index 0000000..fe1e314
--- /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 {InteractableModuleInterface} from './InteractableModuleInterface';
+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');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/TcaMigrationsCheck
+ */
+class TcaMigrationsCheck implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorCheckTrigger: string = '.t3js-tcaMigrationsCheck-check';
+  private selectorOutputContainer: string = '.t3js-tcaMigrationsCheck-output';
+  private currentModal: JQuery;
+
+  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.currentModal.find(this.selectorModalBody);
+    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);
+        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/typo3/sysext/install/Resources/Private/TypeScript/Module/UpgradeDocs.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/UpgradeDocs.ts
new file mode 100644 (file)
index 0000000..3f24f87
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+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 implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  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 currentModal: JQuery;
+  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.currentModal.find(this.selectorModalBody);
+    $.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.currentModal.find(this.selectorModalBody);
+    this.currentModal.find(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.currentModal.find(this.selectorFulltextSearch);
+    this.fulltextSearchField.clearable().focus();
+    this.initializeChosenSelector();
+    this.fulltextSearchField.on('keyup', (): void => {
+      this.combinedFilterSearch();
+    });
+  }
+
+  private initializeChosenSelector(): void {
+    this.chosenField = this.currentModal.find(this.selectorModalBody).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.currentModal.find(selector).chosen(config[selector]);
+      }
+    }
+    this.chosenField.on('change', (): void => {
+      this.combinedFilterSearch();
+    });
+  }
+
+  /**
+   * Appends tags to the chosen selector
+   */
+  private appendItemsToChosenSelector(): void {
+    let tagString = '';
+    $(this.currentModal.find(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.currentModal.find(this.selectorModalBody);
+    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.currentModal.find('.panel-body-read'));
+    $container.find('[data-item-state="notAffected"]').appendTo(this.currentModal.find('.panel-body-not-affected'));
+  }
+
+  private markRead(element: any): void {
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    const executeToken = this.currentModal.find(this.selectorModuleContent).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.currentModal.find('.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.currentModal.find(this.selectorModalBody);
+    const executeToken = this.currentModal.find(this.selectorModuleContent).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.currentModal.find('*[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/typo3/sysext/install/Resources/Private/TypeScript/Module/UpgradeWizards.ts b/typo3/sysext/install/Resources/Private/TypeScript/Module/UpgradeWizards.ts
new file mode 100644 (file)
index 0000000..2763bd5
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ * 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 {InteractableModuleInterface} from './InteractableModuleInterface';
+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 implements InteractableModuleInterface {
+  private selectorModalBody: string = '.t3js-modal-body';
+  private selectorModuleContent: string = '.t3js-module-content';
+  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 currentModal: JQuery;
+  private securityUtility: SecurityUtility;
+
+  constructor() {
+    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.currentModal.find(this.selectorModalBody);
+    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.currentModal.find(this.selectorModalBody);
+    const $outputContainer = this.currentModal.find(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.currentModal.find(this.selectorModalBody);
+    const $outputContainer = this.currentModal.find(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.currentModal.find(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.currentModal.find(this.selectorModalBody);
+    const $outputContainer = this.currentModal.find(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.currentModal.find(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.currentModal.find(this.selectorModuleContent).data('upgrade-wizards-input-token');
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    const $outputContainer = this.currentModal.find(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.currentModal.find(this.selectorModuleContent).data('upgrade-wizards-execute-token');
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    const postData: any = {
+      'install[action]': 'upgradeWizardsExecute',
+      'install[token]': executeToken,
+      'install[identifier]': identifier,
+    };
+    $(this.currentModal.find(this.selectorOutputWizardsContainer + ' form').serializeArray()).each((index: number, element: any): void => {
+      postData[element.name] = element.value;
+    });
+    const $outputContainer = this.currentModal.find(this.selectorOutputWizardsContainer);
+    // modalContent.find(this.selectorOutputWizardsContainer).empty();
+    $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Executing "' + title + '"...'));
+    this.currentModal.find(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.currentModal.find(this.selectorModalBody);
+    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.currentModal.find(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.currentModal.find(this.selectorModuleContent).data('upgrade-wizards-mark-undone-token');
+    const modalContent = this.currentModal.find(this.selectorModalBody);
+    const $outputContainer = this.currentModal.find(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/typo3/sysext/install/Resources/Private/TypeScript/Renderable/FlashMessage.ts b/typo3/sysext/install/Resources/Private/TypeScript/Renderable/FlashMessage.ts
new file mode 100644 (file)
index 0000000..9a91991
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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 * as $ from 'jquery';
+import Severity = require('./Severity');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/FlashMessage
+ */
+class FlashMessage {
+  private template: JQuery = $('<div class="t3js-message typo3-message alert"><h4></h4><p class="messageText"></p></div>');
+
+  public render(severity: number, title: string, message?: string): JQuery {
+    let flashMessage = this.template.clone();
+    flashMessage.addClass('alert-' + Severity.getCssClass(severity));
+    if (title) {
+      flashMessage.find('h4').text(title);
+    }
+    if (message) {
+      flashMessage.find('.messageText').text(message);
+    } else {
+      flashMessage.find('.messageText').remove();
+    }
+    return flashMessage;
+  }
+}
+
+export = new FlashMessage();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Renderable/InfoBox.ts b/typo3/sysext/install/Resources/Private/TypeScript/Renderable/InfoBox.ts
new file mode 100644 (file)
index 0000000..d2eb109
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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 * as $ from 'jquery';
+import Severity = require('./Severity');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/InfoBox
+ */
+class InfoBox {
+  private template: JQuery = $(
+    '<div class="t3js-infobox callout callout-sm">' +
+      '<h4 class="callout-title"></h4>' +
+      '<div class="callout-body"></div>' +
+    '</div>',
+  );
+
+  public render(severity: number, title: string, message?: string): JQuery {
+    let infoBox: JQuery = this.template.clone();
+    infoBox.addClass('callout-' + Severity.getCssClass(severity));
+    if (title) {
+      infoBox.find('h4').text(title);
+    }
+    if (message) {
+      infoBox.find('.callout-body').text(message);
+    } else {
+      infoBox.find('.callout-body').remove();
+    }
+    return infoBox;
+  }
+}
+
+export = new InfoBox();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Renderable/ProgressBar.ts b/typo3/sysext/install/Resources/Private/TypeScript/Renderable/ProgressBar.ts
new file mode 100644 (file)
index 0000000..fff503a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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 * as $ from 'jquery';
+import Severity = require('./Severity');
+
+/**
+ * Module: TYPO3/CMS/Install/Module/ProgressBar
+ */
+class ProgressBar {
+  private template: JQuery = $(
+    '<div class="progress">' +
+      '<div class="t3js-progressbar progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="100" ' +
+        'aria-valuemin="0" aria-valuemax="100" style="width: 100%"> <span></span>' +
+      '</div>' +
+    '</div>');
+
+  public render(severity: number, title: string, progress: string): JQuery {
+    let progressBar = this.template.clone();
+    progressBar.addClass('progress-bar-' + Severity.getCssClass(severity));
+    if (progress) {
+      progressBar.css('width', progress + '%');
+      progressBar.attr('aria-valuenow', progress);
+    }
+    if (title) {
+      progressBar.find('span').text(title);
+    }
+    return progressBar;
+  }
+}
+
+export = new ProgressBar();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Renderable/Severity.ts b/typo3/sysext/install/Resources/Private/TypeScript/Renderable/Severity.ts
new file mode 100644 (file)
index 0000000..3de899d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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!
+ */
+
+/**
+ * Module: TYPO3/CMS/Install/Module/Severity
+ */
+class Severity {
+  public readonly loading: number = -3;
+  public readonly notice: number = -2;
+  public readonly info: number = -1;
+  public readonly ok: number = 0;
+  public readonly warning: number = 1;
+  public readonly error: number = 2;
+
+  public getCssClass(severity: number): string {
+    let severityClass;
+    switch (severity) {
+      case this.loading:
+        severityClass = 'notice alert-loading';
+        break;
+      case this.notice:
+        severityClass = 'notice';
+        break;
+      case this.ok:
+        severityClass = 'success';
+        break;
+      case this.warning:
+        severityClass = 'warning';
+        break;
+      case this.error:
+        severityClass = 'danger';
+        break;
+      case this.info:
+      default:
+        severityClass = 'info';
+    }
+    return severityClass;
+  }
+}
+
+export = new Severity();
diff --git a/typo3/sysext/install/Resources/Private/TypeScript/Router.ts b/typo3/sysext/install/Resources/Private/TypeScript/Router.ts
new file mode 100644 (file)
index 0000000..66e605c
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * 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 './Module/InlineModuleInterface';
+import {InteractableModuleInterface} from './Module/InteractableModuleInterface';
+import * as $ from 'jquery';
+import InfoBox = require('./Renderable/InfoBox');
+import Severity = require('./Renderable/Severity');
+import ProgressBar = require('./Renderable/ProgressBar');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Icons = require('TYPO3/CMS/Backend/Icons');
+
+class Router {
+  private selectorBody: string = '.t3js-body';
+  private selectorMainContent: string = '.t3js-module-body';
+
+  public initialize(): void {
+    this.registerInstallToolRoutes();
+
+    $(document).on('click', '.t3js-login-lockInstallTool', (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.logout();
+    });
+    $(document).on('click', '.t3js-login-login', (e: JQueryEventObject): void => {
+      e.preventDefault();
+      this.login();
+    });
+    $(document).on('keydown', '#t3-install-form-password', (e: JQueryEventObject): void => {
+      if (e.keyCode === 13) {
+        e.preventDefault();
+        $('.t3js-login-login').click();
+      }
+    });
+
+    $(document).on('click', '.card .btn', (e: JQueryEventObject): void => {
+      e.preventDefault();
+
+      const $me = $(e.currentTarget);
+      const requireModule = $me.data('require');
+      const inlineState = $me.data('inline');
+      const isInline = typeof inlineState !== 'undefined' && parseInt(inlineState, 10) === 1;
+      if (isInline) {
+        require([requireModule], (aModule: InlineModuleInterface): void => {
+          aModule.initialize($me);
+        });
+      } else {
+        const modalTitle = $me.closest('.card').find('.card-title').html();
+        const modalSize = $me.data('modalSize') || Modal.sizes.large;
+
+        Icons.getIcon('spinner-circle', Icons.sizes.default, null, null, Icons.markupIdentifiers.inline).done((icon: any): void => {
+          const configuration = {
+            type: Modal.types.default,
+            title: modalTitle,
+            size: modalSize,
+            content: $('<div class="modal-loading">').append(icon),
+            additionalCssClasses: ['install-tool-modal'],
+            callback: (currentModal: any): void => {
+              require([requireModule], (aModule: InteractableModuleInterface): void => {
+                aModule.initialize(currentModal);
+              });
+            },
+          };
+          Modal.advanced(configuration);
+        });
+      }
+    });
+
+    this.executeSilentConfigurationUpdate();
+  }
+
+  public registerInstallToolRoutes(): void {
+    if (typeof TYPO3.settings === 'undefined') {
+      TYPO3.settings = {
+        ajaxUrls: {
+          icons: '?install[controller]=icon&install[action]=getIcon',
+          icons_cache: '?install[controller]=icon&install[action]=getCacheIdentifier',
+        },
+      };
+    }
+  }
+
+  public getUrl(action?: string, controller?: string): string {
+    const context = $(this.selectorBody).data('context');
+    let url = location.href;
+    url = url.replace(location.search, '');
+    if (controller === undefined) {
+      controller = $(this.selectorBody).data('controller');
+    }
+    url = url + '?install[controller]=' + controller;
+    if (context !== undefined && context !== '') {
+      url = url + '&install[context]=' + context;
+    }
+    if (action !== undefined) {
+      url = url + '&install[action]=' + action;
+    }
+    return url;
+  }
+
+  public executeSilentConfigurationUpdate(): void {
+    this.updateLoadingInfo('Checking session and executing silent configuration update');
+    $.ajax({
+      url: this.getUrl('executeSilentConfigurationUpdate', 'layout'),
+      cache: false,
+      success: (data): void => {
+        if (data.success === true) {
+          this.executeSilentExtensionConfigurationSynchronization();
+        } else {
+          this.executeSilentConfigurationUpdate();
+        }
+      },
+      error: (xhr): void => {
+        this.handleAjaxError(xhr);
+      },
+    });
+  }
+
+  /**
+   * Extensions which come with new default settings in ext_conf_template.txt extension
+   * configuration files get their new defaults written to LocalConfiguration.
+   */
+  public executeSilentExtensionConfigurationSynchronization(): void {
+    const $outputContainer = $(this.selectorBody);
+    this.updateLoadingInfo('Executing silent extension configuration synchronization');
+    $.ajax({
+      url: this.getUrl('executeSilentExtensionConfigurationSynchronization', 'layout'),
+      cache: false,
+      success: (data): void => {
+        if (data.success === true) {
+          this.loadMainLayout();
+        } else {
+          const message = InfoBox.render(Severity.error, 'Something went wrong', '');
+          $outputContainer.empty().append(message);
+        }
+      },
+      error: (xhr): void => {
+        this.handleAjaxError(xhr);
+      },
+    });
+  }
+
+  public loadMainLayout(): void {
+    const $outputContainer = $(this.selectorBody);
+    this.updateLoadingInfo('Loading main layout');
+    $.ajax({
+      url: this.getUrl('mainLayout', 'layout'),
+      cache: false,
+      success: (data): void => {
+        if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
+          $outputContainer.empty().append(data.html);
+          // Mark main module as active in standalone
+          if ($(this.selectorBody).data('context') !== 'backend') {
+            const controller = $outputContainer.data('controller');
+            $outputContainer.find('.t3js-mainmodule[data-controller="' + controller + '"]').addClass('active');
+          }
+          this.loadCards();
+        } else {
+          const message = InfoBox.render(Severity.error, 'Something went wrong', '');
+          $outputContainer.empty().append(message);
+        }
+      },
+      error: (xhr): void => {
+        this.handleAjaxError(xhr);
+      },
+    });
+  }
+
+  public handleAjaxError(xhr: XMLHttpRequest, $outputContainer?: JQuery): void {
+    let $message: any;
+    if (xhr.status === 403) {
+      // Install tool session expired - depending on context render error message or login
+      const $context = $(this.selectorBody).data('context');
+      if ($context === 'backend') {
+        $message = InfoBox.render(Severity.error, 'The install tool session expired. Please reload the backend and try again.');
+        $(this.selectorBody).empty().append($message);
+      } else {
+        this.checkEnableInstallToolFile();
+      }
+    } else {
+      // @todo Recovery tests should be started here
+      const url = this.getUrl(undefined, 'upgrade');
+      $message = $(
+        '<div class="t3js-infobox callout callout-sm callout-danger">'
+          + '<div class="callout-body">'
+            + '<p>Something went wrong. Please use <b><a href="' + url + '">Check for broken'
+            + ' extensions</a></b> to see if a loaded extension breaks this part of the install tool'
+            + ' and unload it.</p>'
+            + '<p>The box below may additionally reveal further details on what went wrong depending on your debug settings.'
+            + ' It may help to temporarily switch to debug mode using <b>Settings > Configuration Presets > Debug settings.</b></p>'
+            + '<p>If this error happens at an early state and no full exception back trace is shown, it may also help'
+            + ' to manually increase debugging output in <code>typo3conf/LocalConfiguration.php</code>:'
+            + '<code>[\'BE\'][\'debug\'] => true</code>, <code>[\'SYS\'][\'devIPmask\'] => \'*\'</code>, '
+            + '<code>[\'SYS\'][\'displayErrors\'] => 1</code>,'
+            + '<code>[\'SYS\'][\'systemLogLevel\'] => 0</code>, <code>[\'SYS\'][\'exceptionalErrors\'] => 12290</code></p>'
+          + '</div>'
+        + '</div>'
+        + '<div class="panel-group" role="tablist" aria-multiselectable="true">'
+          + '<div class="panel panel-default panel-flat searchhit">'
+            + '<div class="panel-heading" role="tab" id="heading-error">'
+              + '<h3 class="panel-title">'
+                + '<a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse-error" aria-expanded="true" '
+                    + 'aria-controls="collapse-error" class="collapsed">'
+                  + '<span class="caret"></span>'
+                  + '<strong>Ajax error</strong>'
+                + '</a>'
+              + '</h3>'
+            + '</div>'
+            + '<div id="collapse-error" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading-error">'
+              + '<div class="panel-body">'
+                + xhr.responseText
+              + '</div>'
+            + '</div>'
+          + '</div>'
+        + '</div>',
+      );
+
+      if (typeof $outputContainer !== 'undefined') {
+        // Write to given output container. This is typically a modal if given
+        $($outputContainer).empty().html($message);
+      } else {
+        // Else write to main frame
+        $(this.selectorBody).empty().html($message);
+      }
+    }
+  }
+
+  public checkEnableInstallToolFile(): void {
+    $.ajax({
+      url: this.getUrl('checkEnableInstallToolFile'),
+      cache: false,
+      success: (data): void => {
+        if (data.success === true) {
+          this.checkLogin();
+        } else {
+          this.showEnableInstallTool();
+        }
+      },
+      error: (xhr): void => {
+        this.handleAjaxError(xhr);
+      },
+    });
+  }
+
+  public showEnableInstallTool(): void {
+    $.ajax({
+      url: this.getUrl('showEnableInstallToolFile'),
+      cache: false,
+      success: (data): void => {
+        if (data.success === true) {
+          $(this.selectorBody).empty().append(data.html);
+        }
+      },
+      error: (xhr): void => {
+        this.handleAjaxError(xhr);
+      },
+    });
+  }
+
+  public checkLogin(): void {
+    $.ajax({
+      url: this.getUrl('checkLogin'),
+      cache: false,
+      success: (data): void => {
+        if (data.success === true) {
+          this.loadMainLayout();
+        } else {
+          this.showLogin();
+        }
+      },
+      error: (xhr): void => {
+        this.handleAjaxError(xhr);
+      },
+    });
+  }
+
+  public showLogin(): void {
+    $.ajax({
+      url: this.getUrl('showLogin'),
+      cache: false,
+      success: (data): void => {
+        if (data.success === true) {
+          $(this.selectorBody).empty().append(data.html);
+        }
+      },
+      error: (xhr): void => {
+        this.handleAjaxError(xhr);
+      },
+    });
+  }
+
+  public login(): void {
+    const $outputContainer: JQuery = $('.t3js-login-output');
+    const message: any = ProgressBar.render(Severity.loading, 'Loading...', '');
+    $outputContainer.empty().html(message);
+    $.ajax({
+      url: this.getUrl(),
+      cache: false,
+      method: 'POST',
+      data: {
+        'install': {
+          'action': 'login',
+          'token': $('[data-login-token]').data('login-token'),
+          'password': $('.t3-install-form-input-text').val(),
+        },
+      },
+      success: (data): void => {
+        if (data.success === true) {
+          this.executeSilentConfigurationUpdate();
+        } else {
+          data.status.forEach((element: any): void => {
+            const m: any = InfoBox.render(element.severity, element.title, element.message);
+            $outputContainer.empty().html(m);
+          });
+        }
+      },
+      error: (xhr): void => {
+        this.handleAjaxError(xhr);
+      },
+    });
+  }
+
+  public logout(): void {
+    $.ajax({
+      url: this.getUrl('logout'),
+      cache: false,
+      success: (data): void => {
+        if (data.success === true) {
+          this.showEnableInstallTool();
+        }
+      },
+      error: (xhr): void => {
+        this.handleAjaxError(xhr);
+      },
+    });
+  }
+
+  public loadCards(): void {
+    const outputContainer = $(this.selectorMainContent);
+    $.ajax({
+      url: this.getUrl('cards'),
+      cache: false,
+      success: (data): void => {
+        if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
+          outputContainer.empty().append(data.html);
+        } else {
+          const message = InfoBox.render(Severity.error, 'Something went wrong', '');
+          outputContainer.empty().append(message);
+        }
+      },
+      error: (xhr): void => {
+        this.handleAjaxError(xhr);
+      },
+    });
+  }
+
+  public updateLoadingInfo(info: string): void {
+    const $outputContainer = $(this.selectorBody);
+    $outputContainer.find('#t3js-ui-block-detail').text(info);
+  }
+}
+
+export = new Router();
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Ajax/AjaxQueue.js b/typo3/sysext/install/Resources/Public/JavaScript/Ajax/AjaxQueue.js
new file mode 100644 (file)
index 0000000..270a012
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery"],function(e,t,u){"use strict";return new(function(){function e(){this.requestCount=0,this.threshold=10,this.queue=[]}return e.prototype.add=function(e){var t=this,n=e.complete;e.complete=function(e,o){t.queue.length>0&&t.requestCount<=t.threshold?u.ajax(t.queue.shift()).always(function(){t.decrementRequestCount()}):t.decrementRequestCount(),n&&n(e,o)},this.requestCount>=this.threshold?this.queue.push(e):(this.incrementRequestCount(),u.ajax(e))},e.prototype.incrementRequestCount=function(){this.requestCount++},e.prototype.decrementRequestCount=function(){this.requestCount>0&&this.requestCount--},e}())});
\ No newline at end of file
index 2dc48c2..a40d811 100644 (file)
  *
  * The TYPO3 project - inspiring people to share!
  */
-
-require([
-  'jquery',
-  'TYPO3/CMS/Install/Router'
-], function($, Router) {
-  'use strict';
-
-  $(function() {
-    Router.initialize();
-  });
-});
+define(["require","exports","jquery","./Router"],function(e,i,n,t){"use strict";return new function(){n(function(){t.initialize()})}});
\ No newline at end of file
index 5510640..1676680 100644 (file)
  *
  * The TYPO3 project - inspiring people to share!
  */
-
-/**
- * Walk through the installation process of TYPO3
- */
-require([
-  'jquery',
-  'TYPO3/CMS/Install/InfoBox',
-  'TYPO3/CMS/Install/Severity',
-  'TYPO3/CMS/Install/ProgressBar',
-  'TYPO3/CMS/Install/PasswordStrength'
-], function($, InfoBox, Severity, ProgressBar, PasswordStrength) {
-  'use strict';
-
-  $(function() {
-    Installer.initialize();
-  });
-
-  var Installer = {
-    selectorBody: '.t3js-body',
-    selectorModuleContent: '.t3js-module-content',
-    selectorMainContent: '.t3js-installer-content',
-    selectorProgressBar: '.t3js-installer-progress',
-    selectorDatabaseConnectOutput: '.t3js-installer-databaseConnect-output',
-    selectorDatabaseSelectOutput: '.t3js-installer-databaseSelect-output',
-    selectorDatabaseDataOutput: '.t3js-installer-databaseData-output',
-    selectorDefaultConfigurationOutput: '.t3js-installer-defaultConfiguration-output',
-
-    initialize: function() {
-      var self = this;
-
-      $(document).on('click', '.t3js-installer-environmentFolders-retry', function(e) {
-        e.preventDefault();
-        self.showEnvironmentAndFolders();
-      });
-      $(document).on('click', '.t3js-installer-environmentFolders-execute', function(e) {
-        e.preventDefault();
-        self.executeEnvironmentAndFolders();
-      });
-      $(document).on('click', '.t3js-installer-databaseConnect-execute', function(e) {
-        e.preventDefault();
-        self.executeDatabaseConnect();
-      });
-      $(document).on('click', '.t3js-installer-databaseSelect-execute', function(e) {
-        e.preventDefault();
-        self.executeDatabaseSelect();
-      });
-      $(document).on('click', '.t3js-installer-databaseData-execute', function(e) {
-        e.preventDefault();
-        self.executeDatabaseData();
-      });
-      $(document).on('click', '.t3js-installer-defaultConfiguration-execute', function(e) {
-        e.preventDefault();
-        self.executeDefaultConfiguration();
-      });
-
-      $(document).on('keyup', '.t3-install-form-password-strength', function() {
-        PasswordStrength.initialize('.t3-install-form-password-strength');
-      });
-
-      // Database connect db driver selection
-      $(document).on('change', '#t3js-connect-database-driver', function() {
-        var driver = $(this).val();
-        $('.t3-install-driver-data').hide();
-        $('.t3-install-driver-data input').attr('disabled', 'disabled');
-        $('#' + driver + ' input').attr('disabled', false);
-        $('#' + driver).show();
-      });
-
-      this.setProgress(0);
-      this.getMainLayout();
-    },
-
-    getUrl: function(action) {
-      var url = location.href;
-      url = url.replace(location.search, '');
-      if (action !== undefined) {
-        url = url + '?install[action]=' + action;
-      }
-      return url;
-    },
-
-    setProgress: function(done) {
-      var $progressBar = $(this.selectorProgressBar);
-      var percent = 0;
-      if (done !== 0) {
-        percent = (done / 5) * 100;
-        $progressBar.find('.progress-bar').empty().text(done + ' / 5 - ' + percent + '% Complete');
-      }
-      $progressBar
-        .find('.progress-bar')
-        .css('width', percent + '%')
-        .attr('aria-valuenow', percent);
-    },
-
-    getMainLayout: function() {
-      var self = this;
-      $.ajax({
-        url: this.getUrl('mainLayout'),
-        cache: false,
-        success: function(data) {
-          $(self.selectorBody).empty().append(data.html);
-          self.checkInstallerAvailable();
-        }
-      })
-    },
-
-    checkInstallerAvailable: function() {
-      var self = this;
-      $.ajax({
-        url: this.getUrl('checkInstallerAvailable'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            self.checkEnvironmentAndFolders();
-          } else {
-            self.showInstallerNotAvailable();
-          }
-        }
-      });
-    },
-
-    showInstallerNotAvailable: function() {
-      var $outputContainer = $(this.selectorMainContent);
-      $.ajax({
-        url: this.getUrl('showInstallerNotAvailable'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            $outputContainer.empty().append(data.html);
-          }
-        }
-      });
-    },
-
-    checkEnvironmentAndFolders: function() {
-      var self = this;
-      this.setProgress(1);
-      $.ajax({
-        url: this.getUrl('checkEnvironmentAndFolders'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            self.checkTrustedHostsPattern();
-          } else {
-            self.showEnvironmentAndFolders();
-          }
-        }
-      });
-    },
-
-    showEnvironmentAndFolders: function() {
-      var $outputContainer = $(this.selectorMainContent);
-      $.ajax({
-        url: this.getUrl('showEnvironmentAndFolders'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            $outputContainer.empty().html(data.html);
-            var $detailContainer = $('.t3js-installer-environment-details');
-            var hasMessage = false;
-            if (Array.isArray(data.environmentStatusErrors)) {
-              data.environmentStatusErrors.forEach(function(element) {
-                hasMessage = true;
-                var message = InfoBox.render(element.severity, element.title, element.message);
-                $detailContainer.append(message);
-              });
-            }
-            if (Array.isArray(data.environmentStatusWarnings)) {
-              data.environmentStatusWarnings.forEach(function(element) {
-                hasMessage = true;
-                var message = InfoBox.render(element.severity, element.title, element.message);
-                $detailContainer.append(message);
-              });
-            }
-            if (Array.isArray(data.structureErrors)) {
-              data.structureErrors.forEach(function(element) {
-                hasMessage = true;
-                var message = InfoBox.render(element.severity, element.title, element.message);
-                $detailContainer.append(message);
-              });
-            }
-            if (hasMessage === true) {
-              $detailContainer.show();
-              $('.t3js-installer-environmentFolders-bad').show();
-            } else {
-              $('.t3js-installer-environmentFolders-good').show();
-            }
-          }
-        }
-      });
-    },
-
-    executeEnvironmentAndFolders: function() {
-      var self = this;
-      $.ajax({
-        url: this.getUrl('executeEnvironmentAndFolders'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            self.checkTrustedHostsPattern();
-          } else {
-            // @todo message output handling
-          }
-        }
-      });
-    },
-
-    checkTrustedHostsPattern: function() {
-      var self = this;
-      $.ajax({
-        url: this.getUrl('checkTrustedHostsPattern'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            self.executeSilentConfigurationUpdate();
-          } else {
-            self.executeAdjustTrustedHostsPattern();
-          }
-        }
-      });
-    },
-
-    executeAdjustTrustedHostsPattern: function() {
-      var self = this;
-      $.ajax({
-        url: this.getUrl('executeAdjustTrustedHostsPattern'),
-        cache: false,
-        success: function(data) {
-          self.executeSilentConfigurationUpdate();
-        }
-      });
-    },
-
-    executeSilentConfigurationUpdate: function() {
-      var self = this;
-      $.ajax({
-        url: this.getUrl('executeSilentConfigurationUpdate'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            self.checkDatabaseConnect();
-          } else {
-            self.executeSilentConfigurationUpdate();
-          }
-        }
-      });
-    },
-
-    checkDatabaseConnect: function() {
-      this.setProgress(2);
-      var self = this;
-      $.ajax({
-        url: this.getUrl('checkDatabaseConnect'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            self.checkDatabaseSelect();
-          } else {
-            self.showDatabaseConnect();
-          }
-        }
-      });
-    },
-
-    showDatabaseConnect: function() {
-      var $outputContainer = $(this.selectorMainContent);
-      $.ajax({
-        url: this.getUrl('showDatabaseConnect'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            $outputContainer.empty().html(data.html);
-            $('#t3js-connect-database-driver').trigger('change');
-          }
-        }
-      });
-    },
-
-    executeDatabaseConnect: function() {
-      var self = this;
-      var $outputContainer = $(this.selectorDatabaseConnectOutput);
-      var postData = {
-        'install[action]': 'executeDatabaseConnect',
-        'install[token]': $(self.selectorModuleContent).data('installer-database-connect-execute-token')
-      };
-      $($(this.selectorBody + ' form').serializeArray()).each(function() {
-        postData[this.name] = this.value;
-      });
-      $.ajax({
-        url: this.getUrl(),
-        cache: false,
-        method: 'POST',
-        data: postData,
-        success: function(data) {
-          if (data.success === true) {
-            self.checkDatabaseSelect();
-          } else {
-            if (Array.isArray(data.status)) {
-              data.status.forEach(function(element) {
-                var message = InfoBox.render(element.severity, element.title, element.message);
-                $outputContainer.empty().append(message);
-              });
-            }
-          }
-        }
-      });
-    },
-
-    checkDatabaseSelect: function() {
-      var self = this;
-      this.setProgress(3);
-      $.ajax({
-        url: this.getUrl('checkDatabaseSelect'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            self.checkDatabaseData();
-          } else {
-            self.showDatabaseSelect();
-          }
-        }
-      });
-    },
-
-    showDatabaseSelect: function() {
-      var $outputContainer = $(this.selectorMainContent);
-      $.ajax({
-        url: this.getUrl('showDatabaseSelect'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            $outputContainer.empty().html(data.html);
-          }
-        }
-      });
-    },
-
-    executeDatabaseSelect: function() {
-      var self = this;
-      var $outputContainer = $(this.selectorDatabaseSelectOutput);
-      var postData = {
-        'install[action]': 'executeDatabaseSelect',
-        'install[token]': $(self.selectorModuleContent).data('installer-database-select-execute-token')
-      };
-      $($(this.selectorBody + ' form').serializeArray()).each(function() {
-        postData[this.name] = this.value;
-      });
-      $.ajax({
-        url: this.getUrl(),
-        cache: false,
-        method: 'POST',
-        data: postData,
-        success: function(data) {
-          if (data.success === true) {
-            self.checkDatabaseData();
-          } else {
-            if (Array.isArray(data.status)) {
-              data.status.forEach(function(element) {
-                var message = InfoBox.render(element.severity, element.title, element.message);
-                $outputContainer.empty().append(message);
-              });
-            }
-          }
-        }
-      });
-    },
-
-    checkDatabaseData: function() {
-      var self = this;
-      this.setProgress(4);
-      $.ajax({
-        url: this.getUrl('checkDatabaseData'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            self.showDefaultConfiguration();
-          } else {
-            self.showDatabaseData();
-          }
-        }
-      });
-    },
-
-    showDatabaseData: function() {
-      var $outputContainer = $(this.selectorMainContent);
-      $.ajax({
-        url: this.getUrl('showDatabaseData'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            $outputContainer.empty().html(data.html);
-          }
-        }
-      });
-    },
-
-    executeDatabaseData: function() {
-      var self = this;
-      var $outputContainer = $(this.selectorDatabaseDataOutput);
-      var postData = {
-        'install[action]': 'executeDatabaseData',
-        'install[token]': $(self.selectorModuleContent).data('installer-database-data-execute-token')
-      };
-      $($(this.selectorBody + ' form').serializeArray()).each(function() {
-        postData[this.name] = this.value;
-      });
-      var message = ProgressBar.render(Severity.loading, 'Loading...', '');
-      $outputContainer.empty().html(message);
-      $.ajax({
-        url: this.getUrl(),
-        cache: false,
-        method: 'POST',
-        data: postData,
-        success: function(data) {
-          if (data.success === true) {
-            self.showDefaultConfiguration();
-          } else {
-            if (Array.isArray(data.status)) {
-              data.status.forEach(function(element) {
-                var message = InfoBox.render(element.severity, element.title, element.message);
-                $outputContainer.empty().append(message);
-              });
-            }
-          }
-        }
-      });
-    },
-
-    showDefaultConfiguration: function() {
-      var $outputContainer = $(this.selectorMainContent);
-      this.setProgress(5);
-      $.ajax({
-        url: this.getUrl('showDefaultConfiguration'),
-        cache: false,
-        success: function(data) {
-          if (data.success === true) {
-            $outputContainer.empty().html(data.html);
-          }
-        }
-      });
-    },
-
-    executeDefaultConfiguration: function() {
-      var self = this;
-      var postData = {
-        'install[action]': 'executeDefaultConfiguration',
-        'install[token]': $(self.selectorModuleContent).data('installer-default-configuration-execute-token')
-      };
-      $($(this.selectorBody + ' form').serializeArray()).each(function() {
-        postData[this.name] = this.value;
-      });
-      $.ajax({
-        url: this.getUrl(),
-        cache: false,
-        method: 'POST',
-        data: postData,
-        success: function(data) {
-          top.location.href = data.redirect;
-        }
-      });
-    }
-  };
-});
+define(["require","exports","jquery","./Renderable/InfoBox","./Renderable/Severity","./Renderable/ProgressBar","./Module/PasswordStrength"],function(t,e,a,s,n,r,o){"use strict";return new(function(){function t(){var t=this;this.selectorBody=".t3js-body",this.selectorModuleContent=".t3js-module-content",this.selectorMainContent=".t3js-installer-content",this.selectorProgressBar=".t3js-installer-progress",this.selectorDatabaseConnectOutput=".t3js-installer-databaseConnect-output",this.selectorDatabaseSelectOutput=".t3js-installer-databaseSelect-output",this.selectorDatabaseDataOutput=".t3js-installer-databaseData-output",this.initializeEvents(),a(function(){t.initialize()})}return t.prototype.initializeEvents=function(){var t=this;a(document).on("click",".t3js-installer-environmentFolders-retry",function(e){e.preventDefault(),t.showEnvironmentAndFolders()}),a(document).on("click",".t3js-installer-environmentFolders-execute",function(e){e.preventDefault(),t.executeEnvironmentAndFolders()}),a(document).on("click",".t3js-installer-databaseConnect-execute",function(e){e.preventDefault(),t.executeDatabaseConnect()}),a(document).on("click",".t3js-installer-databaseSelect-execute",function(e){e.preventDefault(),t.executeDatabaseSelect()}),a(document).on("click",".t3js-installer-databaseData-execute",function(e){e.preventDefault(),t.executeDatabaseData()}),a(document).on("click",".t3js-installer-defaultConfiguration-execute",function(e){e.preventDefault(),t.executeDefaultConfiguration()}),a(document).on("keyup",".t3-install-form-password-strength",function(){o.initialize(".t3-install-form-password-strength")}),a(document).on("change","#t3js-connect-database-driver",function(t){var e=a(t.currentTarget).val();a(".t3-install-driver-data").hide(),a(".t3-install-driver-data input").attr("disabled","disabled"),a("#"+e+" input").attr("disabled",null),a("#"+e).show()})},t.prototype.initialize=function(){this.setProgress(0),this.getMainLayout()},t.prototype.getUrl=function(t){var e=location.href;return e=e.replace(location.search,""),void 0!==t&&(e=e+"?install[action]="+t),e},t.prototype.setProgress=function(t){var e=a(this.selectorProgressBar),s=0;0!==t&&(s=t/5*100,e.find(".progress-bar").empty().text(t+" / 5 - "+s+"% Complete")),e.find(".progress-bar").css("width",s+"%").attr("aria-valuenow",s)},t.prototype.getMainLayout=function(){var t=this;a.ajax({url:this.getUrl("mainLayout"),cache:!1,success:function(e){a(t.selectorBody).empty().append(e.html),t.checkInstallerAvailable()}})},t.prototype.checkInstallerAvailable=function(){var t=this;a.ajax({url:this.getUrl("checkInstallerAvailable"),cache:!1,success:function(e){e.success?t.checkEnvironmentAndFolders():t.showInstallerNotAvailable()}})},t.prototype.showInstallerNotAvailable=function(){var t=a(this.selectorMainContent);a.ajax({url:this.getUrl("showInstallerNotAvailable"),cache:!1,success:function(e){!0===e.success&&t.empty().append(e.html)}})},t.prototype.checkEnvironmentAndFolders=function(){var t=this;this.setProgress(1),a.ajax({url:this.getUrl("checkEnvironmentAndFolders"),cache:!1,success:function(e){!0===e.success?t.checkTrustedHostsPattern():t.showEnvironmentAndFolders()}})},t.prototype.showEnvironmentAndFolders=function(){var t=a(this.selectorMainContent);a.ajax({url:this.getUrl("showEnvironmentAndFolders"),cache:!1,success:function(e){if(!0===e.success){t.empty().html(e.html);var n=a(".t3js-installer-environment-details"),r=!1;Array.isArray(e.environmentStatusErrors)&&e.environmentStatusErrors.forEach(function(t){r=!0;var e=s.render(t.severity,t.title,t.message);n.append(e)}),Array.isArray(e.environmentStatusWarnings)&&e.environmentStatusWarnings.forEach(function(t){r=!0;var e=s.render(t.severity,t.title,t.message);n.append(e)}),Array.isArray(e.structureErrors)&&e.structureErrors.forEach(function(t){r=!0;var e=s.render(t.severity,t.title,t.message);n.append(e)}),r?(n.show(),a(".t3js-installer-environmentFolders-bad").show()):a(".t3js-installer-environmentFolders-good").show()}}})},t.prototype.executeEnvironmentAndFolders=function(){var t=this;a.ajax({url:this.getUrl("executeEnvironmentAndFolders"),cache:!1,success:function(e){!0===e.success&&t.checkTrustedHostsPattern()}})},t.prototype.checkTrustedHostsPattern=function(){var t=this;a.ajax({url:this.getUrl("checkTrustedHostsPattern"),cache:!1,success:function(e){!0===e.success?t.executeSilentConfigurationUpdate():t.executeAdjustTrustedHostsPattern()}})},t.prototype.executeAdjustTrustedHostsPattern=function(){var t=this;a.ajax({url:this.getUrl("executeAdjustTrustedHostsPattern"),cache:!1,success:function(){t.executeSilentConfigurationUpdate()}})},t.prototype.executeSilentConfigurationUpdate=function(){var t=this;a.ajax({url:this.getUrl("executeSilentConfigurationUpdate"),cache:!1,success:function(e){!0===e.success?t.checkDatabaseConnect():t.executeSilentConfigurationUpdate()}})},t.prototype.checkDatabaseConnect=function(){var t=this;this.setProgress(2),a.ajax({url:this.getUrl("checkDatabaseConnect"),cache:!1,success:function(e){!0===e.success?t.checkDatabaseSelect():t.showDatabaseConnect()}})},t.prototype.showDatabaseConnect=function(){var t=a(this.selectorMainContent);a.ajax({url:this.getUrl("showDatabaseConnect"),cache:!1,success:function(e){!0===e.success&&(t.empty().html(e.html),a("#t3js-connect-database-driver").trigger("change"))}})},t.prototype.executeDatabaseConnect=function(){var t=this,e=a(this.selectorDatabaseConnectOutput),n={"install[action]":"executeDatabaseConnect","install[token]":a(this.selectorModuleContent).data("installer-database-connect-execute-token")};a(a(this.selectorBody+" form").serializeArray()).each(function(t,e){n[e.name]=e.value}),a.ajax({url:this.getUrl(),cache:!1,method:"POST",data:n,success:function(a){!0===a.success?t.checkDatabaseSelect():Array.isArray(a.status)&&a.status.forEach(function(t){var a=s.render(t.severity,t.title,t.message);e.empty().append(a)})}})},t.prototype.checkDatabaseSelect=function(){var t=this;this.setProgress(3),a.ajax({url:this.getUrl("checkDatabaseSelect"),cache:!1,success:function(e){!0===e.success?t.checkDatabaseData():t.showDatabaseSelect()}})},t.prototype.showDatabaseSelect=function(){var t=a(this.selectorMainContent);a.ajax({url:this.getUrl("showDatabaseSelect"),cache:!1,success:function(e){!0===e.success&&t.empty().html(e.html)}})},t.prototype.executeDatabaseSelect=function(){var t=this,e=a(this.selectorDatabaseSelectOutput),n={"install[action]":"executeDatabaseSelect","install[token]":a(this.selectorModuleContent).data("installer-database-select-execute-token")};a(a(this.selectorBody+" form").serializeArray()).each(function(t,e){n[e.name]=e.value}),a.ajax({url:this.getUrl(),cache:!1,method:"POST",data:n,success:function(a){!0===a.success?t.checkDatabaseData():Array.isArray(a.status)&&a.status.forEach(function(t){var a=s.render(t.severity,t.title,t.message);e.empty().append(a)})}})},t.prototype.checkDatabaseData=function(){var t=this;this.setProgress(4),a.ajax({url:this.getUrl("checkDatabaseData"),cache:!1,success:function(e){!0===e.success?t.showDefaultConfiguration():t.showDatabaseData()}})},t.prototype.showDatabaseData=function(){var t=a(this.selectorMainContent);a.ajax({url:this.getUrl("showDatabaseData"),cache:!1,success:function(e){!0===e.success&&t.empty().html(e.html)}})},t.prototype.executeDatabaseData=function(){var t=this,e=a(this.selectorDatabaseDataOutput),o={"install[action]":"executeDatabaseData","install[token]":a(this.selectorModuleContent).data("installer-database-data-execute-token")};a(a(this.selectorBody+" form").serializeArray()).each(function(t,e){o[e.name]=e.value});var c=r.render(n.loading,"Loading...","");e.empty().html(c),a.ajax({url:this.getUrl(),cache:!1,method:"POST",data:o,success:function(a){!0===a.success?t.showDefaultConfiguration():Array.isArray(a.status)&&a.status.forEach(function(t){var a=s.render(t.severity,t.title,t.message);e.empty().append(a)})}})},t.prototype.showDefaultConfiguration=function(){var t=a(this.selectorMainContent);this.setProgress(5),a.ajax({url:this.getUrl("showDefaultConfiguration"),cache:!1,success:function(e){!0===e.success&&t.empty().html(e.html)}})},t.prototype.executeDefaultConfiguration=function(){var t={"install[action]":"executeDefaultConfiguration","install[token]":a(this.selectorModuleContent).data("installer-default-configuration-execute-token")};a(a(this.selectorBody+" form").serializeArray()).each(function(e,a){t[a.name]=a.value}),a.ajax({url:this.getUrl(),cache:!1,method:"POST",data:t,success:function(t){top.location.href=t.redirect}})},t}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/Cache.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/Cache.js
new file mode 100644 (file)
index 0000000..38c13eb
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","TYPO3/CMS/Backend/Notification"],function(e,n,t,r,s){"use strict";return new(function(){function e(){}return e.prototype.initialize=function(e){t.ajax({url:r.getUrl("cacheClearAll","maintenance"),cache:!1,beforeSend:function(){e.addClass("disabled")},success:function(e){!0===e.success&&Array.isArray(e.status)?e.status.length>0&&e.status.forEach(function(e){s.success(e.title,e.message)}):s.error("Something went wrong clearing caches")},error:function(){s.error("Clearing caches went wrong on the server side. Check the system for broken extensions or missing database tables and try again")},complete:function(){e.removeClass("disabled")}})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/ChangeInstallToolPassword.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/ChangeInstallToolPassword.js
new file mode 100644 (file)
index 0000000..c341971
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","./PasswordStrength","TYPO3/CMS/Backend/Notification"],function(t,o,s,e,n,r){"use strict";return new(function(){function t(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.selectorChangeForm="#t3js-changeInstallToolPassword-form",this.currentModal={}}return t.prototype.initialize=function(t){var o=this;this.currentModal=t,this.getData(),t.on("submit",this.selectorChangeForm,function(t){t.preventDefault(),o.change()}),t.on("click",".t3-install-form-password-strength",function(t){n.initialize(".t3-install-form-password-strength")})},t.prototype.getData=function(){var t=this.currentModal.find(this.selectorModalBody);s.ajax({url:e.getUrl("changeInstallToolPasswordGetData"),cache:!1,success:function(o){!0===o.success?t.empty().append(o.html):r.error("Something went wrong")},error:function(o){e.handleAjaxError(o,t)}})},t.prototype.change=function(){var t=this,o=this.currentModal.find(this.selectorModalBody),n=this.currentModal.find(this.selectorModuleContent).data("install-tool-token");s.ajax({url:e.getUrl(),method:"POST",data:{install:{action:"changeInstallToolPassword",token:n,password:this.currentModal.find(".t3js-changeInstallToolPassword-password").val(),passwordCheck:this.currentModal.find(".t3js-changeInstallToolPassword-password-check").val()}},cache:!1,success:function(t){!0===t.success&&Array.isArray(t.status)?t.status.forEach(function(t){r.showMessage("",t.message,t.severity)}):r.error("Something went wrong")},error:function(t){e.handleAjaxError(t,o)},complete:function(){t.currentModal.find(".t3js-changeInstallToolPassword-password,.t3js-changeInstallToolPassword-password-check").val("")}})},t}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/ClearTables.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/ClearTables.js
new file mode 100644 (file)
index 0000000..39dd1ab
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","TYPO3/CMS/Backend/Notification"],function(t,e,r,a,s){"use strict";return new(function(){function t(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.selectorClearTrigger=".t3js-clearTables-clear",this.selectorStatsTrigger=".t3js-clearTables-stats",this.selectorOutputContainer=".t3js-clearTables-output",this.selectorStatContainer=".t3js-clearTables-stat-container",this.selectorStatTemplate=".t3js-clearTables-stat-template",this.selectorStatDescription=".t3js-clearTables-stat-description",this.selectorStatRows=".t3js-clearTables-stat-rows",this.selectorStatName=".t3js-clearTables-stat-name",this.currentModal={}}return t.prototype.initialize=function(t){var e=this;this.currentModal=t,this.getStats(),t.on("click",this.selectorStatsTrigger,function(t){t.preventDefault(),r(e.selectorOutputContainer).empty(),e.getStats()}),t.on("click",this.selectorClearTrigger,function(t){var a=r(t.target).closest(e.selectorClearTrigger).data("table");t.preventDefault(),e.clear(a)})},t.prototype.getStats=function(){var t=this,e=this.currentModal.find(this.selectorModalBody);r.ajax({url:a.getUrl("clearTablesStats"),cache:!1,success:function(r){!0===r.success?(e.empty().append(r.html),Array.isArray(r.stats)&&r.stats.length>0&&r.stats.forEach(function(r){if(r.rowCount>0){var a=e.find(t.selectorStatTemplate).clone();a.find(t.selectorStatDescription).text(r.description),a.find(t.selectorStatName).text(r.name),a.find(t.selectorStatRows).text(r.rowCount),a.find(t.selectorClearTrigger).attr("data-table",r.name),e.find(t.selectorStatContainer).append(a.html())}})):s.error("Something went wrong")},error:function(t){a.handleAjaxError(t,e)}})},t.prototype.clear=function(t){var e=this,o=this.currentModal.find(this.selectorModalBody),n=this.currentModal.find(this.selectorModuleContent).data("clear-tables-clear-token");r.ajax({url:a.getUrl(),method:"POST",context:this,data:{install:{action:"clearTablesClear",token:n,table:t}},cache:!1,success:function(t){!0===t.success&&Array.isArray(t.status)?t.status.forEach(function(t){s.success(t.message)}):s.error("Something went wrong"),e.getStats()},error:function(t){a.handleAjaxError(t,o)}})},t}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/ClearTypo3tempFiles.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/ClearTypo3tempFiles.js
new file mode 100644 (file)
index 0000000..d7d7a9d
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","TYPO3/CMS/Backend/Notification"],function(t,e,r,o,s){"use strict";return new(function(){function t(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.selectorDeleteTrigger=".t3js-clearTypo3temp-delete",this.selectorOutputContainer=".t3js-clearTypo3temp-output",this.selectorStatContainer=".t3js-clearTypo3temp-stat-container",this.selectorStatsTrigger=".t3js-clearTypo3temp-stats",this.selectorStatTemplate=".t3js-clearTypo3temp-stat-template",this.selectorStatNumberOfFiles=".t3js-clearTypo3temp-stat-numberOfFiles",this.selectorStatDirectory=".t3js-clearTypo3temp-stat-directory"}return t.prototype.initialize=function(t){var e=this;this.currentModal=t,this.getStats(),t.on("click",this.selectorStatsTrigger,function(t){t.preventDefault(),r(e.selectorOutputContainer).empty(),e.getStats()}),t.on("click",this.selectorDeleteTrigger,function(t){var o=r(t.currentTarget).data("folder"),s=r(t.currentTarget).data("storage-uid");t.preventDefault(),e.delete(o,s)})},t.prototype.getStats=function(){var t=this,e=this.currentModal.find(this.selectorModalBody);r.ajax({url:o.getUrl("clearTypo3tempFilesStats"),cache:!1,success:function(r){!0===r.success?(e.empty().append(r.html),Array.isArray(r.stats)&&r.stats.length>0&&r.stats.forEach(function(r){if(r.numberOfFiles>0){var o=e.find(t.selectorStatTemplate).clone();o.find(t.selectorStatNumberOfFiles).text(r.numberOfFiles),o.find(t.selectorStatDirectory).text(r.directory),o.find(t.selectorDeleteTrigger).attr("data-folder",r.directory),o.find(t.selectorDeleteTrigger).attr("data-storage-uid",r.storageUid),e.find(t.selectorStatContainer).append(o.html())}})):s.error("Something went wrong")},error:function(t){o.handleAjaxError(t,e)}})},t.prototype.delete=function(t,e){var a=this,i=this.currentModal.find(this.selectorModalBody),n=this.currentModal.find(this.selectorModuleContent).data("clear-typo3temp-delete-token");r.ajax({method:"POST",url:o.getUrl(),context:this,data:{install:{action:"clearTypo3tempFiles",token:n,folder:t,storageUid:e}},cache:!1,success:function(t){!0===t.success&&Array.isArray(t.status)?(t.status.forEach(function(t){s.success(t.message)}),a.getStats()):s.error("Something went wrong")},error:function(t){o.handleAjaxError(t,i)}})},t}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/CoreUpdate.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/CoreUpdate.js
new file mode 100644 (file)
index 0000000..54d029d
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","../Renderable/FlashMessage","../Renderable/Severity","TYPO3/CMS/Backend/Notification"],function(e,t,o,a,n,i,s){"use strict";return new(function(){function e(){this.actionQueue={coreUpdateIsUpdateAvailable:{loadingMessage:"Checking for possible regular or security update",finishMessage:void 0,nextActionName:void 0},coreUpdateCheckPreConditions:{loadingMessage:"Checking if update is possible",finishMessage:"System can be updated",nextActionName:"coreUpdateDownload"},coreUpdateDownload:{loadingMessage:"Downloading new core",finishMessage:void 0,nextActionName:"coreUpdateVerifyChecksum"},coreUpdateVerifyChecksum:{loadingMessage:"Verifying checksum of downloaded core",finishMessage:void 0,nextActionName:"coreUpdateUnpack"},coreUpdateUnpack:{loadingMessage:"Unpacking core",finishMessage:void 0,nextActionName:"coreUpdateMove"},coreUpdateMove:{loadingMessage:"Moving core",finishMessage:void 0,nextActionName:"coreUpdateActivate"},coreUpdateActivate:{loadingMessage:"Activating core",finishMessage:"Core updated - please reload your browser",nextActionName:void 0}},this.selectorModalBody=".t3js-modal-body",this.selectorOutput=".t3js-coreUpdate-output",this.selectorTemplate=".t3js-coreUpdate-buttonTemplate",this.buttonTemplate=null,this.currentModal=null}return e.prototype.initialize=function(t){var a=this;this.currentModal=t,this.getData().done(function(){var e=t.find(a.selectorTemplate);a.buttonTemplate=e.children().clone()}),t.on("click",".t3js-coreUpdate-init",function(n){n.preventDefault();var i=o(n.target).attr("data-action");t.find(a.selectorOutput).empty(),e.call(i)})},e.prototype.getData=function(){var e=this.currentModal.find(this.selectorModalBody);return o.ajax({url:a.getUrl("coreUpdateGetData"),cache:!1,success:function(t){!0===t.success?e.empty().append(t.html):s.error("Something went wrong")},error:function(t){a.handleAjaxError(t,e)}})},e.prototype.checkForUpdate=function(){this.callAction("coreUpdateIsUpdateAvailable")},e.prototype.updateDevelopment=function(){this.update("development")},e.prototype.updateRegular=function(){this.update("regular")},e.prototype.update=function(e){"development"!==e&&(e="regular"),this.callAction("coreUpdateCheckPreConditions",e)},e.prototype.callAction=function(e,t){var n=this,i={install:{action:e}};void 0!==t&&(i.install.type=t),this.addLoadingMessage(this.actionQueue[e].loadingMessage),o.ajax({url:a.getUrl(),data:i,cache:!1,success:function(o){!0===n.handleResult(o,n.actionQueue[e].finishMessage)&&void 0!==n.actionQueue[e].nextActionName&&n.callAction(n.actionQueue[e].nextActionName,t)},error:function(e){a.handleAjaxError(e,n.currentModal.find(n.selectorModalBody))}})},e.prototype.handleResult=function(e,t){var o=e.success;return this.removeLoadingMessage(),e.status&&"object"==typeof e.status&&this.showStatusMessages(e.status),e.action&&"object"==typeof e.action&&this.showActionButton(e.action),t&&this.addMessage(i.ok,t),o},e.prototype.addLoadingMessage=function(e){var t=n.render(i.loading,e);this.currentModal.find(this.selectorOutput).append(t)},e.prototype.removeLoadingMessage=function(){this.currentModal.find(this.selectorOutput).find(".alert-loading").remove()},e.prototype.showStatusMessages=function(e){var t=this;o.each(e,function(e,o){var a="",n="",i=o.severity;o.title&&(a=o.title),o.message&&(n=o.message),t.addMessage(i,a,n)})},e.prototype.showActionButton=function(e){var t=!1,o=!1;e.title&&(t=e.title),e.action&&(o=e.action);var a=this.buttonTemplate;o&&a.attr("data-action",o),t&&a.text(t),this.currentModal.find(this.selectorOutput).append(a)},e.prototype.addMessage=function(e,t,o){var a=n.render(e,t,o);this.currentModal.find(this.selectorOutput).append(a)},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/CreateAdmin.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/CreateAdmin.js
new file mode 100644 (file)
index 0000000..7d85165
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","./PasswordStrength","TYPO3/CMS/Backend/Notification"],function(t,e,r,n,s,a){"use strict";return new(function(){function t(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.selectorCreateForm="#t3js-createAdmin-form"}return t.prototype.initialize=function(t){var e=this;this.currentModal=t,this.getData(),t.on("submit",this.selectorCreateForm,function(t){t.preventDefault(),e.create()}),t.on("click",".t3-install-form-password-strength",function(t){s.initialize(".t3-install-form-password-strength")})},t.prototype.getData=function(){var t=this.currentModal.find(this.selectorModalBody);r.ajax({url:n.getUrl("createAdminGetData"),cache:!1,success:function(e){!0===e.success?t.empty().append(e.html):a.error("Something went wrong")},error:function(e){n.handleAjaxError(e,t)}})},t.prototype.create=function(){var t=this.currentModal.find(this.selectorModalBody),e=this.currentModal.find(this.selectorModuleContent).data("create-admin-token");r.ajax({url:n.getUrl(),method:"POST",data:{install:{action:"createAdmin",token:e,userName:this.currentModal.find(".t3js-createAdmin-user").val(),userPassword:this.currentModal.find(".t3js-createAdmin-password").val(),userPasswordCheck:this.currentModal.find(".t3js-createAdmin-password-check").val(),userSystemMaintainer:this.currentModal.find(".t3js-createAdmin-system-maintainer").is(":checked")?1:0}},cache:!1,success:function(t){!0===t.success&&Array.isArray(t.status)?t.status.forEach(function(t){2===t.severity?a.error(t.message):a.success(t.title)}):a.error("Something went wrong")},error:function(e){n.handleAjaxError(e,t)}}),this.currentModal.find(".t3js-createAdmin-user").val(""),this.currentModal.find(".t3js-createAdmin-password").val(""),this.currentModal.find(".t3js-createAdmin-password-check").val(""),this.currentModal.find(".t3js-createAdmin-system-maintainer").prop("checked",!1)},t}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/DatabaseAnalyzer.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/DatabaseAnalyzer.js
new file mode 100644 (file)
index 0000000..86fcc72
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","../Renderable/ProgressBar","../Renderable/InfoBox","../Renderable/Severity","TYPO3/CMS/Backend/Notification"],function(e,t,a,n,s,r,o,i){"use strict";return new(function(){function e(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.selectorAnalyzeTrigger=".t3js-databaseAnalyzer-analyze",this.selectorExecuteTrigger=".t3js-databaseAnalyzer-execute",this.selectorOutputContainer=".t3js-databaseAnalyzer-output",this.selectorSuggestionBlock=".t3js-databaseAnalyzer-suggestion-block",this.selectorSuggestionList=".t3js-databaseAnalyzer-suggestion-list",this.selectorSuggestionLineTemplate=".t3js-databaseAnalyzer-suggestion-line-template"}return e.prototype.initialize=function(e){var t=this;this.currentModal=e,this.getData(),e.on("click",".t3js-databaseAnalyzer-suggestion-block-checkbox",function(e){var t=a(e.currentTarget);t.closest("fieldset").find(":checkbox").prop("checked",t.get(0).checked)}),e.on("click",this.selectorAnalyzeTrigger,function(e){e.preventDefault(),t.analyze()}),e.on("click",this.selectorExecuteTrigger,function(e){e.preventDefault(),t.execute()})},e.prototype.getData=function(){var e=this,t=this.currentModal.find(this.selectorModalBody);a.ajax({url:n.getUrl("databaseAnalyzer"),cache:!1,success:function(a){!0===a.success?(t.empty().append(a.html),e.analyze()):i.error("Something went wrong")},error:function(e){n.handleAjaxError(e,t)}})},e.prototype.analyze=function(){var e=this,t=this.currentModal.find(this.selectorModalBody),l=t.find(this.selectorOutputContainer),c=t.find(this.selectorExecuteTrigger),d=t.find(this.selectorAnalyzeTrigger);l.empty().append(s.render(o.loading,"Analyzing current database schema...","")),d.prop("disabled",!0),c.prop("disabled",!0),l.on("change",'input[type="checkbox"]',function(){var e=l.find(":checked").length>0;c.prop("disabled",!e)}),a.ajax({url:n.getUrl("databaseAnalyzerAnalyze"),cache:!1,success:function(a){if(!0===a.success){if(Array.isArray(a.status)&&(l.find(".alert-loading").remove(),a.status.forEach(function(e){var t=r.render(e.severity,e.title,e.message);l.append(t)})),Array.isArray(a.suggestions)){a.suggestions.forEach(function(a){var n=t.find(e.selectorSuggestionBlock).clone();n.removeClass(e.selectorSuggestionBlock.substr(1));var s=a.key;n.find(".t3js-databaseAnalyzer-suggestion-block-legend").text(a.label),n.find(".t3js-databaseAnalyzer-suggestion-block-checkbox").attr("id","t3-install-"+s+"-checkbox"),a.enabled&&n.find(".t3js-databaseAnalyzer-suggestion-block-checkbox").attr("checked","checked"),n.find(".t3js-databaseAnalyzer-suggestion-block-label").attr("for","t3-install-"+s+"-checkbox"),a.children.forEach(function(s){var r=t.find(e.selectorSuggestionLineTemplate).children().clone(),o=s.hash,i=r.find(".t3js-databaseAnalyzer-suggestion-line-checkbox");i.attr("id","t3-install-db-"+o).attr("data-hash",o),a.enabled&&i.attr("checked","checked"),r.find(".t3js-databaseAnalyzer-suggestion-line-label").attr("for","t3-install-db-"+o),r.find(".t3js-databaseAnalyzer-suggestion-line-statement").text(s.statement),void 0!==s.current&&(r.find(".t3js-databaseAnalyzer-suggestion-line-current-value").text(s.current),r.find(".t3js-databaseAnalyzer-suggestion-line-current").show()),void 0!==s.rowCount&&(r.find(".t3js-databaseAnalyzer-suggestion-line-count-value").text(s.rowCount),r.find(".t3js-databaseAnalyzer-suggestion-line-count").show()),n.find(e.selectorSuggestionList).append(r)}),l.append(n.html())});var n=0===l.find(":checked").length;d.prop("disabled",!1),c.prop("disabled",n)}0===a.suggestions.length&&0===a.status.length&&l.append(r.render(o.ok,"Database schema is up to date. Good job!",""))}else i.error("Something went wrong")},error:function(e){n.handleAjaxError(e,t)}})},e.prototype.execute=function(){var e=this,t=this.currentModal.find(this.selectorModalBody),r=this.currentModal.find(this.selectorModuleContent).data("database-analyzer-execute-token"),l=t.find(this.selectorOutputContainer),c=[];l.find(".t3js-databaseAnalyzer-suggestion-line input:checked").each(function(e,t){c.push(a(t).data("hash"))}),l.empty().append(s.render(o.loading,"Executing database updates...","")),t.find(this.selectorExecuteTrigger).prop("disabled",!0),t.find(this.selectorAnalyzeTrigger).prop("disabled",!0),a.ajax({url:n.getUrl(),method:"POST",data:{install:{action:"databaseAnalyzerExecute",token:r,hashes:c}},cache:!1,success:function(t){!0===t.success&&Array.isArray(t.status)&&t.status.forEach(function(e){i.showMessage(e.title,e.message,e.severity)}),e.analyze()},error:function(e){n.handleAjaxError(e,t)}})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/DumpAutoload.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/DumpAutoload.js
new file mode 100644 (file)
index 0000000..891ab56
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","TYPO3/CMS/Backend/Notification"],function(e,n,t,r,s){"use strict";return new(function(){function e(){}return e.prototype.initialize=function(e){t.ajax({url:r.getUrl("dumpAutoload"),cache:!1,beforeSend:function(){e.addClass("disabled")},success:function(e){!0===e.success&&Array.isArray(e.status)?e.status.length>0&&e.status.forEach(function(e){s.success(e.message)}):s.error("Something went wrong")},error:function(){s.error("Dumping autoload files went wrong on the server side. Check the system for broken extensions and try again")},complete:function(){e.removeClass("disabled")}})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/EnvironmentCheck.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/EnvironmentCheck.js
new file mode 100644 (file)
index 0000000..4c97fe3
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","../Renderable/ProgressBar","../Renderable/InfoBox","../Renderable/Severity","TYPO3/CMS/Backend/Notification","bootstrap"],function(e,t,r,n,s,o,i,a){"use strict";return new(function(){function e(){this.selectorModalBody=".t3js-modal-body",this.selectorGridderBadge=".t3js-environmentCheck-badge",this.selectorExecuteTrigger=".t3js-environmentCheck-execute",this.selectorOutputContainer=".t3js-environmentCheck-output"}return e.prototype.initialize=function(e){var t=this;this.currentModal=e,this.runTests(),e.on("click",this.selectorExecuteTrigger,function(e){e.preventDefault(),t.runTests()})},e.prototype.runTests=function(){var e=this,t=this.currentModal.find(this.selectorModalBody),c=r(this.selectorGridderBadge);c.text("").hide();var l=s.render(i.loading,"Loading...","");t.find(this.selectorOutputContainer).empty().append(l),r.ajax({url:n.getUrl("environmentCheckGetStatus"),cache:!1,success:function(n){t.empty().append(n.html);var s=0,i=0;!0===n.success&&"object"==typeof n.status?(r.each(n.status,function(r,n){Array.isArray(n)&&n.length>0&&n.forEach(function(r){1===r.severity&&s++,2===r.severity&&i++;var n=o.render(r.severity,r.title,r.message);t.find(e.selectorOutputContainer).append(n)})}),i>0?c.removeClass("label-warning").addClass("label-danger").text(i).show():s>0&&c.removeClass("label-error").addClass("label-warning").text(s).show()):a.error("Something went wrong")},error:function(e){n.handleAjaxError(e,t)}})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/ExtensionCompatTester.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/ExtensionCompatTester.js
new file mode 100644 (file)
index 0000000..f0ef31c
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","../Renderable/ProgressBar","../Renderable/InfoBox","../Renderable/Severity","TYPO3/CMS/Backend/Notification","bootstrap"],function(e,t,n,o,r,i,s,a){"use strict";return new(function(){function e(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.selectorCheckTrigger=".t3js-extensionCompatTester-check",this.selectorUninstallTrigger=".t3js-extensionCompatTester-uninstall",this.selectorOutputContainer=".t3js-extensionCompatTester-output"}return e.prototype.initialize=function(e){var t=this;this.currentModal=e,this.getLoadedExtensionList(),e.on("click",this.selectorCheckTrigger,function(n){e.find(t.selectorUninstallTrigger).hide(),e.find(t.selectorOutputContainer).empty(),t.getLoadedExtensionList()}),e.on("click",this.selectorUninstallTrigger,function(e){t.uninstallExtension(n(e.target).data("extension"))})},e.prototype.getLoadedExtensionList=function(){var e=this;this.currentModal.find(this.selectorCheckTrigger).prop("disabled",!0),this.currentModal.find(".modal-loading").hide();var t=this.currentModal.find(this.selectorModalBody),l=this.currentModal.find(this.selectorOutputContainer),d=r.render(s.loading,"Loading...","");l.append(d),n.ajax({url:o.getUrl("extensionCompatTesterLoadedExtensionList"),cache:!1,success:function(o){t.empty().append(o.html);var l,d,c=e.currentModal.find(e.selectorOutputContainer),u=r.render(s.loading,"Loading...","");if(c.append(u),!0===o.success&&Array.isArray(o.extensions)){n.when((d=[],o.extensions.forEach(function(t){d.push(e.loadExtLocalconf(t))}),n.when.apply(n,d).done(function(){var e=i.render(s.ok,"ext_localconf.php of all loaded extensions successfully loaded","");c.append(e)})),(l=[],o.extensions.forEach(function(t){l.push(e.loadExtTables(t))}),n.when.apply(n,l).done(function(){var e=i.render(s.ok,"ext_tables.php of all loaded extensions successfully loaded","");c.append(e)}))).fail(function(n){var o=i.render(s.error,"Loading "+n.scope+' of extension "'+n.extension+'" failed');c.append(o),t.find(e.selectorUninstallTrigger).text('Unload extension "'+n.extension+'"').attr("data-extension",n.extension).show()}).always(function(){c.find(".alert-loading").remove(),e.currentModal.find(e.selectorCheckTrigger).prop("disabled",!1)})}else a.error("Something went wrong")},error:function(e){o.handleAjaxError(e,t)}})},e.prototype.loadExtLocalconf=function(e){var t=this.currentModal.find(this.selectorModuleContent).data("extension-compat-tester-load-ext_localconf-token");return n.ajax({url:o.getUrl(),method:"POST",cache:!1,data:{install:{action:"extensionCompatTesterLoadExtLocalconf",token:t,extension:e}}}).promise().then(null,function(){throw{scope:"ext_localconf.php",extension:e}})},e.prototype.loadExtTables=function(e){var t=this.currentModal.find(this.selectorModuleContent).data("extension-compat-tester-load-ext_tables-token");return n.ajax({url:o.getUrl(),method:"POST",cache:!1,data:{install:{action:"extensionCompatTesterLoadExtTables",token:t,extension:e}}}).promise().then(null,function(){throw{scope:"ext_tables.php",extension:e}})},e.prototype.uninstallExtension=function(e){var t=this,l=this.currentModal.find(this.selectorModuleContent).data("extension-compat-tester-uninstall-extension-token"),d=this.currentModal.find(this.selectorModalBody),c=n(this.selectorOutputContainer),u=r.render(s.loading,"Loading...","");c.append(u),n.ajax({url:o.getUrl(),cache:!1,method:"POST",data:{install:{action:"extensionCompatTesterUninstallExtension",token:l,extension:e}},success:function(e){e.success?(Array.isArray(e.status)&&e.status.forEach(function(e){var n=i.render(e.severity,e.title,e.message);d.find(t.selectorOutputContainer).empty().append(n)}),n(t.selectorUninstallTrigger).hide(),t.getLoadedExtensionList()):a.error("Something went wrong")},error:function(e){o.handleAjaxError(e,d)}})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/ExtensionConfiguration.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/ExtensionConfiguration.js
new file mode 100644 (file)
index 0000000..6d66046
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","TYPO3/CMS/Backend/Notification","bootstrap"],function(t,e,a,r,n){"use strict";return new(function(){function e(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.selectorFormListener=".t3js-extensionConfiguration-form",this.selectorSearchInput=".t3js-extensionConfiguration-search"}return e.prototype.initialize=function(e){var r=this;this.currentModal=e,this.getContent(),e.on("keydown",function(t){var a=e.find(r.selectorSearchInput);t.ctrlKey||t.metaKey?"f"===String.fromCharCode(t.which).toLowerCase()&&(t.preventDefault(),a.focus()):27===t.keyCode&&(t.preventDefault(),a.val("").focus())}),e.on("keyup",this.selectorSearchInput,function(n){var i=a(n.target).val(),o=e.find(r.selectorSearchInput);e.find(".search-item").each(function(t,e){var r=a(e);a(":contains("+i+")",r).length>0||a('input[value*="'+i+'"]',r).length>0?r.removeClass("hidden").addClass("searchhit"):r.removeClass("searchhit").addClass("hidden")}),e.find(".searchhit").collapse("show"),t(["jquery.clearable"],function(){o.clearable().focus()})}),e.on("submit",this.selectorFormListener,function(t){t.preventDefault(),r.write(a(t.currentTarget))})},e.prototype.getContent=function(){var t=this,e=this.currentModal.find(this.selectorModalBody);a.ajax({url:r.getUrl("extensionConfigurationGetContent"),cache:!1,success:function(a){!0===a.success&&(Array.isArray(a.status)&&a.status.forEach(function(t){n.success(t.title,t.message)}),e.html(a.html),t.initializeWrap())},error:function(t){r.handleAjaxError(t,e)}})},e.prototype.write=function(t){var e=this.currentModal.find(this.selectorModalBody),i=this.currentModal.find(this.selectorModuleContent).data("extension-configuration-write-token"),o={};a.each(t.serializeArray(),function(t,e){o[e.name]=e.value}),a.ajax({url:r.getUrl(),method:"POST",data:{install:{token:i,action:"extensionConfigurationWrite",extensionKey:t.attr("data-extensionKey"),extensionConfiguration:o}},success:function(t){!0===t.success&&Array.isArray(t.status)?t.status.forEach(function(t){n.showMessage(t.title,t.message,t.severity)}):n.error("Something went wrong")},error:function(t){r.handleAjaxError(t,e)}}).always(function(){})},e.prototype.initializeWrap=function(){this.currentModal.find(".t3js-emconf-offset").each(function(t,e){var r=a(e),n=r.parent(),i=r.attr("id"),o=r.attr("value").split(",");r.attr("data-offsetfield-x","#"+i+"_offset_x").attr("data-offsetfield-y","#"+i+"_offset_y").wrap('<div class="hidden"></div>');var s=a("<div>",{class:"form-multigroup-item"}).append(a("<div>",{class:"input-group"}).append(a("<div>",{class:"input-group-addon"}).text("x"),a("<input>",{id:i+"_offset_x",class:"form-control t3js-emconf-offsetfield","data-target":"#"+i,value:a.trim(o[0])}))),d=a("<div>",{class:"form-multigroup-item"}).append(a("<div>",{class:"input-group"}).append(a("<div>",{class:"input-group-addon"}).text("y"),a("<input>",{id:i+"_offset_y",class:"form-control t3js-emconf-offsetfield","data-target":"#"+i,value:a.trim(o[1])}))),f=a("<div>",{class:"form-multigroup-wrap"}).append(s,d);n.append(f),n.find(".t3js-emconf-offsetfield").keyup(function(t){var e=n.find(a(t.currentTarget).data("target"));e.val(n.find(e.data("offsetfield-x")).val()+","+n.find(e.data("offsetfield-y")).val())})}),this.currentModal.find(".t3js-emconf-wrap").each(function(t,e){var r=a(e),n=r.parent(),i=r.attr("id"),o=r.attr("value").split("|");r.attr("data-wrapfield-start","#"+i+"_wrap_start").attr("data-wrapfield-end","#"+i+"_wrap_end").wrap('<div class="hidden"></div>');var s=a("<div>",{class:"form-multigroup-wrap"}).append(a("<div>",{class:"form-multigroup-item"}).append(a("<input>",{id:i+"_wrap_start",class:"form-control t3js-emconf-wrapfield","data-target":"#"+i,value:a.trim(o[0])})),a("<div>",{class:"form-multigroup-item"}).append(a("<input>",{id:i+"_wrap_end",class:"form-control t3js-emconf-wrapfield","data-target":"#"+i,value:a.trim(o[1])})));n.append(s),n.find(".t3js-emconf-wrapfield").keyup(function(t){var e=n.find(a(t.currentTarget).data("target"));e.val(n.find(e.data("wrapfield-start")).val()+"|"+n.find(e.data("wrapfield-end")).val())})})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/ExtensionScanner.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/ExtensionScanner.js
new file mode 100644 (file)
index 0000000..a7f902a
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Ajax/AjaxQueue","../Router","TYPO3/CMS/Backend/Notification","bootstrap"],function(e,n,t,s,i,a){"use strict";return new(function(){function e(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.listOfAffectedRestFileHashes=[],this.selectorExtensionContainer=".t3js-extensionScanner-extension",this.selectorNumberOfFiles=".t3js-extensionScanner-number-of-files",this.selectorScanSingleTrigger=".t3js-extensionScanner-scan-single"}return e.prototype.initialize=function(e){var n=this;this.currentModal=e,this.getData(),e.on("show.bs.collapse",this.selectorExtensionContainer,function(e){var s=t(e.currentTarget);if(void 0===s.data("scanned")){var i=s.data("extension");n.scanSingleExtension(i),s.data("scanned",!0)}}).on("click",this.selectorScanSingleTrigger,function(e){e.preventDefault();var s=t(e.currentTarget).closest(n.selectorExtensionContainer).data("extension");n.scanSingleExtension(s)}).on("click",".t3js-extensionScanner-scan-all",function(t){t.preventDefault();var s=e.find(n.selectorExtensionContainer);n.scanAll(s)})},e.prototype.getData=function(){var e=this.currentModal.find(this.selectorModalBody);s.add({url:i.getUrl("extensionScannerGetData"),cache:!1,success:function(n){!0===n.success?e.empty().append(n.html):a.error("Something went wrong")},error:function(n){i.handleAjaxError(n,e)}})},e.prototype.getExtensionSelector=function(e){return this.selectorExtensionContainer+"-"+e},e.prototype.scanAll=function(e){var n=this;this.currentModal.find(this.selectorExtensionContainer).removeClass("panel-danger panel-warning panel-success").find(".panel-progress-bar").css("width",0).attr("aria-valuenow",0).find("span").text("0%"),this.setProgressForAll(),e.each(function(e,s){var i=t(s),a=i.data("extension");n.scanSingleExtension(a),i.data("scanned",!0)})},e.prototype.setStatusMessageForScan=function(e,n,t){this.currentModal.find(this.getExtensionSelector(e)).find(this.selectorNumberOfFiles).text("Checked "+n+" of "+t+" files")},e.prototype.setProgressForScan=function(e,n,t){var s=n/t*100;this.currentModal.find(this.getExtensionSelector(e)).find(".panel-progress-bar").css("width",s+"%").attr("aria-valuenow",s).find("span").text(s+"%")},e.prototype.setProgressForAll=function(){var e=this.currentModal.find(this.selectorExtensionContainer).length,n=this.currentModal.find(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-success").length+this.currentModal.find(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-warning").length+this.currentModal.find(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-danger").length,t=n/e*100,o=this.currentModal.find(this.selectorModalBody);this.currentModal.find(".t3js-extensionScanner-progress-all-extension .progress-bar").css("width",t+"%").attr("aria-valuenow",t).find("span").text(n+" of "+e+" scanned"),n===e&&(a.success("Scan finished","All extensions have been scanned"),s.add({url:i.getUrl(),method:"POST",data:{install:{action:"extensionScannerMarkFullyScannedRestFiles",token:this.currentModal.find(this.selectorModuleContent).data("extension-scanner-mark-fully-scanned-rest-files-token"),hashes:this.uniqueArray(this.listOfAffectedRestFileHashes)}},cache:!1,success:function(e){!0===e.success&&a.success("Marked not affected files","Marked "+e.markedAsNotAffected+" ReST files as not affected.")},error:function(e){i.handleAjaxError(e,o)}}))},e.prototype.uniqueArray=function(e){return e.filter(function(e,n,t){return t.indexOf(e)===n})},e.prototype.scanSingleExtension=function(e){var n=this,o=this.currentModal.find(this.selectorModuleContent).data("extension-scanner-files-token"),r=this.currentModal.find(this.selectorModalBody),l=this.currentModal.find(this.getExtensionSelector(e)),c=!1;l.removeClass("panel-danger panel-warning panel-success t3js-extensionscan-finished"),l.data("hasRun","true"),l.find(".t3js-extensionScanner-scan-single").text("Scanning...").attr("disabled","disabled"),l.find(".t3js-extensionScanner-extension-body-loc").empty().text("0"),l.find(".t3js-extensionScanner-extension-body-ignored-files").empty().text("0"),l.find(".t3js-extensionScanner-extension-body-ignored-lines").empty().text("0"),this.setProgressForAll(),s.add({url:i.getUrl(),method:"POST",data:{install:{action:"extensionScannerFiles",token:o,extension:e}},cache:!1,success:function(o){if(!0===o.success&&Array.isArray(o.files)){var d=o.files.length;if(d>0){n.setStatusMessageForScan(e,0,d),l.find(".t3js-extensionScanner-extension-body").text("");var f=0;o.files.forEach(function(o){s.add({method:"POST",data:{install:{action:"extensionScannerScanFile",token:n.currentModal.find(n.selectorModuleContent).data("extension-scanner-scan-file-token"),extension:e,file:o}},url:i.getUrl(),cache:!1,success:function(s){if(f++,n.setStatusMessageForScan(e,f,d),n.setProgressForScan(e,f,d),s.success&&t.isArray(s.matches)&&s.matches.forEach(function(e){c=!0;var s=r.find("#t3js-extensionScanner-file-hit-template").clone();s.find(".t3js-extensionScanner-hit-file-panel-head").attr("href","#collapse"+e.uniqueId),s.find(".t3js-extensionScanner-hit-file-panel-body").attr("id","collapse"+e.uniqueId),s.find(".t3js-extensionScanner-hit-filename").text(o),s.find(".t3js-extensionScanner-hit-message").text(e.message),"strong"===e.indicator?s.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge" title="Reliable match, false positive unlikely">strong</span>'):s.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge" title="Probable match, but can be a false positive">weak</span>'),!0===e.silenced&&s.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>'),s.find(".t3js-extensionScanner-hit-file-lineContent").empty().text(e.lineContent),s.find(".t3js-extensionScanner-hit-file-line").empty().text(e.line+": "),t.isArray(e.restFiles)&&e.restFiles.forEach(function(e){var t=r.find("#t3js-extensionScanner-file-hit-rest-template").clone();t.find(".t3js-extensionScanner-hit-rest-panel-head").attr("href","#collapse"+e.uniqueId),t.find(".t3js-extensionScanner-hit-rest-panel-head .badge").empty().text(e.version),t.find(".t3js-extensionScanner-hit-rest-panel-body").attr("id","collapse"+e.uniqueId),t.find(".t3js-extensionScanner-hit-rest-headline").text(e.headline),t.find(".t3js-extensionScanner-hit-rest-body").text(e.content),t.addClass("panel-"+e.class),s.find(".t3js-extensionScanner-hit-file-rest-container").append(t),n.listOfAffectedRestFileHashes.push(e.file_hash)});var i=s.find(".panel-breaking",".t3js-extensionScanner-hit-file-rest-container").length>0?"panel-danger":"panel-warning";s.addClass(i),l.find(".t3js-extensionScanner-extension-body").removeClass("hide").append(s),"panel-danger"===i&&l.removeClass("panel-warning").addClass(i),"panel-warning"!==i||l.hasClass("panel-danger")||l.addClass(i)}),s.success){var i=parseInt(l.find(".t3js-extensionScanner-extension-body-loc").text(),10);if(l.find(".t3js-extensionScanner-extension-body-loc").empty().text(i+s.effectiveCodeLines),s.isFileIgnored){var a=parseInt(l.find(".t3js-extensionScanner-extension-body-ignored-files").text(),10);l.find(".t3js-extensionScanner-extension-body-ignored-files").empty().text(a+1)}var h=parseInt(l.find(".t3js-extensionScanner-extension-body-ignored-lines").text(),10);l.find(".t3js-extensionScanner-extension-body-ignored-lines").empty().text(h+s.ignoredLines)}f===d&&(c||l.addClass("panel-success"),l.addClass("t3js-extensionscan-finished"),n.setProgressForAll(),l.find(".t3js-extensionScanner-scan-single").text("Rescan").attr("disabled",null))},error:function(t){f+=1,n.setStatusMessageForScan(e,f,d),n.setProgressForScan(e,f,d),n.setProgressForAll(),a.error("Oops, an error occurred","Please look at the console output for details"),console.error(t)}})})}else a.warning("No files found","The extension EXT:"+e+" contains no files we can scan")}else a.error("Oops, an error occurred","Please look at the console output for details"),console.error(o)},error:function(e){i.handleAjaxError(e,r)}})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/Features.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/Features.js
new file mode 100644 (file)
index 0000000..72dd49b
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","TYPO3/CMS/Backend/Notification"],function(e,t,r,n,o){"use strict";return new(function(){function e(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-features-content",this.selectorSaveTrigger=".t3js-features-save"}return e.prototype.initialize=function(e){var t=this;this.currentModal=e,this.getContent(),e.on("click",this.selectorSaveTrigger,function(e){e.preventDefault(),t.save()})},e.prototype.getContent=function(){var e=this.currentModal.find(this.selectorModalBody);r.ajax({url:n.getUrl("featuresGetContent"),cache:!1,success:function(t){!0===t.success&&"undefined"!==t.html&&t.html.length>0?e.empty().append(t.html):o.error("Something went wrong")},error:function(t){n.handleAjaxError(t,e)}})},e.prototype.save=function(){var e=this.currentModal.find(this.selectorModalBody),t=this.currentModal.find(this.selectorModuleContent).data("features-save-token"),s={};r(this.currentModal.find(this.selectorModuleContent+" form").serializeArray()).each(function(e,t){s[t.name]=t.value}),s["install[action]"]="featuresSave",s["install[token]"]=t,r.ajax({url:n.getUrl(),method:"POST",data:s,cache:!1,success:function(e){!0===e.success&&Array.isArray(e.status)?e.status.forEach(function(e){o.showMessage(e.title,e.message,e.severity)}):o.error("Something went wrong")},error:function(t){n.handleAjaxError(t,e)}})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/FolderStructure.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/FolderStructure.js
new file mode 100644 (file)
index 0000000..c225ad7
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","../Renderable/ProgressBar","../Renderable/InfoBox","../Renderable/Severity","TYPO3/CMS/Backend/Notification","bootstrap"],function(e,r,t,o,s,i,n,a){"use strict";return new(function(){function e(){this.selectorModalBody=".t3js-modal-body",this.selectorGridderBadge=".t3js-folderStructure-badge",this.selectorOutputContainer=".t3js-folderStructure-output",this.selectorErrorContainer=".t3js-folderStructure-errors",this.selectorErrorList=".t3js-folderStructure-errors-list",this.selectorErrorFixTrigger=".t3js-folderStructure-errors-fix",this.selectorOkContainer=".t3js-folderStructure-ok",this.selectorOkList=".t3js-folderStructure-ok-list",this.selectorPermissionContainer=".t3js-folderStructure-permissions"}return e.removeLoadingMessage=function(e){e.find(".alert-loading").remove()},e.prototype.initialize=function(e){var r=this;this.currentModal=e,this.getStatus(),e.on("click",this.selectorErrorFixTrigger,function(e){e.preventDefault(),r.fix()})},e.prototype.getStatus=function(){var e=this,r=this.currentModal.find(this.selectorModalBody),a=t(this.selectorGridderBadge);a.text("").hide(),r.find(this.selectorOutputContainer).empty().append(s.render(n.loading,"Loading...","")),t.ajax({url:o.getUrl("folderStructureGetStatus"),cache:!1,success:function(t){if(r.empty().append(t.html),!0===t.success&&Array.isArray(t.errorStatus)){var o=0;t.errorStatus.length>0?(r.find(e.selectorErrorContainer).show(),r.find(e.selectorErrorList).empty(),t.errorStatus.forEach(function(t){o++,a.text(o).show();var s=i.render(t.severity,t.title,t.message);r.find(e.selectorErrorList).append(s)})):r.find(e.selectorErrorContainer).hide()}!0===t.success&&Array.isArray(t.okStatus)&&(t.okStatus.length>0?(r.find(e.selectorOkContainer).show(),r.find(e.selectorOkList).empty(),t.okStatus.forEach(function(t){var o=i.render(t.severity,t.title,t.message);r.find(e.selectorOkList).append(o)})):r.find(e.selectorOkContainer).hide());var s=t.folderStructureFilePermissionStatus;r.find(e.selectorPermissionContainer).empty().append(i.render(s.severity,s.title,s.message)),s=t.folderStructureDirectoryPermissionStatus,r.find(e.selectorPermissionContainer).append(i.render(s.severity,s.title,s.message))},error:function(e){o.handleAjaxError(e,r)}})},e.prototype.fix=function(){var r=this,d=this.currentModal.find(this.selectorModalBody),u=this.currentModal.find(this.selectorOutputContainer),c=s.render(n.loading,"Loading...","");u.empty().html(c),t.ajax({url:o.getUrl("folderStructureFix"),cache:!1,success:function(t){e.removeLoadingMessage(u),!0===t.success&&Array.isArray(t.fixedStatus)?(t.fixedStatus.length>0?t.fixedStatus.forEach(function(e){u.append(i.render(e.severity,e.title,e.message))}):u.append(i.render(n.warning,"Nothing fixed","")),r.getStatus()):a.error("Something went wrong")},error:function(e){o.handleAjaxError(e,d)}})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/ImageProcessing.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/ImageProcessing.js
new file mode 100644 (file)
index 0000000..bf444e9
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","../Renderable/InfoBox","../Renderable/Severity","TYPO3/CMS/Backend/Notification","bootstrap"],function(e,t,r,n,s,i,o){"use strict";return new(function(){function e(){this.selectorModalBody=".t3js-modal-body",this.selectorExecuteTrigger=".t3js-imageProcessing-execute",this.selectorTestContainer=".t3js-imageProcessing-twinContainer",this.selectorTwinImageTemplate=".t3js-imageProcessing-twinImage-template",this.selectorCommandContainer=".t3js-imageProcessing-command",this.selectorCommandText=".t3js-imageProcessing-command-text",this.selectorTwinImages=".t3js-imageProcessing-images"}return e.prototype.initialize=function(e){var t=this;this.currentModal=e,this.getData(),e.on("click",this.selectorExecuteTrigger,function(e){e.preventDefault(),t.runTests()})},e.prototype.getData=function(){var e=this,t=this.currentModal.find(this.selectorModalBody);r.ajax({url:n.getUrl("imageProcessingGetData"),cache:!1,success:function(r){!0===r.success?(t.empty().append(r.html),e.runTests()):o.error("Something went wrong")},error:function(e){n.handleAjaxError(e,t)}})},e.prototype.runTests=function(){var e=this,t=this.currentModal.find(this.selectorModalBody),o=this.currentModal.find(this.selectorTwinImageTemplate);t.find(this.selectorTestContainer).each(function(a,c){var l=r(c),m=l.data("test"),d=s.render(i.loading,"Loading...","");l.empty().html(d),r.ajax({url:n.getUrl(m),cache:!1,success:function(t){if(!0===t.success){l.empty(),Array.isArray(t.status)&&t.status.forEach(function(e){var t=s.render(c.severity,c.title,c.message);l.append(t)});var r=o.clone();if(r.removeClass("t3js-imageProcessing-twinImage-template"),!0===t.fileExists&&(r.find("img.reference").attr("src",t.referenceFile),r.find("img.result").attr("src",t.outputFile),r.find(e.selectorTwinImages).show()),Array.isArray(t.command)&&t.command.length>0){r.find(e.selectorCommandContainer).show();var n=[];t.command.forEach(function(e){n.push("<strong>Command:</strong>\n"+e[1]),3===e.length&&n.push("<strong>Result:</strong>\n"+e[2])}),r.find(e.selectorCommandText).html(n.join("\n"))}l.append(r)}},error:function(e){n.handleAjaxError(e,t)}})})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/InlineModuleInterface.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/InlineModuleInterface.js
new file mode 100644 (file)
index 0000000..d580a28
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports"],function(e,r){"use strict";Object.defineProperty(r,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/InteractableModuleInterface.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/InteractableModuleInterface.js
new file mode 100644 (file)
index 0000000..d580a28
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports"],function(e,r){"use strict";Object.defineProperty(r,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/LanguagePacks.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/LanguagePacks.js
new file mode 100644 (file)
index 0000000..625c3e2
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","../Renderable/FlashMessage","../Renderable/ProgressBar","../Renderable/InfoBox","../Renderable/Severity","bootstrap"],function(t,a,e,n,s,i,o,d){"use strict";return new(function(){function t(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.selectorOutputContainer=".t3js-languagePacks-output",this.selectorContentContainer=".t3js-languagePacks-mainContent",this.selectorActivateLanguage=".t3js-languagePacks-activateLanguage",this.selectorActivateLanguageIcon="#t3js-languagePacks-activate-icon",this.selectorAddLanguageToggle=".t3js-languagePacks-addLanguage-toggle",this.selectorLanguageInactive=".t3js-languagePacks-inactive",this.selectorDeactivateLanguage=".t3js-languagePacks-deactivateLanguage",this.selectorDeactivateLanguageIcon="#t3js-languagePacks-deactivate-icon",this.selectorUpdate=".t3js-languagePacks-update",this.selectorLanguageUpdateIcon="#t3js-languagePacks-languageUpdate-icon",this.selectorExtensionPackMissesIcon="#t3js-languagePacks-extensionPack-misses-icon",this.selectorNotifications=".t3js-languagePacks-notifications",this.activeLanguages=[],this.activeExtensions=[],this.packsUpdateDetails={toHandle:0,handled:0,updated:0,new:0,failed:0},this.notifications=[]}return t.prototype.initialize=function(t){var a=this;this.currentModal=t,this.getData(),t.on("click",this.selectorAddLanguageToggle,function(){t.find(a.selectorContentContainer+" "+a.selectorLanguageInactive).toggle()}),t.on("click",this.selectorActivateLanguage,function(t){var n=e(t.target).closest(a.selectorActivateLanguage).data("iso");t.preventDefault(),a.activateLanguage(n)}),t.on("click",this.selectorDeactivateLanguage,function(t){var n=e(t.target).closest(a.selectorDeactivateLanguage).data("iso");t.preventDefault(),a.deactivateLanguage(n)}),t.on("click",this.selectorUpdate,function(t){var n=e(t.target).closest(a.selectorUpdate).data("iso"),s=e(t.target).closest(a.selectorUpdate).data("extension");t.preventDefault(),a.updatePacks(n,s)})},t.prototype.getData=function(){var t=this,a=this.currentModal.find(this.selectorModalBody);e.ajax({url:n.getUrl("languagePacksGetData"),cache:!1,success:function(n){if(!0===n.success){t.activeLanguages=n.activeLanguages,t.activeExtensions=n.activeExtensions,a.empty().append(n.html);var s=a.parent().find(t.selectorContentContainer);s.empty(),s.append(t.languageMatrixHtml(n)),s.append(t.extensionMatrixHtml(n)),e('[data-toggle="tooltip"]').tooltip({container:s})}else{var i=o.render(d.error,"Something went wrong","");t.addNotification(i)}t.renderNotifications()},error:function(t){n.handleAjaxError(t,a)}})},t.prototype.activateLanguage=function(t){var a=this,c=this.currentModal.find(this.selectorModalBody),l=this.currentModal.find(this.selectorOutputContainer),r=i.render(d.loading,"Loading...","");l.empty().append(r),e.ajax({url:n.getUrl(),method:"POST",context:this,data:{install:{action:"languagePacksActivateLanguage",token:this.currentModal.find(this.selectorModuleContent).data("language-packs-activate-language-token"),iso:t}},cache:!1,beforeSend:function(){a.getNotificationBox().empty()},success:function(t){if(l.empty(),!0===t.success&&Array.isArray(t.status))t.status.forEach(function(t){var e=o.render(t.severity,t.title,t.message);a.addNotification(e)});else{var e=s.render(d.error,"Something went wrong","");a.addNotification(e)}a.getData()},error:function(t){n.handleAjaxError(t,c)}})},t.prototype.deactivateLanguage=function(t){var a=this,c=this.currentModal.find(this.selectorModalBody),l=this.currentModal.find(this.selectorOutputContainer),r=i.render(d.loading,"Loading...","");l.empty().append(r),e.ajax({url:n.getUrl(),method:"POST",context:this,data:{install:{action:"languagePacksDeactivateLanguage",token:this.currentModal.find(this.selectorModuleContent).data("language-packs-deactivate-language-token"),iso:t}},cache:!1,beforeSend:function(){a.getNotificationBox().empty()},success:function(t){if(l.empty(),!0===t.success&&Array.isArray(t.status))t.status.forEach(function(t){var e=o.render(t.severity,t.title,t.message);a.addNotification(e)});else{var e=s.render(d.error,"Something went wrong","");a.addNotification(e)}a.getData()},error:function(t){n.handleAjaxError(t,c)}})},t.prototype.updatePacks=function(t,a){var s=this,i=this.currentModal.find(this.selectorOutputContainer),o=this.currentModal.find(this.selectorContentContainer),d=void 0===t?this.activeLanguages:[t],c=!0,l=this.activeExtensions;void 0!==a&&(l=[a],c=!1),this.packsUpdateDetails={toHandle:d.length*l.length,handled:0,updated:0,new:0,failed:0},i.empty().append(e("<div>",{class:"progress"}).append(e("<div>",{class:"progress-bar progress-bar-info",role:"progressbar","aria-valuenow":0,"aria-valuemin":0,"aria-valuemax":100,style:"width: 0;"}).append(e("<span>",{class:"text-nowrap"}).text("0 of "+this.packsUpdateDetails.toHandle+" language packs updated")))),o.empty(),d.forEach(function(t){l.forEach(function(a){e.ajax({url:n.getUrl(),method:"POST",context:s,data:{install:{action:"languagePacksUpdatePack",token:s.currentModal.find(s.selectorModuleContent).data("language-packs-update-pack-token"),iso:t,extension:a}},cache:!1,beforeSend:function(){s.getNotificationBox().empty()},success:function(t){!0===t.success?(s.packsUpdateDetails.handled++,"new"===t.packResult?s.packsUpdateDetails.new++:"update"===t.packResult?s.packsUpdateDetails.updated++:s.packsUpdateDetails.failed++,s.packUpdateDone(c,d)):(s.packsUpdateDetails.handled++,s.packsUpdateDetails.failed++,s.packUpdateDone(c,d))},error:function(){s.packsUpdateDetails.handled++,s.packsUpdateDetails.failed++,s.packUpdateDone(c,d)}})})})},t.prototype.packUpdateDone=function(t,a){var i=this,c=this.currentModal.find(this.selectorModalBody),l=this.currentModal.find(this.selectorOutputContainer);if(this.packsUpdateDetails.handled===this.packsUpdateDetails.toHandle){var r=o.render(d.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(r),!0===t?e.ajax({url:n.getUrl(),method:"POST",context:this,data:{install:{action:"languagePacksUpdateIsoTimes",token:this.currentModal.find(this.selectorModuleContent).data("language-packs-update-iso-times-token"),isos:a}},cache:!1,success:function(t){if(!0===t.success)i.getData();else{var a=s.render(d.error,"Something went wrong","");i.addNotification(a)}},error:function(t){n.handleAjaxError(t,c)}}):this.getData()}else{var p=this.packsUpdateDetails.handled/this.packsUpdateDetails.toHandle*100;l.find(".progress-bar").css("width",p+"%").attr("aria-valuenow",p).find("span").text(this.packsUpdateDetails.handled+" of "+this.packsUpdateDetails.toHandle+" language packs updated")}},t.prototype.languageMatrixHtml=function(t){var a=this.currentModal.find(this.selectorActivateLanguageIcon).html(),n=this.currentModal.find(this.selectorDeactivateLanguageIcon).html(),s=this.currentModal.find(this.selectorLanguageUpdateIcon).html(),i=e("<div>"),o=e("<tbody>");return t.languages.forEach(function(t){var i=t.active,d=e("<tr>");i?o.append(d.append(e("<td>").append(e("<a>",{class:"btn btn-default t3js-languagePacks-deactivateLanguage","data-iso":t.iso,"data-toggle":"tooltip",title:"Deactivate"}).append(n),e("<a>",{class:"btn btn-default t3js-languagePacks-update","data-iso":t.iso,"data-toggle":"tooltip",title:"Download language packs"}).append(s)))):o.append(d.addClass("t3-languagePacks-inactive t3js-languagePacks-inactive").css({display:"none"}).append(e("<td>").append(e("<a>",{class:"btn btn-default t3js-languagePacks-activateLanguage","data-iso":t.iso,"data-toggle":"tooltip",title:"Activate"}).append(a)))),d.append(e("<td>").text(t.name),e("<td>").text(t.iso),e("<td>").text(t.dependencies.join(", ")),e("<td>").text(null===t.lastUpdate?"":t.lastUpdate)),o.append(d)}),i.append(e("<h3>").text("Active languages"),e("<table>",{class:"table table-striped table-bordered"}).append(e("<thead>").append(e("<tr>").append(e("<th>").append(e("<button>",{class:"btn btn-default t3js-languagePacks-addLanguage-toggle",type:"button"}).append(e("<span>").append(a)," Add language"),e("<button>",{class:"btn btn-default t3js-languagePacks-update",type:"button"}).append(e("<span>").append(s)," Update all")),e("<th>").text("Language"),e("<th>").text("Locale"),e("<th>").text("Dependencies"),e("<th>").text("Last update"))),o)),i.html()},t.prototype.extensionMatrixHtml=function(t){var a,n=this.currentModal.find(this.selectorExtensionPackMissesIcon).html(),s=this.currentModal.find(this.selectorLanguageUpdateIcon).html(),i="",c=!0,l=0,r=e("<div>"),p=e("<tr>");p.append(e("<th>").text("Extension"),e("<th>").text("Key")),t.activeLanguages.forEach(function(t){p.append(e("<th>").append(e("<a>",{class:"btn btn-default t3js-languagePacks-update","data-iso":t,"data-toggle":"tooltip",title:"Download and update all language packs"}).append(e("<span>").append(s)," "+t)))});var g=e("<tbody>");return t.extensions.forEach(function(t){if(c=!0,t.packs.forEach(function(t){!1===t.exists&&(c=!1)}),!0!==c){l++,a=""!==t.icon?e("<span>").append(e("<img>",{style:"max-height: 16px; max-width: 16px;",src:"../"+t.icon,alt:t.title}),e("<span>").text(t.title)):e("<span>").text(t.title);var s=e("<tr>");s.append(e("<td>").html(a.html()),e("<td>").text(t.key)),t.packs.forEach(function(a){!0!==a.exists&&(i=null!==a.lastUpdate?"No language pack available when tried at "+a.lastUpdate+". Click to re-try.":"Language pack not downloaded. Click to download",s.append(e("<td>").append(e("<a>",{class:"btn btn-default t3js-languagePacks-update","data-extension":t.key,"data-iso":a.iso,"data-toggle":"tooltip",title:i}).append(n))))}),g.append(s)}}),r.append(e("<h3>").text("Translation status"),e("<table>",{class:"table table-striped table-bordered"}).append(e("<thead>").append(p),g)),0===l?o.render(d.ok,"Language packs have been found for every installed extension.","To download the latest changes, use the refresh button in the list above."):r.html()},t.prototype.getNotificationBox=function(){return this.currentModal.find(this.selectorNotifications)},t.prototype.addNotification=function(t){this.notifications.push(t)},t.prototype.renderNotifications=function(){for(var t=this.getNotificationBox(),a=0;a<this.notifications.length;++a)t.append(this.notifications[a]);this.notifications=[]},t}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/LocalConfiguration.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/LocalConfiguration.js
new file mode 100644 (file)
index 0000000..fcc0d16
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","TYPO3/CMS/Backend/Notification","bootstrap"],function(e,t,r,o,n){"use strict";return new(function(){function t(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.selectorToggleAllTrigger=".t3js-localConfiguration-toggleAll",this.selectorWriteTrigger=".t3js-localConfiguration-write",this.selectorSearchTrigger=".t3js-localConfiguration-search"}return t.prototype.initialize=function(t){var o=this;this.currentModal=t,this.getContent(),t.on("click",this.selectorWriteTrigger,function(){o.write()}),t.on("click",this.selectorToggleAllTrigger,function(){var e=o.currentModal.find(o.selectorModalBody).find(".panel-collapse"),t=e.eq(0).hasClass("in")?"hide":"show";e.collapse(t)}),jQuery.expr[":"].contains=jQuery.expr.createPseudo(function(e){return function(t){return jQuery(t).text().toUpperCase().indexOf(e.toUpperCase())>=0}}),t.on("keydown",function(e){var r=t.find(o.selectorSearchTrigger);e.ctrlKey||e.metaKey?"f"===String.fromCharCode(e.which).toLowerCase()&&(e.preventDefault(),r.focus()):27===e.keyCode&&(e.preventDefault(),r.val("").focus())}),t.on("keyup",this.selectorSearchTrigger,function(n){var a=r(n.target).val(),i=t.find(o.selectorSearchTrigger);t.find("div.item").each(function(e,t){var o=r(t);r(":contains("+a+")",o).length>0||r('input[value*="'+a+'"]',o).length>0?o.removeClass("hidden").addClass("searchhit"):o.removeClass("searchhit").addClass("hidden")}),t.find(".searchhit").parent().collapse("show"),e(["jquery.clearable"],function(){i.clearable().focus()})})},t.prototype.getContent=function(){var e=this.currentModal.find(this.selectorModalBody);r.ajax({url:o.getUrl("localConfigurationGetContent"),cache:!1,success:function(t){!0===t.success&&(Array.isArray(t.status)&&t.status.forEach(function(e){n.success(e.title,e.message)}),e.html(t.html))},error:function(t){o.handleAjaxError(t,e)}})},t.prototype.write=function(){var e=this.currentModal.find(this.selectorModalBody),t=this.currentModal.find(this.selectorModuleContent).data("local-configuration-write-token"),a={};this.currentModal.find(".t3js-localConfiguration-pathValue").each(function(e,t){var o=r(t);"checkbox"===o.attr("type")?t.checked?a[o.data("path")]="1":a[o.data("path")]="0":a[o.data("path")]=o.val()}),r.ajax({url:o.getUrl(),method:"POST",data:{install:{action:"localConfigurationWrite",token:t,configurationValues:a}},cache:!1,success:function(e){!0===e.success&&Array.isArray(e.status)?e.status.forEach(function(e){n.showMessage(e.title,e.message,e.severity)}):n.error("Something went wrong")},error:function(t){o.handleAjaxError(t,e)}})},t}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/MailTest.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/MailTest.js
new file mode 100644 (file)
index 0000000..f381c41
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","../Renderable/ProgressBar","../Renderable/Severity","../Renderable/InfoBox","TYPO3/CMS/Backend/Notification","bootstrap"],function(t,e,r,o,n,s,i,a){"use strict";return new(function(){function t(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.selectorForm="#t3js-mailTest-form",this.selectorOutputContainer=".t3js-mailTest-output"}return t.prototype.initialize=function(t){var e=this;this.currentModal=t,this.getData(),t.on("submit",this.selectorForm,function(t){t.preventDefault(),e.send()})},t.prototype.getData=function(){var t=this.currentModal.find(this.selectorModalBody);r.ajax({url:o.getUrl("mailTestGetData"),cache:!1,success:function(e){!0===e.success?t.empty().append(e.html):a.error("Something went wrong")},error:function(e){o.handleAjaxError(e,t)}})},t.prototype.send=function(){var t=this.currentModal.find(this.selectorModuleContent).data("mail-test-token"),e=this.currentModal.find(this.selectorOutputContainer),l=n.render(s.loading,"Loading...","");e.empty().html(l),r.ajax({url:o.getUrl(),method:"POST",data:{install:{action:"mailTest",token:t,email:this.currentModal.find(".t3js-mailTest-email").val()}},cache:!1,success:function(t){e.empty(),!0===t.success&&Array.isArray(t.status)?t.status.forEach(function(t){var r=i.render(t.severity,t.title,t.message);e.html(r)}):a.error("Something went wrong")},error:function(){a.error("Something went wrong")}})},t}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/PasswordStrength.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/PasswordStrength.js
new file mode 100644 (file)
index 0000000..d80d7d9
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery"],function(t,e,r){"use strict";return new(function(){function t(){}return t.prototype.initialize=function(t){r(document).on("keyup",t,function(t){var e=r(t.currentTarget),o=e.val(),n=new RegExp("^(?=.{8,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*\\W).*$","g"),u=new RegExp("^(?=.{8,})(((?=.*[A-Z])(?=.*[a-z]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[0-9]))).*$","g"),c=new RegExp("(?=.{8,}).*","g");0===o.length?e.attr("style","background-color:#FBB19B; border:1px solid #DC4C42"):c.test(o)?n.test(o)?e.attr("style","background-color:#CDEACA; border:1px solid #58B548"):(u.test(o),e.attr("style","background-color:#FBFFB3; border:1px solid #C4B70D")):e.attr("style","background-color:#FBB19B; border:1px solid #DC4C42")})},t}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/PhpInfo.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/PhpInfo.js
new file mode 100644 (file)
index 0000000..7ffbc70
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","TYPO3/CMS/Backend/Notification"],function(t,e,r,o,n){"use strict";return new(function(){function t(){this.selectorModalBody=".t3js-modal-body",this.currentModal={}}return t.prototype.initialize=function(t){this.currentModal=t,this.getData()},t.prototype.getData=function(){var t=this.currentModal.find(this.selectorModalBody);r.ajax({url:o.getUrl("phpInfoGetData"),cache:!1,success:function(e){!0===e.success?t.empty().append(e.html):n.error("Something went wrong")},error:function(e){o.handleAjaxError(e,t)}})},t}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/Presets.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/Presets.js
new file mode 100644 (file)
index 0000000..59b028c
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","TYPO3/CMS/Backend/Notification","bootstrap"],function(t,e,r,n,o){"use strict";return new(function(){function t(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-presets-content",this.selectorActivateTrigger=".t3js-presets-activate",this.selectorImageExecutable=".t3js-presets-image-executable",this.selectorImageExecutableTrigger=".t3js-presets-image-executable-trigger"}return t.prototype.initialize=function(t){var e=this;this.currentModal=t,this.getContent(),t.on("click",this.selectorImageExecutableTrigger,function(t){t.preventDefault(),e.getCustomImagePathContent()}),t.on("click",this.selectorActivateTrigger,function(t){t.preventDefault(),e.activate()}),t.find(".t3js-custom-preset").on("input",".t3js-custom-preset",function(t){r("#"+r(t.currentTarget).data("radio")).prop("checked",!0)})},t.prototype.getContent=function(){var t=this.currentModal.find(this.selectorModalBody);r.ajax({url:n.getUrl("presetsGetContent"),cache:!1,success:function(e){!0===e.success&&"undefined"!==e.html&&e.html.length>0?t.empty().append(e.html):o.error("Something went wrong")},error:function(e){n.handleAjaxError(e,t)}})},t.prototype.getCustomImagePathContent=function(){var t=this.currentModal.find(this.selectorModalBody),e=this.currentModal.find(this.selectorModuleContent).data("presets-content-token");r.ajax({url:n.getUrl(),method:"POST",data:{install:{token:e,action:"presetsGetContent",values:{Image:{additionalSearchPath:this.currentModal.find(this.selectorImageExecutable).val()}}}},cache:!1,success:function(e){!0===e.success&&"undefined"!==e.html&&e.html.length>0?t.empty().append(e.html):o.error("Something went wrong")},error:function(e){n.handleAjaxError(e,t)}})},t.prototype.activate=function(){var t=this.currentModal.find(this.selectorModalBody),e=this.currentModal.find(this.selectorModuleContent).data("presets-activate-token"),s={};r(this.currentModal.find(this.selectorModuleContent+" form").serializeArray()).each(function(t,e){s[e.name]=e.value}),s["install[action]"]="presetsActivate",s["install[token]"]=e,r.ajax({url:n.getUrl(),method:"POST",data:s,cache:!1,success:function(t){!0===t.success&&Array.isArray(t.status)?t.status.forEach(function(t){o.showMessage(t.title,t.message,t.severity)}):o.error("Something went wrong")},error:function(e){n.handleAjaxError(e,t)}})},t}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/ResetBackendUserUc.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/ResetBackendUserUc.js
new file mode 100644 (file)
index 0000000..95d4212
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","TYPO3/CMS/Backend/Notification"],function(e,s,t,n,r){"use strict";return new(function(){function e(){}return e.prototype.initialize=function(e){t.ajax({url:n.getUrl("resetBackendUserUc"),cache:!1,beforeSend:function(){e.addClass("disabled")},success:function(e){!0===e.success&&Array.isArray(e.status)?e.status.length>0&&e.status.forEach(function(e){r.success(e.message)}):r.error("Something went wrong ...")},error:function(e){r.error("Resetting backend user uc failed. Please check the system for missing database fields and try again.")},complete:function(){e.removeClass("disabled")}})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/SystemInformation.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/SystemInformation.js
new file mode 100644 (file)
index 0000000..c7d9a83
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","TYPO3/CMS/Backend/Notification"],function(t,e,r,o,n){"use strict";return new(function(){function t(){this.selectorModalBody=".t3js-modal-body",this.currentModal={}}return t.prototype.initialize=function(t){this.currentModal=t,this.getData()},t.prototype.getData=function(){var t=this.currentModal.find(this.selectorModalBody);r.ajax({url:o.getUrl("systemInformationGetData"),cache:!1,success:function(e){!0===e.success?t.empty().append(e.html):n.error("Something went wrong")},error:function(e){o.handleAjaxError(e,t)}})},t}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/SystemMaintainer.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/SystemMaintainer.js
new file mode 100644 (file)
index 0000000..a295a67
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","TYPO3/CMS/Backend/Notification","bootstrap"],function(t,e,s,r,n){"use strict";return new(function(){function e(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.selectorWriteTrigger=".t3js-systemMaintainer-write",this.selectorChosenContainer=".t3js-systemMaintainer-chosen",this.selectorChosenField=".t3js-systemMaintainer-chosen-select"}return e.prototype.initialize=function(e){var s=this;this.currentModal=e,window.location!==window.parent.location?top.require(["TYPO3/CMS/Install/chosen.jquery.min"],function(){s.getList()}):t(["TYPO3/CMS/Install/chosen.jquery.min"],function(){s.getList()}),e.on("click",this.selectorWriteTrigger,function(t){t.preventDefault(),s.write()})},e.prototype.getList=function(){var t=this,e=this.currentModal.find(this.selectorModalBody);s.ajax({url:r.getUrl("systemMaintainerGetList"),cache:!1,success:function(r){if(!0===r.success){Array.isArray(r.status)&&r.status.forEach(function(t){n.success(t.title,t.message)}),e.html(r.html),Array.isArray(r.users)&&r.users.forEach(function(r){var n=r.username;r.disable&&(n="[DISABLED] "+n);var i=s("<option>",{value:r.uid}).text(n);r.isSystemMaintainer&&i.attr("selected","selected"),e.find(t.selectorChosenField).append(i)});var i={".t3js-systemMaintainer-chosen-select":{width:"100%",placeholder_text_multiple:"users"}};for(var o in i)i.hasOwnProperty(o)&&e.find(o).chosen(i[o]);e.find(t.selectorChosenContainer).show(),e.find(t.selectorChosenField).trigger("chosen:updated")}},error:function(t){r.handleAjaxError(t,e)}})},e.prototype.write=function(){var t=this.currentModal.find(this.selectorModalBody),e=this.currentModal.find(this.selectorModuleContent).data("system-maintainer-write-token"),i=this.currentModal.find(this.selectorChosenField).val();s.ajax({method:"POST",url:r.getUrl(),data:{install:{users:i,token:e,action:"systemMaintainerWrite"}},success:function(t){!0===t.success?Array.isArray(t.status)&&t.status.forEach(function(t){n.success(t.title,t.message)}):n.error("Something went wrong")},error:function(e){r.handleAjaxError(e,t)}})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/TcaExtTablesCheck.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/TcaExtTablesCheck.js
new file mode 100644 (file)
index 0000000..97dbaf1
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","../Renderable/ProgressBar","../Renderable/Severity","../Renderable/InfoBox","TYPO3/CMS/Backend/Notification"],function(e,t,n,r,o,i,s,a){"use strict";return new(function(){function e(){this.selectorModalBody=".t3js-modal-body",this.selectorCheckTrigger=".t3js-tcaExtTablesCheck-check",this.selectorOutputContainer=".t3js-tcaExtTablesCheck-output"}return e.prototype.initialize=function(e){var t=this;this.currentModal=e,this.check(),e.on("click",this.selectorCheckTrigger,function(e){e.preventDefault(),t.check()})},e.prototype.check=function(){var e=this,t=this.currentModal.find(this.selectorModalBody),c=n(this.selectorOutputContainer),l=o.render(i.loading,"Loading...","");c.empty().html(l),n.ajax({url:r.getUrl("tcaExtTablesCheck"),cache:!1,success:function(n){if(t.empty().append(n.html),!0===n.success&&Array.isArray(n.status))if(n.status.length>0){var r=s.render(i.warning,"Extensions change TCA in ext_tables.php",'Check for ExtensionManagementUtility and $GLOBALS["TCA"]');t.find(e.selectorOutputContainer).append(r),n.status.forEach(function(e){var n=s.render(e.severity,e.title,e.message);c.append(n),t.append(n)})}else{r=s.render(i.ok,"No TCA changes in ext_tables.php files. Good job!","");t.find(e.selectorOutputContainer).append(r)}else a.error("Something went wrong",'Use "Check for broken extensions"')},error:function(e){r.handleAjaxError(e,t)}})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/TcaMigrationsCheck.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/TcaMigrationsCheck.js
new file mode 100644 (file)
index 0000000..0f03d95
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","../Renderable/ProgressBar","../Renderable/FlashMessage","../Renderable/Severity","../Renderable/InfoBox"],function(e,t,r,n,o,i,s,a){"use strict";return new(function(){function e(){this.selectorModalBody=".t3js-modal-body",this.selectorCheckTrigger=".t3js-tcaMigrationsCheck-check",this.selectorOutputContainer=".t3js-tcaMigrationsCheck-output"}return e.prototype.initialize=function(e){var t=this;this.currentModal=e,this.check(),e.on("click",this.selectorCheckTrigger,function(e){e.preventDefault(),t.check()})},e.prototype.check=function(){var e=this,t=r(this.selectorOutputContainer),c=this.currentModal.find(this.selectorModalBody),l=o.render(s.loading,"Loading...","");t.empty().html(l),r.ajax({url:n.getUrl("tcaMigrationsCheck"),cache:!1,success:function(t){if(c.empty().append(t.html),!0===t.success&&Array.isArray(t.status))if(t.status.length>0){var r=a.render(s.warning,"TCA migrations need to be applied","Check the following list and apply needed changes.");c.find(e.selectorOutputContainer).empty(),c.find(e.selectorOutputContainer).append(r),t.status.forEach(function(t){var r=a.render(t.severity,t.title,t.message);c.find(e.selectorOutputContainer).append(r)})}else{var n=a.render(s.ok,"No TCA migrations need to be applied","Your TCA looks good.");c.find(e.selectorOutputContainer).append(n)}else{var o=i.render(s.error,"Something went wrong",'Use "Check for broken extensions"');c.find(e.selectorOutputContainer).append(o)}},error:function(e){n.handleAjaxError(e,c)}})},e}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/UpgradeDocs.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/UpgradeDocs.js
new file mode 100644 (file)
index 0000000..afec552
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","TYPO3/CMS/Backend/Notification","bootstrap"],function(e,t,n,o,a){"use strict";return new(function(){function t(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.selectorRestFileItem=".upgrade_analysis_item_to_filter",this.selectorFulltextSearch=".t3js-upgradeDocs-fulltext-search",this.selectorChosenField=".t3js-upgradeDocs-chosen-select",this.selectorChangeLogsForVersionContainer=".t3js-version-changes",this.selectorChangeLogsForVersion=".t3js-changelog-list"}return t.trimExplodeAndUnique=function(e,t){for(var o=[],a=t.split(e),r=0;r<a.length;r++){var i=a[r].trim();i.length>0&&-1===n.inArray(i,o)&&o.push(i)}return o},t.prototype.initialize=function(t){var n=this;this.currentModal=t,window.location!==window.parent.location?top.require(["TYPO3/CMS/Install/chosen.jquery.min"],function(){n.getContent()}):e(["TYPO3/CMS/Install/chosen.jquery.min"],function(){n.getContent()}),t.on("click",".t3js-upgradeDocs-markRead",function(e){n.markRead(e.target)}),t.on("click",".t3js-upgradeDocs-unmarkRead",function(e){n.unmarkRead(e.target)}),jQuery.expr[":"].contains=jQuery.expr.createPseudo(function(e){return function(t){return jQuery(t).text().toUpperCase().indexOf(e.toUpperCase())>=0}}),e(["jquery.clearable"],function(){t.find(n.selectorFulltextSearch).clearable().focus()})},t.prototype.getContent=function(){var e=this,t=this.currentModal.find(this.selectorModalBody);n.ajax({url:o.getUrl("upgradeDocsGetContent"),cache:!1,success:function(n){!0===n.success&&"undefined"!==n.html&&n.html.length>0&&(t.empty().append(n.html),e.initializeFullTextSearch(),e.initializeChosenSelector(),e.loadChangelogs())},error:function(e){o.handleAjaxError(e,t)}})},t.prototype.loadChangelogs=function(){var e=this,t=[],r=this.currentModal.find(this.selectorModalBody);this.currentModal.find(this.selectorChangeLogsForVersionContainer).each(function(i,s){var l=n.ajax({url:o.getUrl("upgradeDocsGetChangelogForVersion"),cache:!1,data:{install:{version:s.dataset.version}},success:function(t){if(!0===t.success){var o=n(s),r=o.find(e.selectorChangeLogsForVersion);r.html(t.html),e.renderTags(r),e.moveNotRelevantDocuments(r),o.find(".t3js-panel-loading").remove()}else a.error("Something went wrong")},error:function(e){o.handleAjaxError(e,r)}});t.push(l)}),n.when.apply(n,t).done(function(){e.fulltextSearchField.prop("disabled",!1),e.appendItemsToChosenSelector()})},t.prototype.initializeFullTextSearch=function(){var e=this;this.fulltextSearchField=this.currentModal.find(this.selectorFulltextSearch),this.fulltextSearchField.clearable().focus(),this.initializeChosenSelector(),this.fulltextSearchField.on("keyup",function(){e.combinedFilterSearch()})},t.prototype.initializeChosenSelector=function(){var e=this;this.chosenField=this.currentModal.find(this.selectorModalBody).find(this.selectorChosenField);var t={".chosen-select":{width:"100%",placeholder_text_multiple:"tags"},".chosen-select-deselect":{allow_single_deselect:!0},".chosen-select-no-single":{disable_search_threshold:10},".chosen-select-no-results":{no_results_text:"Oops, nothing found!"},".chosen-select-width":{width:"100%"}};for(var n in t)t.hasOwnProperty(n)&&this.currentModal.find(n).chosen(t[n]);this.chosenField.on("change",function(){e.combinedFilterSearch()})},t.prototype.appendItemsToChosenSelector=function(){var e=this,o="";n(this.currentModal.find(this.selectorRestFileItem)).each(function(e,t){o+=n(t).data("item-tags")+","});var a=t.trimExplodeAndUnique(",",o).sort(function(e,t){return e.toLowerCase().localeCompare(t.toLowerCase())});this.chosenField.prop("disabled",!1),n.each(a,function(t,o){e.chosenField.append(n("<option>").text(o))}),this.chosenField.trigger("chosen:updated")},t.prototype.combinedFilterSearch=function(){var e=this.currentModal.find(this.selectorModalBody),t=e.find("div.item");if(this.chosenField.val().length<1&&this.fulltextSearchField.val().length<1)return n(".panel-version:not(:first) > .panel-collapse").collapse("hide"),t.removeClass("hidden searchhit filterhit"),!1;if(t.addClass("hidden").removeClass("searchhit filterhit"),this.chosenField.val().length>0){t.addClass("hidden").removeClass("filterhit");var o=[],a=[];n.each(this.chosenField.val(),function(e,t){var n='[data-item-tags*="'+t+'"]';t.indexOf(":")>0?o.push(n):a.push(n)});var r=a.join(""),i=[];if(o.length)for(var s=0;s<o.length;s++)i.push(r+o[s]);else i.push(r);var l=i.join(",");e.find(l).removeClass("hidden").addClass("searchhit filterhit")}else t.addClass("filterhit").removeClass("hidden");var d=this.fulltextSearchField.val();return e.find("div.item.filterhit").each(function(e,t){var o=n(t);n(":contains("+d+")",o).length>0||n('input[value*="'+d+'"]',o).length>0?o.removeClass("hidden").addClass("searchhit"):o.removeClass("searchhit").addClass("hidden")}),e.find(".searchhit").closest(".panel-collapse").collapse("show"),e.find(".panel-version").each(function(e,t){var o=n(t);o.find(".searchhit",".filterhit").length<1&&o.find(" > .panel-collapse").collapse("hide")}),!0},t.prototype.renderTags=function(e){n.each(e.find(this.selectorRestFileItem),function(e,t){var o=n(t),a=o.data("item-tags").split(","),r=o.find(".t3js-tags");a.forEach(function(e){r.append(n("<span />",{class:"label"}).text(e))})})},t.prototype.moveNotRelevantDocuments=function(e){e.find('[data-item-state="read"]').appendTo(this.currentModal.find(".panel-body-read")),e.find('[data-item-state="notAffected"]').appendTo(this.currentModal.find(".panel-body-not-affected"))},t.prototype.markRead=function(e){var t=this.currentModal.find(this.selectorModalBody),a=this.currentModal.find(this.selectorModuleContent).data("upgrade-docs-mark-read-token"),r=n(e).closest("a");r.toggleClass("t3js-upgradeDocs-unmarkRead t3js-upgradeDocs-markRead"),r.find("i").toggleClass("fa-check fa-ban"),r.closest(".panel").appendTo(this.currentModal.find(".panel-body-read")),n.ajax({method:"POST",url:o.getUrl(),data:{install:{ignoreFile:r.data("filepath"),token:a,action:"upgradeDocsMarkRead"}},error:function(e){o.handleAjaxError(e,t)}})},t.prototype.unmarkRead=function(e){var t=this.currentModal.find(this.selectorModalBody),a=this.currentModal.find(this.selectorModuleContent).data("upgrade-docs-unmark-read-token"),r=n(e).closest("a"),i=r.closest(".panel").data("item-version");r.toggleClass("t3js-upgradeDocs-markRead t3js-upgradeDocs-unmarkRead"),r.find("i").toggleClass("fa-check fa-ban"),r.closest(".panel").appendTo(this.currentModal.find('*[data-group-version="'+i+'"] .panel-body')),n.ajax({method:"POST",url:o.getUrl(),data:{install:{ignoreFile:r.data("filepath"),token:a,action:"upgradeDocsUnmarkRead"}},error:function(e){o.handleAjaxError(e,t)}})},t}())});
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/UpgradeWizards.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/UpgradeWizards.js
new file mode 100644 (file)
index 0000000..4e2706f
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","../Router","../Renderable/Severity","../Renderable/ProgressBar","../Renderable/InfoBox","../Renderable/FlashMessage","TYPO3/CMS/Backend/Notification","TYPO3/CMS/Core/SecurityUtility","bootstrap"],function(e,t,r,s,a,i,n,o,d,l){"use strict";return new(function(){function e(){this.selectorModalBody=".t3js-modal-body",this.selectorModuleContent=".t3js-module-content",this.selectorOutputWizardsContainer=".t3js-upgradeWizards-wizards-output",this.selectorOutputDoneContainer=".t3js-upgradeWizards-done-output",this.selectorWizardsBlockingAddsTemplate=".t3js-upgradeWizards-blocking-adds-template",this.selectorWizardsBlockingAddsRows=".t3js-upgradeWizards-blocking-adds-rows",this.selectorWizardsBlockingAddsExecute=".t3js-upgradeWizards-blocking-adds-execute",this.selectorWizardsBlockingCharsetTemplate=".t3js-upgradeWizards-blocking-charset-template",this.selectorWizardsBlockingCharsetFix=".t3js-upgradeWizards-blocking-charset-fix",this.selectorWizardsDoneBodyTemplate=".t3js-upgradeWizards-done-body-template",this.selectorWizardsDoneRows=".t3js-upgradeWizards-done-rows",this.selectorWizardsDoneRowTemplate=".t3js-upgradeWizards-done-row-template table tr",this.selectorWizardsDoneRowMarkUndone=".t3js-upgradeWizards-done-markUndone",this.selectorWizardsDoneRowTitle=".t3js-upgradeWizards-done-title",this.selectorWizardsListTemplate=".t3js-upgradeWizards-list-template",this.selectorWizardsListRows=".t3js-upgradeWizards-list-rows",this.selectorWizardsListRowTemplate=".t3js-upgradeWizards-list-row-template",this.selectorWizardsListRowTitle=".t3js-upgradeWizards-list-row-title",this.selectorWizardsListRowExplanation=".t3js-upgradeWizards-list-row-explanation",this.selectorWizardsListRowExecute=".t3js-upgradeWizards-list-row-execute",this.selectorWizardsInputTemplate=".t3js-upgradeWizards-input",this.selectorWizardsInputTitle=".t3js-upgradeWizards-input-title",this.selectorWizardsInputHtml=".t3js-upgradeWizards-input-html",this.selectorWizardsInputPerform=".t3js-upgradeWizards-input-perform",this.securityUtility=new l}return e.removeLoadingMessage=function(e){e.find(".alert-loading").remove()},e.renderProgressBar=function(e){return i.render(a.loading,e,"")},e.prototype.initialize=function(e){var t=this;this.currentModal=e,this.getData().done(function(){t.doneUpgrades()}),e.on("click",this.selectorWizardsDoneRowMarkUndone,function(e){t.markUndone(e.target.dataset.identifier)}),e.on("click",this.selectorWizardsBlockingCharsetFix,function(e){t.blockingUpgradesDatabaseCharsetFix()}),e.on("click",this.selectorWizardsBlockingAddsExecute,function(e){t.blockingUpgradesDatabaseAddsExecute()}),e.on("click",this.selectorWizardsListRowExecute,function(e){t.wizardInput(e.target.dataset.identifier,e.target.dataset.title)}),e.on("click",this.selectorWizardsInputPerform,function(e){t.wizardExecute(e.target.dataset.identifier,e.target.dataset.title)})},e.prototype.getData=function(){var e=this,t=this.currentModal.find(this.selectorModalBody);return r.ajax({url:s.getUrl("upgradeWizardsGetData"),cache:!1,success:function(r){!0===r.success?(t.empty().append(r.html),e.blockingUpgradesDatabaseCharsetTest()):d.error("Something went wrong")},error:function(e){s.handleAjaxError(e)}})},e.prototype.blockingUpgradesDatabaseCharsetTest=function(){var t=this,a=this.currentModal.find(this.selectorModalBody),i=this.currentModal.find(this.selectorOutputWizardsContainer);i.empty().html(e.renderProgressBar("Checking&nbs