Commit 99038b21 authored by Benni Mack's avatar Benni Mack
Browse files

[TASK] Reduce usage to tx_ter_extensions

This patch changes
- the extensions.xml generator
- various logic regarding checks if a version exists
to not query the tx_ter_extensions database table anymore.

Now the only place where tx_ter_extensions is actually used
is the SOAP API Upload and the Migration to tx_terfe2_* db structure
which will be migrated in the next patch.
parent c6816027
Pipeline #9365 failed with stages
in 3 minutes and 23 seconds
...@@ -229,16 +229,20 @@ class ExtensionKey ...@@ -229,16 +229,20 @@ class ExtensionKey
throw new UnauthorizedException('Access denied.', ResultCodes::ERROR_DELETEEXTENSIONKEY_ACCESSDENIED); throw new UnauthorizedException('Access denied.', ResultCodes::ERROR_DELETEEXTENSIONKEY_ACCESSDENIED);
} }
$conn = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_ter_extensions'); if ($this->hasUploadedVersions()) {
$items = $conn->select(['*'], 'tx_ter_extensions', ['extensionkey' => $this->extensionKey])->fetchAll();
if (!empty($items)) {
throw new VersionExistsException('Cannot delete an extension, versions still exist', ResultCodes::ERROR_DELETEEXTENSIONKEY_CANTDELETEBECAUSEVERSIONSEXIST); throw new VersionExistsException('Cannot delete an extension, versions still exist', ResultCodes::ERROR_DELETEEXTENSIONKEY_CANTDELETEBECAUSEVERSIONSEXIST);
} }
$conn = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_terfe2_domain_model_extension'); $conn = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_terfe2_domain_model_extension');
$conn->delete('tx_terfe2_domain_model_extension', ['ext_key' => $this->extensionKey]); $conn->delete('tx_terfe2_domain_model_extension', ['ext_key' => $this->extensionKey]);
} }
public function hasUploadedVersions(): bool
{
$conn = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_ter_extensions');
$items = $conn->select(['*'], 'tx_ter_extensions', ['extensionkey' => $this->extensionKey])->fetchAll();
return !empty($items);
}
/** /**
* Check if a user with such a name exists, and return the username from the DB (= with proper lowercase etc) * Check if a user with such a name exists, and return the username from the DB (= with proper lowercase etc)
* or null, if the user does not exist. * or null, if the user does not exist.
......
...@@ -19,7 +19,6 @@ use T3o\Ter\Api\ExtensionKey; ...@@ -19,7 +19,6 @@ use T3o\Ter\Api\ExtensionKey;
use T3o\Ter\Api\ExtensionVersion; use T3o\Ter\Api\ExtensionVersion;
use T3o\TerFe2\Provider\FileProvider; use T3o\TerFe2\Provider\FileProvider;
use T3o\TerFe2\Validation\Validator\ComposerNameValidator; use T3o\TerFe2\Validation\Validator\ComposerNameValidator;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Page\PageRenderer; use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
...@@ -492,7 +491,10 @@ class ExtensionController extends \T3o\TerFe2\Controller\AbstractController ...@@ -492,7 +491,10 @@ class ExtensionController extends \T3o\TerFe2\Controller\AbstractController
if (!$this->userIsAllowedToUploadExtension($extensionKey)) { if (!$this->userIsAllowedToUploadExtension($extensionKey)) {
$this->forwardWithError($this->translate('msg.createVersionUploadNotAllowed'), 'uploadVersion'); $this->forwardWithError($this->translate('msg.createVersionUploadNotAllowed'), 'uploadVersion');
} }
if (!$this->versionIsPossibleForExtension($extensionKey, $extensionInfo->version)) { $extensionKeyObject = new ExtensionKey($extensionKey);
$extensionVersion = new ExtensionVersion($extensionKeyObject, $extensionInfo->version);
if ($extensionVersion->doesExtensionVersionExist()) {
$this->forwardWithError($this->translate('msg.createVersionVersionExists'), 'uploadVersion'); $this->forwardWithError($this->translate('msg.createVersionVersionExists'), 'uploadVersion');
} }
$extensionInfo->extensionKey = $extensionKey; $extensionInfo->extensionKey = $extensionKey;
...@@ -502,8 +504,6 @@ class ExtensionController extends \T3o\TerFe2\Controller\AbstractController ...@@ -502,8 +504,6 @@ class ExtensionController extends \T3o\TerFe2\Controller\AbstractController
// This code will be reworked into a full-blown API // This code will be reworked into a full-blown API
$uploader = new ApiUser($this->frontendUser['username']); $uploader = new ApiUser($this->frontendUser['username']);
$uploader->authenticate(); $uploader->authenticate();
$extensionKeyObject = new ExtensionKey($extensionKey);
$extensionVersion = new ExtensionVersion($extensionKeyObject, $extensionInfo->version);
$extensionVersion->upload($uploader, $extensionInfo, $files); $extensionVersion->upload($uploader, $extensionInfo, $files);
$this->redirect('index', 'Registerkey', null, ['uploaded' => true], $this->settings['pages']['manageKeysPID']); $this->redirect('index', 'Registerkey', null, ['uploaded' => true], $this->settings['pages']['manageKeysPID']);
} catch (\Exception $exception) { } catch (\Exception $exception) {
...@@ -607,36 +607,6 @@ class ExtensionController extends \T3o\TerFe2\Controller\AbstractController ...@@ -607,36 +607,6 @@ class ExtensionController extends \T3o\TerFe2\Controller\AbstractController
return null; return null;
} }
/**
* Check if an version does not exist for extension
*
* There is no better (and faster) way to do this at the moment.
*
* @param string $extensionKey The extension key
* @param string $versionString The extension version
* @return bool TRUE if version already exists
*
* TODO: Discuss with Thomas, logic looks incorrect with naming and return value.
*/
protected function versionIsPossibleForExtension($extensionKey, $versionString)
{
if (empty($extensionKey) || empty($versionString)) {
return false;
}
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_ter_extensions');
$versionExistsForExtension = $connection->count(
'uid',
'tx_ter_extensions',
[
'extensionkey' => $extensionKey,
'version' => $versionString
]
);
return !$versionExistsForExtension;
}
/** /**
* sets SYS_LASTCHANGED to this date if it is newer than the currently set * sets SYS_LASTCHANGED to this date if it is newer than the currently set
* @param int $dateTime * @param int $dateTime
......
...@@ -13,13 +13,12 @@ namespace T3o\TerFe2\Domain\Repository; ...@@ -13,13 +13,12 @@ namespace T3o\TerFe2\Domain\Repository;
use T3o\TerFe2\Service\DocumentationService; use T3o\TerFe2\Service\DocumentationService;
use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
/** /**
* This class combines all data of an extension and all versions from various database tables * This class combines all data of an extension and all versions from various database tables
* *
* tx_ter_extensions -> contains all versions of all extensions * tx_terfe2_domain_model_version -> contains all versions of all extensions
* tx_terfe2_domain_model_extension -> contains all registered extension keys and meta data (even if there was no version uploaded) * tx_terfe2_domain_model_extension -> contains all registered extension keys and meta data (even if there was no version uploaded)
* *
* Currently this class is used as a wrapper, but should vanish once we migrated the database structures properly * Currently this class is used as a wrapper, but should vanish once we migrated the database structures properly
...@@ -36,65 +35,56 @@ class CombinedExtensionRepository ...@@ -36,65 +35,56 @@ class CombinedExtensionRepository
$this->documentationService = $documentationService ?? GeneralUtility::makeInstance(DocumentationService::class); $this->documentationService = $documentationService ?? GeneralUtility::makeInstance(DocumentationService::class);
} }
/**
* Fetches all information about a given list of extension keys that is not version-specific.
*
* @param array $extensionKeys
* @return array
*/
protected function getBasicExtensionInformation(array $extensionKeys): array
{
$basicExtensionInformation = [];
foreach ($extensionKeys as $extensionKey) {
$keysQueryBuilder = $this->getQueryBuilder('tx_terfe2_domain_model_extension');
$extensionKeyRow = $keysQueryBuilder
->select('uid', 'frontend_user', 'downloads', 'external_manual')
->from('tx_terfe2_domain_model_extension')
->where(
$keysQueryBuilder->expr()->eq('ext_key', $keysQueryBuilder->createNamedParameter($extensionKey))
)
->execute()
->fetch();
$basicExtensionInformation[$extensionKey] = [
'extensionuid' => $extensionKeyRow['uid'],
'ownerusername' => $extensionKeyRow['frontend_user'],
'downloads' => $extensionKeyRow['downloads'],
'external_manual' => $extensionKeyRow['external_manual'] ?: ''
];
}
return $basicExtensionInformation;
}
public function getExtensionDetailsWithVersionsAndDownloadNumbers(): array public function getExtensionDetailsWithVersionsAndDownloadNumbers(): array
{ {
$groupedVersionsByExtension = []; $groupedVersionsByExtension = [];
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_terfe2_domain_model_version');
$queryBuilder->getRestrictions()->removeAll();
$stmt = $queryBuilder
->select(
'v.uid',
'v.tstamp',
'v.extension',
'e.ext_key as extensionkey',
'e.downloads AS totaldownloads',
'e.frontend_user',
'e.external_manual',
'v.version_string AS version',
'v.title',
'v.description',
'v.state',
'v.review_state',
'v.em_category',
'v.download_counter',
'v.file_hash',
'v.upload_date',
'v.upload_comment',
'v.dependencies',
'v.composer_info',
'v.authorname',
'v.authoremail',
'v.authorcompany',
)
->from('tx_terfe2_domain_model_version', 'v')
->leftJoin(
'v',
'tx_terfe2_domain_model_extension',
'e',
$queryBuilder->expr()->eq('v.extension', $queryBuilder->quoteIdentifier('e.uid'))
)
->where(
$queryBuilder->expr()->eq('v.hidden', 0),
$queryBuilder->expr()->eq('v.deleted', 0)
)
->execute();
$queryBuilder = $this->getQueryBuilder('tx_ter_extensions'); while ($row = $stmt->fetch()) {
$versions = $queryBuilder
->select('uid', 'tstamp', 'extensionkey', 'version', 'title', 'description', 'state', 'reviewstate', 'category', 'downloadcounter', 't3xfilemd5', 'lastuploaddate', 'uploadcomment', 'dependencies', 'composerinfo', 'authorname', 'authoremail', 'authorcompany')
->from('tx_ter_extensions')
->execute()
->fetchAll();
$usedExtensionKeys = array_column($versions, 'extensionkey');
$usedExtensionKeys = array_unique($usedExtensionKeys);
$basicExtensionInformation = $this->getBasicExtensionInformation($usedExtensionKeys);
foreach ($versions as $row) {
$extensionKey = $row['extensionkey']; $extensionKey = $row['extensionkey'];
$versionNumber = $row['version']; $versionNumber = $row['version'];
$genericExtensionInformation = $basicExtensionInformation[$extensionKey]; $groupedVersionsByExtension[$extensionKey]['frontend_user'] = $row['frontend_user'];
$groupedVersionsByExtension[$extensionKey]['ownerusername'] = $genericExtensionInformation['ownerusername']; $groupedVersionsByExtension[$extensionKey]['downloads'] = $row['downloads'];
$groupedVersionsByExtension[$extensionKey]['downloads'] = $genericExtensionInformation['downloads'];
$versionInformation = $this->getVersionInformation($basicExtensionInformation[$extensionKey]['extensionuid'], $versionNumber); if (empty($row['external_manual'])) {
$row['reviewstate'] = $versionInformation['review_state'];
if (!empty($genericExtensionInformation['external_manual'])) {
$row['external_manual'] = $genericExtensionInformation['external_manual'];
} else {
try { try {
$row['external_manual'] = $this->documentationService->getDocumentationLink($extensionKey, $versionNumber, true); $row['external_manual'] = $this->documentationService->getDocumentationLink($extensionKey, $versionNumber, true);
} catch (\Exception $e) { } catch (\Exception $e) {
...@@ -106,30 +96,4 @@ class CombinedExtensionRepository ...@@ -106,30 +96,4 @@ class CombinedExtensionRepository
return $groupedVersionsByExtension; return $groupedVersionsByExtension;
} }
protected function getVersionInformation(int $extensionUid, string $versionNumber)
{
$queryBuilder = $this->getQueryBuilder('tx_terfe2_domain_model_version');
$queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction::class));
$version = $queryBuilder
->select('*')
->from('tx_terfe2_domain_model_version')
->where(
$queryBuilder->expr()->andX(
$queryBuilder->expr()->eq('extension', $queryBuilder->createNamedParameter($extensionUid)),
$queryBuilder->expr()->eq('version_string', $queryBuilder->createNamedParameter($versionNumber, \PDO::PARAM_STR))
)
)
->execute()
->fetch();
return $version;
}
protected function getQueryBuilder(string $table): QueryBuilder
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
$queryBuilder->getRestrictions()->removeAll();
return $queryBuilder;
}
} }
...@@ -135,8 +135,8 @@ class ExtensionIndexService implements LoggerAwareInterface ...@@ -135,8 +135,8 @@ class ExtensionIndexService implements LoggerAwareInterface
$versionObj->appendChild(new \DOMElement('title', $this->xmlentities((string)$extensionVersionArr['title']))); $versionObj->appendChild(new \DOMElement('title', $this->xmlentities((string)$extensionVersionArr['title'])));
$versionObj->appendChild(new \DOMElement('description', $this->xmlentities((string)$extensionVersionArr['description']))); $versionObj->appendChild(new \DOMElement('description', $this->xmlentities((string)$extensionVersionArr['description'])));
$versionObj->appendChild(new \DOMElement('state', $this->xmlentities((string)$extensionVersionArr['state']))); $versionObj->appendChild(new \DOMElement('state', $this->xmlentities((string)$extensionVersionArr['state'])));
$versionObj->appendChild(new \DOMElement('reviewstate', (string)$extensionVersionArr['reviewstate'])); $versionObj->appendChild(new \DOMElement('reviewstate', (string)$extensionVersionArr['review_state']));
$versionObj->appendChild(new \DOMElement('category', $this->xmlentities((string)$extensionVersionArr['category']))); $versionObj->appendChild(new \DOMElement('category', $this->xmlentities((string)$extensionVersionArr['em_category'])));
if ($extensionVersionArr['category'] === 'distribution') { if ($extensionVersionArr['category'] === 'distribution') {
$prefixDistributionFilePath = $extensionKey[0] . '/' . $extensionKey[1] . '/' . $extensionKey . '_' . $versionNumber . '_'; $prefixDistributionFilePath = $extensionKey[0] . '/' . $extensionKey[1] . '/' . $extensionKey . '_' . $versionNumber . '_';
$distributionImage = $prefixDistributionFilePath . 'Distribution.png'; $distributionImage = $prefixDistributionFilePath . 'Distribution.png';
...@@ -151,18 +151,18 @@ class ExtensionIndexService implements LoggerAwareInterface ...@@ -151,18 +151,18 @@ class ExtensionIndexService implements LoggerAwareInterface
$versionObj->appendChild( $versionObj->appendChild(
new \DOMElement( new \DOMElement(
'downloadcounter', 'downloadcounter',
$this->xmlentities((string)$extensionVersionArr['downloadcounter']) $this->xmlentities((string)$extensionVersionArr['download_counter'])
) )
); );
$versionObj->appendChild(new \DOMElement('lastuploaddate', (string)$extensionVersionArr['lastuploaddate'])); $versionObj->appendChild(new \DOMElement('lastuploaddate', (string)$extensionVersionArr['upload_date']));
$versionObj->appendChild( $versionObj->appendChild(
new \DOMElement( new \DOMElement(
'uploadcomment', 'uploadcomment',
$this->xmlentities((string)$extensionVersionArr['uploadcomment']) $this->xmlentities((string)$extensionVersionArr['upload_comment'])
) )
); );
$versionObj->appendChild(new \DOMElement('dependencies', (string)$extensionVersionArr['dependencies'])); $versionObj->appendChild(new \DOMElement('dependencies', (string)$extensionVersionArr['dependencies']));
$versionObj->appendChild(new \DOMElement('composerinfo', (string)$extensionVersionArr['composerinfo'])); $versionObj->appendChild(new \DOMElement('composerinfo', (string)$extensionVersionArr['composer_info']));
$versionObj->appendChild(new \DOMElement('authorname', $this->xmlentities((string)$extensionVersionArr['authorname']))); $versionObj->appendChild(new \DOMElement('authorname', $this->xmlentities((string)$extensionVersionArr['authorname'])));
$versionObj->appendChild(new \DOMElement('authoremail', $this->xmlentities((string)$extensionVersionArr['authoremail']))); $versionObj->appendChild(new \DOMElement('authoremail', $this->xmlentities((string)$extensionVersionArr['authoremail'])));
$versionObj->appendChild( $versionObj->appendChild(
...@@ -174,10 +174,10 @@ class ExtensionIndexService implements LoggerAwareInterface ...@@ -174,10 +174,10 @@ class ExtensionIndexService implements LoggerAwareInterface
$versionObj->appendChild( $versionObj->appendChild(
new \DOMElement( new \DOMElement(
'ownerusername', 'ownerusername',
$this->xmlentities((string)$extensionVersionsArr['ownerusername']) $this->xmlentities((string)$extensionVersionsArr['frontend_user'])
) )
); );
$versionObj->appendChild(new \DOMElement('t3xfilemd5', (string)$extensionVersionArr['t3xfilemd5'])); $versionObj->appendChild(new \DOMElement('t3xfilemd5', (string)$extensionVersionArr['file_hash']));
$versionObj->appendChild(new \DOMElement('documentation_link', (string)$extensionVersionArr['external_manual'] ?: '')); $versionObj->appendChild(new \DOMElement('documentation_link', (string)$extensionVersionArr['external_manual'] ?: ''));
} }
} }
......
...@@ -15,6 +15,7 @@ namespace T3o\TerFe2\Task; ...@@ -15,6 +15,7 @@ namespace T3o\TerFe2\Task;
*/ */
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use T3o\Ter\Api\ExtensionKey;
use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Log\LogManager; use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Mail\MailMessage; use TYPO3\CMS\Core\Mail\MailMessage;
...@@ -205,8 +206,8 @@ class CheckForExpiredExtensions extends Task ...@@ -205,8 +206,8 @@ class CheckForExpiredExtensions extends Task
$uidsToDelete = []; $uidsToDelete = [];
while ($expiredExtension = $expiredExtensions->fetch(\PDO::FETCH_ASSOC)) { while ($expiredExtension = $expiredExtensions->fetch(\PDO::FETCH_ASSOC)) {
// Deleted in ter, then delete the key in the ter_fe2 extension table $extensionKey = new ExtensionKey($expiredExtension['ext_key']);
if ($expiredExtension['ext_key'] && $this->deleteExtensionKeyInTer($expiredExtension['ext_key'])) { if (!$extensionKey->hasUploadedVersions()) {
$uidsToDelete[] = $expiredExtension['uid']; $uidsToDelete[] = $expiredExtension['uid'];
} }
} }
...@@ -225,31 +226,4 @@ class CheckForExpiredExtensions extends Task ...@@ -225,31 +226,4 @@ class CheckForExpiredExtensions extends Task
)); ));
} }
} }
/**
* This method can be removed as soon as tx_ter_extensions is not in use anymore.
* @param $extensionKey
* @return bool|resource
*/
protected function deleteExtensionKeyInTer($extensionKey)
{
// check if there are extension versions
$extensionsConnection = GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionForTable('tx_ter_extensions');
$versionCount = $extensionsConnection->count(
'extensionkey',
'tx_ter_extensions',
[
'extensionkey' => $extensionKey
]
);
if (!$versionCount || $versionCount === 0) {
$this->logger->info(sprintf('Deleted extension key %s as it did not have versions.', $extensionKey));
return true;
}
return false;
}
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment