Commit 62fe2acc authored by Benni Mack's avatar Benni Mack
Browse files

[FEATURE] Add ExtensionVersion API

A new API class "ExtensionVersion" deals with a specific uploaded
version of an extension.

This class is responsible for checking sanitized versions, and serves
as entrypoint to hide the logic behind "tx_ter_extensions" (and soon "tx_ter_extensiondetails"), by also handling deletion, updating reviewstate
or uploading of new extension versions.

On top, all TYPO3_DB calls are removed from EXT:ter with this change, moving
more actual logic out of the SOAP API endpoints.

In addition, the non-SOAP-API is now using non-static calls, as all
logic is wrapped in a doUpload() method.
parent 79ab1165
Pipeline #9138 failed with stages
in 52 seconds
......@@ -61,6 +61,11 @@ class Configuration implements SingletonInterface
return $this->repositoryDirectory;
}
public function doesRepositoryBasePathExist(): bool
{
return @is_dir($this->repositoryDirectory);
}
public function getAdministratorsGroupId(): int
{
return $this->administratorsGroupId;
......
......@@ -17,6 +17,7 @@ use T3o\Ter\Exception\InternalServerErrorException;
use T3o\Ter\Exception\InvalidExtensionKeyFormatException;
use T3o\Ter\Exception\UnauthorizedException;
use T3o\Ter\Exception\UserNotFoundException;
use T3o\Ter\Exception\VersionExistsException;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
......@@ -112,6 +113,20 @@ class ExtensionKey
return false;
}
/**
* Add a new extension key.
*
* @param ApiUser $authenticatedUser
* @param string $title
* @param string $description
* @param string $ownerUserName
* @return bool
* @throws ExtensionKeyAlreadyInUseException
* @throws InternalServerErrorException
* @throws InvalidExtensionKeyFormatException
* @throws UnauthorizedException
* @throws UserNotFoundException
*/
public function registerKey(
ApiUser $authenticatedUser,
string $title,
......@@ -160,6 +175,16 @@ class ExtensionKey
return true;
}
/**
* Owners or administrators can transfer the ownership of an extension key to somebody else (fe_user must exist)
*
* @param ApiUser $authenticatedUser
* @param string $newOwnerName
* @throws ExtensionKeyNotFoundException
* @throws InternalServerErrorException
* @throws UnauthorizedException
* @throws UserNotFoundException
*/
public function transferOwnership(ApiUser $authenticatedUser, string $newOwnerName): void
{
if (!$authenticatedUser->isAuthenticated()) {
......@@ -195,6 +220,30 @@ class ExtensionKey
}
}
/**
* Removes an extension key from the database, only possible if there are no versions of an extensions released.
* Only possible for owners or administrators.
*
* @param ApiUser $user
* @throws UnauthorizedException
* @throws VersionExistsException
*/
public function unregister(ApiUser $user): void
{
if (!$this->isApiUserOwnerOrAdmin($user)) {
throw new UnauthorizedException('Access denied.', ResultCodes::ERROR_DELETEEXTENSIONKEY_ACCESSDENIED);
}
$conn = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_ter_extensions');
$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);
}
$conn = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_ter_extensionkeys');
$conn->delete('tx_ter_extensionkeys', ['extensionkey' => $this->extensionKey]);
}
/**
* 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.
......
<?php
declare(strict_types = 1);
namespace T3o\Ter\Api;
/*
* This file is part of TYPO3 CMS-extension "ter", created by Benni Mack.
*
* 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.
*/
use T3o\Ter\Exception\FailedDependencyException;
use T3o\Ter\Exception\InternalServerErrorException;
use T3o\Ter\Exception\NotFoundException;
use T3o\Ter\Exception\UnauthorizedException;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* API class checking against specific versions (= uploaded extensions) of an extensionkey.
*
* This class should also contain "add()" in the future to add files to the extension version.
* In addition, all checks against "tx_ter_extensions" and "tx_ter_extensiondetails" should be coupled in there,
* so no outside checks are needed anymore.
*/
class ExtensionVersion
{
/**
* @var ExtensionKey
*/
protected $extensionKey;
/**
* @var string
*/
protected $version;
public function __construct(ExtensionKey $extensionKey, string $version)
{
$this->extensionKey = $extensionKey;
$this->version = $version;
}
public function getExtensionKey(): ExtensionKey
{
return $this->extensionKey;
}
public function getVersion(): string
{
return $this->version;
}
/**
* Checks if the version number consists of 4.10.99, no checks against the database are made.
*
* @return bool
*/
public function isValidVersionNumber(): bool
{
return preg_match('/^(0|[1-9]\d{0,2})\.(0|[1-9]\d{0,2})\.(0|[1-9]\d{0,2})$/', $this->version) !== false;
// alternative (preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $version) !== false) {
}
/**
* Checks if the version of the uploaded extension already exists in the database.
*/
public function doesExtensionVersionExist(): bool
{
return $this->getUidOfExtensionVersion() !== null;
}
/**
* Fetches the UID, currently needed because the "uid" is matched against "tx_ter_extensiondetails.extensionuid".
*
* @return int|null
*/
public function getUidOfExtensionVersion(): ?int
{
$conn = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_ter_extensions');
$record = $conn->select(['uid'], 'tx_ter_extensions', ['extensionkey' => (string)$this->extensionKey, 'version' => $this->version])->fetch();
if ($record['uid'] ?? 0) {
return (int)$record['uid'];
}
return null;
}
public function getStoragePrefix(): string
{
[$majorVersion, $minorVersion, $devVersion] = GeneralUtility::intExplode(
'.',
$this->getVersion()
);
return $this->getStorageFolder() . strtolower(((string)$this->extensionKey) . '_' . $majorVersion . '.' . $minorVersion . '.' . $devVersion);
}
protected function getStorageFolder(): string
{
$extensionKey = (string)$this->extensionKey;
$firstLetter = strtolower(substr($extensionKey, 0, 1));
$secondLetter = strtolower(substr($extensionKey, 1, 1));
$basePath = GeneralUtility::makeInstance(Configuration::class)->getRepositoryBasePath();
return $basePath . $firstLetter . '/' . $secondLetter . '/';
}
/**
* Checks if the extension has a dependency on one of the supported TYPO3 versions.
*
* @param object|array|null $dependencies
*/
public function checkExtensionDependencyOnSupportedTypo3Version($dependencies): void
{
[$oldestSupportedCoreVersion, $newestCoreVersion] = $this->getSupportedCoreVersions();
// Compare currently supported core version with the dependency in the extension
$typo3Range = '';
if (!is_array($dependencies)) {
throw new FailedDependencyException('No dependency for TYPO3 Core given.', ResultCodes::ERROR_UPLOADEXTENSION_TYPO3DEPENDENCYINCORRECT);
}
foreach ($dependencies as $dependency) {
if (is_object($dependency)) {
if ($dependency->kind == 'depends' && $dependency->extensionKey == 'typo3') {
$typo3Range = $dependency->versionRange;
break;
}
} else {
if ($dependency['kind'] == 'depends' && $dependency['extensionKey'] == 'typo3') {
$typo3Range = $dependency['versionRange'];
break;
}
}
}
[$lower, $upper] = GeneralUtility::trimExplode('-', $typo3Range);
if (empty($lower) || empty($upper)) {
// Either part of the range is empty
throw new FailedDependencyException('No range for TYPO3 Core given.', ResultCodes::ERROR_UPLOADEXTENSION_TYPO3DEPENDENCYINCORRECT);
} elseif (!preg_match('/^\d+\.\d+\.\d+$/', $lower) || !preg_match('/^\d+\.\d+\.\d+$/', $upper)) {
// Either part is not a full version number
throw new FailedDependencyException('No full version for TYPO3 Core given.', ResultCodes::ERROR_UPLOADEXTENSION_TYPO3DEPENDENCYINCORRECT);
} elseif (version_compare($lower, '0.0.0', '<=') || version_compare($upper, '0.0.0', '<=')) {
// Either part is a zero version (n < n.0 < n.0.0)
throw new FailedDependencyException('Invalid version for TYPO3 Core given.', ResultCodes::ERROR_UPLOADEXTENSION_TYPO3DEPENDENCYINCORRECT);
} elseif (version_compare($upper, $oldestSupportedCoreVersion, '<')) {
// Upper limit is lower than oldest core version
throw new FailedDependencyException('Invalid version constraint for TYPO3 Core given. Upper limit is lower than oldest core version.', ResultCodes::ERROR_UPLOADEXTENSION_TYPO3DEPENDENCYINCORRECT);
} elseif (version_compare($upper, $newestCoreVersion, '>')) {
// Upper limit is larger than newest core version
throw new FailedDependencyException('Invalid version constraint for TYPO3 Core given. Upper limit is higher than latest core version.', ResultCodes::ERROR_UPLOADEXTENSION_TYPO3DEPENDENCYINCORRECT);
} elseif (version_compare($lower, $upper, '>')) {
// Lower limit is higher than upper limit
throw new FailedDependencyException('Invalid version constraint for TYPO3 Core given. Lower limit is higher than upper limit.', ResultCodes::ERROR_UPLOADEXTENSION_TYPO3DEPENDENCYINCORRECT);
}
}
/**
* Set the review state, if the user is a reviewer.
*
* @param ApiUser $user
* @param int $reviewState
* @throws InternalServerErrorException
* @throws NotFoundException
* @throws UnauthorizedException
*/
public function updateReviewState(ApiUser $user, int $reviewState): void
{
if (!$user->isAuthenticated() || !$user->isReviewer()) {
throw new UnauthorizedException('Access denied.', ResultCodes::ERROR_SETREVIEWSTATE_ACCESSDENIED);
}
if (!$this->doesExtensionVersionExist()) {
throw new NotFoundException(
'Extension version does not exist.',
ResultCodes::ERROR_SETREVIEWSTATE_EXTENSIONVERSIONDOESNOTEXIST
);
}
$conn = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_ter_extensions');
$result = $conn->update('tx_ter_extensions', ['reviewstate' => $reviewState], ['extensionkey' => (string)$this->extensionKey, 'version' => $this->version]);
if ($result === 0) {
throw new InternalServerErrorException(
'Database error while updating extension review state.',
ResultCodes::ERROR_GENERAL_DATABASEERROR
);
}
}
/**
* Removes the extensiondetails, the preview files of a specific version.
* Only possible for administrators.
*
* @param ApiUser $user
* @throws InternalServerErrorException
* @throws UnauthorizedException
*/
public function delete(ApiUser $user): void
{
if (!$user->isAuthenticated() || !$user->isAdministrator()) {
throw new UnauthorizedException(
'Access denied. You must be administrator in order to delete extensions',
ResultCodes::ERROR_DELETEEXTENSION_ACCESS_DENIED
);
}
$apiConfiguration = GeneralUtility::makeInstance(Configuration::class);
if (!$apiConfiguration->doesRepositoryBasePathExist()) {
throw new InternalServerErrorException(
'Extension repository directory does not exist.',
ResultCodes::ERROR_GENERAL_EXTREPDIRDOESNTEXIST
);
}
$uidOfExtensionVersion = $this->getUidOfExtensionVersion();
if ($uidOfExtensionVersion === null) {
throw new InternalServerErrorException(
'deleteExtension_deleteFromDBAndRemoveFiles: Extension does not exist. (extensionkey: ' . (string)$this->extensionKey . ' version: ' . $this->version . ')',
ResultCodes::ERROR_DELETEEXTENSION_EXTENSIONDOESNTEXIST
);
}
$conn = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_ter_extensiondetails');
$conn->delete('tx_ter_extensiondetails', ['extensionuid' => $uidOfExtensionVersion]);
$conn = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_ter_extensions');
$affectedRows = $conn->delete('tx_ter_extensions', ['extensionkey' => (string)$this->extensionKey, 'version' => $this->version]);
if (!$affectedRows) {
throw new InternalServerErrorException(
'Database error while deleting extension. (extensionkey: ' . (string)$this->extensionKey . ' version: ' . $this->version . ')',
ResultCodes::ERROR_GENERAL_DATABASEERROR
);
}
$fullPathPrefix = $this->getStoragePrefix();
$filesToDelete = [
'.t3x',
'.gif',
'.png',
'_Distribution.png',
'_DistributionWelcome.png',
];
foreach ($filesToDelete as $file) {
if (file_exists($fullPathPrefix . $file)) {
@unlink($fullPathPrefix . $file);
}
}
}
/**
* Helper functions to fetch the latest and lowest supported TYPO3 Core versions. This method should be extracted into its own class
*
* @return array
* @throws FailedDependencyException
*/
protected function getSupportedCoreVersions(): array
{
$coreVersionData = GeneralUtility::getUrl(
Environment::getPublicPath() . '/' . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . 'currentcoredata.json'
);
$currentCores = json_decode($coreVersionData, true);
if ($currentCores === null) {
throw new FailedDependencyException('No core data found', ResultCodes::ERROR_UPLOADEXTENSION_TYPO3DEPENDENCYCHECKFAILED);
}
// Collect currently supported core versions
$supportedCoreVersions = [];
$oldestSupportedCoreVersion = '99.99.99';
$newestCoreVersion = '0.0.0';
foreach ($currentCores as $version => $coreInfo) {
if (is_array($coreInfo) && ($coreInfo['active'] === true || $coreInfo['elts'] === true)) {
// Only use keys that represent a branch number
if (strpos($version, '.') && preg_match('/^(\d+)\.\d+$/', $version, $matches)) {
$latestBranchVersion = $coreInfo['latest'];
} else {
// Manage core version without branch (>= 7 LTS)
$latestBranchVersion = $version . '.99.999';
}
// Checks the latest version
if (!preg_match('/dev|alpha/', $latestBranchVersion)) {
$supportedCoreVersions[] = $latestBranchVersion;
if (version_compare($newestCoreVersion, $latestBranchVersion, '<')) {
$newestCoreVersion = $latestBranchVersion;
}
}
// Check the oldest active version
if (version_compare($version . '.0', $oldestSupportedCoreVersion, '<')) {
$oldestSupportedCoreVersion = $version;
}
}
}
// clean newest core version
preg_match('/^(\d+)\.(\d+)\.(\d+)/', $newestCoreVersion, $matches);
$newestCoreVersion = $matches[1] . '.' . $matches[2] . '.999';
// get first beta of oldest active version
$oldestSupportedCoreVersionReleases = array_reverse($currentCores[$oldestSupportedCoreVersion]['releases']);
foreach ($oldestSupportedCoreVersionReleases as $subVersion => $subVersionInfo) {
if (!preg_match('/dev|alpha/', $subVersion)) {
$oldestSupportedCoreVersion = $subVersion;
break;
}
}
return [$oldestSupportedCoreVersion, $newestCoreVersion];
}
}
\ No newline at end of file
......@@ -23,14 +23,14 @@ class UploadQueue
{
private $tableName = 'tx_ter_extensionqueue';
public function addExtensionVersionToQueue(int $uidOfExtensionVersionRecord, string $extensionKey)
public function addExtensionVersionToQueue(ExtensionVersion $extensionVersion)
{
$this->getConnection()->insert(
$this->tableName,
[
'crdate' => $GLOBALS['EXEC_TIME'],
'extensionkey' => $extensionKey,
'extensionuid' => $uidOfExtensionVersionRecord,
'extensionkey' => (string)$extensionVersion->getExtensionKey(),
'extensionuid' => $extensionVersion->getUidOfExtensionVersion(),
'imported_to_fe' => 0,
'tstamp' => 0
]
......
......@@ -19,6 +19,7 @@
*/
use T3o\Ter\Api\ApiUser;
use T3o\Ter\Api\ExtensionKey;
use T3o\Ter\Api\ExtensionVersion;
use T3o\Ter\Api\ResultCodes;
use TYPO3\CMS\Core\Utility\GeneralUtility;
......@@ -105,7 +106,7 @@ class tx_ter_api
*
* @param object $accountData : Username and passwords for upload the extension
* @param object $extensionInfoData : The general extension information as received by the SOAP server
* @param array $filesData : The array of file data objects as received by the SOAP server
* @param object $filesData : The array of file data objects as received by the SOAP server
*
* @return array|object uploadExtensionResult object if upload was successful, otherwise an exception is thrown.
* @throws \T3o\Ter\Exception\Exception
......@@ -117,77 +118,36 @@ class tx_ter_api
$accountData,
$extensionInfoData,
$filesData
) {// When the extension has only one dependency, $extensionInfoData->technicalData->dependencies is a stdObj instead of an array
) {
// When the extension has only one dependency, $extensionInfoData->technicalData->dependencies is a stdObj instead of an array
// All following code will then fail, because it is tested if it was an array.
// To make it work, we wrap the object into an array, so that the following code works as expected
if (!empty($extensionInfoData->technicalData->dependencies) && is_object(
$extensionInfoData->technicalData->dependencies
)
) {
if (is_object($extensionInfoData->technicalData->dependencies)) {
$extensionInfoData->technicalData->dependencies = [$extensionInfoData->technicalData->dependencies];
}
$extensionKey = strtolower($extensionInfoData->extensionKey);
$this->logger->info('Upload of extension ' . $extensionKey . ' (' . $extensionInfoData->version . ') by user ' . $accountData->username);
$user = ApiUser::createFromSoapAccountData($accountData);
$user->authenticate();
$extensionKeyObject = new ExtensionKey($extensionKey);
if (!$extensionKeyObject->isRegistered()) {
throw new \T3o\Ter\Exception\NotFoundException('Extension "' . $extensionKey . '" does not exist.', ResultCodes::ERROR_UPLOADEXTENSION_EXTENSIONDOESNTEXIST);
}
if (!$extensionKeyObject->isApiUserOwnerOrAdmin($user)) {
throw new \T3o\Ter\Exception\UnauthorizedException('Access denied.', ResultCodes::ERROR_UPLOADEXTENSION_ACCESSDENIED);
}
$extensionKey = new ExtensionKey(strtolower($extensionInfoData->extensionKey));
$version = new ExtensionVersion($extensionKey, $extensionInfoData->version);
$this->logger->info('Upload of extension ' . ((string)$extensionKey) . ' (' . $version->getVersion() . ') by user ' . $user->getUsername());
// check for a valid version number
if (preg_match('/^(0|[1-9]\d{0,2})\.(0|[1-9]\d{0,2})\.(0|[1-9]\d{0,2})$/', $extensionInfoData->version) == false) {
throw new \T3o\Ter\Exception\Exception(
'Your version number "' . htmlspecialchars(
$extensionInfoData->version
) . '" is invalid. Allowed are three numbers with maximum 999, e.g. "7.8.999".',
1429912029
);
}
if ($this->checkUploadedExtensionVersionExistsInRepository($extensionInfoData)) {
if ($version->doesExtensionVersionExist()) {
throw new \T3o\Ter\Exception\VersionExistsException(
'Version number ' . $extensionInfoData->version . ' already exists in repository.',
'Version number ' . htmlspecialchars($version->getVersion()). ' already exists in repository.',
ResultCodes::ERROR_UPLOADEXTENSION_EXTENSIONVERSIONEXISTS
);
}
if (trim((string)$extensionInfoData->infoData->uploadComment) === '') {
throw new \T3o\Ter\Exception\NoUploadCommentException(
'You need to set an upload comment!',
ResultCodes::ERROR_UPLOADEXTENSION_NOUPLOADCOMMENT
);
}
if (($typo3DependencyCheck = static::checkExtensionDependencyOnSupportedTypo3Version($extensionInfoData)) !== true && !$user->isAdministrator()) {
switch ($typo3DependencyCheck) {
case ResultCodes::ERROR_UPLOADEXTENSION_TYPO3DEPENDENCYINCORRECT:
$message = 'Extension does not have a dependency for a supported version of TYPO3. See http://typo3.org/news/article/announcing-ter-cleanup-process/ for how to fix this.';
break;
case ResultCodes::ERROR_UPLOADEXTENSION_TYPO3DEPENDENCYCHECKFAILED:
default:
$message = 'Check on dependency for a supported version of TYPO3 failed due to technical reasons';
break;
}
throw new \T3o\Ter\Exception\FailedDependencyException($message, $typo3DependencyCheck);
}
$this->uploadExtension_writeExtensionAndIconFile($extensionInfoData, $filesData);
$this->uploadExtension_writeExtensionInfoToDB($accountData, $extensionInfoData, $filesData);
$this->requestUpdateOfExtensionIndexFile();
$this->doUpload($user, $version, $extensionInfoData, $filesData);
return [
'resultCode' => ResultCodes::RESULT_EXTENSIONSUCCESSFULLYUPLOADED,
'resultMessages' => [
'Please note that it might take a while (up to an hour) until your extension and the documentation appear on TYPO3.org.'
],
'version' => $extensionInfoData->version,
'version' => $version,
];
}
......@@ -196,57 +156,19 @@ class tx_ter_api
*
* @param string $username Username for upload the extension
* @param object $extensionInfoData The general extension information
* @param array $filesData The array of file data objects
*
* @return bool TRUE on success
* @param object $filesData The array of file data objects
*
* @throws \T3o\Ter\Exception\Exception
* @throws \T3o\Ter\Exception\NotFoundException
* @throws \T3o\Ter\Exception\UnauthorizedException
*/
public static function uploadExtensionWithoutSoap($username, $extensionInfoData, $filesData)
public function uploadExtensionWithoutSoap($username, $extensionInfoData, $filesData): void
{
// Make an instance of the api
$instance = GeneralUtility::makeInstance(\tx_ter_api::class);
$accountData = (object)['username' => $username];
$uploader = new ApiUser($username);
$uploader->authenticate();
$extensionKeyObject = new ExtensionKey($extensionInfoData->extensionKey);
if (!$extensionKeyObject->isRegistered()) {
throw new \T3o\Ter\Exception\NotFoundException('Extension does not exist.', ResultCodes::ERROR_UPLOADEXTENSION_EXTENSIONDOESNTEXIST);
}
if (strtolower($extensionKeyObject->getOwner()) !== strtolower($username)) {
throw new \T3o\Ter\Exception\UnauthorizedException('Access denied.', ResultCodes::ERROR_UPLOADEXTENSION_ACCESSDENIED);
}
// check for a valid version number
if (preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $extensionInfoData->version) == false) {
throw new \T3o\Ter\Exception\Exception(
'Your version number "' . htmlspecialchars(
$extensionInfoData->version
) . '" is invalid. Allowed are three numbers with maximum 999, e.g. "7.8.999".',
1429912029
);
}
if (($typo3DependencyCheck = static::checkExtensionDependencyOnSupportedTypo3Version($extensionInfoData)) !== true) {
switch ($typo3DependencyCheck) {
case ResultCodes::ERROR_UPLOADEXTENSION_TYPO3DEPENDENCYINCORRECT:
$message = 'Extension does not have a dependency for a supported version of TYPO3. See http://typo3.org/news/article/announcing-ter-cleanup-process/ for how to fix this.';
break;
case ResultCodes::ERROR_UPLOADEXTENSION_TYPO3DEPENDENCYCHECKFAILED:
// same behaviour as default
default:
$message = 'Check on dependency for a supported version of TYPO3 failed due to technical reasons';
break;
}
throw new \T3o\Ter\Exception\Exception($message, $typo3DependencyCheck);
}
// Upload...
$instance->uploadExtension_writeExtensionAndIconFile($extensionInfoData, $filesData);
$instance->uploadExtension_writeExtensionInfoToDB($accountData, $extensionInfoData, $filesData);
$instance->requestUpdateOfExtensionIndexFile();
return true;
$extensionVersion = new ExtensionVersion($extensionKeyObject, $extensionInfoData->version);
$this->doUpload($uploader, $extensionVersion, $extensionInfoData, $filesData);
}
/**
......@@ -268,12 +190,6 @@ class tx_ter_api
$user = ApiUser::createFromSoapAccountData($accountData);
$user->authenticate();
if (!$user->isAdministrator()) {
throw new \T3o\Ter\Exception\UnauthorizedException(
'Access denied. You must be administrator in order to delete extensions',
ResultCodes::ERROR_DELETEEXTENSION_ACCESS_DENIED
);
}
$extensionKeyObject = new ExtensionKey($extensionKey);
if (!$extensionKeyObject->isRegistered()) {
......@@ -283,7 +199,8 @@ class tx_ter_api
);