[FEATURE] Use new REST API for update checks 68/56468/17
authorSusanne Moog <susanne.moog@typo3.org>
Fri, 30 Mar 2018 12:35:55 +0000 (14:35 +0200)
committerStefan Neufeind <typo3.neufeind@speedpartner.de>
Wed, 4 Apr 2018 09:26:41 +0000 (11:26 +0200)
A new REST api for get.typo3.org was built and should be used
for update checks and additional information gathering.

With this patch the core upgrade and version service is refactored
to use the new API and an additional information in the system
informatian tool bar is displayed.

With the new API it would be possible to add change logs or
links to news for new versions easily in the future.

Additional info:
https://get.typo3.org/v1/api/doc

Resolves: #84549
Releases: master
Change-Id: I9d9c923605a853e9e676367daaa7074bff68aedb
Reviewed-on: https://review.typo3.org/56468
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Alexander Opitz <opitz.alexander@googlemail.com>
Tested-by: Alexander Opitz <opitz.alexander@googlemail.com>
Reviewed-by: Stefan Neufeind <typo3.neufeind@speedpartner.de>
Tested-by: Stefan Neufeind <typo3.neufeind@speedpartner.de>
14 files changed:
typo3/sysext/core/Documentation/Changelog/master/Deprecation-84549-DeprecateMethodsInCoreVersionService.rst [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Feature-84549-UsageOfNewRESTAPIOnGettypo3org.rst [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/UpgradeController.php
typo3/sysext/install/Classes/Report/InstallStatusReport.php
typo3/sysext/install/Classes/Service/CoreUpdateService.php
typo3/sysext/install/Classes/Service/CoreVersionService.php
typo3/sysext/install/Classes/SystemInformation/Typo3VersionMessage.php [new file with mode: 0644]
typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
typo3/sysext/install/Resources/Private/Language/Report/locallang.xlf
typo3/sysext/install/Resources/Public/JavaScript/Modules/CoreUpdate.js
typo3/sysext/install/Tests/Unit/Service/CoreVersionServiceTest.php
typo3/sysext/install/Tests/Unit/Service/Fixtures/VersionMatrixFixture.php [deleted file]
typo3/sysext/install/Tests/UnitDeprecated/Service/CoreVersionServiceTest.php [new file with mode: 0644]
typo3/sysext/install/ext_localconf.php

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-84549-DeprecateMethodsInCoreVersionService.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-84549-DeprecateMethodsInCoreVersionService.rst
new file mode 100644 (file)
index 0000000..617437d
--- /dev/null
@@ -0,0 +1,44 @@
+.. include:: ../../Includes.txt
+
+=============================================================
+Deprecation: #84549 - Deprecate methods in CoreVersionService
+=============================================================
+
+See :issue:`84549`
+
+Description
+===========
+
+The core version service was refactored to make use of the new REST API available via `https://get.typo3.org/v1/api/doc <https://get.typo3.org/v1/api/doc>`_.
+
+Due to that refactoring multiple methods in class :php:`CoreVersionService` have been deprecated:
+
+* :php:`getDownloadBaseUrl()`
+* :php:`isYoungerPatchDevelopmentReleaseAvailable()`
+* :php:`getYoungestPatchDevelopmentRelease()`
+* :php:`updateVersionMatrix()`
+
+
+Impact
+======
+
+Usage of any of these methods will trigger a PHP :php:`E_USER_DEPRECATED` error.
+
+
+Affected Installations
+======================
+
+Any that use the mentioned methods.
+
+
+Migration
+=========
+
+* For :php:`getDownloadBaseUrl()` use `https://get.typo3.org` directly
+* For :php:`isYoungerPatchDevelopmentReleaseAvailable()` use :php:`isYoungerPatchReleaseAvailable()`
+  as the current releases do not make use of development suffixes (like alpha or rc) anymore
+* For :php:`getYoungestPatchDevelopmentRelease()` use :php:`getYoungestPatchRelease()`
+* :php:`updateVersionMatrix()` needs no replacement method - instead the necessary information can be
+  fetched directly via the REST API
+
+.. index:: Backend, PHP-API, PartiallyScanned, ext:install
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-84549-UsageOfNewRESTAPIOnGettypo3org.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-84549-UsageOfNewRESTAPIOnGettypo3org.rst
new file mode 100644 (file)
index 0000000..85c7092
--- /dev/null
@@ -0,0 +1,31 @@
+.. include:: ../../Includes.txt
+
+========================================================
+Feature: #84549 - Usage of new REST API on get.typo3.org
+========================================================
+
+See :issue:`84549`
+
+Description
+===========
+
+Instead of providing only a JSON file, the get.typo3.org website was refactored to
+provide a REST web API for information on TYPO3 releases.
+
+The core uses that information to check for available upgrades and download new versions.
+With this change the information will be fetched via the new API. Additionally information
+about new releases will also be displayed in the system information toolbar, both in Composer
+Mode and Classic Mode mode to notify users that TYPO3 might be updated. If the version is
+out-of-support or has known security issues, the notification is displayed as an error.
+
+
+Impact
+======
+
+* :php:`CoreVersionService` makes use of the REST API directly - no complete version listing
+  is stored in the registry anymore as the new API provides direct access to necessary information
+* The system information toolbar contains a message hinting at the availability of updates - the
+  message is purposely also displayed for editors as they are exposed to the system more often
+  and will be able to quickly notify administrators in case security relevant updates are released.
+
+.. index:: Backend, PHP-API, ext:install
index 46d49ea..62808f2 100644 (file)
@@ -22,7 +22,6 @@ use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Symfony\Component\Finder\Finder;
 use Symfony\Component\Finder\SplFileInfo;
-use TYPO3\CMS\Core\Core\Bootstrap;
 use TYPO3\CMS\Core\Core\Environment;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
@@ -338,20 +337,6 @@ class UpgradeController extends AbstractController
     }
 
     /**
-     * Update available core version list
-     *
-     * @return ResponseInterface
-     */
-    public function coreUpdateUpdateVersionMatrixAction(): ResponseInterface
-    {
-        $this->coreUpdateInitialize();
-        return new JsonResponse([
-            'success' => $this->coreUpdateService->updateVersionMatrix(),
-            'status' => $this->coreUpdateService->getMessages(),
-        ]);
-    }
-
-    /**
      * Verify downloaded core checksum
      *
      * @param ServerRequestInterface $request
index a2d8666..bc473b0 100644 (file)
@@ -184,18 +184,19 @@ class InstallStatusReport implements \TYPO3\CMS\Reports\StatusProviderInterface
             return GeneralUtility::makeInstance(Status::class, 'TYPO3', TYPO3_version, $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_isDevelopmentVersion'), Status::NOTICE);
         }
 
-        // If fetching version matrix fails we can not do anything except print out the current version
-        try {
-            $coreVersionService->updateVersionMatrix();
-        } catch (Exception\RemoteFetchException $remoteFetchException) {
-            return GeneralUtility::makeInstance(Status::class, 'TYPO3', TYPO3_version, $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_remoteFetchException'), Status::NOTICE);
-        }
-
         try {
             $isUpdateAvailable = $coreVersionService->isYoungerPatchReleaseAvailable();
             $isMaintainedVersion = $coreVersionService->isVersionActivelyMaintained();
-        } catch (Exception\CoreVersionServiceException $coreVersionServiceException) {
-            return GeneralUtility::makeInstance(Status::class, 'TYPO3', TYPO3_version, $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_patchLevelNotFoundInReleaseMatrix'), Status::WARNING);
+        } catch (Exception\RemoteFetchException $remoteFetchException) {
+            return GeneralUtility::makeInstance(
+                Status::class,
+                'TYPO3',
+                TYPO3_version,
+                $languageService->sL(
+                    'LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_remoteFetchException'
+                ),
+                Status::NOTICE
+            );
         }
 
         if (!$isUpdateAvailable && $isMaintainedVersion) {
index c05371d..6f421c6 100644 (file)
@@ -75,7 +75,7 @@ class CoreUpdateService
         $this->coreVersionService = $coreVersionService ?: GeneralUtility::makeInstance(CoreVersionService::class);
         $this->setDownloadTargetPath(Environment::getVarPath() . '/transient/');
         $this->symlinkToCoreFiles = $this->discoverCurrentCoreSymlink();
-        $this->downloadBaseUri = $this->coreVersionService->getDownloadBaseUri();
+        $this->downloadBaseUri = 'https://get.typo3.org';
         $this->messages = new FlashMessageQueue('install');
     }
 
@@ -127,19 +127,24 @@ class CoreUpdateService
     /**
      * Wrapper method for CoreVersionService
      *
+     * @deprecated Since TYPO3v9 and will be removed in v10 - use REST api directly (see https://get.typo3.org/v1/api/doc)
      * @return bool TRUE on success
      */
     public function updateVersionMatrix()
     {
+        trigger_error(
+            'The method updateVersionMatrix() is deprecated since v9 and will be removed in v10, use the REST api directly (see https://get.typo3.org/v1/api/doc).',
+            \E_USER_DEPRECATED
+        );
         $success = true;
         try {
-            $this->coreVersionService->updateVersionMatrix();
+            $this->coreVersionService->getYoungestPatchRelease();
         } catch (RemoteFetchException $e) {
             $success = false;
             $this->messages->enqueue(new FlashMessage(
-                'Current version specification could not be fetched from http://get.typo3.org/json.'
+                'Current version specification could not be fetched from https://get.typo3.org.'
                     . ' This is probably a network issue, please fix it.',
-                'Version matrix could not be fetched from get.typo3.org',
+                'Version information could not be fetched from get.typo3.org',
                 FlashMessage::ERROR
             ));
         }
index af4cd53..9fa7586 100644 (file)
@@ -1,4 +1,6 @@
 <?php
+declare(strict_types = 1);
+
 namespace TYPO3\CMS\Install\Service;
 
 /*
@@ -29,48 +31,28 @@ class CoreVersionService
     protected $registry;
 
     /**
-     * Base URI for TYPO3 downloads
+     * Base URI for TYPO3 Version REST api
      *
      * @var string
      */
-    protected $downloadBaseUri;
+    protected $apiBaseUrl = 'https://get.typo3.org/v1/api/';
 
     /**
      * Initialize update URI
      *
-     * @param Registry $registry
+     * @param Registry $registry Deprecated
      */
     public function __construct(Registry $registry = null)
     {
-        $this->downloadBaseUri = 'https://get.typo3.org/';
-        $this->registry = $registry ?: GeneralUtility::makeInstance(Registry::class);
-    }
-
-    /**
-     * @return mixed
-     */
-    public function getDownloadBaseUri()
-    {
-        return $this->downloadBaseUri;
-    }
-
-    /**
-     * Update version matrix from remote and store in registry
-     *
-     * @throws Exception\RemoteFetchException
-     */
-    public function updateVersionMatrix()
-    {
-        $versionArray = $this->fetchVersionMatrixFromRemote();
-        $installedMajorVersion = (int)$this->getInstalledMajorVersion();
-
-        foreach ($versionArray as $versionNumber => $versionDetails) {
-            if (is_array($versionDetails) && (int)$this->getMajorVersion($versionNumber) < $installedMajorVersion) {
-                unset($versionArray[$versionNumber]);
-            }
+        if (null !== $registry) {
+            trigger_error(
+                'The constructor parameter $registry for CoreVersionService is deprecated since v9 and will be removed in v10.',
+                E_USER_DEPRECATED
+            );
+            $this->registry = $registry;
+        } else {
+            $this->registry = GeneralUtility::makeInstance(Registry::class);
         }
-
-        $this->registry->set('TYPO3.CMS.Install', 'coreVersionMatrix', $versionArray);
     }
 
     /**
@@ -79,7 +61,7 @@ class CoreVersionService
      *
      * @return bool FALSE If some development version is installed
      */
-    public function isInstalledVersionAReleasedVersion()
+    public function isInstalledVersionAReleasedVersion(): bool
     {
         $version = $this->getInstalledVersion();
         return substr($version, -4) !== '-dev';
@@ -92,22 +74,12 @@ class CoreVersionService
      * @return string sha1 of version
      * @throws Exception\CoreVersionServiceException
      */
-    public function getTarGzSha1OfVersion($version)
+    public function getTarGzSha1OfVersion(string $version): string
     {
-        $this->ensureVersionExistsInMatrix($version);
-
-        $majorVersion = $this->getMajorVersion($version);
-        $versionMatrix = $this->getVersionMatrix();
+        $url = 'release/' . $version;
+        $result = $this->fetchFromRemote($url);
 
-        if (empty($versionMatrix[$majorVersion]['releases'][$version]['checksums']['tar']['sha1'])) {
-            throw new Exception\CoreVersionServiceException(
-                'Release sha1 of version ' . $version . ' not found in version matrix.'
-                . ' This is probably a bug on get.typo3.org.',
-                1381263173
-            );
-        }
-
-        return $versionMatrix[$majorVersion]['releases'][$version]['checksums']['tar']['sha1'];
+        return $result['tar_package']['sha1sum'] ?? '';
     }
 
     /**
@@ -115,7 +87,7 @@ class CoreVersionService
      *
      * @return string
      */
-    public function getInstalledVersion()
+    public function getInstalledVersion(): string
     {
         return VersionNumberUtility::getCurrentTypo3Version();
     }
@@ -124,116 +96,75 @@ class CoreVersionService
      * Checks if TYPO3 version (e.g. 6.2) is an actively maintained version
      *
      * @return bool TRUE if version is actively maintained
+     * @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
      */
-    public function isVersionActivelyMaintained()
+    public function isVersionActivelyMaintained(): bool
     {
-        $majorVersion = $this->getInstalledMajorVersion();
-        $versionMatrix = $this->getVersionMatrix();
-        return (bool)$versionMatrix[$majorVersion]['active'];
+        $url = 'major/' . $this->getInstalledMajorVersion();
+        $result = $this->fetchFromRemote($url);
+
+        return !isset($result['maintained_until']) ||
+               (
+                   new \DateTimeImmutable($result['maintained_until']) >=
+                   new \DateTimeImmutable('now', new \DateTimeZone('UTC'))
+               );
     }
 
     /**
      * Returns TRUE if a younger patch level release exists in version matrix.
      *
      * @return bool TRUE if younger patch release is exists
+     * @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
      */
-    public function isYoungerPatchReleaseAvailable()
+    public function isYoungerPatchReleaseAvailable(): bool
     {
-        $version = $this->getInstalledVersion();
-        $youngestVersion = $this->getYoungestPatchRelease();
-        return $youngestVersion !== $version;
-    }
-
-    /**
-     * Returns TRUE if a younger patch level release exists in version matrix that may be a development release.
-     *
-     * @return bool 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;
+        return version_compare($this->getInstalledVersion(), $this->getYoungestPatchRelease()) === -1;
     }
 
     /**
      * Returns TRUE if an upgrade from current version is security relevant
      *
      * @return bool TRUE if there is a pending security update
+     * @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
      */
-    public function isUpdateSecurityRelevant()
+    public function isUpdateSecurityRelevant(): bool
     {
-        $result = false;
-        $version = $this->getInstalledVersion();
-        $youngestVersion = $this->getYoungestReleaseByType(['security']);
-        if ($youngestVersion !== $version) {
-            $result = true;
+        $url = 'major/' . $this->getInstalledMajorVersion() . '/release/latest/security';
+        $result = $this->fetchFromRemote($url);
+
+        if (isset($result['version'])) {
+            return version_compare($this->getInstalledVersion(), $result['version']) === -1;
         }
-        return $result;
+        return false;
     }
 
     /**
      * Youngest patch release, e.g., 6.2.2
      *
      * @return string Version string of youngest patch level release
+     * @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
      */
-    public function getYoungestPatchRelease()
-    {
-        return $this->getYoungestReleaseByType(['release', 'security', 'regular']);
-    }
-
-    /**
-     * Youngest development patch release, e.g., 6.2.0alpha3 or 6.2-snapshot-20131004
-     *
-     * @return string
-     */
-    public function getYoungestPatchDevelopmentRelease()
+    public function getYoungestPatchRelease(): string
     {
-        return $this->getYoungestReleaseByType(['release', 'security', 'regular', 'development']);
+        $url = 'major/' . $this->getInstalledMajorVersion() . '/release/latest';
+        $result = $this->fetchFromRemote($url);
+        return $result['version'];
     }
 
     /**
-     * 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, e.g., 7.2.0alpha3 or 7.3.0
+     * @param string $url
+     * @return array
+     * @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
      */
-    protected function getYoungestReleaseByType(array $types)
+    protected function fetchFromRemote(string $url): array
     {
-        $version = $this->getInstalledVersion();
-
-        $majorVersion = $this->getMajorVersion($version);
-        $versionMatrix = $this->getVersionMatrix();
+        $url = $this->apiBaseUrl . $url;
+        $json = GeneralUtility::getUrl($url);
 
-        $youngestRelease = $version;
-        $versionReleaseTimestamp = $this->getReleaseTimestampOfVersion($version);
-
-        $patchLevelVersions = $versionMatrix[$majorVersion]['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;
-            }
+        if (!$json) {
+            $this->throwFetchException($url);
         }
-        return $youngestRelease;
+        return json_decode($json, true);
     }
 
     /**
@@ -241,7 +172,7 @@ class CoreVersionService
      *
      * @return string For example 7
      */
-    protected function getInstalledMajorVersion()
+    protected function getInstalledMajorVersion(): string
     {
         return $this->getMajorVersion($this->getInstalledVersion());
     }
@@ -252,93 +183,96 @@ class CoreVersionService
      * @param string $version to check
      * @return string Major version, e.g., '7'
      */
-    protected function getMajorVersion($version)
+    protected function getMajorVersion(string $version): string
     {
         $explodedVersion = explode('.', $version);
         return $explodedVersion[0];
     }
 
     /**
-     * Get version matrix from registry
+     * Update version matrix from remote and store in registry
      *
-     * @return array
-     * @throws Exception
+     * @deprecated Since TYPO3v9 and will be removed in v10 - use new REST API directly (see https://get.typo3.org/v1/api/doc)
+     * @throws Exception\RemoteFetchException
      */
-    protected function getVersionMatrix()
+    public function updateVersionMatrix(): void
     {
-        $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
-            );
+        trigger_error('Method updateVersionMatrix is deprecated, use new REST API directly (see https://get.typo3.org/v1/api/doc).', \E_USER_DEPRECATED);
+        $url = 'https://get.typo3.org/json';
+        $versionJson = GeneralUtility::getUrl($url);
+        if (!$versionJson) {
+            $this->throwFetchException($url);
+        }
+        $versionArray = json_decode($versionJson, true);
+        $installedMajorVersion = (int)$this->getInstalledMajorVersion();
+
+        foreach ($versionArray as $versionNumber => $versionDetails) {
+            if (is_array($versionDetails) && (int)($this->getMajorVersion((string)$versionNumber)) < $installedMajorVersion) {
+                unset($versionArray[$versionNumber]);
+            }
         }
-        return $versionMatrix;
+
+        $this->registry->set('TYPO3.CMS.Install', 'coreVersionMatrix', $versionArray);
     }
 
     /**
-     * Get available version string from get.typo3.org
+     * Youngest development patch release, e.g., 6.2.0alpha3 or 6.2-snapshot-20131004
      *
-     * @return array
-     * @throws Exception\RemoteFetchException
+     * @deprecated Since TYPO3v9 and will be removed in v10 - TYPO3 release cycles do not contain development releases anymore
+     * @return string
+     * @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
      */
-    protected function fetchVersionMatrixFromRemote()
+    public function getYoungestPatchDevelopmentRelease(): string
     {
-        $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);
+        trigger_error(
+            'Method getYoungestPatchDevelopmentRelease() is deprecated since v9 and will be removed in v10, use getYoungestPatchRelease() instead.',
+            \E_USER_DEPRECATED
+        );
+        return $this->getYoungestPatchRelease();
     }
 
     /**
-     * Returns release timestamp of a specific version
+     * Returns TRUE if a younger patch level release exists in version matrix that may be a development release.
      *
-     * @param $version String to check in version matrix, e.g., 7.2.0alpha3 or 7.3.0
-     * @throws Exception\CoreVersionServiceException
-     * @return int Timestamp of release
+     * @deprecated Since TYPO3v9 and will be removed in v10 - TYPO3 release cycles do not contain development releases anymore
+     * @return bool TRUE if younger patch release is exists
+     * @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
      */
-    protected function getReleaseTimestampOfVersion($version)
+    public function isYoungerPatchDevelopmentReleaseAvailable(): bool
     {
-        $majorVersion = $this->getMajorVersion($version);
-        $versionMatrix = $this->getVersionMatrix();
-        $this->ensureVersionExistsInMatrix($version);
-        if (!array_key_exists('date', $versionMatrix[$majorVersion]['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[$majorVersion]['releases'][$version]['date'];
-        $date = new \DateTime($dateString);
-        return $date->getTimestamp();
+        trigger_error(
+            'Method isYoungerPatchDevelopmentReleaseAvailable() is deprecated since v9 and will be removed in v10, use isYoungerPatchReleaseAvailable() instead.',
+            \E_USER_DEPRECATED
+        );
+        return $this->isYoungerPatchReleaseAvailable();
     }
 
     /**
-     * Throws an exception if specified version does not exist in version matrix
+     * @deprecated Since TYPO3v9 and will be removed in v10, use 'https://get.typo3.org' directly
+     * @return string
+     */
+    public function getDownloadBaseUrl(): string
+    {
+        trigger_error(
+            'Method getDownloadBaseUrl() is deprecated since v9 and will be removed in v10, use https://get.typo3.org directly.',
+            \E_USER_DEPRECATED
+        );
+        return $this->apiBaseUrl;
+    }
+
+    /**
+     * Helper method to throw same exception in multiple places
      *
-     * @param $version String to check in version matrix, e.g., 7.2.0alpha3 or 7.3.0
-     * @throws Exception\CoreVersionServiceException
+     * @param string $url
+     * @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
      */
-    protected function ensureVersionExistsInMatrix($version)
+    protected function throwFetchException(string $url): void
     {
-        $majorVersion = $this->getMajorVersion($version);
-        $versionMatrix = $this->getVersionMatrix();
-        if (!array_key_exists($majorVersion, $versionMatrix)) {
-            throw new Exception\CoreVersionServiceException(
-                'Major release ' . $majorVersion . ' not found in version matrix.',
-                1380905851
-            );
-        }
-        if (!array_key_exists($version, $versionMatrix[$majorVersion]['releases'])) {
-            throw new Exception\CoreVersionServiceException(
-                'Patch level release ' . $version . ' not found in version matrix.',
-                1380905852
-            );
-        }
+        throw new Exception\RemoteFetchException(
+            'Fetching ' .
+            $url .
+            ' failed. Maybe this instance can not connect to the remote system properly.',
+            1380897593
+        );
     }
 }
diff --git a/typo3/sysext/install/Classes/SystemInformation/Typo3VersionMessage.php b/typo3/sysext/install/Classes/SystemInformation/Typo3VersionMessage.php
new file mode 100644 (file)
index 0000000..852d605
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Install\SystemInformation;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Backend\Backend\ToolbarItems\SystemInformationToolbarItem;
+use TYPO3\CMS\Backend\Toolbar\Enumeration\InformationStatus;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
+use TYPO3\CMS\Install\Service\CoreVersionService;
+use TYPO3\CMS\Install\Service\Exception\RemoteFetchException;
+
+/**
+ * Count newest exceptions for the system information menu
+ */
+class Typo3VersionMessage
+{
+    /**
+     * Modifies the SystemInformation array
+     *
+     * @param SystemInformationToolbarItem $systemInformationToolbarItem
+     */
+    public function appendMessage(SystemInformationToolbarItem $systemInformationToolbarItem): void
+    {
+        $coreVersionService = GeneralUtility::makeInstance(CoreVersionService::class);
+
+        try {
+            if ($coreVersionService->isVersionActivelyMaintained()) {
+                $isYoungerPatchReleaseAvailable = $coreVersionService->isYoungerPatchReleaseAvailable();
+
+                if (true === $isYoungerPatchReleaseAvailable) {
+                    $release = $coreVersionService->getYoungestPatchRelease();
+
+                    if ($coreVersionService->isUpdateSecurityRelevant()) {
+                        $severity = InformationStatus::STATUS_ERROR;
+                        $message = LocalizationUtility::translate(
+                            'LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_newVersionSecurityRelevant'
+                        );
+                    } else {
+                        $severity = InformationStatus::STATUS_WARNING;
+                        $message = LocalizationUtility::translate(
+                            'LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_newVersion'
+                        );
+                    }
+
+                    $message = sprintf($message, $release);
+                } else {
+                    $severity = InformationStatus::STATUS_OK;
+                    $message = LocalizationUtility::translate(
+                        'LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_uptodate'
+                    );
+                }
+            } else {
+                $severity = InformationStatus::STATUS_ERROR;
+                $message = LocalizationUtility::translate(
+                    'LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_versionOutdated'
+                );
+            }
+        } catch (RemoteFetchException $exception) {
+            $message = LocalizationUtility::translate(
+                'LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_noAutomaticCheck'
+            );
+            $severity = InformationStatus::STATUS_WARNING;
+        }
+
+        $systemInformationToolbarItem->addSystemMessage(
+            $message,
+            $severity
+        );
+    }
+}
index 94a0a74..b40bd02 100644 (file)
@@ -2032,4 +2032,32 @@ return [
             'Deprecation-83475-AggregateValidatorInformationInClassSchema-1.rst',
         ],
     ],
+    'TYPO3\CMS\Install\Service\CoreVersionService->getDownloadBaseUrl' => [
+        'numberOfMandatoryArguments' => 0,
+        'maximumNumberOfArguments' => 0,
+        'restFiles' => [
+            'Deprecation-84549-DeprecateMethodsInCoreVersionService.rst',
+        ],
+    ],
+    'TYPO3\CMS\Install\Service\CoreVersionService->isYoungerPatchDevelopmentReleaseAvailable' => [
+        'numberOfMandatoryArguments' => 0,
+        'maximumNumberOfArguments' => 0,
+        'restFiles' => [
+            'Deprecation-84549-DeprecateMethodsInCoreVersionService.rst',
+        ],
+    ],
+    'TYPO3\CMS\Install\Service\CoreVersionService->getYoungestPatchDevelopmentRelease' => [
+        'numberOfMandatoryArguments' => 0,
+        'maximumNumberOfArguments' => 0,
+        'restFiles' => [
+            'Deprecation-84549-DeprecateMethodsInCoreVersionService.rst',
+        ],
+    ],
+    'TYPO3\CMS\Install\Service\CoreVersionService->updateVersionMatrix' => [
+        'numberOfMandatoryArguments' => 0,
+        'maximumNumberOfArguments' => 0,
+        'restFiles' => [
+            'Deprecation-84549-DeprecateMethodsInCoreVersionService.rst',
+        ],
+    ],
 ];
index 239e62d..97ca8bd 100644 (file)
@@ -70,7 +70,7 @@
                                <source>It seems that this TYPO3 version has never been released. Thus no update information exists.</source>
                        </trans-unit>
                        <trans-unit id="status_newVersionSecurityRelevant">
-                               <source>There is a new version of TYPO3 available. This update is security relevant. Please update to version %s</source>
+                               <source>There is a new version of TYPO3 available, updating is security relevant. Please update to version %s</source>
                        </trans-unit>
                        <trans-unit id="status_newVersion">
                                <source>There is a new version of TYPO3 available. You can update to version %s</source>
                        <trans-unit id="status_versionOutdated">
                                <source>The TYPO3 version you're using is not supported anymore. You should update to a supported version.</source>
                        </trans-unit>
+                       <trans-unit id="status_noAutomaticCheck">
+                               <source>Automatic update checking failed, please check for updates manually.</source>
+                       </trans-unit>
+                       <trans-unit id="status_uptodate">
+                               <source>Your TYPO3 version is up-to-date.</source>
+                       </trans-unit>
                        <trans-unit id="status_installToolPassword">
                                <source>Install Tool Password</source>
                        </trans-unit>
index 089245c..d9c93cd 100644 (file)
@@ -27,11 +27,6 @@ define([
      * 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,
@@ -102,7 +97,7 @@ define([
      * Public method checkForUpdate
      */
     checkForUpdate: function() {
-      this.callAction('coreUpdateUpdateVersionMatrix');
+      this.callAction('coreUpdateIsUpdateAvailable');
     },
 
     /**
index 843da05..4b9f9e2 100644 (file)
@@ -1,4 +1,6 @@
 <?php
+declare(strict_types=1);
+
 namespace TYPO3\CMS\Install\Tests\Unit\Service;
 
 /*
@@ -14,338 +16,257 @@ namespace TYPO3\CMS\Install\Tests\Unit\Service;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Core\Registry;
+use Prophecy\Argument;
+use TYPO3\CMS\Core\Http\JsonResponse;
+use TYPO3\CMS\Core\Http\RequestFactory;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Install\Service\CoreVersionService;
-use TYPO3\CMS\Install\Service\Exception\CoreVersionServiceException;
 
 /**
  * Test case
  */
 class CoreVersionServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
 {
-    /**
-     * @test
-     */
-    public function updateVersionMatrixStoresVersionMatrixInRegistry()
+    public function setUpApiResponse(string $url, array $responseData)
     {
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(CoreVersionService::class, ['fetchVersionMatrixFromRemote'], [], '', false);
-        $registry = $this->createMock(Registry::class);
-        $versionArray = [9 => []];
-        $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();
+        $response = new JsonResponse($responseData);
+        $requestFactory = $this->prophesize(RequestFactory::class);
+        $requestFactory->request('https://get.typo3.org/v1/api/' . $url, Argument::cetera())->willReturn($response);
+        GeneralUtility::addInstance(RequestFactory::class, $requestFactory->reveal());
     }
 
     /**
      * @test
      */
-    public function updateVersionMatrixRemovesOldReleasesFromMatrix()
+    public function getTarGzSha1OfVersionReturnsSha1(): void
     {
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(CoreVersionService::class, ['fetchVersionMatrixFromRemote'], [], '', false);
-        $registry = $this->createMock(Registry::class);
-        $versionArray = [
-            '7' => [],
-            '6.2' => [],
-        ];
-        $registry
-            ->expects($this->once())
-            ->method('set')
-            ->with('TYPO3.CMS.Install', 'coreVersionMatrix', $this->logicalNot($this->arrayHasKey('6.2')));
-        $instance->_set('registry', $registry);
-        $instance->expects($this->once())->method('fetchVersionMatrixFromRemote')->will($this->returnValue($versionArray));
-        $instance->updateVersionMatrix();
-    }
-
-    /**
-     * @test
-     */
-    public function isInstalledVersionAReleasedVersionReturnsTrueForNonDevelopmentVersion()
-    {
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion'], [], '', false);
-        $instance->expects($this->once())->method('getInstalledVersion')->will($this->returnValue('7.2.0'));
-        $this->assertTrue($instance->isInstalledVersionAReleasedVersion());
+        $this->setUpApiResponse(
+            'release/8.7.12',
+            [
+                'version' => '8.7.12',
+                'date' => '2018-03-22T11:36:39+00:00',
+                'type' => 'regular',
+                'tar_package' =>
+                    [
+                        'md5sum' => 'e835f454229b1077c9042f1bae4d46c7',
+                        'sha1sum' => '185f3796751a903554a03378634a438beeef966e',
+                        'sha256sum' => '77c3589161bea9d2c30e5d3d944443ba64b56813314ac2511b830e37d3297881',
+                    ],
+                'zip_package' =>
+                    [
+                        'md5sum' => 'e5736ca3b3725966a4528a0c53fc849f',
+                        'sha1sum' => 'eba49b9033da52d98f48876e97ed090a0c5593e0',
+                        'sha256sum' => '7aad3f5864256f3f989c0378cec8bb729e728b30adb25e55ae713d8e682ef72b',
+                    ],
+            ]
+        );
+        $coreVersionService = new CoreVersionService();
+        $result = $coreVersionService->getTarGzSha1OfVersion('8.7.12');
+        self::assertSame('185f3796751a903554a03378634a438beeef966e', $result);
     }
 
     /**
      * @test
      */
-    public function isInstalledVersionAReleasedVersionReturnsFalseForDevelopmentVersion()
+    public function getTarGzSha1OfVersionReturnsSha1ReturnsEmptyStringIfNoVersionData(): void
     {
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion'], [], '', false);
-        $instance->expects($this->once())->method('getInstalledVersion')->will($this->returnValue('7.4-dev'));
-        $this->assertFalse($instance->isInstalledVersionAReleasedVersion());
+        $this->setUpApiResponse(
+            'release/8.7.44',
+            [
+                'error' =>
+                    [
+                        'code' => 404,
+                        'message' => 'Not Found',
+                    ],
+            ]
+        );
+        $coreVersionService = new CoreVersionService();
+        $result = $coreVersionService->getTarGzSha1OfVersion('8.7.44');
+        self::assertSame('', $result);
     }
 
     /**
      * @test
      */
-    public function getTarGzSha1OfVersionThrowsExceptionIfSha1DoesNotExistInMatrix()
+    public function isVersionActivelyMaintainedReturnsTrueIfMaintainedUntilIsNotSet(): void
     {
-        $this->expectException(CoreVersionServiceException::class);
-        $this->expectExceptionCode(1381263173);
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(
-            CoreVersionService::class,
-            ['getVersionMatrix', 'getMajorVersion', 'ensureVersionExistsInMatrix'],
-            [],
-            '',
-            false
+        $this->setUpApiResponse(
+            'major/9',
+            [
+                'version' => 9.0,
+                'title' => 'TYPO3 v9',
+                'release_date' => '2018-01-30T00:00:00+01:00',
+            ]
         );
-        $versionMatrix = [
-            '7' => [
-                'releases' => [
-                    '7.2.0' => [],
-                ],
-            ],
-        ];
-        $instance->expects($this->once())->method('getMajorVersion')->will($this->returnValue('7'));
-        $instance->expects($this->any())->method('getVersionMatrix')->will($this->returnValue($versionMatrix));
-        $this->assertTrue($instance->getTarGzSha1OfVersion('7.2.0'));
+
+        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion']);
+        $instance->expects($this->once())->method('getInstalledVersion')->will($this->returnValue('9.1.0'));
+
+        $result = $instance->isVersionActivelyMaintained();
+
+        self::assertTrue($result);
     }
 
     /**
      * @test
      */
-    public function getTarGzSha1OfVersionReturnsSha1OfSpecifiedVersion()
+    public function isVersionActivelyMaintainedReturnsTrueIfMaintainedUntilIsAfterToday(): void
     {
-        $versionMatrixFixtureFile = __DIR__ . '/Fixtures/VersionMatrixFixture.php';
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(
-            CoreVersionService::class,
-            ['getVersionMatrix', 'getMajorVersion', 'ensureVersionExistsInMatrix'],
-            [],
-            '',
-            false
+        $this->setUpApiResponse(
+            'major/9',
+            [
+                'version' => 9.0,
+                'title' => 'TYPO3 v9',
+                'release_date' => '2018-01-30T00:00:00+01:00',
+                'maintained_until' => '2222-01-30T00:00:00+01:00',
+            ]
         );
-        $instance->expects($this->any())->method('getMajorVersion')->will($this->returnValue('7'));
-        $instance->expects($this->any())->method('getVersionMatrix')->will($this->returnValue(require $versionMatrixFixtureFile));
-        $this->assertSame('3dc156eed4b99577232f537d798a8691493f8a83', $instance->getTarGzSha1OfVersion('7.2.0'));
+
+        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion']);
+        $instance->expects($this->once())->method('getInstalledVersion')->will($this->returnValue('9.1.0'));
+
+        $result = $instance->isVersionActivelyMaintained();
+
+        self::assertTrue($result);
     }
 
     /**
-     * Whitebox test of API method: This tests multiple methods, only 'current version' and 'version matrix' are mocked.
-     *
      * @test
      */
-    public function isYoungerPatchReleaseAvailableReturnsTrueIfYoungerReleaseIsAvailable()
+    public function isVersionActivelyMaintainedReturnsFalseIfMaintainedUntilWasBeforeToday(): void
     {
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(
-            CoreVersionService::class,
-            ['getVersionMatrix', 'getInstalledVersion'],
-            [],
-            '',
-            false
+        $this->setUpApiResponse(
+            'major/7',
+            [
+                'version' => 7,
+                'title' => 'TYPO3 v7',
+                'maintained_until' => '2003-02-18T08:10:14+00:00',
+                'release_date' => '2002-06-03T12:01:07+00:00',
+            ]
         );
-        $versionMatrix = [
-            '7' => [
-                'releases' => [
-                    '7.2.1' => [
-                        'type' => 'security',
-                        'date' => '2013-12-01 18:24:25 UTC',
-                    ],
-                    '7.2.0' => [
-                        '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('7.2.0'));
-        $this->assertTrue($instance->isYoungerPatchReleaseAvailable());
+
+        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion']);
+        $instance->expects($this->once())->method('getInstalledVersion')->will($this->returnValue('7.6.25'));
+
+        $result = $instance->isVersionActivelyMaintained();
+
+        self::assertFalse($result);
     }
 
     /**
-     * Whitebox test of API method: This tests multiple methods, only 'current version' and 'version matrix' are mocked.
-     *
      * @test
      */
-    public function isYoungerReleaseAvailableReturnsFalseIfNoYoungerReleaseExists()
+    public function isYoungerPatchReleaseAvailableReturnsTrueIfNewerVersionExists(): void
     {
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(
-            CoreVersionService::class,
-            ['getVersionMatrix', 'getInstalledVersion'],
-            [],
-            '',
-            false
+        $this->setUpApiResponse(
+            'major/9/release/latest',
+            [
+                'version' => '9.1.0',
+                'date' => '2018-01-30T15:44:52+00:00',
+                'type' => 'regular',
+            ]
         );
-        $versionMatrix = [
-            '7' => [
-                'releases' => [
-                    '7.2.0' => [
-                        'type' => 'regular',
-                        'date' => '2013-12-01 18:24:25 UTC',
-                    ],
-                    '7.1.0' => [
-                        '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('7.2.0'));
-        $this->assertFalse($instance->isYoungerPatchReleaseAvailable());
+
+        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion']);
+        $instance->expects($this->atLeastOnce())->method('getInstalledVersion')->will($this->returnValue('9.0.0'));
+
+        $result = $instance->isYoungerPatchReleaseAvailable();
+
+        self::assertTrue($result);
     }
 
     /**
-     * Whitebox test of API method: This tests multiple methods, only 'current version' and 'version matrix' are mocked.
-     *
      * @test
      */
-    public function isYoungerReleaseAvailableReturnsFalseIfOnlyADevelopmentReleaseIsYounger()
+    public function isYoungerPatchReleaseAvailableReturnsFalseIfNoNewerVersionExists(): void
     {
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(
-            CoreVersionService::class,
-            ['getVersionMatrix', 'getInstalledVersion'],
-            [],
-            '',
-            false
+        $this->setUpApiResponse(
+            'major/9/release/latest',
+            [
+                'version' => '9.1.0',
+                'date' => '2018-01-30T15:44:52+00:00',
+                'type' => 'regular',
+            ]
         );
-        $versionMatrix = [
-            '7' => [
-                'releases' => [
-                    '7.3.0' => [
-                        'type' => 'development',
-                        'date' => '2013-12-01 18:24:25 UTC',
-                    ],
-                    '7.2.0' => [
-                        '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('7.2.0'));
-        $this->assertFalse($instance->isYoungerPatchReleaseAvailable());
+
+        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion']);
+        $instance->expects($this->atLeastOnce())->method('getInstalledVersion')->will($this->returnValue('9.1.0'));
+
+        $result = $instance->isYoungerPatchReleaseAvailable();
+
+        self::assertFalse($result);
     }
 
     /**
-     * Whitebox test of API method: This tests multiple methods, only 'current version' and 'version matrix' are mocked.
-     *
      * @test
      */
-    public function isYoungerDevelopmentReleaseAvailableReturnsTrueIfADevelopmentReleaseIsYounger()
+    public function isUpdateSecurityRelevantReturnsTrueIfNewerSecurityUpdateExists(): void
     {
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(
-            CoreVersionService::class,
-            ['getVersionMatrix', 'getInstalledVersion'],
-            [],
-            '',
-            false
+        $this->setUpApiResponse(
+            'major/8/release/latest/security',
+            [
+                'version' => '8.7.5',
+                'date' => '2017-09-05T10:54:18+00:00',
+                'type' => 'security',
+            ]
         );
-        $versionMatrix = [
-            '7' => [
-                'releases' => [
-                    '7.3.0' => [
-                        'type' => 'development',
-                        'date' => '2013-12-01 18:24:25 UTC',
-                    ],
-                    '7.2.0' => [
-                        '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('7.2.0'));
-        $this->assertTrue($instance->isYoungerPatchDevelopmentReleaseAvailable());
+
+        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion']);
+        $instance->expects($this->atLeastOnce())->method('getInstalledVersion')->will($this->returnValue('8.7.1'));
+
+        $result = $instance->isUpdateSecurityRelevant();
+
+        self::assertTrue($result);
     }
 
     /**
-     * Whitebox test of API method: This tests multiple methods, only 'current version' and 'version matrix' are mocked.
-     *
      * @test
      */
-    public function isUpdateSecurityRelevantReturnsTrueIfAnUpdateIsSecurityRelevant()
+    public function isUpdateSecurityRelevantReturnsFalseIfNoNewerSecurityUpdatesExist(): void
     {
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(
-            CoreVersionService::class,
-            ['getVersionMatrix', 'getInstalledVersion'],
-            [],
-            '',
-            false
+        $this->setUpApiResponse(
+            'major/8/release/latest/security',
+            [
+                'version' => '8.7.5',
+                'date' => '2017-09-05T10:54:18+00:00',
+                'type' => 'security',
+            ]
         );
-        $versionMatrix = [
-            '7' => [
-                'releases' => [
-                    '7.3.0' => [
-                        'type' => 'security',
-                        'date' => '2013-12-01 18:24:25 UTC',
-                    ],
-                    '7.2.0' => [
-                        '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('7.2.0'));
-        $this->assertTrue($instance->isUpdateSecurityRelevant());
+
+        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion']);
+        $instance->expects($this->atLeastOnce())->method('getInstalledVersion')->will($this->returnValue('8.7.5'));
+
+        $result = $instance->isUpdateSecurityRelevant();
+
+        self::assertFalse($result);
     }
 
     /**
-     * Whitebox test of API method: This tests multiple methods, only 'current version' and 'version matrix' are mocked.
-     *
      * @test
      */
-    public function isUpdateSecurityRelevantReturnsFalseIfUpdateIsNotSecurityRelevant()
+    public function getYoungestPatchReleaseReturnsLatestReleaseForCurrentMajorVersion(): void
     {
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(
-            CoreVersionService::class,
-            ['getVersionMatrix', 'getInstalledVersion'],
-            [],
-            '',
-            false
+        $this->setUpApiResponse(
+            'major/9/release/latest',
+            [
+                'version' => '9.1.0',
+                'date' => '2018-01-30T15:44:52+00:00',
+                'type' => 'regular',
+            ]
         );
-        $versionMatrix = [
-            '7' => [
-                'releases' => [
-                    '7.3.0' => [
-                        'type' => 'regular',
-                        'date' => '2013-12-01 18:24:25 UTC',
-                    ],
-                    '7.2.0' => [
-                        '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('7.2.0'));
-        $this->assertFalse($instance->isUpdateSecurityRelevant());
-    }
 
-    /**
-     * @test
-     */
-    public function getInstalledMajorVersionFetchesInstalledVersionNumber()
-    {
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion'], [], '', false);
-        $instance->expects($this->once())->method('getInstalledVersion')->will($this->returnValue('7.2.0'));
-        $this->assertSame('7', $instance->_call('getInstalledMajorVersion'));
+        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion']);
+        $instance->expects($this->atLeastOnce())->method('getInstalledVersion')->will($this->returnValue('9.0.0'));
+
+        $result = $instance->getYoungestPatchRelease();
+
+        self::assertSame('9.1.0', $result);
     }
 
     /**
      * Data provider
      */
-    public function getMajorVersionDataProvider()
+    public function getMajorVersionDataProvider(): array
     {
         return [
             '7.2' => [
@@ -370,7 +291,7 @@ class CoreVersionServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestC
      * @param string $expectedMajor
      * @throws \InvalidArgumentException
      */
-    public function getMajorVersionReturnsCorrectMajorVersion($version, $expectedMajor)
+    public function getMajorVersionReturnsCorrectMajorVersion($version, $expectedMajor): void
     {
         /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
         $instance = $this->getAccessibleMock(CoreVersionService::class, ['dummy'], [], '', false);
@@ -380,117 +301,22 @@ class CoreVersionServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestC
     /**
      * @test
      */
-    public function getVersionMatrixThrowsExceptionIfVersionMatrixIsNotYetSetInRegistry()
-    {
-        $this->expectException(CoreVersionServiceException::class);
-        $this->expectExceptionCode(1380898792);
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(CoreVersionService::class, ['fetchVersionMatrixFromRemote'], [], '', false);
-        $registry = $this->createMock(Registry::class);
-        $registry->expects($this->once())->method('get')->will($this->returnValue(null));
-        $instance->_set('registry', $registry);
-        $instance->_call('getVersionMatrix');
-    }
-
-    /**
-     * @test
-     */
-    public function getVersionMatrixReturnsMatrixFromRegistry()
-    {
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(CoreVersionService::class, ['fetchVersionMatrixFromRemote'], [], '', false);
-        $registry = $this->createMock(Registry::class);
-        $versionArray = [$this->getUniqueId()];
-        $registry->expects($this->once())->method('get')->will($this->returnValue($versionArray));
-        $instance->_set('registry', $registry);
-        $this->assertSame($versionArray, $instance->_call('getVersionMatrix'));
-    }
-
-    /**
-     * @test
-     */
-    public function getReleaseTimestampOfVersionThrowsExceptionIfReleaseDateIsNotDefined()
+    public function isInstalledVersionAReleasedVersionReturnsTrueForNonDevelopmentVersion(): void
     {
-        $this->expectException(CoreVersionServiceException::class);
-        $this->expectExceptionCode(1380905853);
-        $versionMatrix = [
-            '7' => [
-                'releases' => [
-                    '7.2.0' => []
-                ],
-            ],
-        ];
         /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(
-            CoreVersionService::class,
-            ['getVersionMatrix', 'getMajorVersion', 'ensureVersionExistsInMatrix'],
-            [],
-            '',
-            false
-        );
-        $instance->expects($this->once())->method('getMajorVersion')->will($this->returnValue('7'));
-        $instance->expects($this->once())->method('getVersionMatrix')->will($this->returnValue($versionMatrix));
-        $instance->_call('getReleaseTimestampOfVersion', '7.2.0');
-    }
-
-    /**
-     * @test
-     */
-    public function getReleaseTimestampOfVersionReturnsTimestamp()
-    {
-        $versionMatrixFixtureFile = __DIR__ . '/Fixtures/VersionMatrixFixture.php';
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(
-            CoreVersionService::class,
-            ['getVersionMatrix', 'getMajorVersion', 'ensureVersionExistsInMatrix'],
-            [],
-            '',
-            false
-        );
-        $instance->expects($this->once())->method('getMajorVersion')->will($this->returnValue('7'));
-        $instance->expects($this->once())->method('getVersionMatrix')->will($this->returnValue(require $versionMatrixFixtureFile));
-        $this->assertSame(1398968665, $instance->_call('getReleaseTimestampOfVersion', '7.3.1'));
-    }
-
-    /**
-     * @test
-     */
-    public function ensureVersionExistsInMatrixThrowsExceptionIfMinorVersionDoesNotExist()
-    {
-        $this->expectException(CoreVersionServiceException::class);
-        $this->expectExceptionCode(1380905851);
-        $versionMatrixFixtureFile = __DIR__ . '/Fixtures/VersionMatrixFixture.php';
-        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(
-            CoreVersionService::class,
-            ['getVersionMatrix', 'getMajorVersion'],
-            [],
-            '',
-            false
-        );
-        $instance->expects($this->once())->method('getMajorVersion')->will($this->returnValue('2'));
-        $instance->expects($this->once())->method('getVersionMatrix')->will($this->returnValue(require $versionMatrixFixtureFile));
-        $instance->_call('ensureVersionExistsInMatrix', '2.0.42');
+        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion'], [], '', false);
+        $instance->expects($this->once())->method('getInstalledVersion')->will($this->returnValue('7.2.0'));
+        $this->assertTrue($instance->isInstalledVersionAReleasedVersion());
     }
 
     /**
      * @test
      */
-    public function ensureVersionExistsInMatrixThrowsExceptionIfPatchLevelDoesNotExist()
+    public function isInstalledVersionAReleasedVersionReturnsFalseForDevelopmentVersion()
     {
-        $this->expectException(CoreVersionServiceException::class);
-        $this->expectExceptionCode(1380905852);
-        $versionMatrixFixtureFile = __DIR__ . '/Fixtures/VersionMatrixFixture.php';
         /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
-        $instance = $this->getAccessibleMock(
-            CoreVersionService::class,
-            ['getVersionMatrix', 'getMajorVersion'],
-            [],
-            '',
-            false
-        );
-        $instance->expects($this->once())->method('getMajorVersion')->will($this->returnValue('7'));
-        $instance->expects($this->once())->method('getVersionMatrix')->will($this->returnValue(require $versionMatrixFixtureFile));
-        $instance->_call('ensureVersionExistsInMatrix', '7.2.5');
+        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion'], [], '', false);
+        $instance->expects($this->once())->method('getInstalledVersion')->will($this->returnValue('7.4-dev'));
+        $this->assertFalse($instance->isInstalledVersionAReleasedVersion());
     }
 }
diff --git a/typo3/sysext/install/Tests/Unit/Service/Fixtures/VersionMatrixFixture.php b/typo3/sysext/install/Tests/Unit/Service/Fixtures/VersionMatrixFixture.php
deleted file mode 100644 (file)
index 51be07a..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-return [
-    '7' => [
-        'releases' => [
-            '7.4-dev' => [
-                'type' => 'development',
-                'date' => '2014-06-01 18:24:25 UTC',
-            ],
-            '7.3.1' => [
-                'type' => 'regular',
-                'date' => '2014-05-01 18:24:25 UTC',
-            ],
-            '7.3.0' => [
-                'type' => 'security',
-                'date' => '2014-04-01 18:24:25 UTC',
-            ],
-            '7.2.0' => [
-                'type' => 'regular',
-                'date' => '2014-03-01 18:24:25 UTC',
-                'checksums' => [
-                    'tar' => [
-                        'md5' => 'e91acf53bb03cb943bd27e76643901c5',
-                        'sha1' => '3dc156eed4b99577232f537d798a8691493f8a83',
-                    ],
-                    'zip' => [
-                        'md5' => 'f8d166e9979a43490ec0ae03e0ff46a1',
-                        'sha1' => '87448a8745b6eae36bd1e7cb6705a42771edfa03',
-                    ],
-                ],
-                'url' => [
-                    'zip' => 'http://get.typo3.org/7.2/zip',
-                    'tar' => 'http://get.typo3.org/7.2',
-                ],
-            ],
-        ],
-        'latest' => '7.3.1',
-        'stable' => '7.3.1',
-        'active' => true,
-    ],
-];
diff --git a/typo3/sysext/install/Tests/UnitDeprecated/Service/CoreVersionServiceTest.php b/typo3/sysext/install/Tests/UnitDeprecated/Service/CoreVersionServiceTest.php
new file mode 100644 (file)
index 0000000..718ce84
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+namespace TYPO3\CMS\Install\Tests\UnitDeprecated\Service;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Prophecy\Argument;
+use TYPO3\CMS\Core\Http\JsonResponse;
+use TYPO3\CMS\Core\Http\RequestFactory;
+use TYPO3\CMS\Core\Registry;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Install\Service\CoreVersionService;
+
+/**
+ * Test case
+ */
+class CoreVersionServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
+{
+    /**
+     * @test
+     */
+    public function updateVersionMatrixRemovesOldReleasesFromMatrix(): void
+    {
+        $this->setUpApiResponse([
+            '7' => [],
+            '6.2' => []
+        ]);
+        /** @var $instance CoreVersionService|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+        $instance = $this->getAccessibleMock(CoreVersionService::class, ['getInstalledVersion']);
+        $registry = $this->createMock(Registry::class);
+        $registry
+            ->expects($this->once())
+            ->method('set')
+            ->with('TYPO3.CMS.Install', 'coreVersionMatrix', $this->logicalNot($this->arrayHasKey('6.2')));
+        $instance->expects($this->once())->method('getInstalledVersion')->will($this->returnValue('7.6.25'));
+        $instance->_set('registry', $registry);
+        $instance->updateVersionMatrix();
+    }
+
+    public function setUpApiResponse(array $responseData)
+    {
+        $response = new JsonResponse($responseData);
+        $requestFactory = $this->prophesize(RequestFactory::class);
+        $requestFactory->request('https://get.typo3.org/json', Argument::cetera())->willReturn($response);
+        GeneralUtility::addInstance(RequestFactory::class, $requestFactory->reveal());
+    }
+}
index a443ac9..41e79ab 100644 (file)
@@ -87,3 +87,11 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['reports']['tx_reports']['status']['pr
 if (!(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI)) {
     $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['reports']['tx_reports']['status']['providers']['system'][] = \TYPO3\CMS\Install\Report\EnvironmentStatusReport::class;
 }
+
+\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class)
+    ->connect(
+        \TYPO3\CMS\Backend\Backend\ToolbarItems\SystemInformationToolbarItem::class,
+        'loadMessages',
+        \TYPO3\CMS\Install\SystemInformation\Typo3VersionMessage::class,
+        'appendMessage'
+    );