[FEATURE] ext:install Core update 96/24396/12
authorChristian Kuhn <lolli@schwarzbu.ch>
Sat, 5 Oct 2013 09:45:24 +0000 (11:45 +0200)
committerHelmut Hummel <helmut.hummel@typo3.org>
Mon, 7 Oct 2013 22:14:17 +0000 (00:14 +0200)
The patch provides the code infrastructure to test for possible
core updates using information from get.typo3.org and implements
a "one-click" update within "important actions".

The update itself is implemented as a list of ajax calls performed
after each other to have minimal impact and side effects to current
core code.

The service classes "CoreVersionService" and "CoreUpdateService"
contain the main business logic and may be used in a reports module
or scheduler task later on.

Change-Id: I5947252ca805b237b513fdafcf2696e9da586fc0
Resolves: #52547
Releases: 6.2
Reviewed-on: https://review.typo3.org/24396
Reviewed-by: Anja Leichsenring
Tested-by: Anja Leichsenring
Reviewed-by: Helmut Hummel
Tested-by: Helmut Hummel
25 files changed:
typo3/sysext/core/Classes/Utility/VersionNumberUtility.php
typo3/sysext/install/Classes/Controller/Action/AbstractAction.php
typo3/sysext/install/Classes/Controller/Action/Ajax/AbstractCoreUpdate.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateActivate.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateCheckPreConditions.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateDownload.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateIsUpdateAvailable.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateMove.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateUnpack.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateUpdateVersionMatrix.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/AjaxController.php
typo3/sysext/install/Classes/Service/CoreUpdateService.php [new file with mode: 0644]
typo3/sysext/install/Classes/Service/CoreVersionService.php [new file with mode: 0644]
typo3/sysext/install/Classes/Service/Exception.php [new file with mode: 0644]
typo3/sysext/install/Classes/Service/Exception/CoreVersionServiceException.php [new file with mode: 0644]
typo3/sysext/install/Classes/Service/Exception/RemoteFetchException.php [new file with mode: 0644]
typo3/sysext/install/Classes/View/JsonView.php [new file with mode: 0644]
typo3/sysext/install/Resources/Private/Partials/Action/Tool/ImportantActions/CoreUpdateButton.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/Tests/Unit/Service/CoreUpdateServiceTest.php [new file with mode: 0644]
typo3/sysext/install/Tests/Unit/Service/CoreVersionServiceTest.php [new file with mode: 0644]
typo3/sysext/install/Tests/Unit/Service/Fixtures/VersionMatrixFixture.php [new file with mode: 0644]
typo3/sysext/install/Tests/Unit/Status/StatusUtilityTest.php
typo3/sysext/install/Tests/Unit/View/JsonViewTest.php [new file with mode: 0644]

index 37676a2..ac9b9e4 100644 (file)
@@ -95,7 +95,6 @@ class VersionNumberUtility {
         * Removes -dev -alpha -beta -RC states from a version number
         * and replaces them by .0
         *
-        * @static
         * @return string
         */
        static public function getNumericTypo3Version() {
@@ -111,10 +110,9 @@ class VersionNumberUtility {
         * Wrapper function for TYPO3_version constant to make functions using
         * the constant unit testable
         *
-        * @static
         * @return string
         */
-       static protected function getCurrentTypo3Version() {
+       static public function getCurrentTypo3Version() {
                return TYPO3_version;
        }
 
index aabdf0e..39a816c 100644 (file)
@@ -74,6 +74,7 @@ abstract class AbstractAction {
         * @return string content
         */
        protected function initializeHandle() {
+               /** @var \TYPO3\CMS\Install\Status\StatusUtility $statusUtility */
                $statusUtility = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\StatusUtility');
 
                // Count of failed environment checks are displayed in the left navigation menu
diff --git a/typo3/sysext/install/Classes/Controller/Action/Ajax/AbstractCoreUpdate.php b/typo3/sysext/install/Classes/Controller/Action/Ajax/AbstractCoreUpdate.php
new file mode 100644 (file)
index 0000000..7df66bf
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+namespace TYPO3\CMS\Install\Controller\Action\Ajax;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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;
+
+/**
+ * Abstract core update class contains general core update
+ * related methods
+ */
+abstract class AbstractCoreUpdate extends Action\AbstractAction {
+
+       /**
+        * @var \TYPO3\CMS\Install\View\JsonView
+        * @inject
+        */
+       protected $view = NULL;
+
+       /**
+        * @var \TYPO3\CMS\Install\Service\CoreUpdateService
+        * @inject
+        */
+       protected $coreUpdateService;
+
+       /**
+        * @var \TYPO3\CMS\Install\Status\StatusUtility
+        * @inject
+        */
+       protected $statusUtility;
+
+       /**
+        * @var \TYPO3\CMS\Install\Service\CoreVersionService
+        * @inject
+        */
+       protected $coreVersionService;
+
+       /**
+        * Common stuff to be done before performing update tasks
+        *
+        * @return void
+        */
+       protected function initializeCoreUpdate() {
+               $this->loadExtLocalconfDatabaseAndExtTables();
+       }
+
+       /**
+        * Find out which version upgrade should be handled. This may
+        * be different depending on whether development or regular release.
+        *
+        * @throws \TYPO3\CMS\Install\Controller\Exception
+        * @return string Version to handle, eg. 6.2.2
+        */
+       protected function getVersionToHandle() {
+               $getVars = \TYPO3\CMS\Core\Utility\GeneralUtility::_GET('install');
+               if (!isset($getVars['type'])) {
+                       throw new \TYPO3\CMS\Install\Controller\Exception(
+                               'Type must be set to either "regular" or "development"',
+                               1380975303
+                       );
+               }
+               $type = $getVars['type'];
+               if ($type === 'development') {
+                       $versionToHandle = $this->coreVersionService->getYoungestPatchDevelopmentRelease();
+               } else {
+                       $versionToHandle = $this->coreVersionService->getYoungestPatchRelease();
+               }
+               return $versionToHandle;
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateActivate.php b/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateActivate.php
new file mode 100644 (file)
index 0000000..7b59074
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+namespace TYPO3\CMS\Install\Controller\Action\Ajax;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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;
+
+/**
+ * Activate a downloaded core
+ */
+class CoreUpdateActivate extends AbstractCoreUpdate implements Action\ActionInterface {
+       /**
+        * Handle this action
+        *
+        * @return string content
+        */
+       public function handle() {
+               $this->initializeCoreUpdate();
+               $this->view->assignMultiple(array(
+                               'success' => $this->coreUpdateService->activateVersion($this->getVersionToHandle()),
+                               'status' => $this->coreUpdateService->getMessages(),
+                       ));
+               return $this->view->render();
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateCheckPreConditions.php b/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateCheckPreConditions.php
new file mode 100644 (file)
index 0000000..9fb1723
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+namespace TYPO3\CMS\Install\Controller\Action\Ajax;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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;
+
+/**
+ * Activate a downloaded core
+ */
+class CoreUpdateCheckPreConditions extends AbstractCoreUpdate implements Action\ActionInterface {
+       /**
+        * Handle this action
+        *
+        * @return string content
+        */
+       public function handle() {
+               $this->initializeCoreUpdate();
+               $this->view->assignMultiple(array(
+                               'success' => $this->coreUpdateService->checkPreConditions(),
+                               'status' => $this->coreUpdateService->getMessages(),
+                       ));
+               return $this->view->render();
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateDownload.php b/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateDownload.php
new file mode 100644 (file)
index 0000000..b417f1f
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+namespace TYPO3\CMS\Install\Controller\Action\Ajax;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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;
+
+/**
+ * Download latest core version
+ */
+class CoreUpdateDownload extends AbstractCoreUpdate implements Action\ActionInterface {
+       /**
+        * Handle this action
+        *
+        * @return string content
+        */
+       public function handle() {
+               $this->initializeCoreUpdate();
+               $this->view->assignMultiple(array(
+                               'success' => $this->coreUpdateService->downloadVersion($this->getVersionToHandle()),
+                               'status' => $this->coreUpdateService->getMessages(),
+                       ));
+               return $this->view->render();
+
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateIsUpdateAvailable.php b/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateIsUpdateAvailable.php
new file mode 100644 (file)
index 0000000..25fb686
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+namespace TYPO3\CMS\Install\Controller\Action\Ajax;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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;
+
+/**
+ * Check if a younger version is available
+ */
+class CoreUpdateIsUpdateAvailable extends AbstractCoreUpdate implements Action\ActionInterface {
+       /**
+        * Handle this action
+        *
+        * @return string content
+        */
+       public function handle() {
+               $this->initializeCoreUpdate();
+
+               $status = array();
+               if ($this->coreVersionService->isInstalledVersionAReleasedVersion()) {
+                       $isDevelopmentUpdateAvailable = $this->coreVersionService->isYoungerPatchDevelopmentReleaseAvailable();
+                       $isUpdateAvailable = $this->coreVersionService->isYoungerPatchReleaseAvailable();
+                       $isUpdateSecurityRelevant = $this->coreVersionService->isUpdateSecurityRelevant();
+
+                       if (!$isUpdateAvailable && !$isDevelopmentUpdateAvailable) {
+                               $status = $this->getMessage('notice', 'No regular update available');
+                       } elseif ($isUpdateAvailable) {
+                               $newVersion = $this->coreVersionService->getYoungestPatchRelease();
+                               if ($isUpdateSecurityRelevant) {
+                                       $status = $this->getMessage('warning', 'Update to security relevant released version ' . $newVersion . ' is available!');
+                                       $action = $this->getAction('Update now', 'updateRegular');
+                               } else {
+                                       $status = $this->getMessage('info', 'Update to regular released version ' . $newVersion . ' is available!');
+                                       $action = $this->getAction('Update now', 'updateRegular');
+                               }
+                       } elseif ($isDevelopmentUpdateAvailable) {
+                               $newVersion = $this->coreVersionService->getYoungestPatchDevelopmentRelease();
+                               $status = $this->getMessage('info', 'Update to development release ' . $newVersion . ' is available!');
+                               $action = $this->getAction('Update now', 'updateDevelopment');
+                       }
+               } else {
+                       $status = $this->getMessage('warning', 'Current version is a development version and can not be updated');
+               }
+
+               $this->view->assign('success', TRUE);
+               $this->view->assign('status', array($status));
+               if (isset($action)) {
+                       $this->view->assign('action', $action);
+               }
+
+               return $this->view->render();
+       }
+
+       /**
+        * @param string $severity
+        * @param string $title
+        * @param string $message
+        * @return \TYPO3\CMS\Install\Status\StatusInterface
+        */
+       protected function getMessage($severity, $title, $message = '') {
+               /** @var $statusMessage \TYPO3\CMS\Install\Status\StatusInterface */
+               $statusMessage = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\' . ucfirst($severity) . 'Status');
+               $statusMessage->setTitle($title);
+               $statusMessage->setMessage($message);
+
+               return $statusMessage;
+       }
+
+       /**
+        * @param string $title
+        * @param string $action
+        * @return array
+        */
+       protected function getAction($title, $action) {
+               return array(
+                       'title' => $title,
+                       'action' => $action,
+               );
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateMove.php b/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateMove.php
new file mode 100644 (file)
index 0000000..e9e41bd
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+namespace TYPO3\CMS\Install\Controller\Action\Ajax;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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;
+
+/**
+ * Move a downloaded core
+ */
+class CoreUpdateMove extends AbstractCoreUpdate implements Action\ActionInterface {
+       /**
+        * Handle this action
+        *
+        * @return string content
+        */
+       public function handle() {
+               $this->initializeCoreUpdate();
+               $this->view->assignMultiple(array(
+                               'success' => $this->coreUpdateService->moveVersion($this->getVersionToHandle()),
+                               'status' => $this->coreUpdateService->getMessages(),
+                       ));
+               return $this->view->render();
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateUnpack.php b/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateUnpack.php
new file mode 100644 (file)
index 0000000..ceed025
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+namespace TYPO3\CMS\Install\Controller\Action\Ajax;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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;
+
+/**
+ * Unpack a downloaded core
+ */
+class CoreUpdateUnpack extends AbstractCoreUpdate implements Action\ActionInterface {
+       /**
+        * Handle this action
+        *
+        * @return string content
+        */
+       public function handle() {
+               $this->initializeCoreUpdate();
+               $this->view->assignMultiple(array(
+                               'success' => $this->coreUpdateService->unpackVersion($this->getVersionToHandle()),
+                               'status' => $this->coreUpdateService->getMessages(),
+                       ));
+               return $this->view->render();
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateUpdateVersionMatrix.php b/typo3/sysext/install/Classes/Controller/Action/Ajax/CoreUpdateUpdateVersionMatrix.php
new file mode 100644 (file)
index 0000000..159d497
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+namespace TYPO3\CMS\Install\Controller\Action\Ajax;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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;
+
+/**
+ * Update version matrix from get.typo3.org
+ */
+class CoreUpdateUpdateVersionMatrix extends AbstractCoreUpdate implements Action\ActionInterface {
+       /**
+        * Handle this action
+        *
+        * @return string content
+        */
+       public function handle() {
+               $this->initializeCoreUpdate();
+               $this->view->assignMultiple(array(
+                               'success' => $this->coreUpdateService->updateVersionMatrix(),
+                               'status' => $this->coreUpdateService->getMessages(),
+                       ));
+               return $this->view->render();
+       }
+}
\ No newline at end of file
index c58ec5a..224f26e 100644 (file)
@@ -44,7 +44,14 @@ class AjaxController extends AbstractController {
        protected $authenticationActions = array(
                'extensionCompatibilityTester',
                'uninstallExtension',
-               'clearCache'
+               'clearCache',
+               'coreUpdateUpdateVersionMatrix',
+               'coreUpdateIsUpdateAvailable',
+               'coreUpdateCheckPreConditions',
+               'coreUpdateDownload',
+               'coreUpdateUnpack',
+               'coreUpdateMove',
+               'coreUpdateActivate',
        );
 
        /**
@@ -163,10 +170,10 @@ class AjaxController extends AbstractController {
         * @param string $content Content to output
         */
        protected function output($content = '') {
-               header('Content-Type: text/html; charset=utf-8');
+               header('Content-Type: application/json; charset=utf-8');
                header('Cache-Control: no-cache, must-revalidate');
                header('Pragma: no-cache');
-               echo $content;
+               echo json_encode($content);
                die;
        }
 }
diff --git a/typo3/sysext/install/Classes/Service/CoreUpdateService.php b/typo3/sysext/install/Classes/Service/CoreUpdateService.php
new file mode 100644 (file)
index 0000000..8e5992f
--- /dev/null
@@ -0,0 +1,411 @@
+<?php
+namespace TYPO3\CMS\Install\Service;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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!
+ ***************************************************************/
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Core update service.
+ * This service handles core updates, all the nasty details are encapsulated
+ * here. The single public methods 'depend' on each other, for example a new
+ * core has to be downloaded before it can be unpacked.
+ *
+ * Each method returns only TRUE of FALSE indicating if it was successful or
+ * not. Detailed information can be fetched with getMessages() and will return
+ * a list of status messages of the previous operation.
+ */
+class CoreUpdateService {
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Object\ObjectManager
+        * @inject
+        */
+       protected $objectManager;
+
+       /**
+        * @var \TYPO3\CMS\Install\Service\CoreVersionService
+        * @inject
+        */
+       protected $coreVersionService;
+
+       /**
+        * @var array<\TYPO3\CMS\Install\Status\StatusInterface>
+        */
+       protected $messages = array();
+
+       /**
+        * Absolute path to download location
+        *
+        * @var string
+        */
+       protected $downloadTargetPath;
+
+       /**
+        * Absolute path to the current core files
+        *
+        * @var string
+        */
+       protected $currentCoreLocation;
+
+       /**
+        * Base URI for TYPO3 downloads
+        *
+        * @var string
+        */
+       protected $downloadBaseUri;
+
+       /**
+        * Initialize update paths
+        */
+       public function initializeObject() {
+               $this->setDownloadTargetPath(PATH_site . 'typo3temp/core-update/');
+               $this->currentCoreLocation = $this->discoverCurrentCoreLocation();
+               $this->downloadBaseUri = $this->coreVersionService->getDownloadBaseUri();
+       }
+
+       /**
+        * In future implementations we might implement some sarter logic here
+        *
+        * @return string
+        */
+       protected function discoverCurrentCoreLocation() {
+               return PATH_site . 'typo3_src';
+       }
+
+       /**
+        * Create download location in case the folder does not exist
+        * @todo move this to folder structure
+        *
+        * @param string $downloadTargetPath
+        */
+       protected function setDownloadTargetPath($downloadTargetPath) {
+               if (!is_dir($downloadTargetPath)) {
+                       GeneralUtility::mkdir_deep($this->downloadTargetPath);
+               }
+               $this->downloadTargetPath = $downloadTargetPath;
+       }
+
+       /**
+        * Get messages of previous method call
+        *
+        * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
+        */
+       public function getMessages() {
+               return $this->messages;
+       }
+
+       /**
+        * Wrapper method for CoreVersionService
+        *
+        * @return boolean TRUE on success
+        */
+       public function updateVersionMatrix() {
+               $success = TRUE;
+               try {
+                       $this->coreVersionService->updateVersionMatrix();
+               } catch (\TYPO3\CMS\Install\Service\Exception\RemoteFetchException $e) {
+                       $success = FALSE;
+                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                       $message->setTitle('Version matrix could not be fetched from get.typo3.org');
+                       $message->setMessage(
+                               'Current version specification could not be fetched from http://get.typo3.org/json.'
+                               . ' This is probably a network issue, please fix it.'
+                       );
+                       $this->messages = array($message);
+               }
+               return $success;
+       }
+
+       /**
+        * Check if an update is possible at all
+        *
+        * @return boolean TRUE on success
+        */
+       public function checkPreConditions() {
+               $success = TRUE;
+               $messages = array();
+
+               /** @var \TYPO3\CMS\Install\Status\StatusUtility $statusUtility */
+               $statusUtility = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\StatusUtility');
+
+               // Folder structure test: Update can be done only if folder structure returns no errors
+               /** @var $folderStructureFacade \TYPO3\CMS\Install\FolderStructure\StructureFacade */
+               $folderStructureFacade = $this->objectManager->get('TYPO3\\CMS\\Install\\FolderStructure\\DefaultFactory')->getStructure();
+               $folderStructureErrors = $statusUtility->filterBySeverity($folderStructureFacade->getStatus(), 'error');
+               $folderStructureWarnings = $statusUtility->filterBySeverity($folderStructureFacade->getStatus(), 'warning');
+               if (count($folderStructureErrors) > 0 || count($folderStructureWarnings) > 0) {
+                       $success = FALSE;
+                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                       $message->setTitle('Automatic core update not possible: Folder structure has errors or warnings');
+                       $message->setMessage(
+                               'To perform an update the folder structure of this TYPO3 CMS instance must'
+                               . ' stick to the conventions, or the update process could lead to unexpected'
+                               . ' results and may commit hazard to your system'
+                       );
+                       $messages[] = $message;
+               }
+
+               // No core update on windows
+               if (TYPO3_OS === 'WIN') {
+                       $success = FALSE;
+                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                       $message->setTitle('Automatic core update not possible: Update not supported on Windows OS');
+                       $messages[] = $message;
+               }
+
+               if ($success) {
+                       // Explicit write check to document root
+                       $file = PATH_site . uniqid('install-core-update-test-');
+                       $result = @touch($file);
+                       if (!$result) {
+                               $success = FALSE;
+                               /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                               $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                               $message->setTitle('Automatic core update not possible: No write access to document root');
+                               $message->setMessage('Could not write a file in path "' . PATH_site . '"!');
+                               $messages[] = $message;
+                       } else {
+                               unlink($file);
+                       }
+
+                       // Explicit write check to upper directory of current core location
+                       $coreLocation = @realPath($this->currentCoreLocation . '/../');
+                       $file = $coreLocation . '/' . uniqid('install-core-update-test-');
+                       $result = @touch($file);
+                       if (!$result) {
+                               $success = FALSE;
+                               /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                               $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                               $message->setTitle('Automatic core update not possible: No write access to core location');
+                               $message->setMessage(
+                                       'New core should be installed in "' . $coreLocation . '", but this directory is not writable!'
+                               );
+                               $messages[] = $message;
+                       } else {
+                               unlink($file);
+                       }
+               }
+
+               if ($success && !$this->coreVersionService->isInstalledVersionAReleasedVersion()) {
+                       $success = FALSE;
+                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                       $message->setTitle('Automatic core update not possible: You are running a development version of TYPO3');
+                       $message->setMessage(
+                               'Your current version is specified as ' . $this->coreVersionService->getInstalledVersion() . '.'
+                               . ' This is a development version and can not be updated automatically. If this is a "git"'
+                               . ' checkout, please update using git directly.'
+                       );
+                       $messages[] = $message;
+               }
+
+               $this->messages = $messages;
+               return $success;
+       }
+
+       /**
+        * Download the specified version
+        *
+        * @param string $version A version to download
+        * @return boolean TRUE on success
+        */
+       public function downloadVersion($version) {
+               $downloadUri = $this->downloadBaseUri . $version;
+               $fileLocation = $this->downloadTargetPath . $version . '.tar.gz';
+
+               $messages = array();
+               $success = TRUE;
+
+               if (@file_exists($fileLocation)) {
+                       $success = FALSE;
+                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                       $message->setTitle('Core download exists in download location: ' . substr($this->downloadTargetPath, strlen(PATH_site)));
+                       $messages[] = $message;
+               } else {
+                       $fileContent = GeneralUtility::getUrl($downloadUri);
+                       if (!$fileContent) {
+                               $success = FALSE;
+                               /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                               $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                               $message->setTitle('Download not successful');
+                               $messages[] = $message;
+                       } else {
+                               $fileStoreResult = file_put_contents($fileLocation, $fileContent);
+                               if (!$fileStoreResult) {
+                                       $success = FALSE;
+                                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                                       $message->setTitle('Unable to store download content');
+                                       $messages[] = $message;
+                               }
+                       }
+               }
+               $this->messages = $messages;
+               return $success;
+       }
+
+       /**
+        * Unpack a downloaded core
+        *
+        * @param string $version A version to unpack
+        * @return boolean TRUE on success
+        */
+       public function unpackVersion($version) {
+               $fileLocation = $this->downloadTargetPath . $version . '.tar.gz';
+
+               $messages = array();
+               $success = TRUE;
+
+               if (!@is_file($fileLocation)) {
+                       $success = FALSE;
+                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                       $message->setTitle('Downloaded core not found');
+                       $messages[] = $message;
+               } elseif (@file_exists($this->downloadTargetPath . 'typo3_src-' . $version)) {
+                       $success = FALSE;
+                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                       $message->setTitle('Unpacked core exists in download location: ' . substr($this->downloadTargetPath, strlen(PATH_site)));
+                       $messages[] = $message;
+               } else {
+                       $unpackCommand = 'tar xf ' . escapeshellarg($fileLocation) . ' -C ' . escapeshellarg($this->downloadTargetPath) . ' 2>&1';
+                       exec($unpackCommand, $output, $errorCode);
+                       if ($errorCode) {
+                               $success = FALSE;
+                               /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                               $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                               $message->setTitle('Unpacking core not successful');
+                               $messages[] = $message;
+                       } else {
+                               $removePackedFileResult = unlink($fileLocation);
+                               if (!$removePackedFileResult) {
+                                       $success = FALSE;
+                                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                                       $message->setTitle('Removing packed core not successful');
+                                       $messages[] = $message;
+                               }
+                       }
+               }
+               $this->messages = $messages;
+               return $success;
+       }
+
+       /**
+        * Move an unpacked core to its final destination
+        *
+        * @param string $version A version to move
+        * @return boolean TRUE on success
+        */
+       public function moveVersion($version) {
+               $downloadedCoreLocation = $this->downloadTargetPath . 'typo3_src-' . $version;
+               $newCoreLocation = @realPath($this->currentCoreLocation . '/../') . '/typo3_src-' . $version;
+
+               $messages = array();
+               $success = TRUE;
+
+               if (!@is_dir($downloadedCoreLocation)) {
+                       $success = FALSE;
+                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                       $message->setTitle('Unpacked core not found');
+                       $messages[] = $message;
+               } elseif (@is_dir($newCoreLocation)) {
+                       $success = FALSE;
+                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                       $message->setTitle('Another core source directory already exists in path ' . $newCoreLocation);
+                       $messages[] = $message;
+               } else {
+                       $moveResult = rename($downloadedCoreLocation, $newCoreLocation);
+                       if (!$moveResult) {
+                               $success = FALSE;
+                               /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                               $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                               $message->setTitle('Moving core to ' . $newCoreLocation . ' failed');
+                               $messages[] = $message;
+                       }
+               }
+
+               $this->messages = $messages;
+               return $success;
+       }
+
+       /**
+        * Activate a core version
+        *
+        * @param string $version A version to activate
+        * @return boolean TRUE on success
+        */
+       public function activateVersion($version) {
+               $newCoreLocation = @realPath($this->currentCoreLocation . '/../') . '/typo3_src-' . $version;
+
+               $messages = array();
+               $success = TRUE;
+
+               if (!is_dir($newCoreLocation)) {
+                       $success = FALSE;
+                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                       $message->setTitle('New core not found');
+                       $messages[] = $message;
+               } elseif (!is_link($this->currentCoreLocation)) {
+                       $success = FALSE;
+                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                       $message->setTitle('TYPO3 source directory (typo3_src) is not a link');
+                       $messages[] = $message;
+               } else {
+                       $unlinkResult = unlink($this->currentCoreLocation);
+                       if (!$unlinkResult) {
+                               $success = FALSE;
+                               /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                               $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                               $message->setTitle('Removing old symlink failed');
+                               $messages[] = $message;
+                       } else {
+                               $symlinkResult = symlink($newCoreLocation, $this->currentCoreLocation);
+                               if (!$symlinkResult) {
+                                       $success = FALSE;
+                                       /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
+                                       $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
+                                       $message->setTitle('Linking new core failed');
+                                       $messages[] = $message;
+                               }
+                       }
+               }
+
+               $this->messages = $messages;
+               return $success;
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/install/Classes/Service/CoreVersionService.php b/typo3/sysext/install/Classes/Service/CoreVersionService.php
new file mode 100644 (file)
index 0000000..6464d43
--- /dev/null
@@ -0,0 +1,312 @@
+<?php
+namespace TYPO3\CMS\Install\Service;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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!
+ ***************************************************************/
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\VersionNumberUtility;
+
+/**
+ * Core version service
+ */
+class CoreVersionService {
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Object\ObjectManager
+        * @inject
+        */
+       protected $objectManager;
+
+       /**
+        * @var \TYPO3\CMS\Core\Registry
+        * @inject
+        */
+       protected $registry;
+
+       /**
+        * Base URI for TYPO3 downloads
+        *
+        * @var string
+        */
+       protected $downloadBaseUri;
+
+       /**
+        * @return mixed
+        */
+       public function getDownloadBaseUri() {
+               return $this->downloadBaseUri;
+       }
+
+       /**
+        * Initialize update URI
+        */
+       public function __construct() {
+               $this->downloadBaseUri = 'https://get.typo3.org/';
+       }
+
+       /**
+        * Update version matrix from remote and store in registry
+        *
+        * @return void
+        * @throws Exception\RemoteFetchException
+        */
+       public function updateVersionMatrix() {
+               $versionArray = $this->fetchVersionMatrixFromRemote();
+               // This is a 'hack' to keep the string stored in the registry small. We are usually only
+               // interested in information from 6.2 and up and older releases do not matter in current
+               // use cases. If this unset() is removed and everything is stored for some reason, the
+               // table sys_file field entry_value needs to be extended from blob to longblob.
+               unset($versionArray['6.1'], $versionArray['6.0'], $versionArray['4.7'], $versionArray['4.6'],
+                       $versionArray['4.5'], $versionArray['4.4'], $versionArray['4.3'], $versionArray['4.2'],
+                       $versionArray['4.1'], $versionArray['4.0'], $versionArray['3.8'], $versionArray['3.7'],
+                       $versionArray['3.6'], $versionArray['3.5'], $versionArray['3.3']);
+               $this->registry->set('TYPO3.CMS.Install', 'coreVersionMatrix', $versionArray);
+       }
+
+       /**
+        * Development git checkout versions always end with '-dev'. They are
+        * not "released" as such and can not be updated.
+        *
+        * @return boolean FALSE If some development version is installed
+        */
+       public function isInstalledVersionAReleasedVersion() {
+               $version = $this->getInstalledVersion();
+               return substr($version, -4) !== '-dev';
+       }
+
+       /**
+        * Get current installed version number
+        *
+        * @return string
+        */
+       public function getInstalledVersion() {
+               return VersionNumberUtility::getCurrentTypo3Version();
+       }
+
+       /**
+        * Returns TRUE if a younger patch level release exists in version matrix.
+        *
+        * @return boolean TRUE if younger patch release is exists
+        */
+       public function isYoungerPatchReleaseAvailable() {
+               $result = FALSE;
+               $version = $this->getInstalledVersion();
+               $youngestVersion = $this->getYoungestPatchRelease();
+               if ($youngestVersion !== $version) {
+                       $result = TRUE;
+               }
+               return $result;
+       }
+
+       /**
+        * Returns TRUE if a younger patch level release exists in version matrix that may be a development release.
+        *
+        * @return boolean TRUE if younger patch release is exists
+        */
+       public function isYoungerPatchDevelopmentReleaseAvailable() {
+               $result = FALSE;
+               $version = $this->getInstalledVersion();
+               $youngestVersion = $this->getYoungestPatchDevelopmentRelease();
+               if ($youngestVersion !== $version) {
+                       $result = TRUE;
+               }
+               return $result;
+       }
+
+       /**
+        * Returns TRUE if an upgrade from current version is security relevant
+        *
+        * @return boolean TRUE if there is a pending security update
+        */
+       public function isUpdateSecurityRelevant() {
+               $result = FALSE;
+               $version = $this->getInstalledVersion();
+               $youngestVersion = $this->getYoungestReleaseByType(array('security'));
+               if ($youngestVersion !== $version) {
+                       $result = TRUE;
+               }
+               return $result;
+       }
+
+       /**
+        * Youngest patch release, eg. 6.2.2
+        *
+        * @return string Version string of youngest patch level release
+        */
+       public function getYoungestPatchRelease() {
+               return $this->getYoungestReleaseByType(array('release', 'security', 'regular'));
+       }
+
+       /**
+        * Youngest development patch release, eg. 6.2.0alpha3 or 6.2-snapshot-20131004
+        *
+        * @return string
+        */
+       public function getYoungestPatchDevelopmentRelease() {
+               return $this->getYoungestReleaseByType(array('release', 'security', 'regular', 'development'));
+       }
+
+       /**
+        * Get youngest release version string.
+        * Returns same version number if no younger release was found.
+        *
+        * @param array $types List of allowed types: development, release, security, regular
+        * @throws Exception\CoreVersionServiceException
+        * @return string Youngest release, eg. 6.2.3 or 6.2.alpha3
+        */
+       protected function getYoungestReleaseByType(array $types) {
+               $version = $this->getInstalledVersion();
+
+               $minorVersion = $this->getMinorVersion($version);
+               $versionMatrix = $this->getVersionMatrix();
+
+               $youngestRelease = $version;
+               $versionReleaseTimestamp = $this->getReleaseTimestampOfVersion($version);
+
+               $patchLevelVersions = $versionMatrix[$minorVersion]['releases'];
+               foreach ($patchLevelVersions as $aVersionNumber => $aVersionDetails) {
+                       if (!array_key_exists('type', $aVersionDetails)) {
+                               throw new Exception\CoreVersionServiceException(
+                                       'Release type of version ' . $aVersionNumber . ' not found in version matrix.'
+                                               . ' This is probably a bug on get.typo3.org.',
+                                       1380909029
+                               );
+                       }
+                       $type = $aVersionDetails['type'];
+                       $aVersionNumberReleaseTimestamp = $this->getReleaseTimestampOfVersion($aVersionNumber);
+                       if (
+                               $aVersionNumberReleaseTimestamp > $versionReleaseTimestamp
+                               && in_array($type, $types)
+                       ) {
+                               $youngestRelease = $aVersionNumber;
+                               $versionReleaseTimestamp = $aVersionNumberReleaseTimestamp;
+                       }
+               }
+               return $youngestRelease;
+       }
+
+       /**
+        * Get 'minor version' from version string, eg '6.2' from '6.2.2'
+        *
+        * @return string For example 6.2
+        */
+       protected function getInstalledMinorVersion() {
+               return $this->getMinorVersion($this->getInstalledVersion());
+       }
+
+       /**
+        * Get 'minor version' of version, eg. '6.2' from '6.2.2'
+        *
+        * @param string $version to check
+        * @return string Minor version, eg. '6.2'
+        */
+       protected function getMinorVersion($version) {
+               $explodedVersion = explode('.', $version);
+               $minor = explode('-', $explodedVersion[1]);
+               return $explodedVersion[0] . '.' . $minor[0];
+       }
+
+       /**
+        * Get version matrix from registry
+        *
+        * @return array
+        * @throws Exception
+        */
+       protected function getVersionMatrix() {
+               $versionMatrix = $this->registry->get('TYPO3.CMS.Install', 'coreVersionMatrix');
+               if (empty($versionMatrix) || !is_array($versionMatrix)) {
+                       throw new Exception\CoreVersionServiceException(
+                               'No version matrix found in registry, call updateVersionMatrix() first.',
+                               1380898792
+                       );
+               }
+               return $versionMatrix;
+       }
+
+       /**
+        * Get available version string from get.typo3.org
+        *
+        * @return array
+        * @throws Exception\RemoteFetchException
+        */
+       protected function fetchVersionMatrixFromRemote() {
+               $url = $this->downloadBaseUri . 'json';
+               $versionJson = GeneralUtility::getUrl($url);
+               if (!$versionJson) {
+                       throw new Exception\RemoteFetchException(
+                               'Fetching ' . $url . ' failed. Maybe this instance can not connect to the remote system properly.',
+                               1380897593
+                       );
+               }
+               return json_decode($versionJson, TRUE);
+       }
+
+       /**
+        * Returns release timestamp of a specific version
+        *
+        * @param $version String to check in version matrix, eg. 6.2.0alpha3 or 6.2.2
+        * @throws Exception\CoreVersionServiceException
+        * @return integer Timestamp of release
+        */
+       protected function getReleaseTimestampOfVersion($version) {
+               $minorVersion = $this->getMinorVersion($version);
+               $versionMatrix = $this->getVersionMatrix();
+               $this->ensureVersionExistsInMatrix($version);
+               if (!array_key_exists('date', $versionMatrix[$minorVersion]['releases'][$version])) {
+                       throw new Exception\CoreVersionServiceException(
+                               'Release date of version ' . $version . ' not found in version matrix. This is probably a bug on get.typo3.org',
+                               1380905853
+                       );
+               }
+               $dateString = $versionMatrix[$minorVersion]['releases'][$version]['date'];
+               $date = new \DateTime($dateString);
+               return $date->getTimestamp();
+       }
+
+       /**
+        * Throws an exception if specified version does not exist in version matrix
+        *
+        * @param $version String to check in version matrix, eg. 6.2.0alpha3 or 6.2.2
+        * @throws Exception\CoreVersionServiceException
+        */
+       protected function ensureVersionExistsInMatrix($version) {
+               $minorVersion = $this->getMinorVersion($version);
+               $versionMatrix = $this->getVersionMatrix();
+               if (!array_key_exists($minorVersion, $versionMatrix)) {
+                       throw new Exception\CoreVersionServiceException(
+                               'Minor release ' . $minorVersion . ' not found in version matrix.',
+                               1380905851
+                       );
+               }
+               if (!array_key_exists($version, $versionMatrix[$minorVersion]['releases'])) {
+                       throw new Exception\CoreVersionServiceException(
+                               'Patch level release ' . $version . ' not found in version matrix.',
+                               1380905852
+                       );
+               }
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/install/Classes/Service/Exception.php b/typo3/sysext/install/Classes/Service/Exception.php
new file mode 100644 (file)
index 0000000..8a3fadd
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+namespace TYPO3\CMS\Install\Service;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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!
+ ***************************************************************/
+
+/**
+ * A service exception
+ */
+class Exception extends \TYPO3\CMS\Install\Exception {
+
+}
diff --git a/typo3/sysext/install/Classes/Service/Exception/CoreVersionServiceException.php b/typo3/sysext/install/Classes/Service/Exception/CoreVersionServiceException.php
new file mode 100644 (file)
index 0000000..f61b7da
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+namespace TYPO3\CMS\Install\Service\Exception;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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!
+ ***************************************************************/
+
+/**
+ * An exception thrown if version validation against official version matrix fails
+ */
+class CoreVersionServiceException extends \TYPO3\CMS\Install\Service\Exception {
+
+}
diff --git a/typo3/sysext/install/Classes/Service/Exception/RemoteFetchException.php b/typo3/sysext/install/Classes/Service/Exception/RemoteFetchException.php
new file mode 100644 (file)
index 0000000..ecabf60
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+namespace TYPO3\CMS\Install\Service\Exception;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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!
+ ***************************************************************/
+
+/**
+ * An exception thrown if fetching a resource from a remote server fails
+ */
+class RemoteFetchException extends CoreVersionServiceException {
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/install/Classes/View/JsonView.php b/typo3/sysext/install/Classes/View/JsonView.php
new file mode 100644 (file)
index 0000000..9b9f144
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+namespace TYPO3\CMS\Install\View;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Helmut Hummel <helmut.hummel@typo3.org>
+ *  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\Extbase\Mvc\View\AbstractView;
+use TYPO3\CMS\Install\Status\StatusInterface;
+use TYPO3\CMS\Install\Status\Exception as StatusException;
+
+/**
+ * Simple JsonView (currently returns an associative array)
+ */
+class JsonView extends AbstractView {
+
+       /**
+        * @return string
+        */
+       public function render() {
+               $renderedData = $this->variables;
+               if (isset($renderedData['status']) && is_array($renderedData['status'])) {
+                       try {
+                               $renderedData['status'] = $this->transformStatusMessagesToArray($renderedData['status']);
+                       } catch (StatusException $e) {
+                               $renderedData['status'] = array(array(
+                                       'severity' => 'error',
+                                       'title' => htmlspecialchars($e->getMessage())
+                               ));
+                       }
+               }
+
+               return $renderedData;
+       }
+
+       /**
+        * Transform an array of messages to an associative array.
+        *
+        * @param array<StatusInterface>
+        * @return array
+        * @throws StatusException
+        */
+       protected function transformStatusMessagesToArray(array $statusArray = array()) {
+               $result = array();
+               foreach ($statusArray as $status) {
+                       if (!$status instanceof StatusInterface) {
+                               throw new StatusException (
+                                       'Object must implement StatusInterface',
+                                       1381059600
+                               );
+                       }
+                       $result[] = $this->transformStatusToArray($status);
+               }
+               return $result;
+       }
+
+       /**
+        * Creates an array from a status object.
+        * Used for example to transfer the message as json.
+        *
+        * @param StatusInterface $status
+        * @return array
+        */
+       public function transformStatusToArray(StatusInterface $status) {
+               $arrayStatus = array();
+               $arrayStatus['severity'] = htmlspecialchars($status->getSeverity());
+               $arrayStatus['title'] = htmlspecialchars($status->getTitle());
+               $arrayStatus['message'] = htmlspecialchars($status->getMessage());
+               return $arrayStatus;
+       }
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Private/Partials/Action/Tool/ImportantActions/CoreUpdateButton.html b/typo3/sysext/install/Resources/Private/Partials/Action/Tool/ImportantActions/CoreUpdateButton.html
new file mode 100644 (file)
index 0000000..30cb8b8
--- /dev/null
@@ -0,0 +1,31 @@
+<h4>Core update</h4>
+
+<p>
+       The install tool can automatically update the TYPO3 CMS core to its latest
+       minor release if certain criteria are met.
+</p>
+<div id="coreUpdate">
+       <div id="messageTemplate">
+               <div class="typo3-message">
+                       <div class="header-container">
+                               <div class="message-header">
+                                       <strong></strong>
+                               </div>
+                       </div>
+                       <div class="message-body">
+                       </div>
+               </div>
+               <p></p>
+       </div>
+       <div id="buttonTemplate">
+               <fieldset class="t3-install-form-submit">
+                       <ol>
+                               <li>
+                                       <button class="btn" type="submit" name="coreUpdateCheckForUpdate" data-action="checkForUpdate">
+                                               Check for core updates
+                                       </button>
+                               </li>
+                       </ol>
+               </fieldset>
+       </div>
+</div>
\ No newline at end of file
index 268eb06..8f13e2a 100644 (file)
@@ -28,6 +28,9 @@
        <f:render partial="Action/Tool/ImportantActions/ExtensionCompatibilityTester" arguments="{_all}"/>
        <hr />
 
+       <f:render partial="Action/Tool/ImportantActions/CoreUpdateButton" arguments="{_all}"/>
+       <hr />
+
        <f:render partial="Action/Tool/ImportantActions/NewInstallToolPassword" arguments="{_all}"/>
        <hr />
 
index 7d0e6ea..4c1ce04 100644 (file)
  ***************************************************************/
 
 /**
- * Small javascript helpers of the install tool based on jquery
- *
- * @author Wouter Wolters <typo3@wouterwolters.nl>
+ * Handle core update
  */
+var TYPO3 = {};
+TYPO3.Install = {};
+TYPO3.Install.coreUpdate = {
+       /**
+        * The action queue defines what actions are called in which order
+        */
+       actionQueue: {
+               coreUpdateUpdateVersionMatrix: {
+                       loadingMessage: 'Fetching list of released versions from typo3.org',
+                       finishMessage: 'Fetched list of released versions',
+                       nextActionName: 'coreUpdateIsUpdateAvailable'
+               },
+               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: 'Core download finished',
+                       nextActionName: 'coreUpdateUnpack'
+               },
+               coreUpdateUnpack: {
+                       loadingMessage: 'Unpacking core',
+                       finishMessage: 'Unpacking core successful',
+                       nextActionName: 'coreUpdateMove'
+               },
+               coreUpdateMove: {
+                       loadingMessage: 'Moving core',
+                       finishMessage: 'Moved core to final location',
+                       nextActionName: 'clearCache'
+               },
+               clearCache: {
+                       loadingMessage: 'Clearing caches',
+                       finishMessage: 'Caches cleared',
+                       nextActionName: 'coreUpdateActivate'
+               },
+               coreUpdateActivate: {
+                       loadingMessage: 'Activating core',
+                       finishMessage: 'Core updated - please reload your browser',
+                       nextActionName: undefined
+               }
+       },
+
+       /**
+        * Clone of a DOM object acts as message template
+        */
+       messageTemplate: null,
+
+       /**
+        * Clone of a DOM object acts as button template
+        */
+       buttonTemplate: null,
+
+       /**
+        * Fetching the templates out of the DOM
+        */
+       initialize: function() {
+               var messageTemplateSection = $('#messageTemplate');
+               var buttonTemplateSection = $('#buttonTemplate');
+               this.messageTemplate = messageTemplateSection.children().clone();
+               this.buttonTemplate = buttonTemplateSection.children().clone();
+               messageTemplateSection.remove();
+       },
+
+       /**
+        * Public method checkForUpdate
+        */
+       checkForUpdate: function() {
+               this.callAction('coreUpdateUpdateVersionMatrix');
+       },
+
+       /**
+        * Public method updateDevelopment
+        */
+       updateDevelopment: function() {
+               this.update('development');
+       },
+
+       /**
+        * Public method updateRegular
+        */
+       updateRegular: function() {
+               this.update('regular');
+       },
+
+       /**
+        * Execute core update.
+        *
+        * @param type Either 'development' or 'regular'
+        */
+       update: function(type) {
+               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)
+        */
+       callAction: function(actionName, type) {
+               var self = this;
+               var arguments = {
+                       install: {
+                               controller: 'ajax',
+                               action: actionName
+                       }
+               };
+               if (type !== undefined) {
+                       arguments.install["type"] = type;
+               }
+               this.addLoadingMessage(this.actionQueue[actionName].loadingMessage);
+               $.ajax({
+                       url: location.href,
+                       data: arguments,
+                       cache: false,
+                       success: function(result) {
+                               canContinue = self.handleResult(result, self.actionQueue[actionName].finishMessage);
+                               if (canContinue === true && (self.actionQueue[actionName].nextActionName !== undefined)) {
+                                       self.callAction(self.actionQueue[actionName].nextActionName, type);
+                               }
+                       },
+                       error: function(result) {
+                               self.handleResult(result);
+                       }
+               });
+       },
+
+       /**
+        * Handle ajax result of core update step.
+        *
+        * @param data
+        * @param successMessage Optional success message
+        */
+       handleResult: function(data, successMessage) {
+               var canContinue = false;
+               this.removeLoadingMessage();
+               if (data.success === true) {
+                       canContinue = true;
+                       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('ok', successMessage);
+                       }
+               } else {
+                       // Handle clearcache until it uses the new view object
+                       if (data === "OK") {
+                               canContinue = true;
+                               if (successMessage) {
+                                       this.addMessage('ok', successMessage);
+                               }
+                       } else {
+                               canContinue = false;
+                               if (data.status && typeof(data.status) === 'object') {
+                                       this.showStatusMessages(data.status);
+                               } else {
+                                       this.addMessage('error', 'General error');
+                               }
+                       }
+               }
+               return canContinue;
+       },
+
+       /**
+        * Add a loading message with some text.
+        *
+        * @param messageTitle
+        */
+       addLoadingMessage: function(messageTitle) {
+               var domMessage = this.messageTemplate.clone();
+               domMessage.find('.message-header strong').html(messageTitle);
+               domMessage.addClass('message-loading');
+               $('#coreUpdate').append(domMessage);
+       },
+
+       /**
+        * Remove an enabled loading message
+        */
+       removeLoadingMessage: function() {
+               $('#coreUpdate .message-loading').closest('.typo3-message').remove();
+       },
+
+       /**
+        * Show a list of status messages
+        *
+        * @param messages
+        */
+       showStatusMessages: function(messages) {
+               var self = this;
+               $.each(messages, function(index, element) {
+                       var title = false;
+                       var severity = false;
+                       var message = false;
+                       if (element.severity) {
+                               severity = element.severity;
+                       }
+                       if (element.title) {
+                               title = element.title;
+                       }
+                       if (element.message) {
+                               message = element.message;
+                       }
+                       self.addMessage(severity, title, message);
+               });
+       },
+
+       /**
+        * Show an action button
+        *
+        * @param button
+        */
+       showActionButton: function(button) {
+               var title = false;
+               var action = false;
+               if (button.title) {
+                       title = button.title;
+               }
+               if (button.action) {
+                       action = button.action;
+               }
+               var domButton = this.buttonTemplate;
+               if (action) {
+                       domButton.find('button').data('action', action);
+               }
+               if (title) {
+                       domButton.find('button').html(title);
+               }
+               $('#coreUpdate').append(domButton);
+       },
+
+       /**
+        * Show a status message
+        *
+        * @param severity
+        * @param title
+        * @param message
+        */
+       addMessage: function(severity, title, message) {
+               var domMessage = this.messageTemplate.clone();
+               if (severity) {
+                       domMessage.addClass('message-' + severity);
+               }
+               if (title) {
+                       domMessage.find('.message-header strong').html(title);
+               }
+               if (message) {
+                       domMessage.find('.message-body').html(message);
+               }
+               $('#coreUpdate').append(domMessage);
+       }
+};
+
 $(document).ready(function() {
        // Used in database compare section to select/deselect checkboxes
        $('.checkall').on('click', function() {
@@ -60,6 +323,7 @@ $(document).ready(function() {
                }
        });
 
+       // Install step database settings
        $('#t3-install-step-type').change(function() {
                var connectionType = $(this).val(),
                        hostField = $('#t3-install-step-host'),
@@ -81,6 +345,7 @@ $(document).ready(function() {
                }
        }).trigger('change');
 
+       // Extension compatibility check
        $('.typo3-message', '#checkExtensions').hide();
        $('button', '#checkExtensions').click(function(e) {
                $('button', '#checkExtensions').hide();
@@ -96,10 +361,23 @@ $(document).ready(function() {
                $(this).find('input').focus();
        });
 
+       // Footer scrolling and visibility
        if ($('#fixed-footer-fieldset').length > 0) {
                $(window).scroll(handleButtonScrolling);
                $('body.backend #typo3-docbody').scroll(handleButtonScrolling);
        }
+
+       // Handle core update
+       var $coreUpdateSection = $('#coreUpdate');
+       if ($coreUpdateSection) {
+               TYPO3.Install.coreUpdate.initialize();
+               $coreUpdateSection.on('click', 'button', (function(e) {
+                       e.preventDefault();
+                       var action = $(e.target).data('action');
+                       TYPO3.Install.coreUpdate[action]();
+                       $(e.target).closest('.t3-install-form-submit').remove();
+               }));
+       }
 });
 
 function handleButtonScrolling() {
diff --git a/typo3/sysext/install/Tests/Unit/Service/CoreUpdateServiceTest.php b/typo3/sysext/install/Tests/Unit/Service/CoreUpdateServiceTest.php
new file mode 100644 (file)
index 0000000..9b30bbd
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+namespace TYPO3\CMS\Install\Service;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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!
+ ***************************************************************/
+
+/**
+ * Test case
+ */
+class CoreUpdateServiceTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
+
+       /**
+        * @test
+        */
+       public function getMessagesReturnsPreviouslySetMessage() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreUpdateService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Service\\CoreUpdateService', array('dummy'), array(), '', FALSE);
+               $aMessage = uniqid('message_');
+               $instance->_set('messages', $aMessage);
+               $this->assertSame($aMessage, $instance->getMessages());
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/install/Tests/Unit/Service/CoreVersionServiceTest.php b/typo3/sysext/install/Tests/Unit/Service/CoreVersionServiceTest.php
new file mode 100644 (file)
index 0000000..44ae76c
--- /dev/null
@@ -0,0 +1,437 @@
+<?php
+namespace TYPO3\CMS\Install\Service;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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!
+ ***************************************************************/
+
+/**
+ * Test case
+ */
+class CoreVersionServiceTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
+
+       /**
+        * @test
+        */
+       public function updateVersionMatrixStoresVersionMatrixInRegistry() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Service\\CoreVersionService', array('fetchVersionMatrixFromRemote'), array(), '', FALSE);
+               $registry = $this->getMock('TYPO3\CMS\Core\Registry');
+               $versionArray = array(uniqId());
+               $registry->expects($this->once())->method('set')->with('TYPO3.CMS.Install', 'coreVersionMatrix', $versionArray);
+               $instance->_set('registry', $registry);
+               $instance->expects($this->once())->method('fetchVersionMatrixFromRemote')->will($this->returnValue($versionArray));
+               $instance->updateVersionMatrix();
+       }
+
+       /**
+        * @test
+        */
+       public function updateVersionMatrixRemovesOldReleasesFromMatrix() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Service\\CoreVersionService', array('fetchVersionMatrixFromRemote'), array(), '', FALSE);
+               $registry = $this->getMock('TYPO3\CMS\Core\Registry');
+               $versionArray = array(
+                       '6.2' => array(),
+                       '6.1' => array(),
+               );
+               $registry
+                       ->expects($this->once())
+                       ->method('set')
+                       ->with('TYPO3.CMS.Install', 'coreVersionMatrix', $this->logicalNot($this->arrayHasKey('6.1')));
+               $instance->_set('registry', $registry);
+               $instance->expects($this->once())->method('fetchVersionMatrixFromRemote')->will($this->returnValue($versionArray));
+               $instance->updateVersionMatrix();
+       }
+
+       /**
+        * @test
+        */
+       public function isInstalledVersionAReleasedVersionReturnsTrueForNonDevelopmentVersion() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Service\\CoreVersionService', array('getInstalledVersion'), array(), '', FALSE);
+               $instance->expects($this->once())->method('getInstalledVersion')->will($this->returnValue('6.2.0'));
+               $this->assertTrue($instance->isInstalledVersionAReleasedVersion());
+       }
+
+       /**
+        * @test
+        */
+       public function isInstalledVersionAReleasedVersionReturnsFalseForDevelopmentVersion() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Service\\CoreVersionService', array('getInstalledVersion'), array(), '', FALSE);
+               $instance->expects($this->once())->method('getInstalledVersion')->will($this->returnValue('6.2-dev'));
+               $this->assertFalse($instance->isInstalledVersionAReleasedVersion());
+       }
+
+       /**
+        * Whitebox test of API method: This tests multiple methods, only 'current version' and 'version matrix' are mocked.
+        *
+        * @test
+        */
+       public function isYoungerPatchReleaseAvailableReturnsTrueIfYoungerReleaseIsAvailable() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\Service\\CoreVersionService',
+                       array('getVersionMatrix', 'getInstalledVersion'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $versionMatrix = array(
+                       '6.2' => array(
+                               'releases' => array(
+                                       '6.2.42' => array(
+                                               'type' => 'regular',
+                                               'date' => '2013-12-01 18:24:25 UTC',
+                                       ),
+                                       '6.2.41' => array(
+                                               'type' => 'regular',
+                                               'date' => '2013-11-01 18:24:25 UTC',
+                                       ),
+                               ),
+                       ),
+               );
+               $instance->expects($this->any())->method('getVersionMatrix')->will($this->returnValue($versionMatrix));
+               $instance->expects($this->any())->method('getInstalledVersion')->will($this->returnValue('6.2.41'));
+               $this->assertTrue($instance->isYoungerPatchReleaseAvailable());
+       }
+
+       /**
+        * Whitebox test of API method: This tests multiple methods, only 'current version' and 'version matrix' are mocked.
+        *
+        * @test
+        */
+       public function isYoungerReleaseAvailableReturnsFalseIfNoYoungerReleaseExists() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\Service\\CoreVersionService',
+                       array('getVersionMatrix', 'getInstalledVersion'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $versionMatrix = array(
+                       '6.2' => array(
+                               'releases' => array(
+                                       '6.2.42' => array(
+                                               'type' => 'regular',
+                                               'date' => '2013-12-01 18:24:25 UTC',
+                                       ),
+                                       '6.2.41' => array(
+                                               'type' => 'regular',
+                                               'date' => '2013-11-01 18:24:25 UTC',
+                                       ),
+                               ),
+                       ),
+               );
+               $instance->expects($this->any())->method('getVersionMatrix')->will($this->returnValue($versionMatrix));
+               $instance->expects($this->any())->method('getInstalledVersion')->will($this->returnValue('6.2.42'));
+               $this->assertFalse($instance->isYoungerPatchReleaseAvailable());
+       }
+
+       /**
+        * Whitebox test of API method: This tests multiple methods, only 'current version' and 'version matrix' are mocked.
+        *
+        * @test
+        */
+       public function isYoungerReleaseAvailableReturnsFalseIfOnlyADevelopmentReleaseIsYounger() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\Service\\CoreVersionService',
+                       array('getVersionMatrix', 'getInstalledVersion'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $versionMatrix = array(
+                       '6.2' => array(
+                               'releases' => array(
+                                       '6.2.42' => array(
+                                               'type' => 'development',
+                                               'date' => '2013-12-01 18:24:25 UTC',
+                                       ),
+                                       '6.2.41' => array(
+                                               'type' => 'regular',
+                                               'date' => '2013-11-01 18:24:25 UTC',
+                                       ),
+                               ),
+                       ),
+               );
+               $instance->expects($this->any())->method('getVersionMatrix')->will($this->returnValue($versionMatrix));
+               $instance->expects($this->any())->method('getInstalledVersion')->will($this->returnValue('6.2.41'));
+               $this->assertFalse($instance->isYoungerPatchReleaseAvailable());
+       }
+
+       /**
+        * Whitebox test of API method: This tests multiple methods, only 'current version' and 'version matrix' are mocked.
+        *
+        * @test
+        */
+       public function isYoungerDevelopmentReleaseAvailableReturnsTrueIfADevelopmentReleaseIsYounger() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\Service\\CoreVersionService',
+                       array('getVersionMatrix', 'getInstalledVersion'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $versionMatrix = array(
+                       '6.2' => array(
+                               'releases' => array(
+                                       '6.2.42' => array(
+                                               'type' => 'development',
+                                               'date' => '2013-12-01 18:24:25 UTC',
+                                       ),
+                                       '6.2.41' => array(
+                                               'type' => 'regular',
+                                               'date' => '2013-11-01 18:24:25 UTC',
+                                       ),
+                               ),
+                       ),
+               );
+               $instance->expects($this->any())->method('getVersionMatrix')->will($this->returnValue($versionMatrix));
+               $instance->expects($this->any())->method('getInstalledVersion')->will($this->returnValue('6.2.41'));
+               $this->assertTrue($instance->isYoungerPatchDevelopmentReleaseAvailable());
+       }
+
+       /**
+        * Whitebox test of API method: This tests multiple methods, only 'current version' and 'version matrix' are mocked.
+        *
+        * @test
+        */
+       public function isUpdateSecurityRelevantReturnsTrueIfAnUpdateIsSecurityRelevant() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\Service\\CoreVersionService',
+                       array('getVersionMatrix', 'getInstalledVersion'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $versionMatrix = array(
+                       '6.2' => array(
+                               'releases' => array(
+                                       '6.2.42' => array(
+                                               'type' => 'security',
+                                               'date' => '2013-12-01 18:24:25 UTC',
+                                       ),
+                                       '6.2.41' => array(
+                                               'type' => 'regular',
+                                               'date' => '2013-11-01 18:24:25 UTC',
+                                       ),
+                               ),
+                       ),
+               );
+               $instance->expects($this->any())->method('getVersionMatrix')->will($this->returnValue($versionMatrix));
+               $instance->expects($this->any())->method('getInstalledVersion')->will($this->returnValue('6.2.41'));
+               $this->assertTrue($instance->isUpdateSecurityRelevant());
+       }
+
+       /**
+        * Whitebox test of API method: This tests multiple methods, only 'current version' and 'version matrix' are mocked.
+        *
+        * @test
+        */
+       public function isUpdateSecurityRelevantReturnsFalseIfUpdateIsNotSecurityRelevant() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\Service\\CoreVersionService',
+                       array('getVersionMatrix', 'getInstalledVersion'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $versionMatrix = array(
+                       '6.2' => array(
+                               'releases' => array(
+                                       '6.2.42' => array(
+                                               'type' => 'regular',
+                                               'date' => '2013-12-01 18:24:25 UTC',
+                                       ),
+                                       '6.2.41' => array(
+                                               'type' => 'regular',
+                                               'date' => '2013-11-01 18:24:25 UTC',
+                                       ),
+                               ),
+                       ),
+               );
+               $instance->expects($this->any())->method('getVersionMatrix')->will($this->returnValue($versionMatrix));
+               $instance->expects($this->any())->method('getInstalledVersion')->will($this->returnValue('6.2.41'));
+               $this->assertFalse($instance->isUpdateSecurityRelevant());
+       }
+
+       /**
+        * @test
+        */
+       public function getInstalledMinorVersionFetchesInstalledVersionNumber() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Service\\CoreVersionService', array('getInstalledVersion'), array(), '', FALSE);
+               $instance->expects($this->once())->method('getInstalledVersion')->will($this->returnValue('6.2.0'));
+               $this->assertSame('6.2', $instance->_call('getInstalledMinorVersion'));
+       }
+
+       /**
+        * Data provider
+        */
+       public function getMinorVersionDataProvider() {
+               return array(
+                       '6.2.0' => array(
+                               '6.2.0',
+                               '6.2',
+                       ),
+                       '6.2-dev' => array(
+                               '6.2-dev',
+                               '6.2',
+                       ),
+                       '6.2.0alpha2' => array(
+                               '6.2.0alpha2',
+                               '6.2',
+                       ),
+                       '4.5.25' => array(
+                               '4.5.25',
+                               '4.5',
+                       ),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider getMinorVersionDataProvider
+        */
+       public function getMinorVersionReturnsCorrectMinorVersion($version, $expectedMajor) {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Service\\CoreVersionService', array('dummy'), array(), '', FALSE);
+               $this->assertSame($expectedMajor, $instance->_call('getMinorVersion', $version));
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Install\Service\Exception\CoreVersionServiceException
+        */
+       public function getVersionMatrixThrowsExceptionIfVersionMatrixIsNotYetSetInRegistry() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Service\\CoreVersionService', array('fetchVersionMatrixFromRemote'), array(), '', FALSE);
+               $registry = $this->getMock('TYPO3\CMS\Core\Registry');
+               $registry->expects($this->once())->method('get')->will($this->returnValue(NULL));
+               $instance->_set('registry', $registry);
+               $instance->_call('getVersionMatrix');
+       }
+
+       /**
+        * @test
+        */
+       public function getVersionMatrixReturnsMatrixFromRegistry() {
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock('TYPO3\\CMS\\Install\\Service\\CoreVersionService', array('fetchVersionMatrixFromRemote'), array(), '', FALSE);
+               $registry = $this->getMock('TYPO3\CMS\Core\Registry');
+               $versionArray = array(uniqId());
+               $registry->expects($this->once())->method('get')->will($this->returnValue($versionArray));
+               $instance->_set('registry', $registry);
+               $this->assertSame($versionArray, $instance->_call('getVersionMatrix'));
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Install\Service\Exception\CoreVersionServiceException
+        */
+       public function getReleaseTimestampOfVersionThrowsExceptionIfReleaseDateIsNotDefined() {
+               $versionMatrix = array(
+                       '6.2' => array(
+                               'releases' => array(
+                                       '6.2.42' => array()
+                               ),
+                       ),
+               );
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\Service\\CoreVersionService',
+                       array('getVersionMatrix', 'getMinorVersion', 'ensureVersionExistsInMatrix'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $instance->expects($this->once())->method('getMinorVersion')->will($this->returnValue('6.2'));
+               $instance->expects($this->once())->method('getVersionMatrix')->will($this->returnValue($versionMatrix));
+               $instance->_call('getReleaseTimestampOfVersion', '6.2.42');
+       }
+
+       /**
+        * @test
+        */
+       public function getReleaseTimestampOfVersionReturnsTimestamp() {
+               $versionMatrixFixtureFile = __DIR__ . '/Fixtures/VersionMatrixFixture.php';
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\Service\\CoreVersionService',
+                       array('getVersionMatrix', 'getMinorVersion', 'ensureVersionExistsInMatrix'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $instance->expects($this->once())->method('getMinorVersion')->will($this->returnValue('6.2'));
+               $instance->expects($this->once())->method('getVersionMatrix')->will($this->returnValue(require($versionMatrixFixtureFile)));
+               $this->assertSame(1380651865, $instance->_call('getReleaseTimestampOfVersion', '6.2.0alpha3'));
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Install\Service\Exception\CoreVersionServiceException
+        */
+       public function ensureVersionExistsInMatrixThrowsExceptionIfMinorVersionDoesNotExist() {
+               $versionMatrixFixtureFile = __DIR__ . '/Fixtures/VersionMatrixFixture.php';
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\Service\\CoreVersionService',
+                       array('getVersionMatrix', 'getMinorVersion'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $instance->expects($this->once())->method('getMinorVersion')->will($this->returnValue('2.0'));
+               $instance->expects($this->once())->method('getVersionMatrix')->will($this->returnValue(require($versionMatrixFixtureFile)));
+               $instance->_call('ensureVersionExistsInMatrix', '2.0.42');
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Install\Service\Exception\CoreVersionServiceException
+        */
+       public function ensureVersionExistsInMatrixThrowsExceptionIfPatchLevelDoesNotExist() {
+               $versionMatrixFixtureFile = __DIR__ . '/Fixtures/VersionMatrixFixture.php';
+               /** @var $instance \TYPO3\CMS\Install\Service\CoreVersionService|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $instance = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\Service\\CoreVersionService',
+                       array('getVersionMatrix', 'getMinorVersion'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $instance->expects($this->once())->method('getMinorVersion')->will($this->returnValue('6.2'));
+               $instance->expects($this->once())->method('getVersionMatrix')->will($this->returnValue(require($versionMatrixFixtureFile)));
+               $instance->_call('ensureVersionExistsInMatrix', '6.2.5');
+       }
+}
diff --git a/typo3/sysext/install/Tests/Unit/Service/Fixtures/VersionMatrixFixture.php b/typo3/sysext/install/Tests/Unit/Service/Fixtures/VersionMatrixFixture.php
new file mode 100644 (file)
index 0000000..ef3a7fc
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+return array(
+       '6.2' => array(
+               'releases' => array(
+                       '6.2-snapshot-20131004' => array(
+                               'type' => 'development',
+                               'date' => '2014-06-01 18:24:25 UTC',
+                       ),
+                       '6.2.3' => array(
+                               'type' => 'regular',
+                               'date' => '2014-05-01 18:24:25 UTC',
+                       ),
+                       '6.2.2' => array(
+                               'type' => 'security',
+                               'date' => '2014-04-01 18:24:25 UTC',
+                       ),
+                       '6.2.1' => array(
+                               'type' => 'regular',
+                               'date' => '2014-03-01 18:24:25 UTC',
+                       ),
+                       '6.2.0' => array(
+                               'type' => 'release',
+                               'date' => '2014-02-01 18:24:25 UTC',
+                       ),
+                       '6.2.0rc1' => array(
+                               'type' => 'development',
+                               'date' => '2014-01-01 18:24:25 UTC',
+                       ),
+                       '6.2.0beta2' => array(
+                               'type' => 'development',
+                               'date' => '2013-12-01 18:24:25 UTC',
+                       ),
+                       '6.2.0beta1' => array(
+                               'type' => 'development',
+                               'date' => '2013-11-01 18:24:25 UTC',
+                       ),
+                       '6.2.0alpha3' => array(
+                               'version' => '6.2.0alpha3',
+                               'date' => '2013-10-01 18:24:25 UTC',
+                               'type' => 'development',
+                               'checksums' => array(
+                                       'tar' => array(
+                                               'md5' => 'e91acf53bb03cb943bd27e76643901c5',
+                                               'sha1' => '3dc156eed4b99577232f537d798a8691493f8a83',
+                                       ),
+                                       'zip' => array(
+                                               'md5' => 'f8d166e9979a43490ec0ae03e0ff46a1',
+                                               'sha1' => '87448a8745b6eae36bd1e7cb6705a42771edfa03',
+                                       ),
+                               ),
+                               'url' => array(
+                                       'zip' => 'http://get.typo3.org/6.2.0alpha3/zip',
+                                       'tar' => 'http://get.typo3.org/6.2.0alpha3',
+                               ),
+                       ),
+                       '6.2.0alpha2' => array(
+                               'version' => '6.2.0alpha2',
+                               'date' => '2013-07-11 13:34:39 UTC',
+                               'type' => 'development',
+                               'checksums' => array(
+                                       'tar' => array(
+                                               'md5' => 'd528f7abe0290bfb4302b7ef7b64a2fe',
+                                               'sha1' => 'bf50ae8180604a8232fba3f70380da650b3266a1',
+                                       ),
+                                       'zip' => array(
+                                               'md5' => 'd71b97d09cb1719b76ba3bd86efaba19',
+                                               'sha1' => '73a6d9478fef4a9949931152bcecc82caa0cc0c8',
+                                       ),
+                               ),
+                               'url' => array(
+                                       'zip' => 'http://get.typo3.org/6.2.0alpha2/zip',
+                                       'tar' => 'http://get.typo3.org/6.2.0alpha2',
+                               ),
+                       ),
+                       '6.2.0alpha1' => array(
+                               'version' => '6.2.0alpha1',
+                               'date' => '2013-06-04 21:01:54 UTC',
+                               'type' => 'development',
+                               'checksums' => array(
+                                       'tar' => array(
+                                               'md5' => '74459680910c4982b3368a9b8e6b6aa7',
+                                               'sha1' => '6989d344fbaa6cdc4f0cf87684403f795fe5a97f',
+                                       ),
+                                       'zip' => array(
+                                               'md5' => '6cd76c5885f0e001599ae6c621cc0614',
+                                               'sha1' => '60576909d38084bcba4360bdc0f734540725f615',
+                                       ),
+                               ),
+                               'url' => array(
+                                       'zip' => 'http://get.typo3.org/6.2.0alpha1/zip',
+                                       'tar' => 'http://get.typo3.org/6.2.0alpha1',
+                               ),
+                       ),
+               ),
+               'latest' => '6.2.3',
+               'stable' => '6.2.3',
+               'active' => TRUE,
+       ),
+);
index 82d748f..f098ab8 100644 (file)
@@ -24,6 +24,8 @@ namespace TYPO3\CMS\Install\Tests\Unit\Status;
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
+use \TYPO3\CMS\Install\Status\StatusUtility;
+
 /**
  * Test case
  */
@@ -38,7 +40,7 @@ class StatusUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $okMock = $this->getMock('TYPO3\\CMS\\Install\\Status\\OkStatus', array('dummy'));
                $infoMock = $this->getMock('TYPO3\\CMS\\Install\\Status\\InfoStatus', array('dummy'));
                $noticeMock = $this->getMock('TYPO3\\CMS\\Install\\Status\\NoticeStatus', array('dummy'));
-               $statusUtility = new \TYPO3\CMS\Install\Status\StatusUtility();
+               $statusUtility = new StatusUtility();
                $return = $statusUtility->sortBySeverity(array($noticeMock, $infoMock, $okMock, $warningMock, $errorMock));
                $this->assertSame(array($errorMock), $return['error']);
                $this->assertSame(array($warningMock), $return['warning']);
@@ -52,7 +54,7 @@ class StatusUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
         * @expectedException \TYPO3\CMS\Install\Status\Exception
         */
        public function filterBySeverityThrowsExceptionIfObjectNotImplementingStatusInterfaceIsGiven() {
-               $statusUtility = new \TYPO3\CMS\Install\Status\StatusUtility();
+               $statusUtility = new StatusUtility();
                $statusUtility->filterBySeverity(array(new \stdClass()));
        }
 
@@ -62,7 +64,7 @@ class StatusUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        public function filterBySeverityReturnsSpecificSeverityOnly() {
                $errorMock = $this->getMock('TYPO3\\CMS\\Install\\Status\\ErrorStatus', array('dummy'));
                $warningMock = $this->getMock('TYPO3\\CMS\\Install\\Status\\WarningStatus', array('dummy'));
-               $statusUtility = new \TYPO3\CMS\Install\Status\StatusUtility();
+               $statusUtility = new StatusUtility();
                $return = $statusUtility->filterBySeverity(array($errorMock, $warningMock), 'error');
                $this->assertSame(array($errorMock), $return);
        }
diff --git a/typo3/sysext/install/Tests/Unit/View/JsonViewTest.php b/typo3/sysext/install/Tests/Unit/View/JsonViewTest.php
new file mode 100644 (file)
index 0000000..03dbaf1
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+namespace TYPO3\CMS\Install\Tests\Unit\View;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  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\Tests\UnitTestCase;
+
+/**
+ * Tests for the custom json view class
+ */
+class JsonViewTest extends UnitTestCase {
+       /**
+        * @test
+        */
+       public function transformStatusArrayToArrayReturnsArray() {
+               $jsonView = $this->getAccessibleMock('TYPO3\\CMS\\Install\\View\\JsonView', array('dummy'));
+               $this->assertInternalType('array', $jsonView->_call('transformStatusMessagesToArray'));
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Install\Status\Exception
+        */
+       public function transformStatusArrayToArrayThrowsExceptionIfArrayContainsNotAMessageInterfaceMessage() {
+               $jsonView = $this->getAccessibleMock('TYPO3\\CMS\\Install\\View\\JsonView', array('dummy'));
+               $jsonView->_call('transformStatusMessagesToArray', array('foo'));
+       }
+
+       /**
+        * @test
+        */
+       public function transformStatusToArrayCreatesArrayFromStatusMessage() {
+               $status = $this->getMock('TYPO3\\CMS\\Install\\Status\\StatusInterface');
+               $status->expects($this->once())->method('getSeverity')->will($this->returnValue('aSeverity'));
+               $status->expects($this->once())->method('getTitle')->will($this->returnValue('aTitle'));
+               $status->expects($this->once())->method('getMessage')->will($this->returnValue('aMessage'));
+               $jsonView = $this->getAccessibleMock('TYPO3\\CMS\\Install\\View\\JsonView', array('dummy'));
+               $return = $jsonView->_call('transformStatusToArray', $status);
+               $this->assertSame('aSeverity', $return['severity']);
+               $this->assertSame('aTitle', $return['title']);
+               $this->assertSame('aMessage', $return['message']);
+       }
+}
\ No newline at end of file