[FEATURE] Add check if local extensions can be loaded 47/22047/13
authorSusanne Moog <typo3@susannemoog.de>
Sat, 6 Jul 2013 17:17:45 +0000 (19:17 +0200)
committerWouter Wolters <typo3@wouterwolters.nl>
Sun, 28 Jul 2013 15:08:52 +0000 (17:08 +0200)
Add a check in the install tool that checks if all local
extensions can be loaded without fatal errors by including
ext_localconf and ext_tables files.

Change-Id: I30ff00af83e59681c52901ecc09889f5c3cd4243
Releases: 6.2
Resolves: #49760
Reviewed-on: https://review.typo3.org/22047
Reviewed-by: Christian Kuhn
Tested-by: Christian Kuhn
Reviewed-by: Wouter Wolters
Tested-by: Wouter Wolters
12 files changed:
typo3/sysext/install/Classes/Controller/AbstractController.php
typo3/sysext/install/Classes/Controller/Action/Ajax/ExtensionCompatibilityTester.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/Action/Tool/ImportantActions.php
typo3/sysext/install/Classes/Controller/AjaxController.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/ToolController.php
typo3/sysext/install/Resources/Private/Partials/Action/Tool/ImportantActions/ExtensionCompatibilityTester.html [new file with mode: 0644]
typo3/sysext/install/Resources/Private/Templates/Action/Tool/ImportantActions.html
typo3/sysext/install/Resources/Public/Javascript/Install.js
typo3/sysext/install/Start/Install.php
typo3/sysext/install/Tests/Unit/Controller/Action/Ajax/ExtensionCompatibilityTesterTest.php [new file with mode: 0644]
typo3/sysext/install/Tests/Unit/Controller/Action/Ajax/Fixtures/demo1/ext_localconf.php [new file with mode: 0644]
typo3/sysext/install/Tests/Unit/Controller/Action/Ajax/Fixtures/demo1/ext_tables.php [new file with mode: 0644]

index 708a58a..7da2fb3 100644 (file)
@@ -119,6 +119,18 @@ class AbstractController {
                        $tokenOk = TRUE;
                }
 
+               $this->handleSessionTokenCheck($tokenOk);
+       }
+
+       /**
+        * If session token was not ok, the session is reset and either
+        * a redirect is initialized (will load the same step step controller again) or
+        * if in install tool, the login form is displayed.
+        *
+        * @param boolean $tokenOk
+        * @return void
+        */
+       protected function handleSessionTokenCheck($tokenOk) {
                if (!$tokenOk) {
                        $this->session->resetSession();
                        $this->session->startSession();
@@ -148,17 +160,27 @@ class AbstractController {
                        $this->session->resetSession();
                        $this->session->startSession();
 
-                       if ($this->isInitialInstallationInProgress()) {
-                               $this->redirect();
-                       } else {
-                               /** @var $message \TYPO3\CMS\Install\Status\ErrorStatus */
-                               $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
-                               $message->setTitle('Session expired');
-                               $message->setMessage(
-                                       'Your Install Tool session has expired. You have been logged out, please login and try again.'
-                               );
-                               $this->output($this->loginForm($message));
-                       }
+                       $this->handleSessionLifeTimeExpired();
+               }
+       }
+
+       /**
+        * If session expired, the current step of step controller is reloaded
+        * (if first installation is running) - or the login form is displayed.
+        *
+        * @return void
+        */
+       protected function handleSessionLifeTimeExpired() {
+               if ($this->isInitialInstallationInProgress()) {
+                       $this->redirect();
+               } else {
+                       /** @var $message \TYPO3\CMS\Install\Status\ErrorStatus */
+                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                       $message->setTitle('Session expired');
+                       $message->setMessage(
+                               'Your Install Tool session has expired. You have been logged out, please login and try again.'
+                       );
+                       $this->output($this->loginForm($message));
                }
        }
 
@@ -491,7 +513,7 @@ class AbstractController {
                        $parameters[] = 'install[action]=' . $action;
                }
 
-               $redirectLocation= 'Install.php?' . implode('&', $parameters);
+               $redirectLocation = 'Install.php?' . implode('&', $parameters);
 
                \TYPO3\CMS\Core\Utility\HttpUtility::redirect(
                        $redirectLocation,
diff --git a/typo3/sysext/install/Classes/Controller/Action/Ajax/ExtensionCompatibilityTester.php b/typo3/sysext/install/Classes/Controller/Action/Ajax/ExtensionCompatibilityTester.php
new file mode 100644 (file)
index 0000000..ea81026
--- /dev/null
@@ -0,0 +1,222 @@
+<?php
+namespace TYPO3\CMS\Install\Controller\Action\Ajax;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Susanne Moog <typo3@susanne-moog.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Install\Controller\Action;
+use TYPO3\CMS\Core\Utility;
+
+/**
+ * Load Extensions
+ *
+ * The idea is to load ext_localconf and ext_tables of extensions one-by-one
+ * until one of those files throws a fatal. The javascript will then recognise
+ * the fatal and initiates another run that will leave out the fataling extension
+ * to check the rest.
+ */
+class ExtensionCompatibilityTester extends Action\AbstractAction implements Action\ActionInterface {
+
+       /**
+        * Store extension loading protocol
+        *
+        * @var string
+        */
+       protected $protocolFile = '';
+
+       /**
+        * Construct this class
+        * set default protocol file location
+        */
+       public function __construct() {
+               $this->protocolFile = PATH_site . 'typo3temp/ExtensionCompatibilityTester.txt';
+       }
+
+       /**
+        * Handle this action
+        *
+        * @return string content
+        */
+       public function handle() {
+               $this->initialize();
+               return $this->checkLoadedExtensions();
+       }
+
+       /**
+        * Main entry point for checking extensions to load,
+        * setting up the checks (deleting protocol), and returning
+        * OK if process run through without errors
+        *
+        * @return string
+        */
+       protected function checkLoadedExtensions() {
+               $getVars = Utility\GeneralUtility::_GET('install');
+               if (isset($getVars['extensionCompatibilityTester']) && isset($getVars['extensionCompatibilityTester']['forceCheck']) && ($getVars['extensionCompatibilityTester']['forceCheck'] == 1)) {
+                       $this->deleteProtocolFile();
+               }
+               $this->tryToLoadExtLocalconfAndExtTablesOfExtensions($this->getExtensionsToLoad());
+               return 'OK';
+       }
+
+       /**
+        * Delete the protocol file if it exists
+        *
+        * @return void
+        */
+       protected function deleteProtocolFile() {
+               if (file_exists($this->protocolFile)) {
+                       unlink($this->protocolFile);
+               }
+       }
+
+       /**
+        * Get extensions that should be loaded.
+        * Fills the TYPO3_LOADED_EXT array.
+        * Only considers local extensions
+        *
+        * @return array
+        */
+       protected function getExtensionsToLoad() {
+               $extensionsToLoad = array();
+               $GLOBALS['TYPO3_LOADED_EXT'] = Utility\ExtensionManagementUtility::loadTypo3LoadedExtensionInformation(FALSE);
+               $extensionsToExclude = $this->getExtensionsToExclude();
+               foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $key => $extension) {
+                       if (!in_array($key, $extensionsToExclude)) {
+                               $extensionsToLoad[$key] = $extension;
+                       }
+               }
+               return $extensionsToLoad;
+       }
+
+       /**
+        * Gets extensions already known to be incompatible
+        * This class is recursively called, and this method is needed
+        * to not run into the same errors twice.
+        *
+        * @return array
+        */
+       protected function getExtensionsToExclude() {
+               $exclude = Utility\GeneralUtility::getUrl($this->protocolFile);
+               return Utility\GeneralUtility::trimExplode(',', (string)$exclude);
+       }
+
+       /**
+        * Tries to load the ext_localconf and ext_tables files of all non-core extensions
+        * Writes current extension name to file and deletes it again when inclusion was
+        * successfull.
+        *
+        * @param array $extensions
+        * @return void
+        */
+       protected function tryToLoadExtLocalconfAndExtTablesOfExtensions(array $extensions) {
+               foreach ($extensions as $extensionKey => $extension) {
+                       $this->writeCurrentExtensionToFile($extensionKey);
+                       $this->loadExtLocalconfForExtension($extensionKey, $extension);
+                       $this->removeCurrentExtensionFromFile($extensionKey);
+               }
+               Utility\ExtensionManagementUtility::loadBaseTca(FALSE);
+               foreach ($extensions as $extensionKey => $extension) {
+                       $this->writeCurrentExtensionToFile($extensionKey);
+                       $this->loadExtTablesForExtension($extensionKey, $extension);
+                       $this->removeCurrentExtensionFromFile($extensionKey);
+               }
+       }
+
+       /**
+        * Loads ext_tables.php for a single extension. Method is a modified copy of
+        * the original bootstrap method.
+        *
+        * @param string $extensionKey
+        * @param array $extension
+        * @return void
+        */
+       protected function loadExtTablesForExtension($extensionKey, array $extension) {
+               // In general it is recommended to not rely on it to be globally defined in that
+               // scope, but we can not prohibit this without breaking backwards compatibility
+               global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS;
+               global $TBE_MODULES, $TBE_MODULES_EXT, $TCA;
+               global $PAGES_TYPES, $TBE_STYLES, $FILEICONS;
+               global $_EXTKEY;
+               // Load each ext_tables.php file of loaded extensions
+               $_EXTKEY = $extensionKey;
+               if (is_array($extension) && $extension['ext_tables.php']) {
+                       // $_EXTKEY and $_EXTCONF are available in ext_tables.php
+                       // and are explicitly set in cached file as well
+                       $_EXTCONF = $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$_EXTKEY];
+                       require $extension['ext_tables.php'];
+                       Utility\ExtensionManagementUtility::loadNewTcaColumnsConfigFiles();
+               }
+       }
+
+       /**
+        * Loads ext_localconf.php for a single extension. Method is a modified copy of
+        * the original bootstrap method.
+        *
+        * @param string $extensionKey
+        * @param array $extension
+        * @return void
+        */
+       protected function loadExtLocalconfForExtension($extensionKey, array $extension) {
+               // This is the main array meant to be manipulated in the ext_localconf.php files
+               // In general it is recommended to not rely on it to be globally defined in that
+               // scope but to use $GLOBALS['TYPO3_CONF_VARS'] instead.
+               // Nevertheless we define it here as global for backwards compatibility.
+               global $TYPO3_CONF_VARS;
+               $_EXTKEY = $extensionKey;
+               if (is_array($extension) && $extension['ext_localconf.php']) {
+                       // $_EXTKEY and $_EXTCONF are available in ext_localconf.php
+                       // and are explicitly set in cached file as well
+                       $_EXTCONF = $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$_EXTKEY];
+                       require $extension['ext_localconf.php'];
+               }
+       }
+
+       /**
+        * Writes $extensionKey to the protocol file by adding it comma separated at
+        * the end of the file.
+        *
+        * @param string $extensionKey
+        * @return void
+        */
+       protected function writeCurrentExtensionToFile($extensionKey) {
+               $incompatibleExtensions = array_filter(Utility\GeneralUtility::trimExplode(',', (string)Utility\GeneralUtility::getUrl($this->protocolFile)));
+               $incompatibleExtensions = array_merge($incompatibleExtensions, array($extensionKey));
+               Utility\GeneralUtility::writeFile($this->protocolFile, implode(', ', $incompatibleExtensions));
+       }
+
+       /**
+        * Removes $extensionKey from protocol file.
+        *
+        * @param string $extensionKey
+        * @return void
+        */
+       protected function removeCurrentExtensionFromFile($extensionKey) {
+               $extensionsInFile = array_filter(Utility\GeneralUtility::trimExplode(',', (string)Utility\GeneralUtility::getUrl($this->protocolFile)));
+               $extensionsByKey = array_flip($extensionsInFile);
+               unset($extensionsByKey[$extensionKey]);
+               $extensionsForFile = array_flip($extensionsByKey);
+               Utility\GeneralUtility::writeFile($this->protocolFile, implode(', ', $extensionsForFile));
+       }
+}
+
+?>
\ No newline at end of file
index 2ce7f4c..5fa57aa 100644 (file)
@@ -86,7 +86,8 @@ class ImportantActions extends Action\AbstractAction implements Action\ActionInt
                        ->assign('databaseHost', $GLOBALS['TYPO3_CONF_VARS']['DB']['host'])
                        ->assign('databasePort', $GLOBALS['TYPO3_CONF_VARS']['DB']['port'])
                        ->assign('databaseSocket', $GLOBALS['TYPO3_CONF_VARS']['DB']['socket'])
-                       ->assign('databaseNumberOfTables', count($this->getDatabase()->admin_get_tables()));
+                       ->assign('databaseNumberOfTables', count($this->getDatabase()->admin_get_tables()))
+                       ->assign('extensionCompatibilityTesterProtocolFile', GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . 'typo3temp/ExtensionCompatibilityTester.txt');
 
                return $this->view->render();
        }
diff --git a/typo3/sysext/install/Classes/Controller/AjaxController.php b/typo3/sysext/install/Classes/Controller/AjaxController.php
new file mode 100644 (file)
index 0000000..2182d40
--- /dev/null
@@ -0,0 +1,171 @@
+<?php
+namespace TYPO3\CMS\Install\Controller;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Susanne Moog <typo3@susannemoog.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Install tool ajax controller, handles ajax requests
+ *
+ */
+class AjaxController extends AbstractController {
+
+       /**
+        * @var string
+        */
+       protected $unauthorized = 'unauthorized';
+
+       /**
+        * @var array List of valid action names that need authentication
+        */
+       protected $authenticationActions = array(
+               'extensionCompatibilityTester'
+       );
+
+       /**
+        * Main entry point
+        *
+        * @return void
+        */
+       public function execute() {
+               $this->loadBaseExtensions();
+               $this->initializeObjectManager();
+               // Warning: Order of these methods is security relevant and interferes with different access
+               // conditions (new/existing installation). See the single method comments for details.
+               $this->checkInstallToolEnabled();
+               $this->checkInstallToolPasswordNotSet();
+               $this->initializeSession();
+               $this->checkSessionToken();
+               $this->checkSessionLifetime();
+               $this->checkLogin();
+               $this->dispatchAuthenticationActions();
+       }
+
+       /**
+        * Check whether the install tool is enabled
+        *
+        * @return void
+        */
+       protected function checkInstallToolEnabled() {
+               if (is_dir(PATH_typo3conf)) {
+                       /** @var \TYPO3\CMS\Install\Service\EnableFileService $installToolEnableService */
+                       $installToolEnableService = $this->objectManager->get('TYPO3\\CMS\\Install\\Service\\EnableFileService');
+                       if (!$installToolEnableService->checkInstallToolEnableFile()) {
+                               $this->output($this->unauthorized);
+                       }
+               }
+       }
+
+       /**
+        * Check if the install tool password is set
+        *
+        * @return void
+        */
+       protected function checkInstallToolPasswordNotSet() {
+               if (empty($GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword'])) {
+                       $this->output($this->unauthorized);
+               }
+       }
+
+       /**
+        * Check login status
+        *
+        * @return void
+        */
+       protected function checkLogin() {
+               if (!$this->session->isAuthorized()) {
+                       $this->output($this->unauthorized);
+               } else {
+                       $this->session->refreshSession();
+               }
+       }
+
+       /**
+        * Overwrites abstract method
+        * In contrast to abstract method, a response "you are not authorized is outputted"
+        *
+        * @param boolean $tokenOk
+        * @return void
+        */
+       protected function handleSessionTokenCheck($tokenOk) {
+               if (!$tokenOk) {
+                       $this->output($this->unauthorized);
+               }
+       }
+
+       /**
+        * Overwrites abstract method
+        * In contrast to abstract method, a response "you are not authorized is outputted"
+        *
+        * @return void
+        */
+       protected function handleSessionLifeTimeExpired() {
+               $this->output($this->unauthorized);
+       }
+
+       /**
+        * Call an action that needs authentication
+        *
+        * @throws Exception
+        * @return string Rendered content
+        */
+       protected function dispatchAuthenticationActions() {
+               $action = $this->getAction();
+               if ($action === '') {
+                       $this->output('noAction');
+               }
+               $this->validateAuthenticationAction($action);
+               $actionClass = ucfirst($action);
+               /** @var \TYPO3\CMS\Install\Controller\Action\ActionInterface $toolAction */
+               $toolAction = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\' . $actionClass);
+               if (!($toolAction instanceof \TYPO3\CMS\Install\Controller\Action\ActionInterface)) {
+                       throw new Exception(
+                               $action . ' does not implement ActionInterface',
+                               1369474308
+                       );
+               }
+               $toolAction->setController('ajax');
+               $toolAction->setAction($action);
+               $toolAction->setToken($this->generateTokenForAction($action));
+               $toolAction->setPostValues($this->getPostValues());
+               $this->output($toolAction->handle());
+       }
+
+       /**
+        * Output content.
+        * WARNING: This exits the script execution!
+        *
+        * @param string $content Content to output
+        */
+       protected function output($content = '') {
+               header('Content-Type: text/html; charset=utf-8');
+               header('Cache-Control: no-cache, must-revalidate');
+               header('Pragma: no-cache');
+               echo $content;
+               die;
+       }
+}
+?>
\ No newline at end of file
index 515ded6..2a35d6a 100644 (file)
@@ -112,7 +112,7 @@ class ToolController extends AbstractController {
                $toolAction = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Tool\\' . $actionClass);
                if (!($toolAction instanceof \TYPO3\CMS\Install\Controller\Action\ActionInterface)) {
                        throw new Exception(
-                               $action . ' does non implement ActionInterface',
+                               $action . ' does not implement ActionInterface',
                                1369474308
                        );
                }
diff --git a/typo3/sysext/install/Resources/Private/Partials/Action/Tool/ImportantActions/ExtensionCompatibilityTester.html b/typo3/sysext/install/Resources/Private/Partials/Action/Tool/ImportantActions/ExtensionCompatibilityTester.html
new file mode 100644 (file)
index 0000000..baf51c0
--- /dev/null
@@ -0,0 +1,17 @@
+<h4>Check for broken extensions</h4>
+<p>
+       Checks whether the local extensions ext_tables and ext_localconf files can be included without errors, meaning they can be loaded
+       without breaking the system.
+</p>
+<form method="post">
+       <f:render partial="Action/Common/HiddenFormFields" arguments="{_all}" />
+       <div id="checkExtensions" data-protocolurl="{extensionCompatibilityTesterProtocolFile}">
+               <f:render partial="Action/Common/StatusMessage" arguments="{
+                       message: {severity:'error', title:'Incompatible extension found!', message:'Something went wrong and no protocol was written.'}
+               }" />
+               <f:render partial="Action/Common/StatusMessage" arguments="{
+                       message: {severity:'ok', title:'All local extensions can be loaded!'}
+               }" />
+               <f:render partial="Action/Common/SubmitButton" arguments="{name:'checkExtensions', text:'Check extensions'}"/>
+       </div>
+</form>
\ No newline at end of file
index 51a4a41..268eb06 100644 (file)
@@ -25,6 +25,9 @@
        <f:render partial="Action/Tool/ImportantActions/ClearAllCache" arguments="{_all}"/>
        <hr />
 
+       <f:render partial="Action/Tool/ImportantActions/ExtensionCompatibilityTester" arguments="{_all}"/>
+       <hr />
+
        <f:render partial="Action/Tool/ImportantActions/NewInstallToolPassword" arguments="{_all}"/>
        <hr />
 
@@ -35,4 +38,5 @@
        <hr />
 
        <f:render partial="Action/Tool/ImportantActions/CreateAdministrator" arguments="{_all}"/>
+
 </f:section>
\ No newline at end of file
index 2750b95..2195c00 100644 (file)
@@ -77,4 +77,71 @@ $(document).ready(function() {
                        socketField.parent().fadeOut();
                }
        }).trigger('change');
-});
\ No newline at end of file
+
+       $('#checkExtensions .typo3-message').hide();
+       $('#checkExtensions button').click(function(e) {
+               checkExtensionsCompatibility(true);
+               e.preventDefault();
+               return false;
+       });
+});
+
+function checkExtensionsCompatibility(force) {
+       var url = location.href + '&install[controller]=ajax&install[action]=extensionCompatibilityTester';
+       if (force) {
+               url += '&install[extensionCompatibilityTester][forceCheck]=1';
+       } else {
+               url += '&install[extensionCompatibilityTester][forceCheck]=0';
+       }
+       $.ajax({
+               url: url,
+               cache: false,
+               success: function(data) {
+                       if (data === 'OK') {
+                               handleCheckExtensionsSuccess();
+                       } else {
+                               if(data === 'unauthorized') {
+                                       location.reload();
+                               }
+                               // workaround for xdebug returning 200 OK on fatal errors
+                               if (data.substring(data.length - 2) === 'OK') {
+                                       handleCheckExtensionsSuccess();
+                               } else {
+                                       handleCheckExtensionsError();
+                               }
+                       }
+               },
+               error: function(data) {
+                       handleCheckExtensionsError();
+               }
+       });
+}
+
+function handleCheckExtensionsSuccess() {
+       $.ajax({
+               url: $('#checkExtensions').data('protocolurl'),
+               success: function(data) {
+                       if (data) {
+                               $('.message-error .message-body', '#checkExtensions').html('The extension(s) ' + data + ' are not compatible. Please uninstall them and try again.');
+                               $('.message-error', '#checkExtensions').show();
+                       } else {
+                               $('.message-error', '#checkExtensions').hide();
+                               $('.message-ok', '#checkExtensions').show();
+                               $('button', '#checkExtensions').hide();
+                       }
+               },
+               error: function(data) {
+                       $('.message-error', '#checkExtensions').hide();
+                       $('.message-ok', '#checkExtensions').show();
+                       $('button', '#checkExtensions').hide();
+               }
+       })
+}
+
+/**
+ * Call checkExtensionsCompatibility recursively on error
+ * so we can find all incompatible extensions
+ */
+function handleCheckExtensionsError() {
+       checkExtensionsCompatibility(false);
+}
\ No newline at end of file
index 9f5fcca..fb285c5 100644 (file)
@@ -117,6 +117,10 @@ if (isset($getPost['controller']) && $getPost['controller'] === 'tool') {
        $controller = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
                'TYPO3\\CMS\\Install\\Controller\\ToolController'
        );
+} elseif ($getPost['controller'] === 'ajax') {
+       $controller = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
+               'TYPO3\CMS\install\Controller\AjaxController'
+       );
 } else {
        $controller = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
                'TYPO3\CMS\install\Controller\StepController'
diff --git a/typo3/sysext/install/Tests/Unit/Controller/Action/Ajax/ExtensionCompatibilityTesterTest.php b/typo3/sysext/install/Tests/Unit/Controller/Action/Ajax/ExtensionCompatibilityTesterTest.php
new file mode 100644 (file)
index 0000000..b0d4510
--- /dev/null
@@ -0,0 +1,255 @@
+<?php
+namespace TYPO3\CMS\Install\Tests\Unit\Controller\Action\Ajax;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Susanne Moog <typo3@susannemoog.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Core\Utility;
+
+/**
+ * Test case
+ */
+class ExtensionCompatibilityTesterTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
+
+       /**
+        * Tear down
+        *
+        * @return void
+        */
+       public function tearDown() {
+               if (file_exists(PATH_site . 'typo3temp/ExtensionCompatibilityTester.txt')) {
+                       unlink(PATH_site . 'typo3temp/ExtensionCompatibilityTester.txt');
+               }
+       }
+
+       /**
+        * @test
+        */
+       public function checkLoadedExtensionsReturnsStringOkIfAllIsWell() {
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('getExtensionsToLoad', 'tryToLoadExtLocalconfAndExtTablesOfExtensions', 'deleteProtocolFile'), array());
+               $extensionCompatibilityTesterMock->expects($this->once())->method('getExtensionsToLoad')->will($this->returnValue(array()));
+               $result = $extensionCompatibilityTesterMock->_call('checkLoadedExtensions');
+               $this->assertEquals('OK', $result);
+       }
+
+       /**
+        * @test
+        */
+       public function checkLoadedExtensionsCallsGetExtensionsToLoad() {
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('getExtensionsToLoad', 'deleteProtocolFile', 'tryToLoadExtLocalconfAndExtTablesOfExtensions'), array());
+               $extensionCompatibilityTesterMock->expects($this->once())->method('getExtensionsToLoad')->will($this->returnValue(array()));
+               $extensionCompatibilityTesterMock->expects($this->once())->method('getExtensionsToLoad');
+               $extensionCompatibilityTesterMock->_call('checkLoadedExtensions');
+       }
+
+       /**
+        * @test
+        */
+       public function checkLoadedExtensionsCallsLoadExtensions() {
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('tryToLoadExtLocalconfAndExtTablesOfExtensions', 'getExtensionsToLoad', 'deleteProtocolFile'), array());
+               $extensionCompatibilityTesterMock->expects($this->once())->method('getExtensionsToLoad')->will($this->returnValue(array()));
+               $extensionCompatibilityTesterMock->expects($this->once())->method('tryToLoadExtLocalconfAndExtTablesOfExtensions');
+               $extensionCompatibilityTesterMock->_call('checkLoadedExtensions');
+       }
+
+       /**
+        * @test
+        */
+       public function checkLoadedExtensionsCallsDeleteProtocolFileIfForceCheckIsSet() {
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('tryToLoadExtLocalconfAndExtTablesOfExtensions', 'getExtensionsToLoad', 'deleteProtocolFile'), array());
+               $extensionCompatibilityTesterMock->expects($this->once())->method('getExtensionsToLoad')->will($this->returnValue(array()));
+               $_GET['install']['extensionCompatibilityTester']['forceCheck'] = 1;
+               $extensionCompatibilityTesterMock->expects($this->once())->method('deleteProtocolFile');
+               $extensionCompatibilityTesterMock->_call('checkLoadedExtensions');
+               unset($_GET['install']['extensionCompatibilityTester']['forceCheck']);
+       }
+
+       /**
+        * @test
+        */
+       public function deleteProtocolFileDeletesFile() {
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('dummy'), array());
+               Utility\GeneralUtility::writeFile(PATH_site . 'typo3temp/ExtensionCompatibilityTester.txt', 'foobar');
+               $extensionCompatibilityTesterMock->_call('deleteProtocolFile');
+               $this->assertFalse(file_exists(PATH_site . 'typo3temp/ExtensionCompatibilityTester.txt'));
+       }
+
+       /**
+        * @test
+        */
+       public function getLoadedExtensionsReturnsArray() {
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('dummy'), array());
+               $result = $extensionCompatibilityTesterMock->_call('getExtensionsToLoad');
+               $this->assertInternalType('array', $result);
+       }
+
+       /**
+        * @test
+        */
+       public function loadExtTablesForExtensionIncludesExtTablesPhp() {
+               $extension = array(
+                       'demo1' => array(
+                               'type' => 'L',
+                               'ext_tables.php' => PATH_typo3 . 'sysext/install/Tests/Unit/Controller/Action/Ajax/Fixtures/demo1/ext_tables.php'
+                       )
+               );
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('dummy'), array());
+               $extensionCompatibilityTesterMock->_call('loadExtTablesForExtension', 'demo1', $extension['demo1']);
+               $this->assertArrayHasKey('demo1_executed', $GLOBALS);
+               $this->assertEquals('foobar', $GLOBALS['demo1_executed']);
+               unset($GLOBALS['demo1_executed']);
+       }
+
+       /**
+        * @test
+        */
+       public function tryToLoadExtLocalconfAndExtTablesOfExtensionsCallsLoadExtTablesForExtension() {
+               $extension = array(
+                       'demo1' => array(
+                               'type' => 'L',
+                               'ext_tables.php' => PATH_typo3 . 'sysext/install/Tests/Unit/Controller/Action/Ajax/Fixtures/demo1/ext_tables.php'
+                       )
+               );
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('loadExtLocalconfForExtension', 'writeCurrentExtensionToFile', 'loadExtTablesForExtension', 'removeCurrentExtensionFromFile'), array());
+               $extensionCompatibilityTesterMock->expects($this->atLeastOnce())->method('loadExtTablesForExtension');
+               $extensionCompatibilityTesterMock->_call('tryToLoadExtLocalconfAndExtTablesOfExtensions', $extension);
+       }
+
+       /**
+        * @test
+        */
+       public function tryToLoadExtLocalconfAndExtTablesOfExtensionsCallsLoadExtLocalconfForExtension() {
+               $extension = array(
+                       'demo1' => array(
+                               'type' => 'L',
+                               'ext_localconf.php' => PATH_typo3 . 'sysext/install/Tests/Unit/Controller/Action/Ajax/Fixtures/demo1/ext_localconf.php'
+                       )
+               );
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('loadExtLocalconfForExtension', 'writeCurrentExtensionToFile', 'loadExtTablesForExtension', 'removeCurrentExtensionFromFile'), array());
+               $extensionCompatibilityTesterMock->expects($this->atLeastOnce())->method('loadExtLocalconfForExtension');
+               $extensionCompatibilityTesterMock->_call('tryToLoadExtLocalconfAndExtTablesOfExtensions', $extension);
+       }
+
+       /**
+        * @test
+        */
+       public function loadExtLocalconfForExtensionIncludesExtLocalconfPhp() {
+               $extension = array(
+                       'demo1' => array(
+                               'type' => 'L',
+                               'ext_localconf.php' => PATH_typo3 . 'sysext/install/Tests/Unit/Controller/Action/Ajax/Fixtures/demo1/ext_localconf.php'
+                       )
+               );
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('dummy'), array());
+               $extensionCompatibilityTesterMock->_call('loadExtLocalconfForExtension', 'demo1', $extension['demo1']);
+               $this->assertArrayHasKey('demo1_executed', $GLOBALS);
+               $this->assertEquals('foobaz', $GLOBALS['demo1_executed']);
+               unset($GLOBALS['demo1_executed']);
+       }
+
+       /**
+        * @test
+        */
+       public function tryToLoadExtLocalconfAndExtTablesOfExtensionsCallsWriteCurrentExtensionToFile() {
+               $extension = array(
+                       'demo1' => array(
+                               'type' => 'L',
+                               'ext_tables.php' => PATH_typo3 . 'sysext/install/Tests/Unit/Controller/Action/Ajax/Fixtures/demo1/ext_tables.php'
+                       )
+               );
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('loadExtLocalconfForExtension', 'writeCurrentExtensionToFile', 'loadExtTablesForExtension', 'removeCurrentExtensionFromFile'), array());
+               $extensionCompatibilityTesterMock->expects($this->atLeastOnce())->method('writeCurrentExtensionToFile')->with('demo1');
+               $extensionCompatibilityTesterMock->_call('tryToLoadExtLocalconfAndExtTablesOfExtensions', $extension);
+       }
+
+
+       /**
+        * @test
+        */
+       public function writeCurrentExtensionToFileWritesExtensionKeyToFile() {
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('dummy'), array());
+               $extensionCompatibilityTesterMock->_call('writeCurrentExtensionToFile', 'demo1');
+               $fileContent = file_get_contents($extensionCompatibilityTesterMock->_get('protocolFile'));
+               $this->assertEquals('demo1', $fileContent);
+       }
+
+       /**
+        * @test
+        */
+       public function getExtensionsToLoadCallsGetExtensionsToExclude() {
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('getExtensionsToExclude'), array());
+               $extensionCompatibilityTesterMock
+                       ->expects($this->once())
+                       ->method('getExtensionsToExclude')
+                       ->will($this->returnValue(array()));
+               $extensionCompatibilityTesterMock->_call('getExtensionsToLoad');
+       }
+
+       /**
+        * @test
+        */
+       public function getExtensionsToExcludeReturnsArray() {
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('dummy'), array());
+               $returnValue = $extensionCompatibilityTesterMock->_call('getExtensionsToExclude');
+               $this->assertInternalType('array', $returnValue);
+       }
+
+       /**
+        * removeCurrentExtensionFromFileRemovesGivenExtensionDataProvider
+        *
+        * @return array
+        */
+       public function removeCurrentExtensionFromFileRemovesGivenExtensionDataProvider() {
+               return array(
+                       'first' => array(
+                               'demo1',
+                               'demo1, demo2, demo3',
+                               'demo2, demo3'
+                       ),
+                       'second' => array(
+                               'demo2',
+                               'demo1, demo2, demo3',
+                               'demo1, demo3'
+                       ),
+                       'third' => array(
+                               'demo3',
+                               'demo1, demo2, demo3',
+                               'demo1, demo2'
+                       )
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider removeCurrentExtensionFromFileRemovesGivenExtensionDataProvider
+        */
+       public function removeCurrentExtensionFromFileRemovesGivenExtension($extensionToRemove, $extensions, $expectedExtensions) {
+               $extensionCompatibilityTesterMock = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Controller\\Action\\Ajax\\ExtensionCompatibilityTester', array('dummy'), array());
+               Utility\GeneralUtility::writeFile($extensionCompatibilityTesterMock->_get('protocolFile'), $extensions);
+               $extensionCompatibilityTesterMock->_call('removeCurrentExtensionFromFile', $extensionToRemove);
+
+               $fileContent = file_get_contents($extensionCompatibilityTesterMock->_get('protocolFile'));
+               $this->assertEquals($expectedExtensions, $fileContent);
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/install/Tests/Unit/Controller/Action/Ajax/Fixtures/demo1/ext_localconf.php b/typo3/sysext/install/Tests/Unit/Controller/Action/Ajax/Fixtures/demo1/ext_localconf.php
new file mode 100644 (file)
index 0000000..11f5dc3
--- /dev/null
@@ -0,0 +1,3 @@
+<?php
+$GLOBALS['demo1_executed'] = 'foobaz';
+?>
\ No newline at end of file
diff --git a/typo3/sysext/install/Tests/Unit/Controller/Action/Ajax/Fixtures/demo1/ext_tables.php b/typo3/sysext/install/Tests/Unit/Controller/Action/Ajax/Fixtures/demo1/ext_tables.php
new file mode 100644 (file)
index 0000000..4175823
--- /dev/null
@@ -0,0 +1,3 @@
+<?php
+$GLOBALS['demo1_executed'] = 'foobar';
+?>
\ No newline at end of file