Commit 15d40553 authored by Benni Mack's avatar Benni Mack
Browse files

[!!!][TASK] Remove flexible providers functionality

There is some heavy legacy code regarding "where to find details/ the extension files"
exactly in TER FE2.

* Mirror -> use a mirror from extension repository (none given in TER), 5 tx_terfe2_domain_model_version entries
* SOAP -> fetch the t3x from the SOAP API (no tx_terfe2_domain_model_version entry)
* File -> default for all other files

The type for each extension version is stored in "tx_terfe2_domain_model_version.extension_provider" and is removed
to reduce massive code and complexity, having only the "FileProvider" in place
removing the ProviderManager, Interface etc.

MirrorService, MirrorPovider, SoapProvider, SoapService can be removed
reducing cross-concern functionality.
parent 2205adce
Pipeline #9337 passed with stages
in 9 minutes and 44 seconds
......@@ -17,6 +17,7 @@ namespace T3o\TerFe2\Controller;
use T3o\Ter\Api\ApiUser;
use T3o\Ter\Api\ExtensionKey;
use T3o\Ter\Api\ExtensionVersion;
use T3o\TerFe2\Provider\FileProvider;
use T3o\TerFe2\Validation\Validator\ComposerNameValidator;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Page\PageRenderer;
......@@ -47,11 +48,6 @@ class ExtensionController extends \T3o\TerFe2\Controller\AbstractController
*/
protected $ownerRepository;
/**
* @var \T3o\TerFe2\Provider\ProviderManager
*/
protected $providerManager;
/**
* @var \T3o\TerFe2\Persistence\Session
*/
......@@ -107,16 +103,6 @@ class ExtensionController extends \T3o\TerFe2\Controller\AbstractController
$this->ownerRepository = $ownerRepository;
}
/**
* inject providerManager
*
* @param \T3o\TerFe2\Provider\ProviderManager $providerManager
*/
public function injectProviderManager(\T3o\TerFe2\Provider\ProviderManager $providerManager)
{
$this->providerManager = $providerManager;
}
/**
* inject session
*
......@@ -382,7 +368,7 @@ class ExtensionController extends \T3o\TerFe2\Controller\AbstractController
}
// Get file path
$provider = $this->providerManager->getProvider($version->getExtensionProvider());
$provider = GeneralUtility::makeInstance(FileProvider::class);
$fileUrl = $provider->getFileUrl($version, $format);
if ($format === 'zip') {
......
<?php
namespace T3o\TerFe2\Domain\Model;
/*
* 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\Extbase\DomainObject\AbstractEntity;
/**
* Extension manager cache entry
*/
class ExtensionManagerCacheEntry extends AbstractEntity
{
// No attributes, getters and setters. Only required to get RAW result from respository
}
......@@ -171,13 +171,6 @@ class Version extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
*/
protected $extension;
/**
* Extension provider
*
* @var string
*/
protected $extensionProvider = '';
/**
* Has zip file
*
......@@ -733,26 +726,6 @@ class Version extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
return $this->extension;
}
/**
* Setter for extensionProvider
*
* @param string $extensionProvider Extension Provider
*/
public function setExtensionProvider(string $extensionProvider)
{
$this->extensionProvider = $extensionProvider;
}
/**
* Getter for extensionProvider
*
* @return string Extension Provider
*/
public function getExtensionProvider(): string
{
return $this->extensionProvider;
}
/**
* Setter for hasZipFile
*
......
<?php
namespace T3o\TerFe2\Domain\Repository;
/*
* 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!
*/
/**
* Repository for \T3o\TerFe2\Domain\Model\ExtensionManagerCacheEntry
*/
class ExtensionManagerCacheEntryRepository extends \T3o\TerFe2\Domain\Repository\AbstractRepository
{
/**
* Get all updated extension rows
*
* @param int $lastUpdateDate Date of the last update
* @param int $offset Offset to start with
* @param int $count Extension count to load
* @return array Objects
*/
public function findLastUpdated($lastUpdateDate, $offset = 0, $count = 0)
{
$query = $this->createQuery($offset, $count);
$query->getQuerySettings()->setRespectStoragePage(false);
$query->getQuerySettings()->setRespectSysLanguage(false);
$query->matching($query->greaterThan('lastuploaddate', (int)$lastUpdateDate));
return $query->execute(true);
}
/**
* Returns one extension row by given extKey and versionString
*
* @param string $extKey Extension Key
* @param string $versionString Version of the extension
* @return array Objects
*/
public function findOneByExtKeyAndVersionString($extKey, $versionString)
{
$query = $this->createQuery(0, 1);
$query->getQuerySettings()->setRespectStoragePage(false);
$query->getQuerySettings()->setRespectSysLanguage(false);
$query->matching(
$query->logicalAnd([
$query->equals('extkey', $extKey),
$query->equals('version', $versionString)
])
);
$rows = $query->execute(true);
if (is_array($rows)) {
return reset($rows);
}
return [];
}
}
<?php
namespace T3o\TerFe2\Provider;
/*
* 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\Core\Utility\GeneralUtility;
/**
* Abstract extension provider
*/
abstract class AbstractProvider implements \T3o\TerFe2\Provider\ProviderInterface
{
/**
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
*/
protected $objectManager;
/**
* @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory
*/
protected $dataMapFactory;
/**
* @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
*/
protected $reflectionService;
/**
* @var array Configuration array
*/
protected $configuration;
/**
* @var string
*/
protected $imageCachePath = 'typo3temp/assets/tx_terfe2/images/';
/**
* Get or create absolute path to image cache directory
*/
public function __construct()
{
$this->imageCachePath = \T3o\TerFe2\Utility\FileUtility::getAbsoluteDirectory($this->imageCachePath);
}
/**
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
*/
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
}
/**
* @param \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory $dataMapFactory
*/
public function injectDataMapFactory(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory $dataMapFactory)
{
$this->dataMapFactory = $dataMapFactory;
}
/**
* @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
*/
public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
{
$this->reflectionService = $reflectionService;
}
/**
* Set configuration for the DataProvider
*
* @param array $configuration TypoScript configuration
*/
public function setConfiguration(array $configuration)
{
$this->configuration = $configuration;
}
/**
* Returns the url to an extension related icon
*
* @param \T3o\TerFe2\Domain\Model\Version $version Version object
* @param string $fileType File type
* @return string Url to icon file
*/
public function getIconUrl(\T3o\TerFe2\Domain\Model\Version $version, $fileType)
{
$filename = $this->getFileName($version, $fileType);
$localName = $this->imageCachePath . basename($filename);
// Check local cache first
if (\T3o\TerFe2\Utility\FileUtility::fileExists($localName)) {
return \T3o\TerFe2\Utility\FileUtility::getUrlFromAbsolutePath($localName);
}
// Get icon from concrete extension provider
$iconUrl = $this->getFileUrl($version, $fileType);
// Copy icon to local cache
if (!empty($iconUrl)) {
\T3o\TerFe2\Utility\FileUtility::copyFile($iconUrl, $localName);
}
return \T3o\TerFe2\Utility\FileUtility::getUrlFromAbsolutePath($localName);
}
/**
* Returns an array with minimum and maximum version number from range
*
* @param string $version Range of versions
* @return array Minumum and maximum version number
*/
protected function getVersionByRange($version)
{
$version = GeneralUtility::trimExplode('-', $version);
$minimum = (!empty($version[0]) ? \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger($version[0]) : 0);
$maximum = (!empty($version[1]) ? \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger($version[1]) : 0);
return [$minimum, $maximum];
}
}
......@@ -14,18 +14,24 @@ namespace T3o\TerFe2\Provider;
* The TYPO3 project - inspiring people to share!
*/
use T3o\TerFe2\Utility\CategoryUtility;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use T3o\TerFe2\Domain\Model\Version;
use T3o\TerFe2\Utility\FileUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Extension provider using local files
* Extension provider using local files for downloading an extension or showing
* the extension icon.
*/
class FileProvider extends \T3o\TerFe2\Provider\AbstractProvider
class FileProvider implements LoggerAwareInterface
{
use LoggerAwareTrait;
/**
* @var \T3o\TerFe2\Domain\Repository\ExtensionManagerCacheEntryRepository
* @var string
*/
protected $extensionManagerRepository;
protected $imageCachePath = 'typo3temp/assets/tx_terfe2/images/';
/**
* @var string
......@@ -33,112 +39,82 @@ class FileProvider extends \T3o\TerFe2\Provider\AbstractProvider
protected $extensionRootPath = 'fileadmin/ter/';
/**
* Initialize provider
* Get or create absolute path to image cache directory
*/
public function initializeProvider()
public function __construct()
{
// Set extension root path
if (!empty($this->configuration['extensionRootPath'])) {
$this->extensionRootPath = $this->configuration['extensionRootPath'];
}
$this->extensionRootPath = \T3o\TerFe2\Utility\FileUtility::getAbsoluteDirectory($this->extensionRootPath);
// Get repository for extension manager cache entries
$this->extensionManagerRepository = $this->objectManager->get(\T3o\TerFe2\Domain\Repository\ExtensionManagerCacheEntryRepository::class);
$this->imageCachePath = FileUtility::getAbsoluteDirectory($this->imageCachePath);
$this->extensionRootPath = FileUtility::getAbsoluteDirectory($this->extensionRootPath);
}
/**
* Returns all extensions since last run
* Returns the url to an extension related icon
*
* @param int $lastRun Timestamp of last update
* @param int $offset Offset to start with
* @param int $count Extension count to load
* @return array Extension rows
* @param Version $version Version object
* @param string $fileType File type
* @return string Url to icon file
*/
public function getExtensions($lastRun, $offset, $count)
public function getIconUrl(Version $version, $fileType)
{
// Get extension list
$extensions = $this->extensionManagerRepository->findLastUpdated($lastRun, $offset, $count);
if (empty($extensions)) {
return [];
$filename = $this->getFileName($version, $fileType);
$localName = $this->imageCachePath . basename($filename);
// Check local cache first
if (FileUtility::fileExists($localName)) {
return FileUtility::getUrlFromAbsolutePath($localName);
}
// Load missing information from ext_emconf.php
foreach ($extensions as $extensionKey => $extension) {
$info = $this->getExtensionInfo($extension['extkey'], $extension['version'], $extension['t3xfilemd5']);
if (empty($info) || !is_array($info)) {
unset($extensions[$extensionKey]);
continue;
}
foreach ($info as $key => $value) {
if (empty($extension[$key])) {
$extensions[$extensionKey][$key] = $value;
}
}
// Get icon from concrete extension provider
$iconUrl = $this->getFileUrl($version, $fileType);
// Copy icon to local cache
if (!empty($iconUrl)) {
FileUtility::copyFile($iconUrl, $localName);
}
return $this->buildExtensionStructure($extensions);
return FileUtility::getUrlFromAbsolutePath($localName);
}
/**
* Returns the url to an extension related file
*
* @param \T3o\TerFe2\Domain\Model\Version $version Version object
* @param Version $version Version object
* @param string $fileType File type
* @return string Url to file
*/
public function getFileUrl(\T3o\TerFe2\Domain\Model\Version $version, $fileType)
public function getFileUrl(Version $version, $fileType)
{
$filename = $this->getFileName($version, $fileType);
$filename = $this->extensionRootPath . $filename;
// Check if file exists
if (!\T3o\TerFe2\Utility\FileUtility::fileExists($filename)) {
if (!FileUtility::fileExists($filename)) {
if ($fileType === 't3x' || $fileType === 'zip') {
//throw new \Exception('File "' . $filename . '" not found');
}
\T3o\TerFe2\Utility\LogUtility::addMessage('File "' . $filename . '" not found', 'ter_fe2', 2);
$this->logger->warning('File "' . $filename . '" not found');
return '';
}
// Get local url from absolute path
return \T3o\TerFe2\Utility\FileUtility::getUrlFromAbsolutePath($filename);
return FileUtility::getUrlFromAbsolutePath($filename);
}
/**
* Returns name of an extension related file
*
* @param \T3o\TerFe2\Domain\Model\Version $version Version object
* @param Version $version Version object
* @param string $fileType File type
* @return string File name
*/
public function getFileName(\T3o\TerFe2\Domain\Model\Version $version, $fileType)
public function getFileName(Version $version, $fileType)
{
$extension = $version->getExtension()->getExtKey();
$version = $version->getVersionString();
return $this->generateFileName($extension, $version, $fileType);
}
/**
* Returns all information about an extension version
*
* @param \T3o\TerFe2\Domain\Model\Version $version Version object
* @return array Version details
*/
public function getVersionDetails(\T3o\TerFe2\Domain\Model\Version $version)
{
$extensionKey = $version->getExtension()->getExtKey();
$versionString = $version->getVersionString();
$entry = $this->extensionManagerRepository->findOneByExtKeyAndVersionString($extensionKey, $versionString);
if (!empty($entry) && is_array($entry)) {
return $entry;
}
return [];
}
/**
* Generates the name of an extension related file
*
......@@ -154,142 +130,7 @@ class FileProvider extends \T3o\TerFe2\Provider\AbstractProvider
}
$extension = strtolower($extension);
$fileType = strtolower(trim($fileType, '. '));
$versionNumberInFileName = implode('.', \TYPO3\CMS\Core\Utility\GeneralUtility::intExplode('.', $version));
$versionNumberInFileName = implode('.', GeneralUtility::intExplode('.', $version));
return $extension[0] . '/' . $extension[1] . '/' . $extension . '_' . $versionNumberInFileName . '.' . $fileType;
}
/**
* Returns the content of an ext_emconf.php file
*
* @param string $extension Extension key
* @param string $version Version string
* @param string $fileHash hash of file
* @return array Extension info array
* @throws \Exception
*/
protected function getExtensionInfo($extension, $version, $fileHash)
{
if (empty($extension) || empty($version) || empty($fileHash)) {
throw new \Exception('Extension key, version and file hash are required to get extension info');
}
// Fetch file from extension root path
$filename = $this->generateFileName($extension, $version, 't3x');
$filename = $this->extensionRootPath . $filename;
$content = \TYPO3\CMS\Core\Utility\GeneralUtility::getURL($filename);
$filesize = strlen($content);
if (empty($content)) {
\T3o\TerFe2\Utility\LogUtility::addMessage('File "' . $filename . '" could not be fetched', 'ter_fe2', 2);
return [];
}
// Check file hash
if ($fileHash !== md5($content)) {
\T3o\TerFe2\Utility\LogUtility::addMessage('File hash missmatch of file "' . $filename . '"', 'ter_fe2', 2);
return [];
}
// Get EM_CONF array
$extension = \T3o\TerFe2\Utility\ArchiveUtility::decompressT3xStream($content);
$emConf = [];
if (!empty($extension['EM_CONF'])) {
$emConf = $extension['EM_CONF'];
}
unset($extension);
unset($emConf['dependencies'], $emConf['conflicts'], $emConf['TYPO3_version'], $emConf['PHP_version']);
// Remap keys
$keyMap = [
'clearCacheOnLoad' => 'clearcacheonload',
'author' => 'authorname',
'author_email' => 'authoremail',
'author_company' => 'authorcompany',
];
foreach ($emConf as $key => $value) {
if (!empty($keyMap[$key])) {
$emConf[$keyMap[$key]] = $value;
unset($emConf[$key]);
}
}
// Add file size
$emConf['t3xfilesize'] = (int)$filesize;
return $emConf;
}
/**
* Build multidimensional array of extension information
*
* @param array $extensionRows Extension rows from repository
* @return array All extension information
*/
protected function buildExtensionStructure(array $extensionRows)
{
if (empty($extensionRows)) {
return [];
}
$extensionModel = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension::class);
$states = $extensionModel->getDefaultState(null);
$states = array_flip($states);
$categories = CategoryUtility::getDefaultCategories();
$categories = array_flip($categories);
$extensions = [];
foreach ($extensionRows as $extension) {
// Extension
$extensions[$extension['extkey']]['ext_key'] = $extension['extkey'];
$extensions[$extension['extkey']]['downloads'] = (int)$extension['alldownloadcounter'];
$extensions[$extension['extkey']]['frontend_user'] = $extension['ownerusername'];
// Versions
$versionString = $extension['version'];
$extensions[$extension['extkey']]['versions'][$versionString] = [
'title' => $extension['title'],
'description' => $extension['description'],
'version_number' => $extension['intversion'],
'version_string' => $versionString,
'upload_date' => $extension['lastuploaddate'],