From dc063acb12c8145513134553b82a87b43c5d3c94 Mon Sep 17 00:00:00 2001
From: Christian Kuhn <lolli@schwarzbu.ch>
Date: Sun, 4 Nov 2012 22:44:36 +0100
Subject: [PATCH] [TASK] Re-implement extension status for reports module

The reports module to show the main typo3.org TER repository
extension list status and the security state of loaded and
existing extensions was not implemented with the new
extension manager.
The patch adds the missing reports.

Resolves: #39914
Releases: 6.0

Change-Id: Ib26dad4d798829ee96d900a80311aa28bb021c2c
Reviewed-on: http://review.typo3.org/16446
Reviewed-by: Wouter Wolters
Tested-by: Wouter Wolters
Reviewed-by: Anja Leichsenring
Tested-by: Anja Leichsenring
Reviewed-by: Christian Kuhn
Tested-by: Christian Kuhn
---
 .../Domain/Repository/ExtensionRepository.php |   5 +-
 .../Repository/RepositoryRepository.php       |  19 +-
 .../Classes/Report/ExtensionStatus.php        | 230 +++++++++++++
 .../Classes/Utility/ListUtility.php           |   9 +-
 .../Resources/Private/Language/locallang.xlf  |  49 +++
 .../Repository/RepositoryRepositoryTest.php   |  75 +++++
 .../Tests/Unit/Report/ExtensionStatusTest.php | 304 ++++++++++++++++++
 .../Unit/Task/UpdateExtensionListTaskTest.php |   8 +
 typo3/sysext/extensionmanager/ext_tables.php  |   5 +-
 typo3/sysext/reports/Classes/Status.php       |  14 +-
 10 files changed, 706 insertions(+), 12 deletions(-)
 create mode 100644 typo3/sysext/extensionmanager/Classes/Report/ExtensionStatus.php
 create mode 100644 typo3/sysext/extensionmanager/Tests/Unit/Domain/Repository/RepositoryRepositoryTest.php
 create mode 100644 typo3/sysext/extensionmanager/Tests/Unit/Report/ExtensionStatusTest.php

diff --git a/typo3/sysext/extensionmanager/Classes/Domain/Repository/ExtensionRepository.php b/typo3/sysext/extensionmanager/Classes/Domain/Repository/ExtensionRepository.php
index a1ca1374e1cd..c14376f08bb1 100644
--- a/typo3/sysext/extensionmanager/Classes/Domain/Repository/ExtensionRepository.php
+++ b/typo3/sysext/extensionmanager/Classes/Domain/Repository/ExtensionRepository.php
@@ -109,10 +109,11 @@ class ExtensionRepository extends \TYPO3\CMS\Extbase\Persistence\Repository {
 	 */
 	public function findOneByExtensionKeyAndVersion($extensionKey, $version) {
 		$query = $this->createQuery();
+		// Hint: This method must not filter out insecure extensions, if needed,
+		// it should be done on a different level, or with a helper method.
 		$query->matching($query->logicalAnd(
 			$query->equals('extensionKey', $extensionKey),
-			$query->equals('version', $version),
-			$query->greaterThanOrEqual('reviewState', 0)
+			$query->equals('version', $version)
 		));
 		return $query->setLimit(1)->execute()->getFirst();
 	}
diff --git a/typo3/sysext/extensionmanager/Classes/Domain/Repository/RepositoryRepository.php b/typo3/sysext/extensionmanager/Classes/Domain/Repository/RepositoryRepository.php
index 945ea890c3c0..b9eefad1b4e0 100644
--- a/typo3/sysext/extensionmanager/Classes/Domain/Repository/RepositoryRepository.php
+++ b/typo3/sysext/extensionmanager/Classes/Domain/Repository/RepositoryRepository.php
@@ -58,7 +58,22 @@ class RepositoryRepository extends \TYPO3\CMS\Extbase\Persistence\Repository {
 		));
 	}
 
+	/**
+	 * Find main typo3.org repository
+	 *
+	 * @return \TYPO3\CMS\Extensionmanager\Domain\Model\Repository
+	 */
+	public function findOneTypo3OrgRepository() {
+		$allRepositories = $this->findAll();
+		$typo3OrgRepository = NULL;
+		foreach ($allRepositories as $repository) {
+			/** @var $repository \TYPO3\CMS\Extensionmanager\Domain\Model\Repository */
+			if ($repository->getTitle() === 'TYPO3.org Main Repository') {
+				$typo3OrgRepository = $repository;
+				break;
+			}
+		}
+		return $typo3OrgRepository;
+	}
 }
-
-
 ?>
\ No newline at end of file
diff --git a/typo3/sysext/extensionmanager/Classes/Report/ExtensionStatus.php b/typo3/sysext/extensionmanager/Classes/Report/ExtensionStatus.php
new file mode 100644
index 000000000000..33af3f879bf4
--- /dev/null
+++ b/typo3/sysext/extensionmanager/Classes/Report/ExtensionStatus.php
@@ -0,0 +1,230 @@
+<?php
+namespace TYPO3\CMS\Extensionmanager\Report;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010-2012 Steffen Kamper <steffen@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.
+ *  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!
+ ***************************************************************/
+
+/**
+ * Extension status reports
+ *
+ * @author Christian Kuhn <lolli@schwarzbu.ch>
+ */
+class ExtensionStatus implements \TYPO3\CMS\Reports\StatusProviderInterface {
+
+	/**
+	 * @var string
+	 */
+	protected $ok = '';
+
+	/**
+	 * @var string
+	 */
+	protected $upToDate = '';
+
+	/**
+	 * @var string
+	 */
+	protected $error = '';
+
+	/**
+	 * @var \TYPO3\CMS\Extbase\Object\ObjectManager
+	 */
+	protected $objectManager = NULL;
+
+	/**
+	 * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository
+	 */
+	protected $repositoryRepository = NULL;
+
+	/**
+	 * @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
+	 */
+	protected $listUtility = NULL;
+
+	/**
+	 * Default constructor
+	 */
+	public function __construct() {
+		$this->objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
+		$this->repositoryRepository = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Domain\\Repository\\RepositoryRepository');
+		$this->listUtility = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Utility\\ListUtility');
+	}
+
+	/**
+	 * Determines extension manager status
+	 *
+	 * @return array List of statuses
+	 */
+	public function getStatus() {
+		$status = array();
+		$status['mainRepositoryStatus'] = $this->getMainRepositoryStatus();
+
+		$extensionStatus = $this->getSecurityStatusOfExtensions();
+		$status['extensionsSecurityStatusInstalled'] = $extensionStatus->loaded;
+		$status['extensionsSecurityStatusNotInstalled'] = $extensionStatus->existing;
+
+		return $status;
+	}
+
+	/**
+	 * Check main repository status: existance, has extensions, last update younger than 7 days
+	 *
+	 * @return \TYPO3\CMS\Reports\Report\Status\Status
+	 */
+	protected function getMainRepositoryStatus() {
+		/** @var $mainRepository \TYPO3\CMS\Extensionmanager\Domain\Model\Repository */
+		$mainRepository = $this->repositoryRepository->findOneTypo3OrgRepository();
+
+		if (is_null($mainRepository) === TRUE) {
+			$value = $GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.mainRepository.notFound.value');
+			$message = $GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.mainRepository.notFound.message');
+			$severity = \TYPO3\CMS\Reports\Status::ERROR;
+		} elseif ($mainRepository->getLastUpdate()->getTimestamp() < $GLOBALS['EXEC_TIME'] - 24 * 60 * 60 * 7) {
+			$value = $GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.mainRepository.notUpToDate.value');
+			$message = $GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.mainRepository.notUpToDate.message');
+			$severity = \TYPO3\CMS\Reports\Status::NOTICE;
+		} else {
+			$value = $GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.mainRepository.upToDate.value');
+			$message = '';
+			$severity = \TYPO3\CMS\Reports\Status::OK;
+		}
+
+		/** @var $status \TYPO3\CMS\Reports\Status */
+		$status = $this->objectManager->get(
+			'TYPO3\\CMS\\Reports\\Status',
+			$GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.mainRepository.title'),
+			$value,
+			$message,
+			$severity
+		);
+
+		return $status;
+	}
+
+	/**
+	 * Get security status of loaded and installed extensions
+	 *
+	 * @return \stdClass with properties 'loaded' and 'existing' containing a TYPO3\CMS\Reports\Report\Status\Status object
+	 */
+	protected function getSecurityStatusOfExtensions() {
+		$extensionInformation = $this->listUtility->getAvailableAndInstalledExtensionsWithAdditionalInformation();
+		$loadedInsecure = array();
+		$existingInsecure = array();
+		foreach ($extensionInformation as $extensionKey => $information) {
+			if (
+				array_key_exists('terObject', $information)
+				&& $information['terObject'] instanceof \TYPO3\CMS\Extensionmanager\Domain\Model\Extension
+			) {
+				/** @var $terObject \TYPO3\CMS\Extensionmanager\Domain\Model\Extension */
+				$terObject = $information['terObject'];
+				$insecureStatus = $terObject->getReviewState();
+				if ($insecureStatus === -1) {
+					if (
+						array_key_exists('installed', $information)
+						&& $information['installed'] === TRUE
+					) {
+						$loadedInsecure[] = array(
+							'extensionKey' => $extensionKey,
+							'version' => $terObject->getVersion(),
+						);
+					} else {
+						$existingInsecure[] = array(
+							'extensionKey' => $extensionKey,
+							'version' => $terObject->getVersion(),
+						);
+					}
+				}
+			}
+		}
+
+		$result = new \stdClass();
+
+		if (count($loadedInsecure) === 0) {
+			$value = $GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.loadedExtensions.noInsecureExtensionLoaded.value');
+			$message = '';
+			$severity = \TYPO3\CMS\Reports\Status::OK;
+		} else {
+			$value = sprintf(
+				$GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.loadedExtensions.insecureExtensionLoaded.value'),
+				count($loadedInsecure)
+			);
+			$extensionList = array();
+			foreach ($loadedInsecure as $insecureExtension) {
+				$extensionList[] = sprintf(
+					$GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.loadedExtensions.insecureExtensionLoaded.message.extension'),
+					$insecureExtension['extensionKey'],
+					$insecureExtension['version']
+				);
+			}
+			$message = sprintf(
+				$GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.loadedExtensions.insecureExtensionLoaded.message'),
+				implode('', $extensionList)
+			);
+			$severity = \TYPO3\CMS\Reports\Status::ERROR;
+		}
+		$result->loaded = $this->objectManager->get(
+			'TYPO3\\CMS\\Reports\\Status',
+			$GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.loadedExtensions.title'),
+			$value,
+			$message,
+			$severity
+		);
+
+		if (count($existingInsecure) === 0) {
+			$value = $GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.existingExtensions.noInsecureExtensionExists.value');
+			$message = '';
+			$severity = \TYPO3\CMS\Reports\Status::OK;
+		} else {
+			$value = sprintf(
+				$GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.existingExtensions.insecureExtensionExists.value'),
+				count($existingInsecure)
+			);
+			$extensionList = array();
+			foreach ($existingInsecure as $insecureExtension) {
+				$extensionList[] = sprintf(
+					$GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.existingExtensions.insecureExtensionExists.message.extension'),
+					$insecureExtension['extensionKey'],
+					$insecureExtension['version']
+				);
+			}
+			$message = sprintf(
+				$GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.existingExtensions.insecureExtensionExists.message'),
+				implode('', $extensionList)
+			);
+			$severity = \TYPO3\CMS\Reports\Status::WARNING;
+		}
+		$result->existing = $this->objectManager->get(
+			'TYPO3\\CMS\\Reports\\Status',
+			$GLOBALS['LANG']->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:report.status.existingExtensions.title'),
+			$value,
+			$message,
+			$severity
+		);
+
+		return $result;
+	}
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/extensionmanager/Classes/Utility/ListUtility.php b/typo3/sysext/extensionmanager/Classes/Utility/ListUtility.php
index 72b5f35ee95a..f62dd89d50ac 100644
--- a/typo3/sysext/extensionmanager/Classes/Utility/ListUtility.php
+++ b/typo3/sysext/extensionmanager/Classes/Utility/ListUtility.php
@@ -29,6 +29,11 @@ namespace TYPO3\CMS\Extensionmanager\Utility;
 /**
  * Utility for dealing with extension list related functions
  *
+ * @TODO: Refactor this API class:
+ * - The methods depend on each other, they take each others result, that could be done internally
+ * - There is no good wording to distinguish existing and loaded extensions
+ * - The name 'listUtility' is not good, the methods could be moved to some 'extensionInformationUtility', or a repository?
+ *
  * @author Susanne Moog <typo3@susannemoog.de>
  * @package Extension Manager
  * @subpackage Utility
@@ -92,7 +97,7 @@ class ListUtility implements \TYPO3\CMS\Core\SingletonInterface {
 	}
 
 	/**
-	 * Returns the list of available (installed) extensions
+	 * Returns the list of available, but not necessarily loaded extensions
 	 *
 	 * @return array Array with two sub-arrays, list array (all extensions with info) and category index
 	 * @see getInstExtList()
@@ -123,7 +128,7 @@ class ListUtility implements \TYPO3\CMS\Core\SingletonInterface {
 	}
 
 	/**
-	 * Reduce the available extensions list to only loaded extensions
+	 * Enrich the output of getAvailableExtensions() with an array key installed = 1 if an extension is loaded.
 	 *
 	 * @param array $availableExtensions
 	 * @return array
diff --git a/typo3/sysext/extensionmanager/Resources/Private/Language/locallang.xlf b/typo3/sysext/extensionmanager/Resources/Private/Language/locallang.xlf
index 14692732e700..3d27bf88ccbd 100644
--- a/typo3/sysext/extensionmanager/Resources/Private/Language/locallang.xlf
+++ b/typo3/sysext/extensionmanager/Resources/Private/Language/locallang.xlf
@@ -229,12 +229,61 @@
 			<trans-unit id="extensionList.updateFromTer.label" xml:space="preserve">
 				<source>Retrieving Extension-List from TYPO3 Extension Repository (TER)</source>
 			</trans-unit>
+
 			<trans-unit id="task.updateExtensionListTask.name" xml:space="preserve">
 				<source>Update extension list</source>
 			</trans-unit>
 			<trans-unit id="task.updateExtensionListTask.description" xml:space="preserve">
 				<source>Update TER extension list on a regular basis. Once a day is a good interval.</source>
 			</trans-unit>
+			<trans-unit id="report.status.mainRepository.title" xml:space="preserve">
+				<source>Update status of typo3.org main repository extension list</source>
+			</trans-unit>
+			<trans-unit id="report.status.mainRepository.notFound.value" xml:space="preserve">
+				<source>Error</source>
+			</trans-unit>
+			<trans-unit id="report.status.mainRepository.notFound.message" xml:space="preserve">
+				<source>The typo3.org extension repository was not found. Please import the main typo3.org extension repository in the install tool wizard.</source>
+			</trans-unit>
+			<trans-unit id="report.status.mainRepository.notUpToDate.value" xml:space="preserve">
+				<source>Extension list is not up to date!</source>
+			</trans-unit>
+			<trans-unit id="report.status.mainRepository.notUpToDate.message" xml:space="preserve">
+				<source>The Main Repository extension list is older than 7 days. Please update it in the Extension manager or Scheduler.</source>
+			</trans-unit>
+			<trans-unit id="report.status.mainRepository.upToDate.value" xml:space="preserve">
+				<source>OK</source>
+			</trans-unit>
+			<trans-unit id="report.status.loadedExtensions.title" xml:space="preserve">
+				<source>Security status of loaded extensions</source>
+			</trans-unit>
+			<trans-unit id="report.status.loadedExtensions.noInsecureExtensionLoaded.value" xml:space="preserve">
+				<source>OK</source>
+			</trans-unit>
+			<trans-unit id="report.status.loadedExtensions.insecureExtensionLoaded.value" xml:space="preserve">
+				<source>%s insecure extension(s) found</source>
+			</trans-unit>
+			<trans-unit id="report.status.loadedExtensions.insecureExtensionLoaded.message" xml:space="preserve">
+				<source>The following extensions are insecure and usage might damage your system. Please update these extensions as soon as possible or remove them from your system:&lt;br&gt;&lt;br&gt;%s</source>
+			</trans-unit>
+			<trans-unit id="report.status.loadedExtensions.insecureExtensionLoaded.message.extension" xml:space="preserve">
+				<source>&lt;strong&gt;%1s&lt;/strong&gt; (version %2s)&lt;br&gt;</source>
+			</trans-unit>
+			<trans-unit id="report.status.existingExtensions.title" xml:space="preserve">
+				<source>Security status of existing, but not loaded extensions</source>
+			</trans-unit>
+			<trans-unit id="report.status.existingExtensions.noInsecureExtensionExists.value" xml:space="preserve">
+				<source>OK</source>
+			</trans-unit>
+			<trans-unit id="report.status.existingExtensions.insecureExtensionExists.value" xml:space="preserve">
+				<source>%s insecure extension(s) found</source>
+			</trans-unit>
+			<trans-unit id="report.status.existingExtensions.insecureExtensionExists.message" xml:space="preserve">
+				<source>The following extensions were found on your system, but are currently not installed. Please delete the extensions using the extension manager:&lt;br&gt;&lt;br&gt;%s</source>
+			</trans-unit>
+			<trans-unit id="report.status.existingExtensions.insecureExtensionExists.message.extension" xml:space="preserve">
+				<source>&lt;strong&gt;%1s&lt;/strong&gt; (version %2s)&lt;br&gt;</source>
+			</trans-unit>
 		</body>
 	</file>
 </xliff>
diff --git a/typo3/sysext/extensionmanager/Tests/Unit/Domain/Repository/RepositoryRepositoryTest.php b/typo3/sysext/extensionmanager/Tests/Unit/Domain/Repository/RepositoryRepositoryTest.php
new file mode 100644
index 000000000000..15b5d4261a91
--- /dev/null
+++ b/typo3/sysext/extensionmanager/Tests/Unit/Domain/Repository/RepositoryRepositoryTest.php
@@ -0,0 +1,75 @@
+<?php
+namespace TYPO3\CMS\Extensionmanager\Tests\Unit\Domain\Repository;
+
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2012 Chritian 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!
+ ***************************************************************/
+
+/**
+ * Test case
+ *
+ * @author Christian Kuhn <lolli@schwarzbu.ch>
+ */
+class RepositoryRepositoryTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
+
+	/**
+	 * @test
+	 */
+	public function findOneTypo3OrgRepositoryReturnsNullIfNoRepositoryWithThisTitleExists() {
+		/** @var $fixture \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository|\PHPUnit_Framework_MockObject_MockObject */
+		$fixture = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Domain\\Repository\\RepositoryRepository', array('findAll'));
+		$fixture
+			->expects($this->once())
+			->method('findAll')
+			->will($this->returnValue(array()));
+
+		$this->assertNull($fixture->findOneTypo3OrgRepository());
+	}
+
+	/**
+	 * @test
+	 */
+	public function findOneTypo3OrgRepositoryReturnsRepositoryWithCorrectTitle() {
+		$mockModelOne = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Domain\\Model\\Repository');
+		$mockModelOne
+			->expects(($this->once()))
+			->method('getTitle')
+			->will($this->returnValue('foo'));
+		$mockModelTwo = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Domain\\Model\\Repository');
+		$mockModelTwo
+			->expects(($this->once()))
+			->method('getTitle')
+			->will($this->returnValue('TYPO3.org Main Repository'));
+
+		/** @var $fixture \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository|\PHPUnit_Framework_MockObject_MockObject */
+		$fixture = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Domain\\Repository\\RepositoryRepository', array('findAll'));
+		$fixture
+			->expects($this->once())
+			->method('findAll')
+			->will($this->returnValue(array($mockModelOne, $mockModelTwo)));
+
+		$this->assertSame($mockModelTwo, $fixture->findOneTypo3OrgRepository());
+	}
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/extensionmanager/Tests/Unit/Report/ExtensionStatusTest.php b/typo3/sysext/extensionmanager/Tests/Unit/Report/ExtensionStatusTest.php
new file mode 100644
index 000000000000..14d9f416f3c9
--- /dev/null
+++ b/typo3/sysext/extensionmanager/Tests/Unit/Report/ExtensionStatusTest.php
@@ -0,0 +1,304 @@
+<?php
+namespace TYPO3\CMS\Extensionmanager\Tests\Unit\Report;
+use TYPO3\CMS\Extensionmanager\Report;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010-2012 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!
+ ***************************************************************/
+
+/**
+ * Test case
+ *
+ * @author Christian Kuhn <lolli@schwarzbu.ch>
+ */
+class ExtensionStatusTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
+
+	/**
+	 * @test
+	 */
+	public function extensionStatusImplementsStatusProviderInterface() {
+		$reportMock = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Report\\ExtensionStatus');
+		$this->assertInstanceOf('TYPO3\\CMS\\Reports\\StatusProviderInterface', $reportMock);
+	}
+
+	/**
+	 * @test
+	 */
+	public function getStatusReturnsArray() {
+		$report = new Report\ExtensionStatus();
+		$this->assertInternalType('array', $report->getStatus());
+	}
+
+	/**
+	 * @test
+	 */
+	public function getStatusReturnArrayContainsThreeEntries() {
+		$report = new Report\ExtensionStatus();
+		$this->assertSame(3, count($report->getStatus()));
+	}
+
+	/**
+	 * @test
+	 */
+	public function getStatusReturnArrayContainsInstancesOfReportsStatusStatus() {
+		$report = new Report\ExtensionStatus();
+		$resultStatuses = $report->getStatus();
+		foreach($resultStatuses as $status) {
+			$this->assertInstanceOf('TYPO3\\CMS\\Reports\\Status', $status);
+		}
+	}
+
+	/**
+	 * @test
+	 */
+	public function getStatusCallsGetMainRepositoryStatusForMainRepositoryStatusResult() {
+		/** @var $mockReport \TYPO3\CMS\Extensionmanager\Report\ExtensionStatus|\PHPUnit_Framework_MockObject_MockObject */
+		$mockReport = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Report\\ExtensionStatus', array('getMainRepositoryStatus'));
+		$mockReport
+			->expects($this->once())
+			->method('getMainRepositoryStatus')
+			->will($this->returnValue('foo'));
+		$result = $mockReport->getStatus();
+		$this->assertSame('foo', $result['mainRepositoryStatus']);
+	}
+
+	/**
+	 * @test
+	 */
+	public function getMainRepositoryStatusReturnsErrorStatusIfRepositoryIsNotFound() {
+		/** @var $mockRepositoryRepository \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository|\PHPUnit_Framework_MockObject_MockObject */
+		$mockRepositoryRepository = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Domain\\Repository\\RepositoryRepository');
+		$mockRepositoryRepository
+			->expects($this->once())
+			->method('findOneTypo3OrgRepository')
+			->will($this->returnValue(NULL));
+
+		/** @var $mockReport \TYPO3\CMS\Extensionmanager\Report\ExtensionStatus|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
+		$mockReport = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Report\\ExtensionStatus', array('dummy'));
+		$mockReport->_set('repositoryRepository', $mockRepositoryRepository);
+
+		/** @var $result \TYPO3\CMS\Reports\Status */
+		$result = $mockReport->_call('getMainRepositoryStatus');
+		$this->assertSame(\TYPO3\CMS\Reports\Status::ERROR, $result->getSeverity());
+	}
+
+	/**
+	 * @test
+	 */
+	public function getMainRepositoryStatusReturnsNoticeIfRepositoryUpdateIsLongerThanSevenDaysAgo() {
+		/** @var $mockRepositoryRepository \TYPO3\CMS\Extensionmanager\Domain\Model\Repository|\PHPUnit_Framework_MockObject_MockObject */
+		$mockRepository = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Domain\\Model\\Repository');
+		$mockRepository
+			->expects($this->once())
+			->method('getLastUpdate')
+			->will($this->returnValue(new \DateTime('-8 days')));
+
+		/** @var $mockRepositoryRepository \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository|\PHPUnit_Framework_MockObject_MockObject */
+		$mockRepositoryRepository = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Domain\\Repository\\RepositoryRepository');
+		$mockRepositoryRepository
+			->expects($this->once())
+			->method('findOneTypo3OrgRepository')
+			->will($this->returnValue($mockRepository));
+
+		/** @var $mockReport \TYPO3\CMS\Extensionmanager\Report\ExtensionStatus|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
+		$mockReport = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Report\\ExtensionStatus', array('dummy'));
+		$mockReport->_set('repositoryRepository', $mockRepositoryRepository);
+
+		/** @var $result \TYPO3\CMS\Reports\Status */
+		$result = $mockReport->_call('getMainRepositoryStatus');
+		$this->assertSame(\TYPO3\CMS\Reports\Status::NOTICE, $result->getSeverity());
+	}
+
+	/**
+	 * @test
+	 */
+	public function getMainRepositoryStatusReturnsOkIfUpdatedLessThanSevenDaysAgo() {
+		/** @var $mockRepositoryRepository \TYPO3\CMS\Extensionmanager\Domain\Model\Repository|\PHPUnit_Framework_MockObject_MockObject */
+		$mockRepository = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Domain\\Model\\Repository');
+		$mockRepository
+			->expects($this->once())
+			->method('getLastUpdate')
+			->will($this->returnValue(new \DateTime('-6 days')));
+
+		/** @var $mockRepositoryRepository \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository|\PHPUnit_Framework_MockObject_MockObject */
+		$mockRepositoryRepository = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Domain\\Repository\\RepositoryRepository');
+		$mockRepositoryRepository
+			->expects($this->once())
+			->method('findOneTypo3OrgRepository')
+			->will($this->returnValue($mockRepository));
+
+		/** @var $mockReport \TYPO3\CMS\Extensionmanager\Report\ExtensionStatus|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
+		$mockReport = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Report\\ExtensionStatus', array('dummy'));
+		$mockReport->_set('repositoryRepository', $mockRepositoryRepository);
+
+		/** @var $result \TYPO3\CMS\Reports\Status */
+		$result = $mockReport->_call('getMainRepositoryStatus');
+		$this->assertSame(\TYPO3\CMS\Reports\Status::OK, $result->getSeverity());
+	}
+
+	/**
+	 * @test
+	 */
+	public function getSecurityStatusOfExtensionsReturnsOkForLoadedExtensionIfNoInsecureExtensionIsLoaded() {
+		/** @var $mockTerObject \TYPO3\CMS\Extensionmanager\Domain\Model\Extension|\PHPUnit_Framework_MockObject_MockObject */
+		$mockTerObject = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Domain\\Model\\Extension');
+		$mockTerObject
+			->expects($this->any())
+			->method('getVersion')
+			->will($this->returnValue('1.0.6'));
+		$mockTerObject
+			->expects($this->atLeastOnce())
+			->method('getReviewState')
+			->will($this->returnValue(0));
+		$mockExtensionList = array(
+			'enetcache' => array(
+				'installed' => TRUE,
+				'terObject' => $mockTerObject
+			),
+		);
+		/** @var $mockListUtility \TYPO3\CMS\Extensionmanager\Utility\ListUtility|\PHPUnit_Framework_MockObject_MockObject */
+		$mockListUtility = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Utility\\ListUtility');
+		$mockListUtility
+			->expects($this->once())
+			->method('getAvailableAndInstalledExtensionsWithAdditionalInformation')
+			->will($this->returnValue($mockExtensionList));
+
+		/** @var $mockReport \TYPO3\CMS\Extensionmanager\Report\ExtensionStatus|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
+		$mockReport = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Report\\ExtensionStatus', array('dummy'));
+		$mockReport->_set('listUtility', $mockListUtility);
+
+		$result = $mockReport->_call('getSecurityStatusOfExtensions');
+		/** @var $loadedResult \TYPO3\CMS\Reports\Status */
+		$loadedResult = $result->loaded;
+		$this->assertSame(\TYPO3\CMS\Reports\Status::OK, $loadedResult->getSeverity());
+	}
+
+	/**
+	 * @test
+	 */
+	public function getSecurityStatusOfExtensionsReturnsErrorForLoadedExtensionIfInsecureExtensionIsLoaded() {
+		/** @var $mockTerObject \TYPO3\CMS\Extensionmanager\Domain\Model\Extension|\PHPUnit_Framework_MockObject_MockObject */
+		$mockTerObject = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Domain\\Model\\Extension');
+		$mockTerObject
+			->expects($this->any())
+			->method('getVersion')
+			->will($this->returnValue('1.0.6'));
+		$mockTerObject
+			->expects($this->atLeastOnce())
+			->method('getReviewState')
+			->will($this->returnValue(-1));
+		$mockExtensionList = array(
+			'enetcache' => array(
+				'installed' => TRUE,
+				'terObject' => $mockTerObject
+			),
+		);
+		/** @var $mockListUtility \TYPO3\CMS\Extensionmanager\Utility\ListUtility|\PHPUnit_Framework_MockObject_MockObject */
+		$mockListUtility = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Utility\\ListUtility');
+		$mockListUtility
+			->expects($this->once())
+			->method('getAvailableAndInstalledExtensionsWithAdditionalInformation')
+			->will($this->returnValue($mockExtensionList));
+
+		/** @var $mockReport \TYPO3\CMS\Extensionmanager\Report\ExtensionStatus|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
+		$mockReport = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Report\\ExtensionStatus', array('dummy'));
+		$mockReport->_set('listUtility', $mockListUtility);
+
+		$result = $mockReport->_call('getSecurityStatusOfExtensions');
+		/** @var $loadedResult \TYPO3\CMS\Reports\Status */
+		$loadedResult = $result->loaded;
+		$this->assertSame(\TYPO3\CMS\Reports\Status::ERROR, $loadedResult->getSeverity());
+	}
+
+	/**
+	 * @test
+	 */
+	public function getSecurityStatusOfExtensionsReturnsOkForExistingExtensionIfNoInsecureExtensionExists() {
+		/** @var $mockTerObject \TYPO3\CMS\Extensionmanager\Domain\Model\Extension|\PHPUnit_Framework_MockObject_MockObject */
+		$mockTerObject = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Domain\\Model\\Extension');
+		$mockTerObject
+			->expects($this->any())
+			->method('getVersion')
+			->will($this->returnValue('1.0.6'));
+		$mockTerObject
+			->expects($this->atLeastOnce())
+			->method('getReviewState')
+			->will($this->returnValue(0));
+		$mockExtensionList = array(
+			'enetcache' => array(
+				'terObject' => $mockTerObject
+			),
+		);
+		/** @var $mockListUtility \TYPO3\CMS\Extensionmanager\Utility\ListUtility|\PHPUnit_Framework_MockObject_MockObject */
+		$mockListUtility = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Utility\\ListUtility');
+		$mockListUtility
+			->expects($this->once())
+			->method('getAvailableAndInstalledExtensionsWithAdditionalInformation')
+			->will($this->returnValue($mockExtensionList));
+
+		/** @var $mockReport \TYPO3\CMS\Extensionmanager\Report\ExtensionStatus|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
+		$mockReport = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Report\\ExtensionStatus', array('dummy'));
+		$mockReport->_set('listUtility', $mockListUtility);
+
+		$result = $mockReport->_call('getSecurityStatusOfExtensions');
+		/** @var $existingResult \TYPO3\CMS\Reports\Status */
+		$existingResult = $result->existing;
+		$this->assertSame(\TYPO3\CMS\Reports\Status::OK, $existingResult->getSeverity());
+	}
+
+	/**
+	 * @test
+	 */
+	public function getSecurityStatusOfExtensionsReturnsErrorForExistingExtensionIfInsecureExtensionExists() {
+		/** @var $mockTerObject \TYPO3\CMS\Extensionmanager\Domain\Model\Extension|\PHPUnit_Framework_MockObject_MockObject */
+		$mockTerObject = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Domain\\Model\\Extension');
+		$mockTerObject
+			->expects($this->any())
+			->method('getVersion')
+			->will($this->returnValue('1.0.6'));
+		$mockTerObject
+			->expects($this->atLeastOnce())
+			->method('getReviewState')
+			->will($this->returnValue(-1));
+		$mockExtensionList = array(
+			'enetcache' => array(
+				'terObject' => $mockTerObject
+			),
+		);
+		/** @var $mockListUtility \TYPO3\CMS\Extensionmanager\Utility\ListUtility|\PHPUnit_Framework_MockObject_MockObject */
+		$mockListUtility = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Utility\\ListUtility');
+		$mockListUtility
+			->expects($this->once())
+			->method('getAvailableAndInstalledExtensionsWithAdditionalInformation')
+			->will($this->returnValue($mockExtensionList));
+
+		/** @var $mockReport \TYPO3\CMS\Extensionmanager\Report\ExtensionStatus|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
+		$mockReport = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Report\\ExtensionStatus', array('dummy'));
+		$mockReport->_set('listUtility', $mockListUtility);
+
+		$result = $mockReport->_call('getSecurityStatusOfExtensions');
+		/** @var $existingResult \TYPO3\CMS\Reports\Status */
+		$existingResult = $result->existing;
+		$this->assertSame(\TYPO3\CMS\Reports\Status::WARNING, $existingResult->getSeverity());
+	}
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/extensionmanager/Tests/Unit/Task/UpdateExtensionListTaskTest.php b/typo3/sysext/extensionmanager/Tests/Unit/Task/UpdateExtensionListTaskTest.php
index 4844e62ffa3c..6074b2f5ee1d 100644
--- a/typo3/sysext/extensionmanager/Tests/Unit/Task/UpdateExtensionListTaskTest.php
+++ b/typo3/sysext/extensionmanager/Tests/Unit/Task/UpdateExtensionListTaskTest.php
@@ -50,6 +50,14 @@ class UpdateExtensionListTaskTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTest
 		\TYPO3\CMS\Core\Utility\GeneralUtility::resetSingletonInstances($this->singletonInstances);
 	}
 
+	/**
+	 * @test
+	 */
+	public function updateExtensionListTaskIsInstanceOfAbstractTask() {
+		$taskMock = $this->getMock('TYPO3\CMS\Extensionmanager\Task\\UpdateExtensionListTask');
+		$this->assertInstanceOf('TYPO3\\CMS\\Scheduler\\Task\\AbstractTask', $taskMock);
+	}
+
 	/**
 	 * @test
 	 */
diff --git a/typo3/sysext/extensionmanager/ext_tables.php b/typo3/sysext/extensionmanager/ext_tables.php
index 6d6bd3eb5a9d..c7c97f974b18 100644
--- a/typo3/sysext/extensionmanager/ext_tables.php
+++ b/typo3/sysext/extensionmanager/ext_tables.php
@@ -41,6 +41,9 @@ if (TYPO3_MODE === 'BE') {
 			'labels' => 'LLL:EXT:' . $_EXTKEY . '/Resources/Private/Language/locallang_mod.xml',
 		)
 	);
-}
 
+	// Register extension status report system
+	$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['reports']['tx_reports']['status']['providers']['Extension Manager'][] =
+		'TYPO3\\CMS\\Extensionmanager\\Report\\ExtensionStatus';
+}
 ?>
\ No newline at end of file
diff --git a/typo3/sysext/reports/Classes/Status.php b/typo3/sysext/reports/Classes/Status.php
index 59f5c19398cf..ddf9e93cd934 100644
--- a/typo3/sysext/reports/Classes/Status.php
+++ b/typo3/sysext/reports/Classes/Status.php
@@ -58,12 +58,16 @@ class Status {
 	protected $severity;
 
 	/**
-	 * constructor for class tx_reports_report_status_Status
+	 * Construct a status
 	 *
-	 * @param string $title The status' title
-	 * @param string $value The status' value
-	 * @param string $message An optional message further describing the status
-	 * @param integer $severity A severity level, one of
+	 * All values must be given as constructor arguments.
+	 * All strings should be localized.
+	 *
+	 * @param string $title Status title, eg. "Deprecation log"
+	 * @param string $value Status value, eg. "Disabled"
+	 * @param string $message Optional message further describing the title/value combination
+	 * 			Example:, eg "The deprecation log is imporant and does foo, to disable it do bar"
+	 * @param integer $severity A severity level. Use one of the constants above!
 	 */
 	public function __construct($title, $value, $message = '', $severity = self::OK) {
 		$this->title = (string) $title;
-- 
GitLab