first commit
This commit is contained in:
commit
1218dd1517
12716 files changed
+989233
No files matched your search
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Extensionmanager\Command;
|
||||
|
||||
/*
|
||||
* 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 Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use TYPO3\CMS\Core\Core\Bootstrap;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
|
||||
use TYPO3\CMS\Extensionmanager\Utility\InstallUtility;
|
||||
|
||||
/**
|
||||
* Command for activating an existing extension via CLI.
|
||||
*/
|
||||
class ActivateExtensionCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Defines the allowed options for this command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setDescription('Activates an extension by key')
|
||||
->setHelp('The extension files must be present in one of the recognized extension folder paths in TYPO3.')
|
||||
->setAliases(['extensionmanager:extension:install', 'extension:install'])
|
||||
->addArgument(
|
||||
'extensionkey',
|
||||
InputArgument::REQUIRED,
|
||||
'The extension key of a currently deactivated extension, located in one of TYPO3\'s extension paths.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs an extension
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$extensionKey = $input->getArgument('extensionkey');
|
||||
|
||||
// Ensure the _cli_ user is authenticated because the extension might import data
|
||||
Bootstrap::initializeBackendAuthentication();
|
||||
|
||||
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
||||
// Emits packages may have changed signal
|
||||
$signalSlotDispatcher = $objectManager->get(Dispatcher::class);
|
||||
$signalSlotDispatcher->dispatch('PackageManagement', 'packagesMayHaveChanged');
|
||||
|
||||
// Do the installation process
|
||||
$objectManager->get(InstallUtility::class)->install($extensionKey);
|
||||
|
||||
$io->success('Activated extension ' . $extensionKey . ' successfully.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Extensionmanager\Command;
|
||||
|
||||
/*
|
||||
* 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 Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
use TYPO3\CMS\Extensionmanager\Utility\InstallUtility;
|
||||
|
||||
/**
|
||||
* Command for deactivating an extension via CLI.
|
||||
*/
|
||||
class DeactivateExtensionCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Defines the allowed options for this command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setDescription('Deactivates an extension by extension key')
|
||||
->setAliases(['extensionmanager:extension:uninstall', 'extension:uninstall'])
|
||||
->addArgument(
|
||||
'extensionkey',
|
||||
InputArgument::REQUIRED,
|
||||
'The extension key of a currently activated extension.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs an extension
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$extensionKey = $input->getArgument('extensionkey');
|
||||
|
||||
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
||||
$objectManager->get(InstallUtility::class)->uninstall($extensionKey);
|
||||
|
||||
$io->success('Deactivated extension "' . $extensionKey . '" successfully.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Command;
|
||||
|
||||
/*
|
||||
* 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\Core\ClassLoadingInformation;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\CommandController;
|
||||
|
||||
/**
|
||||
* CommandController for working with extension management through CLI/scheduler
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Use Symfony Command alternatives instead.
|
||||
*/
|
||||
class ExtensionCommandController extends CommandController
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $requestAdminPermissions = true;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
|
||||
*/
|
||||
protected $signalSlotDispatcher;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher
|
||||
*/
|
||||
public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
|
||||
{
|
||||
$this->signalSlotDispatcher = $signalSlotDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs an extension by key
|
||||
*
|
||||
* The extension files must be present in one of the
|
||||
* recognised extension folder paths in TYPO3.
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @cli
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Use the equivalent Symfony Command instead.
|
||||
*/
|
||||
public function installCommand($extensionKey)
|
||||
{
|
||||
trigger_error('Calling ExtensionCommandController->installCommand() will be removed in TYPO3 v10.0. Use the Symfony command "extension:activate" instead, to be called via the "typo3" CLI entrypoint.', E_USER_DEPRECATED);
|
||||
$this->emitPackagesMayHaveChangedSignal();
|
||||
|
||||
/** @var \TYPO3\CMS\Extensionmanager\Utility\InstallUtility $service */
|
||||
$service = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility::class);
|
||||
$service->install($extensionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstalls an extension by key
|
||||
*
|
||||
* The extension files must be present in one of the
|
||||
* recognised extension folder paths in TYPO3.
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @cli
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Use the equivalent Symfony Command instead.
|
||||
*/
|
||||
public function uninstallCommand($extensionKey)
|
||||
{
|
||||
trigger_error('Calling ExtensionCommandController->uninstallCommand() will be removed in TYPO3 v10.0. Use the Symfony command "extension:deactivate" instead, to be called via the "typo3" CLI entrypoint.', E_USER_DEPRECATED);
|
||||
/** @var \TYPO3\CMS\Extensionmanager\Utility\InstallUtility $service */
|
||||
$service = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility::class);
|
||||
$service->uninstall($extensionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates class loading information.
|
||||
*
|
||||
* This command is only needed during development. The extension manager takes care
|
||||
* creating or updating this info properly during extension (de-)activation.
|
||||
*
|
||||
* @cli
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Use the equivalent Symfony Command instead.
|
||||
*/
|
||||
public function dumpClassLoadingInformationCommand()
|
||||
{
|
||||
trigger_error('Calling ExtensionCommandController->dumpClassLoadingInformationCommand() will be removed in TYPO3 v10.0. Use the Symfony command "dumpautoload" instead, to be called via the "typo3" CLI entrypoint.', E_USER_DEPRECATED);
|
||||
if (Environment::isComposerMode()) {
|
||||
$this->output->outputLine('<error>Class loading information is managed by composer. Use "composer dump-autoload" command to update the information.</error>');
|
||||
$this->quit(1);
|
||||
} else {
|
||||
ClassLoadingInformation::dumpClassLoadingInformation();
|
||||
$this->output->outputLine('Class Loading information has been updated.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits packages may have changed signal
|
||||
*/
|
||||
protected function emitPackagesMayHaveChangedSignal()
|
||||
{
|
||||
$this->signalSlotDispatcher->dispatch('PackageManagement', 'packagesMayHaveChanged');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Controller;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract action controller.
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class AbstractController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
|
||||
{
|
||||
const TRIGGER_RefreshModuleMenu = 'refreshModuleMenu';
|
||||
|
||||
const TRIGGER_RefreshTopbar = 'refreshTopbar';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $triggerArguments = [
|
||||
self::TRIGGER_RefreshModuleMenu,
|
||||
self::TRIGGER_RefreshTopbar
|
||||
];
|
||||
|
||||
/**
|
||||
* Translation shortcut
|
||||
*
|
||||
* @param $key
|
||||
* @param array|null $arguments
|
||||
* @return string|null
|
||||
*/
|
||||
protected function translate($key, $arguments = null)
|
||||
{
|
||||
return \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($key, 'extensionmanager', $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles trigger arguments, e.g. refreshing the module menu
|
||||
* widget if an extension with backend modules has been enabled
|
||||
* or disabled.
|
||||
*/
|
||||
protected function handleTriggerArguments()
|
||||
{
|
||||
$triggers = [];
|
||||
|
||||
foreach ($this->triggerArguments as $triggerArgument) {
|
||||
if ($this->request->hasArgument($triggerArgument)) {
|
||||
$triggers[$triggerArgument] = $this->request->getArgument($triggerArgument);
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->assign('triggers', $triggers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Controller;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
use TYPO3\CMS\Backend\View\BackendTemplateView;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
|
||||
|
||||
/**
|
||||
* Abstract action controller.
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class AbstractModuleController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* BackendTemplateContainer
|
||||
*
|
||||
* @var BackendTemplateView
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* Backend Template Container
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultViewObjectName = BackendTemplateView::class;
|
||||
|
||||
/**
|
||||
* Resolve view and initialize the general view-variables extensionName,
|
||||
* controllerName and actionName based on the request object
|
||||
*
|
||||
* @return \TYPO3\CMS\Fluid\View\TemplateView
|
||||
*/
|
||||
protected function resolveView()
|
||||
{
|
||||
$view = parent::resolveView();
|
||||
$view->assignMultiple([
|
||||
'extensionName' => $this->request->getControllerExtensionName(),
|
||||
'controllerName' => $this->request->getControllerName(),
|
||||
'actionName' => $this->request->getControllerActionName()
|
||||
]);
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the action menu
|
||||
*/
|
||||
protected function generateMenu()
|
||||
{
|
||||
$menuItems = [
|
||||
'installedExtensions' => [
|
||||
'controller' => 'List',
|
||||
'action' => 'index',
|
||||
'label' => $this->translate('installedExtensions')
|
||||
]
|
||||
];
|
||||
|
||||
if (!$this->settings['offlineMode'] && !Environment::isComposerMode()) {
|
||||
$menuItems['getExtensions'] = [
|
||||
'controller' => 'List',
|
||||
'action' => 'ter',
|
||||
'label' => $this->translate('getExtensions')
|
||||
];
|
||||
$menuItems['distributions'] = [
|
||||
'controller' => 'List',
|
||||
'action' => 'distributions',
|
||||
'label' => $this->translate('distributions')
|
||||
];
|
||||
|
||||
if ($this->actionMethodName === 'showAllVersionsAction') {
|
||||
$menuItems['showAllVersions'] = [
|
||||
'controller' => 'List',
|
||||
'action' => 'showAllVersions',
|
||||
'label' => $this->translate('showAllVersions') . ' ' . $this->request->getArgument('extensionKey')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$uriBuilder = $this->objectManager->get(UriBuilder::class);
|
||||
$uriBuilder->setRequest($this->request);
|
||||
|
||||
$menu = $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
|
||||
$menu->setIdentifier('ExtensionManagerModuleMenu');
|
||||
|
||||
foreach ($menuItems as $menuItemConfig) {
|
||||
if ($this->request->getControllerName() === $menuItemConfig['controller']) {
|
||||
$isActive = $this->request->getControllerActionName() === $menuItemConfig['action'] ? true : false;
|
||||
} else {
|
||||
$isActive = false;
|
||||
}
|
||||
$menuItem = $menu->makeMenuItem()
|
||||
->setTitle($menuItemConfig['label'])
|
||||
->setHref($this->getHref($menuItemConfig['controller'], $menuItemConfig['action']))
|
||||
->setActive($isActive);
|
||||
$menu->addMenuItem($menuItem);
|
||||
}
|
||||
|
||||
$this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
|
||||
$this->view->getModuleTemplate()->setFlashMessageQueue($this->controllerContext->getFlashMessageQueue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates te URI for a backend action
|
||||
*
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @param array $parameters
|
||||
* @return string
|
||||
*/
|
||||
protected function getHref($controller, $action, $parameters = [])
|
||||
{
|
||||
$uriBuilder = $this->objectManager->get(UriBuilder::class);
|
||||
$uriBuilder->setRequest($this->request);
|
||||
return $uriBuilder->reset()->uriFor($action, $parameters, $controller);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Controller;
|
||||
|
||||
/*
|
||||
* 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\Registry;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Controller for handling extension related actions like
|
||||
* installing, removing, downloading of data or files
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class ActionController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\InstallUtility
|
||||
*/
|
||||
protected $installUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
|
||||
*/
|
||||
protected $fileHandlingUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility
|
||||
*/
|
||||
protected $extensionModelUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService
|
||||
*/
|
||||
protected $managementService;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility
|
||||
*/
|
||||
public function injectInstallUtility(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility)
|
||||
{
|
||||
$this->installUtility = $installUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility
|
||||
*/
|
||||
public function injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility)
|
||||
{
|
||||
$this->fileHandlingUtility = $fileHandlingUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility $extensionModelUtility
|
||||
*/
|
||||
public function injectExtensionModelUtility(\TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility $extensionModelUtility)
|
||||
{
|
||||
$this->extensionModelUtility = $extensionModelUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService
|
||||
*/
|
||||
public function injectManagementService(\TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService)
|
||||
{
|
||||
$this->managementService = $managementService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle extension installation state action
|
||||
*
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
protected function toggleExtensionInstallationStateAction($extensionKey)
|
||||
{
|
||||
$installedExtensions = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getLoadedExtensionListArray();
|
||||
try {
|
||||
if (in_array($extensionKey, $installedExtensions)) {
|
||||
// uninstall
|
||||
$this->installUtility->uninstall($extensionKey);
|
||||
} else {
|
||||
// install
|
||||
$extension = $this->extensionModelUtility->mapExtensionArrayToModel(
|
||||
$this->installUtility->enrichExtensionWithDetails($extensionKey, false)
|
||||
);
|
||||
if ($this->managementService->installExtension($extension) === false) {
|
||||
$this->redirect('unresolvedDependencies', 'List', null, ['extensionKey' => $extensionKey]);
|
||||
}
|
||||
}
|
||||
} catch (\TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException $e) {
|
||||
$this->addFlashMessage($e->getMessage(), '', \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
|
||||
} catch (\TYPO3\CMS\Core\Package\Exception\PackageStatesFileNotWritableException $e) {
|
||||
$this->addFlashMessage($e->getMessage(), '', \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
|
||||
}
|
||||
$this->redirect('index', 'List', null, [
|
||||
self::TRIGGER_RefreshModuleMenu => true,
|
||||
self::TRIGGER_RefreshTopbar => true
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Install an extension and omit dependency checking
|
||||
*
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
public function installExtensionWithoutSystemDependencyCheckAction($extensionKey)
|
||||
{
|
||||
$this->managementService->setSkipDependencyCheck(true);
|
||||
$this->forward('toggleExtensionInstallationState', null, null, ['extensionKey' => $extensionKey]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an extension (if it is still installed, uninstall it first)
|
||||
*
|
||||
* @param string $extension
|
||||
* @return string
|
||||
*/
|
||||
protected function removeExtensionAction($extension)
|
||||
{
|
||||
try {
|
||||
if (\TYPO3\CMS\Core\Core\Environment::isComposerMode()) {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(
|
||||
'The system is set to composer mode. You are not allowed to remove any extension.',
|
||||
1590314046
|
||||
);
|
||||
}
|
||||
|
||||
$this->installUtility->removeExtension($extension);
|
||||
$this->addFlashMessage(
|
||||
\TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate(
|
||||
'extensionList.remove.message',
|
||||
'extensionmanager',
|
||||
[
|
||||
'extension' => $extension,
|
||||
]
|
||||
)
|
||||
);
|
||||
} catch (\TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException $e) {
|
||||
$this->addFlashMessage($e->getMessage(), '', \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
|
||||
} catch (\TYPO3\CMS\Core\Package\Exception $e) {
|
||||
$this->addFlashMessage($e->getMessage(), '', \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Download an extension as a zip file
|
||||
*
|
||||
* @param string $extension
|
||||
*/
|
||||
protected function downloadExtensionZipAction($extension)
|
||||
{
|
||||
$fileName = $this->fileHandlingUtility->createZipFileFromExtension($extension);
|
||||
$this->fileHandlingUtility->sendZipFileToBrowserAndDelete($fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the static SQL data of an extension
|
||||
*
|
||||
* @param string $extension
|
||||
*/
|
||||
protected function reloadExtensionDataAction($extension)
|
||||
{
|
||||
$extension = $this->installUtility->enrichExtensionWithDetails($extension, false);
|
||||
$registryKey = $extension['siteRelPath'] . 'ext_tables_static+adt.sql';
|
||||
|
||||
$registry = GeneralUtility::makeInstance(Registry::class);
|
||||
$registry->remove('extensionDataImport', $registryKey);
|
||||
|
||||
$this->installUtility->processExtensionSetup($extension['key']);
|
||||
|
||||
$this->redirect('index', 'List');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Controller;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
|
||||
use TYPO3\CMS\Backend\View\BackendTemplateView;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
|
||||
|
||||
/**
|
||||
* Controller for distribution related actions
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class DistributionController extends AbstractModuleController
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Package\PackageManager
|
||||
*/
|
||||
protected $packageManager;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Core\Package\PackageManager $packageManager
|
||||
*/
|
||||
public function injectPackageManager(\TYPO3\CMS\Core\Package\PackageManager $packageManager)
|
||||
{
|
||||
$this->packageManager = $packageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the doc header properly here
|
||||
*
|
||||
* @param ViewInterface $view
|
||||
*/
|
||||
protected function initializeView(ViewInterface $view)
|
||||
{
|
||||
if ($view instanceof BackendTemplateView) {
|
||||
/** @var BackendTemplateView $view */
|
||||
parent::initializeView($view);
|
||||
$this->generateMenu();
|
||||
$this->registerDocheaderButtons();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows information about the distribution
|
||||
*
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension
|
||||
*/
|
||||
public function showAction(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension)
|
||||
{
|
||||
$extensionKey = $extension->getExtensionKey();
|
||||
// Check if extension/package is installed
|
||||
$active = $this->packageManager->isPackageActive($extensionKey);
|
||||
|
||||
$this->view->assign('distributionActive', $active);
|
||||
$this->view->assign('extension', $extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the Icons into the docheader
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function registerDocheaderButtons()
|
||||
{
|
||||
/** @var ButtonBar $buttonBar */
|
||||
$buttonBar = $this->view->getModuleTemplate()->getDocHeaderComponent()->getButtonBar();
|
||||
|
||||
$uriBuilder = $this->controllerContext->getUriBuilder();
|
||||
|
||||
$uri = $uriBuilder->reset()->uriFor('distributions', [], 'List');
|
||||
$title = $this->translate('extConfTemplate.backToList');
|
||||
$icon = $this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-view-go-back', Icon::SIZE_SMALL);
|
||||
$button = $buttonBar->makeLinkButton()
|
||||
->setHref($uri)
|
||||
->setTitle($title)
|
||||
->setIcon($icon);
|
||||
$buttonBar->addButton($button, ButtonBar::BUTTON_POSITION_LEFT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Controller;
|
||||
|
||||
/*
|
||||
* 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\Configuration\ExtensionConfiguration;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Mvc\View\JsonView;
|
||||
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
|
||||
|
||||
/**
|
||||
* Controller for actions related to the TER download of an extension
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class DownloadController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
|
||||
*/
|
||||
protected $extensionRepository;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
|
||||
*/
|
||||
protected $fileHandlingUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService
|
||||
*/
|
||||
protected $managementService;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\InstallUtility
|
||||
*/
|
||||
protected $installUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\DownloadUtility
|
||||
*/
|
||||
protected $downloadUtility;
|
||||
|
||||
/**
|
||||
* @var JsonView
|
||||
*/
|
||||
protected $defaultViewObjectName = JsonView::class;
|
||||
|
||||
/**
|
||||
* @var JsonView
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository
|
||||
*/
|
||||
public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
|
||||
{
|
||||
$this->extensionRepository = $extensionRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility
|
||||
*/
|
||||
public function injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility)
|
||||
{
|
||||
$this->fileHandlingUtility = $fileHandlingUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService
|
||||
*/
|
||||
public function injectManagementService(\TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService)
|
||||
{
|
||||
$this->managementService = $managementService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility
|
||||
*/
|
||||
public function injectInstallUtility(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility)
|
||||
{
|
||||
$this->installUtility = $installUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\DownloadUtility $downloadUtility
|
||||
*/
|
||||
public function injectDownloadUtility(\TYPO3\CMS\Extensionmanager\Utility\DownloadUtility $downloadUtility)
|
||||
{
|
||||
$this->downloadUtility = $downloadUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines which view object should be used for the installFromTer action
|
||||
*/
|
||||
protected function initializeInstallFromTerAction()
|
||||
{
|
||||
$this->defaultViewObjectName = \TYPO3\CMS\Fluid\View\TemplateView::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check extension dependencies
|
||||
*
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function checkDependenciesAction(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension)
|
||||
{
|
||||
$message = '';
|
||||
$title = '';
|
||||
$hasDependencies = false;
|
||||
$hasErrors = false;
|
||||
$dependencyTypes = null;
|
||||
$configuration = [
|
||||
'value' => [
|
||||
'dependencies' => [],
|
||||
],
|
||||
];
|
||||
$isAutomaticInstallationEnabled = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('extensionmanager', 'automaticInstallation');
|
||||
if (!$isAutomaticInstallationEnabled) {
|
||||
// if automatic installation is deactivated, no dependency check is needed (download only)
|
||||
$action = 'installExtensionWithoutSystemDependencyCheck';
|
||||
} else {
|
||||
$action = 'installFromTer';
|
||||
try {
|
||||
$dependencyTypes = $this->managementService->getAndResolveDependencies($extension);
|
||||
if (!empty($dependencyTypes)) {
|
||||
$hasDependencies = true;
|
||||
$message = '<p>' . $this->translate('downloadExtension.dependencies.headline') . '</p>';
|
||||
foreach ($dependencyTypes as $dependencyType => $dependencies) {
|
||||
$extensions = '';
|
||||
foreach ($dependencies as $extensionKey => $dependency) {
|
||||
if (!isset($configuration['value']['dependencies'][$dependencyType])) {
|
||||
$configuration['value']['dependencies'][$dependencyType] = [];
|
||||
}
|
||||
$configuration['value']['dependencies'][$dependencyType][$extensionKey] = [
|
||||
'_exclude' => [
|
||||
'categoryIndexFromStringOrNumber',
|
||||
],
|
||||
];
|
||||
$extensions .= $this->translate(
|
||||
'downloadExtension.dependencies.extensionWithVersion',
|
||||
[
|
||||
$extensionKey, $dependency->getVersion()
|
||||
]
|
||||
) . '<br />';
|
||||
}
|
||||
$message .= $this->translate(
|
||||
'downloadExtension.dependencies.typeHeadline',
|
||||
[
|
||||
$this->translate('downloadExtension.dependencyType.' . $dependencyType),
|
||||
$extensions
|
||||
]
|
||||
);
|
||||
}
|
||||
$title = $this->translate('downloadExtension.dependencies.resolveAutomatically');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$hasErrors = true;
|
||||
$title = $this->translate('downloadExtension.dependencies.errorTitle');
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
$url = $this->uriBuilder->uriFor(
|
||||
$action,
|
||||
['extension' => $extension->getUid(), 'format' => 'json'],
|
||||
'Download'
|
||||
);
|
||||
$this->view->setConfiguration($configuration);
|
||||
$this->view->assign('value', [
|
||||
'dependencies' => $dependencyTypes,
|
||||
'url' => $url,
|
||||
'message' => $message,
|
||||
'hasErrors' => $hasErrors,
|
||||
'hasDependencies' => $hasDependencies,
|
||||
'title' => $title
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Install an extension from TER action
|
||||
*
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension
|
||||
* @param string $downloadPath
|
||||
*/
|
||||
public function installFromTerAction(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension, $downloadPath = 'Local')
|
||||
{
|
||||
list($result, $errorMessages) = $this->installFromTer($extension, $downloadPath);
|
||||
$isAutomaticInstallationEnabled = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('extensionmanager', 'automaticInstallation');
|
||||
$this->view
|
||||
->assign('result', $result)
|
||||
->assign('extension', $extension)
|
||||
->assign('installationTypeLanguageKey', $isAutomaticInstallationEnabled ? '' : '.downloadOnly')
|
||||
->assign('unresolvedDependencies', $errorMessages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check extension dependencies with special dependencies
|
||||
*
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function installExtensionWithoutSystemDependencyCheckAction(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension)
|
||||
{
|
||||
$this->managementService->setSkipDependencyCheck(true);
|
||||
$this->forward('installFromTer', null, null, ['extension' => $extension, 'downloadPath' => 'Local']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action for installing a distribution -
|
||||
* redirects directly to configuration after installing
|
||||
*
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension
|
||||
*/
|
||||
public function installDistributionAction(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension)
|
||||
{
|
||||
if (!\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('impexp')) {
|
||||
$this->forward('distributions', 'List');
|
||||
}
|
||||
list($result, $errorMessages) = $this->installFromTer($extension);
|
||||
if ($errorMessages) {
|
||||
foreach ($errorMessages as $extensionKey => $messages) {
|
||||
foreach ($messages as $message) {
|
||||
$this->addFlashMessage(
|
||||
$message['message'],
|
||||
\TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate(
|
||||
'distribution.error.headline',
|
||||
'extensionmanager',
|
||||
[$extensionKey]
|
||||
),
|
||||
\TYPO3\CMS\Core\Messaging\AbstractMessage::ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect back to distributions list action
|
||||
$this->redirect(
|
||||
'distributions',
|
||||
'List'
|
||||
);
|
||||
} else {
|
||||
// FlashMessage that extension is installed
|
||||
$this->addFlashMessage(
|
||||
\TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate(
|
||||
'distribution.welcome.message',
|
||||
'extensionmanager',
|
||||
[$extension->getExtensionKey()]
|
||||
),
|
||||
\TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('distribution.welcome.headline', 'extensionmanager')
|
||||
);
|
||||
|
||||
// Redirect to show action
|
||||
$this->redirect(
|
||||
'show',
|
||||
'Distribution',
|
||||
null,
|
||||
['extension' => $extension]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an extension. Makes no sanity check but directly searches highest
|
||||
* available version from TER and updates. Update check is done by the list
|
||||
* already. This method should only be called if we are sure that there is
|
||||
* an update.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function updateExtensionAction()
|
||||
{
|
||||
$extensionKey = $this->request->getArgument('extension');
|
||||
$version = $this->request->getArgument('version');
|
||||
$extension = $this->extensionRepository->findOneByExtensionKeyAndVersion($extensionKey, $version);
|
||||
if (!$extension instanceof Extension) {
|
||||
$extension = $this->extensionRepository->findHighestAvailableVersion($extensionKey);
|
||||
}
|
||||
$installedExtensions = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getLoadedExtensionListArray();
|
||||
try {
|
||||
if (in_array($extensionKey, $installedExtensions, true)) {
|
||||
// To resolve new dependencies the extension is installed again
|
||||
$this->managementService->installExtension($extension);
|
||||
} else {
|
||||
$this->managementService->downloadMainExtension($extension);
|
||||
}
|
||||
$this->addFlashMessage(
|
||||
$this->translate('extensionList.updateFlashMessage.body', [$extensionKey]),
|
||||
$this->translate('extensionList.updateFlashMessage.title')
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
$this->addFlashMessage($e->getMessage(), '', FlashMessage::ERROR);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Show update comments for extensions that can be updated.
|
||||
* Fetches update comments for all versions between the current
|
||||
* installed and the highest version.
|
||||
*/
|
||||
protected function updateCommentForUpdatableVersionsAction()
|
||||
{
|
||||
$extensionKey = $this->request->getArgument('extension');
|
||||
$versionStart = $this->request->getArgument('integerVersionStart');
|
||||
$versionStop = $this->request->getArgument('integerVersionStop');
|
||||
$updateComments = [];
|
||||
/** @var Extension[] $updatableVersions */
|
||||
$updatableVersions = $this->extensionRepository->findByVersionRangeAndExtensionKeyOrderedByVersion(
|
||||
$extensionKey,
|
||||
$versionStart,
|
||||
$versionStop,
|
||||
false
|
||||
);
|
||||
$highestPossibleVersion = false;
|
||||
|
||||
foreach ($updatableVersions as $updatableVersion) {
|
||||
if ($highestPossibleVersion === false) {
|
||||
$highestPossibleVersion = $updatableVersion->getVersion();
|
||||
}
|
||||
$updateComments[$updatableVersion->getVersion()] = $updatableVersion->getUpdateComment();
|
||||
}
|
||||
|
||||
$this->view->assign('value', [
|
||||
'updateComments' => $updateComments,
|
||||
'url' => $this->uriBuilder->uriFor(
|
||||
'updateExtension',
|
||||
['extension' => $extensionKey, 'version' => $highestPossibleVersion]
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Install an extension from TER
|
||||
* Downloads the extension, resolves dependencies and installs it
|
||||
*
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension
|
||||
* @param string $downloadPath
|
||||
* @return array
|
||||
*/
|
||||
protected function installFromTer(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension, $downloadPath = 'Local')
|
||||
{
|
||||
$result = false;
|
||||
$errorMessages = [];
|
||||
try {
|
||||
$this->downloadUtility->setDownloadPath($downloadPath);
|
||||
$isAutomaticInstallationEnabled = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('extensionmanager', 'automaticInstallation');
|
||||
$this->managementService->setAutomaticInstallationEnabled($isAutomaticInstallationEnabled);
|
||||
if (($result = $this->managementService->installExtension($extension)) === false) {
|
||||
$errorMessages = $this->managementService->getDependencyErrors();
|
||||
}
|
||||
} catch (\TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException $e) {
|
||||
$errorMessages = [
|
||||
$extension->getExtensionKey() => [
|
||||
[
|
||||
'code' => $e->getCode(),
|
||||
'message' => $e->getMessage(),
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return [$result, $errorMessages];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Controller;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
|
||||
use TYPO3\CMS\Backend\View\BackendTemplateView;
|
||||
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
|
||||
use TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility;
|
||||
use TYPO3\CMS\Extensionmanager\Utility\Repository\Helper;
|
||||
|
||||
/**
|
||||
* Controller for extension listings (TER or local extensions)
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class ListController extends AbstractModuleController
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
|
||||
*/
|
||||
protected $extensionRepository;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
|
||||
*/
|
||||
protected $listUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Page\PageRenderer
|
||||
*/
|
||||
protected $pageRenderer;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility
|
||||
*/
|
||||
protected $dependencyUtility;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository
|
||||
*/
|
||||
public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
|
||||
{
|
||||
$this->extensionRepository = $extensionRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility
|
||||
*/
|
||||
public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
|
||||
{
|
||||
$this->listUtility = $listUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Core\Page\PageRenderer $pageRenderer
|
||||
*/
|
||||
public function injectPageRenderer(\TYPO3\CMS\Core\Page\PageRenderer $pageRenderer)
|
||||
{
|
||||
$this->pageRenderer = $pageRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility
|
||||
*/
|
||||
public function injectDependencyUtility(\TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility)
|
||||
{
|
||||
$this->dependencyUtility = $dependencyUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the needed JavaScript files for all actions
|
||||
*/
|
||||
public function initializeAction()
|
||||
{
|
||||
$this->pageRenderer->addInlineLanguageLabelFile('EXT:extensionmanager/Resources/Private/Language/locallang.xlf');
|
||||
$isAutomaticInstallationEnabled = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('extensionmanager', 'offlineMode');
|
||||
if ($isAutomaticInstallationEnabled) {
|
||||
$this->settings['offlineMode'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the doc header properly here
|
||||
*
|
||||
* @param ViewInterface $view
|
||||
*/
|
||||
protected function initializeView(ViewInterface $view)
|
||||
{
|
||||
if ($view instanceof BackendTemplateView) {
|
||||
/** @var BackendTemplateView $view */
|
||||
parent::initializeView($view);
|
||||
$this->generateMenu();
|
||||
$this->registerDocheaderButtons();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an information about composer mode
|
||||
*/
|
||||
protected function addComposerModeNotification()
|
||||
{
|
||||
if (Environment::isComposerMode()) {
|
||||
$this->addFlashMessage(
|
||||
LocalizationUtility::translate(
|
||||
'composerMode.message',
|
||||
'extensionmanager'
|
||||
),
|
||||
LocalizationUtility::translate(
|
||||
'composerMode.title',
|
||||
'extensionmanager'
|
||||
),
|
||||
FlashMessage::INFO
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows list of extensions present in the system
|
||||
*/
|
||||
public function indexAction()
|
||||
{
|
||||
$this->addComposerModeNotification();
|
||||
$availableAndInstalledExtensions = $this->listUtility->getAvailableAndInstalledExtensionsWithAdditionalInformation();
|
||||
ksort($availableAndInstalledExtensions);
|
||||
$this->view->assignMultiple(
|
||||
[
|
||||
'extensions' => $availableAndInstalledExtensions,
|
||||
'isComposerMode' => Environment::isComposerMode(),
|
||||
]
|
||||
);
|
||||
$this->handleTriggerArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a list of unresolved dependency errors with the possibility to bypass the dependency check
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
public function unresolvedDependenciesAction($extensionKey)
|
||||
{
|
||||
$availableExtensions = $this->listUtility->getAvailableExtensions();
|
||||
if (isset($availableExtensions[$extensionKey])) {
|
||||
$extensionArray = $this->listUtility->enrichExtensionsWithEmConfAndTerInformation(
|
||||
[
|
||||
$extensionKey => $availableExtensions[$extensionKey]
|
||||
]
|
||||
);
|
||||
/** @var ExtensionModelUtility $extensionModelUtility */
|
||||
$extensionModelUtility = $this->objectManager->get(ExtensionModelUtility::class);
|
||||
$extension = $extensionModelUtility->mapExtensionArrayToModel($extensionArray[$extensionKey]);
|
||||
} else {
|
||||
throw new ExtensionManagerException('Extension ' . $extensionKey . ' is not available', 1402421007);
|
||||
}
|
||||
$this->dependencyUtility->checkDependencies($extension);
|
||||
$this->view->assign('extension', $extension);
|
||||
$this->view->assign('unresolvedDependencies', $this->dependencyUtility->getDependencyErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows extensions from TER
|
||||
* Either all extensions or depending on a search param
|
||||
*
|
||||
* @param string $search
|
||||
*/
|
||||
public function terAction($search = '')
|
||||
{
|
||||
$this->addComposerModeNotification();
|
||||
$search = trim($search);
|
||||
if (!empty($search)) {
|
||||
$extensions = $this->extensionRepository->findByTitleOrAuthorNameOrExtensionKey($search);
|
||||
} else {
|
||||
$extensions = $this->extensionRepository->findAll();
|
||||
}
|
||||
$availableAndInstalledExtensions = $this->listUtility->getAvailableAndInstalledExtensions($this->listUtility->getAvailableExtensions());
|
||||
$this->view->assign('extensions', $extensions)
|
||||
->assign('search', $search)
|
||||
->assign('availableAndInstalled', $availableAndInstalledExtensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action for listing all possible distributions
|
||||
*
|
||||
* @param bool $showUnsuitableDistributions
|
||||
*/
|
||||
public function distributionsAction($showUnsuitableDistributions = false)
|
||||
{
|
||||
$this->addComposerModeNotification();
|
||||
$importExportInstalled = ExtensionManagementUtility::isLoaded('impexp');
|
||||
if ($importExportInstalled) {
|
||||
try {
|
||||
/** @var Helper $repositoryHelper */
|
||||
$repositoryHelper = $this->objectManager->get(Helper::class);
|
||||
// Check if a TER update has been done at all, if not, fetch it directly
|
||||
// Repository needs an update, but not because of the extension hash has changed
|
||||
$isExtListUpdateNecessary = $repositoryHelper->isExtListUpdateNecessary();
|
||||
if ($isExtListUpdateNecessary > 0 && ($isExtListUpdateNecessary & $repositoryHelper::PROBLEM_EXTENSION_HASH_CHANGED) === 0) {
|
||||
$repositoryHelper->updateExtList();
|
||||
}
|
||||
} catch (ExtensionManagerException $e) {
|
||||
$this->addFlashMessage($e->getMessage(), $e->getCode(), FlashMessage::ERROR);
|
||||
}
|
||||
|
||||
$officialDistributions = $this->extensionRepository->findAllOfficialDistributions();
|
||||
$communityDistributions = $this->extensionRepository->findAllCommunityDistributions();
|
||||
|
||||
$officialDistributions = $this->dependencyUtility->filterYoungestVersionOfExtensionList($officialDistributions->toArray(), $showUnsuitableDistributions);
|
||||
$communityDistributions = $this->dependencyUtility->filterYoungestVersionOfExtensionList($communityDistributions->toArray(), $showUnsuitableDistributions);
|
||||
|
||||
$this->view->assign('officialDistributions', $officialDistributions);
|
||||
$this->view->assign('communityDistributions', $communityDistributions);
|
||||
}
|
||||
$this->view->assign('enableDistributionsView', $importExportInstalled);
|
||||
$this->view->assign('showUnsuitableDistributions', $showUnsuitableDistributions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows all versions of a specific extension
|
||||
*
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
public function showAllVersionsAction($extensionKey)
|
||||
{
|
||||
$currentVersion = $this->extensionRepository->findOneByCurrentVersionByExtensionKey($extensionKey);
|
||||
$extensions = $this->extensionRepository->findByExtensionKeyOrderedByVersion($extensionKey);
|
||||
|
||||
$this->view->assignMultiple(
|
||||
[
|
||||
'extensionKey' => $extensionKey,
|
||||
'currentVersion' => $currentVersion,
|
||||
'extensions' => $extensions
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the Icons into the docheader
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function registerDocheaderButtons()
|
||||
{
|
||||
if (Environment::isComposerMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!in_array($this->actionMethodName, ['indexAction', 'terAction', 'showAllVersionsAction'], true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var ButtonBar $buttonBar */
|
||||
$buttonBar = $this->view->getModuleTemplate()->getDocHeaderComponent()->getButtonBar();
|
||||
$uriBuilder = $this->controllerContext->getUriBuilder();
|
||||
|
||||
if ($this->actionMethodName === 'showAllVersionsAction') {
|
||||
$uri = $uriBuilder->reset()->uriFor('ter', [], 'List');
|
||||
$title = $this->translate('extConfTemplate.backToList');
|
||||
$icon = $this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-view-go-back', Icon::SIZE_SMALL);
|
||||
$classes = '';
|
||||
} else {
|
||||
$uri = $uriBuilder->reset()->uriFor('form', [], 'UploadExtensionFile');
|
||||
$title = $this->translate('extensionList.uploadExtension');
|
||||
$icon = $this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-edit-upload', Icon::SIZE_SMALL);
|
||||
$classes = 't3js-upload';
|
||||
}
|
||||
$button = $buttonBar->makeLinkButton()
|
||||
->setHref($uri)
|
||||
->setTitle($title)
|
||||
->setClasses($classes)
|
||||
->setIcon($icon);
|
||||
$buttonBar->addButton($button, ButtonBar::BUTTON_POSITION_LEFT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Controller;
|
||||
|
||||
/*
|
||||
* 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\Localization\LanguageService;
|
||||
use TYPO3\CMS\Extbase\Mvc\View\JsonView;
|
||||
|
||||
/**
|
||||
* Controller for actions relating to update of full extension list from TER
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class UpdateFromTerController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\Repository\Helper
|
||||
*/
|
||||
protected $repositoryHelper;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository
|
||||
*/
|
||||
protected $repositoryRepository;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
|
||||
*/
|
||||
protected $listUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
|
||||
*/
|
||||
protected $extensionRepository;
|
||||
|
||||
/**
|
||||
* @var JsonView
|
||||
*/
|
||||
protected $defaultViewObjectName = JsonView::class;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\Repository\Helper $repositoryHelper
|
||||
*/
|
||||
public function injectRepositoryHelper(\TYPO3\CMS\Extensionmanager\Utility\Repository\Helper $repositoryHelper)
|
||||
{
|
||||
$this->repositoryHelper = $repositoryHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository $repositoryRepository
|
||||
*/
|
||||
public function injectRepositoryRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository $repositoryRepository)
|
||||
{
|
||||
$this->repositoryRepository = $repositoryRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility
|
||||
*/
|
||||
public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
|
||||
{
|
||||
$this->listUtility = $listUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository
|
||||
*/
|
||||
public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
|
||||
{
|
||||
$this->extensionRepository = $extensionRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update extension list from TER
|
||||
*
|
||||
* @param bool $forceUpdateCheck
|
||||
*/
|
||||
public function updateExtensionListFromTerAction($forceUpdateCheck = false)
|
||||
{
|
||||
$updated = false;
|
||||
$errorMessage = '';
|
||||
|
||||
if ($this->extensionRepository->countAll() === 0 || $forceUpdateCheck) {
|
||||
try {
|
||||
$updated = $this->repositoryHelper->updateExtList();
|
||||
} catch (\TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
}
|
||||
}
|
||||
/** @var \TYPO3\CMS\Extensionmanager\Domain\Model\Repository $repository */
|
||||
$repository = $this->repositoryRepository->findOneTypo3OrgRepository();
|
||||
|
||||
$timeFormat = $this->getLanguageService()->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:extensionList.updateFromTer.lastUpdate.fullTimeFormat');
|
||||
$lastUpdateTime = $repository ? $repository->getLastUpdate() : null;
|
||||
if (null === $lastUpdateTime) {
|
||||
$lastUpdatedSince = $this->getLanguageService()->sL('LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:extensionList.updateFromTer.never');
|
||||
$lastUpdateTime = date($timeFormat);
|
||||
} else {
|
||||
$lastUpdatedSince = \TYPO3\CMS\Backend\Utility\BackendUtility::calcAge(
|
||||
time() - $lastUpdateTime->format('U'),
|
||||
$this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears')
|
||||
);
|
||||
$lastUpdateTime = $lastUpdateTime->format($timeFormat);
|
||||
}
|
||||
$this->view->assign('value', [
|
||||
'updated' => $updated,
|
||||
'lastUpdateTime' => $lastUpdateTime,
|
||||
'timeSinceLastUpdate' => $lastUpdatedSince,
|
||||
'errorMessage' => $errorMessage
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LanguageService
|
||||
*/
|
||||
protected function getLanguageService()
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Controller;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
|
||||
use TYPO3\CMS\Backend\View\BackendTemplateView;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
|
||||
|
||||
/**
|
||||
* Controller for configuration related actions.
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class UpdateScriptController extends AbstractModuleController
|
||||
{
|
||||
/**
|
||||
* Set up the doc header properly here
|
||||
*
|
||||
* @param ViewInterface $view
|
||||
*/
|
||||
protected function initializeView(ViewInterface $view)
|
||||
{
|
||||
if ($view instanceof BackendTemplateView) {
|
||||
/** @var BackendTemplateView $view */
|
||||
parent::initializeView($view);
|
||||
$this->generateMenu();
|
||||
$this->registerDocheaderButtons();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the content of the update script (if any).
|
||||
*
|
||||
* @param string $extensionKey Extension key
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
|
||||
*/
|
||||
public function showAction($extensionKey)
|
||||
{
|
||||
/** @var \TYPO3\CMS\Extensionmanager\Utility\UpdateScriptUtility $updateScriptUtility */
|
||||
$updateScriptUtility = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Utility\UpdateScriptUtility::class);
|
||||
$updateScriptResult = $updateScriptUtility->executeUpdateIfNeeded($extensionKey);
|
||||
$this->view
|
||||
->assign('updateScriptResult', $updateScriptResult)
|
||||
->assign('extensionKey', $extensionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the Icons into the docheader
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function registerDocheaderButtons()
|
||||
{
|
||||
/** @var ButtonBar $buttonBar */
|
||||
$buttonBar = $this->view->getModuleTemplate()->getDocHeaderComponent()->getButtonBar();
|
||||
|
||||
$uriBuilder = $this->controllerContext->getUriBuilder();
|
||||
|
||||
$uri = $uriBuilder->reset()->uriFor('index', [], 'List');
|
||||
$title = $this->translate('extConfTemplate.backToList');
|
||||
$icon = $this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-view-go-back', Icon::SIZE_SMALL);
|
||||
$button = $buttonBar->makeLinkButton()
|
||||
->setHref($uri)
|
||||
->setTitle($title)
|
||||
->setIcon($icon);
|
||||
$buttonBar->addButton($button, ButtonBar::BUTTON_POSITION_LEFT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Controller;
|
||||
|
||||
/*
|
||||
* 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\Configuration\ExtensionConfiguration;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Security\BlockSerializationTrait;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extensionmanager\Exception\DependencyConfigurationNotFoundException;
|
||||
use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
|
||||
|
||||
/**
|
||||
* Controller for handling upload of a local extension file
|
||||
* Handles .t3x or .zip files
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class UploadExtensionFileController extends AbstractController
|
||||
{
|
||||
use BlockSerializationTrait;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
|
||||
*/
|
||||
protected $extensionRepository;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
|
||||
*/
|
||||
protected $fileHandlingUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility
|
||||
*/
|
||||
protected $terUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService
|
||||
*/
|
||||
protected $managementService;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $extensionBackupPath = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $removeFromOriginalPath = false;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository
|
||||
*/
|
||||
public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
|
||||
{
|
||||
$this->extensionRepository = $extensionRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility
|
||||
*/
|
||||
public function injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility)
|
||||
{
|
||||
$this->fileHandlingUtility = $fileHandlingUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility $terUtility
|
||||
*/
|
||||
public function injectTerUtility(\TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility $terUtility)
|
||||
{
|
||||
$this->terUtility = $terUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService
|
||||
*/
|
||||
public function injectManagementService(\TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService)
|
||||
{
|
||||
$this->managementService = $managementService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove backup folder before destruction
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->removeBackupFolder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render upload extension form
|
||||
*/
|
||||
public function formAction()
|
||||
{
|
||||
if (Environment::isComposerMode()) {
|
||||
throw new ExtensionManagerException(
|
||||
'Composer mode is active. You are not allowed to upload any extension file.',
|
||||
1444725828
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract an uploaded file and install the matching extension
|
||||
*
|
||||
* @param bool $overwrite Overwrite existing extension if TRUE
|
||||
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
|
||||
*/
|
||||
public function extractAction($overwrite = false)
|
||||
{
|
||||
if (Environment::isComposerMode()) {
|
||||
throw new ExtensionManagerException(
|
||||
'Composer mode is active. You are not allowed to upload any extension file.',
|
||||
1444725853
|
||||
);
|
||||
}
|
||||
$file = $_FILES['tx_extensionmanager_tools_extensionmanagerextensionmanager'];
|
||||
$fileName = pathinfo($file['name']['extensionFile'], PATHINFO_BASENAME);
|
||||
try {
|
||||
// If the file name isn't valid an error will be thrown
|
||||
$this->checkFileName($fileName);
|
||||
if (!empty($file['tmp_name']['extensionFile'])) {
|
||||
$tempFile = GeneralUtility::upload_to_tempfile($file['tmp_name']['extensionFile']);
|
||||
} else {
|
||||
throw new ExtensionManagerException(
|
||||
'Creating temporary file failed. Check your upload_max_filesize and post_max_size limits.',
|
||||
1342864339
|
||||
);
|
||||
}
|
||||
$extensionData = $this->extractExtensionFromFile($tempFile, $fileName, $overwrite);
|
||||
$isAutomaticInstallationEnabled = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('extensionmanager', 'automaticInstallation');
|
||||
if (!$isAutomaticInstallationEnabled) {
|
||||
$this->addFlashMessage(
|
||||
$this->translate('extensionList.uploadFlashMessage.message', [$extensionData['extKey']]),
|
||||
$this->translate('extensionList.uploadFlashMessage.title'),
|
||||
FlashMessage::OK
|
||||
);
|
||||
} else {
|
||||
if ($this->activateExtension($extensionData['extKey'])) {
|
||||
$this->addFlashMessage(
|
||||
$this->translate('extensionList.installedFlashMessage.message', [$extensionData['extKey']]),
|
||||
'',
|
||||
FlashMessage::OK
|
||||
);
|
||||
} else {
|
||||
$this->redirect('unresolvedDependencies', 'List', null, ['extensionKey' => $extensionData['extKey']]);
|
||||
}
|
||||
}
|
||||
} catch (\TYPO3\CMS\Extbase\Mvc\Exception\StopActionException $exception) {
|
||||
throw $exception;
|
||||
} catch (DependencyConfigurationNotFoundException $exception) {
|
||||
$this->addFlashMessage($exception->getMessage(), '', FlashMessage::ERROR);
|
||||
} catch (\Exception $exception) {
|
||||
$this->removeExtensionAndRestoreFromBackup($fileName);
|
||||
$this->addFlashMessage($exception->getMessage(), '', FlashMessage::ERROR);
|
||||
}
|
||||
$this->redirect('index', 'List', null, [
|
||||
self::TRIGGER_RefreshModuleMenu => true,
|
||||
self::TRIGGER_RefreshTopbar => true
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the filename of an uploaded file
|
||||
*
|
||||
* @param string $fileName
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
|
||||
*/
|
||||
public function checkFileName($fileName)
|
||||
{
|
||||
$extension = pathinfo($fileName, PATHINFO_EXTENSION);
|
||||
if (empty($fileName)) {
|
||||
throw new ExtensionManagerException('No file given.', 1342858852);
|
||||
}
|
||||
if ($extension !== 't3x' && $extension !== 'zip') {
|
||||
throw new ExtensionManagerException('Wrong file format "' . $extension . '" given. Allowed formats are t3x and zip.', 1342858853);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a given t3x or zip file
|
||||
*
|
||||
* @param string $uploadPath Path to existing extension file
|
||||
* @param string $fileName Filename of the uploaded file
|
||||
* @param bool $overwrite If true, extension will be replaced
|
||||
* @return array Extension data
|
||||
* @throws ExtensionManagerException
|
||||
* @throws DependencyConfigurationNotFoundException
|
||||
*/
|
||||
public function extractExtensionFromFile($uploadPath, $fileName, $overwrite)
|
||||
{
|
||||
$fileExtension = pathinfo($fileName, PATHINFO_EXTENSION);
|
||||
if ($fileExtension === 't3x') {
|
||||
$extensionData = $this->getExtensionFromT3xFile($uploadPath, $overwrite);
|
||||
} else {
|
||||
$extensionData = $this->getExtensionFromZipFile($uploadPath, $fileName, $overwrite);
|
||||
}
|
||||
|
||||
return $extensionData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extensionKey
|
||||
* @return bool
|
||||
*/
|
||||
public function activateExtension($extensionKey)
|
||||
{
|
||||
$this->managementService->reloadPackageInformation($extensionKey);
|
||||
$extension = $this->managementService->getExtension($extensionKey);
|
||||
return is_array($this->managementService->installExtension($extension));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a given t3x file and installs the extension
|
||||
*
|
||||
* @param string $file Path to uploaded file
|
||||
* @param bool $overwrite Overwrite existing extension if TRUE
|
||||
* @throws ExtensionManagerException
|
||||
* @throws DependencyConfigurationNotFoundException
|
||||
* @return array
|
||||
*/
|
||||
protected function getExtensionFromT3xFile($file, $overwrite = false)
|
||||
{
|
||||
$fileContent = file_get_contents($file);
|
||||
if (!$fileContent) {
|
||||
throw new ExtensionManagerException('File had no or wrong content.', 1342859339);
|
||||
}
|
||||
$extensionData = $this->terUtility->decodeExchangeData($fileContent);
|
||||
if (empty($extensionData['extKey'])) {
|
||||
throw new ExtensionManagerException('Decoding the file went wrong. No extension key found', 1342864309);
|
||||
}
|
||||
$isExtensionAvailable = $this->managementService->isAvailable($extensionData['extKey']);
|
||||
if (!$overwrite && $isExtensionAvailable) {
|
||||
throw new ExtensionManagerException($this->translate('extensionList.overwritingDisabled'), 1342864310);
|
||||
}
|
||||
if ($isExtensionAvailable) {
|
||||
$this->copyExtensionFolderToTempFolder($extensionData['extKey']);
|
||||
}
|
||||
$this->removeFromOriginalPath = true;
|
||||
$extension = $this->extensionRepository->findOneByExtensionKeyAndVersion($extensionData['extKey'], $extensionData['EM_CONF']['version']);
|
||||
$this->fileHandlingUtility->unpackExtensionFromExtensionDataArray($extensionData, $extension);
|
||||
|
||||
if (empty($extension)
|
||||
&& empty($extensionData['EM_CONF']['constraints'])
|
||||
&& !isset($extensionData['FILES']['ext_emconf.php'])
|
||||
&& !isset($extensionData['FILES']['/ext_emconf.php'])
|
||||
) {
|
||||
throw new DependencyConfigurationNotFoundException('Extension cannot be installed automatically because no dependencies could be found! Please check dependencies manually (on typo3.org) before installing the extension.', 1439587168);
|
||||
}
|
||||
|
||||
return $extensionData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a given zip file and installs the extension
|
||||
* As there is no information about the extension key in the zip
|
||||
* we have to use the file name to get that information
|
||||
* filename format is expected to be extensionkey_version.zip
|
||||
*
|
||||
* @param string $file Path to uploaded file
|
||||
* @param string $fileName Filename (basename) of uploaded file
|
||||
* @param bool $overwrite Overwrite existing extension if TRUE
|
||||
* @return array
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
protected function getExtensionFromZipFile($file, $fileName, $overwrite = false)
|
||||
{
|
||||
// Remove version and extension from filename to determine the extension key
|
||||
$extensionKey = $this->getExtensionKeyFromFileName($fileName);
|
||||
$isExtensionAvailable = $this->managementService->isAvailable($extensionKey);
|
||||
if (!$overwrite && $isExtensionAvailable) {
|
||||
throw new ExtensionManagerException('Extension is already available and overwriting is disabled.', 1342864311);
|
||||
}
|
||||
if ($isExtensionAvailable) {
|
||||
$this->copyExtensionFolderToTempFolder($extensionKey);
|
||||
}
|
||||
$this->removeFromOriginalPath = true;
|
||||
$this->fileHandlingUtility->unzipExtensionFromFile($file, $extensionKey);
|
||||
|
||||
return ['extKey' => $extensionKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes version and file extension from filename to determine extension key
|
||||
*
|
||||
* @param string $fileName
|
||||
* @return string
|
||||
*/
|
||||
protected function getExtensionKeyFromFileName($fileName)
|
||||
{
|
||||
return preg_replace('/_(\\d+)(\\.|\\-)(\\d+)(\\.|\\-)(\\d+).*/i', '', strtolower(substr($fileName, 0, -4)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies current extension folder to typo3temp directory as backup
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
|
||||
*/
|
||||
protected function copyExtensionFolderToTempFolder($extensionKey)
|
||||
{
|
||||
$this->extensionBackupPath = Environment::getVarPath() . '/transient/' . $extensionKey . substr(sha1($extensionKey . microtime()), 0, 7) . '/';
|
||||
GeneralUtility::mkdir($this->extensionBackupPath);
|
||||
GeneralUtility::copyDirectory(
|
||||
$this->fileHandlingUtility->getExtensionDir($extensionKey),
|
||||
$this->extensionBackupPath
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the extension directory and restores the extension from the backup directory
|
||||
*
|
||||
* @param string $fileName
|
||||
* @see UploadExtensionFileController::extractAction
|
||||
*/
|
||||
protected function removeExtensionAndRestoreFromBackup($fileName)
|
||||
{
|
||||
$extDirPath = $this->fileHandlingUtility->getExtensionDir($this->getExtensionKeyFromFileName($fileName));
|
||||
if ($this->removeFromOriginalPath && is_dir($extDirPath)) {
|
||||
GeneralUtility::rmdir($extDirPath, true);
|
||||
}
|
||||
if (!empty($this->extensionBackupPath)) {
|
||||
GeneralUtility::mkdir($extDirPath);
|
||||
GeneralUtility::copyDirectory($this->extensionBackupPath, $extDirPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the backup folder in typo3temp
|
||||
*/
|
||||
protected function removeBackupFolder()
|
||||
{
|
||||
if (!empty($this->extensionBackupPath)) {
|
||||
GeneralUtility::rmdir($this->extensionBackupPath, true);
|
||||
$this->extensionBackupPath = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Main extension model
|
||||
* @internal This class is a specific domain model implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class Dependency extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $identifier = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $lowestVersion = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $highestVersion = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $dependencyTypes = [
|
||||
'depends',
|
||||
'conflicts',
|
||||
'suggests'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $specialDependencies = [
|
||||
'typo3',
|
||||
'php'
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $highestVersion
|
||||
*/
|
||||
public function setHighestVersion($highestVersion)
|
||||
{
|
||||
$this->highestVersion = $highestVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHighestVersion()
|
||||
{
|
||||
return $this->highestVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $identifier
|
||||
*/
|
||||
public function setIdentifier($identifier)
|
||||
{
|
||||
$this->identifier = $identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $lowestVersion
|
||||
*/
|
||||
public function setLowestVersion($lowestVersion)
|
||||
{
|
||||
$this->lowestVersion = $lowestVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLowestVersion()
|
||||
{
|
||||
return $this->lowestVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException if no valid dependency type was given
|
||||
*/
|
||||
public function setType($type)
|
||||
{
|
||||
if (in_array($type, self::$dependencyTypes)) {
|
||||
$this->type = $type;
|
||||
} else {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException($type . ' was not a valid dependency type.', 1476122402);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\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\Extensionmanager\Exception\ExtensionManagerException;
|
||||
|
||||
/**
|
||||
* Download Queue - storage for extensions to be downloaded
|
||||
* @internal This class is a specific domain model implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class DownloadQueue implements \TYPO3\CMS\Core\SingletonInterface
|
||||
{
|
||||
/**
|
||||
* Storage for extensions to be downloaded
|
||||
*
|
||||
* @var Extension[string][string]
|
||||
*/
|
||||
protected $extensionStorage = [];
|
||||
|
||||
/**
|
||||
* Storage for extensions to be installed
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $extensionInstallStorage = [];
|
||||
|
||||
/**
|
||||
* Storage for extensions to be copied
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $extensionCopyStorage = [];
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
|
||||
*/
|
||||
protected $listUtility;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility
|
||||
*/
|
||||
public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
|
||||
{
|
||||
$this->listUtility = $listUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an extension to the download queue.
|
||||
* If the extension was already requested in a different version
|
||||
* an exception is thrown.
|
||||
*
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension
|
||||
* @param string $stack
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
|
||||
*/
|
||||
public function addExtensionToQueue(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension, $stack = 'download')
|
||||
{
|
||||
if (!is_string($stack) || !in_array($stack, ['download', 'update'])) {
|
||||
throw new ExtensionManagerException('Stack has to be either "download" or "update"', 1342432103);
|
||||
}
|
||||
if (!isset($this->extensionStorage[$stack])) {
|
||||
$this->extensionStorage[$stack] = [];
|
||||
}
|
||||
if (array_key_exists($extension->getExtensionKey(), $this->extensionStorage[$stack])) {
|
||||
if ($this->extensionStorage[$stack][$extension->getExtensionKey()] !== $extension) {
|
||||
throw new ExtensionManagerException(
|
||||
$extension->getExtensionKey() . ' was requested to be downloaded in different versions (' . $extension->getVersion()
|
||||
. ' and ' . $this->extensionStorage[$stack][$extension->getExtensionKey()]->getVersion() . ').',
|
||||
1342432101
|
||||
);
|
||||
}
|
||||
}
|
||||
$this->extensionStorage[$stack][$extension->getExtensionKey()] = $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getExtensionQueue()
|
||||
{
|
||||
return $this->extensionStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an extension from download queue
|
||||
*
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension
|
||||
* @param string $stack Stack to remove extension from (download, update or install)
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
|
||||
*/
|
||||
public function removeExtensionFromQueue(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension, $stack = 'download')
|
||||
{
|
||||
if (!is_string($stack) || !in_array($stack, ['download', 'update'])) {
|
||||
throw new ExtensionManagerException('Stack has to be either "download" or "update"', 1342432104);
|
||||
}
|
||||
if (array_key_exists($stack, $this->extensionStorage) && is_array($this->extensionStorage[$stack])) {
|
||||
if (array_key_exists($extension->getExtensionKey(), $this->extensionStorage[$stack])) {
|
||||
unset($this->extensionStorage[$stack][$extension->getExtensionKey()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an extension to the install queue for later installation
|
||||
*
|
||||
* @param Extension $extension
|
||||
*/
|
||||
public function addExtensionToInstallQueue($extension)
|
||||
{
|
||||
$this->extensionInstallStorage[$extension->getExtensionKey()] = $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an extension from the install queue
|
||||
*
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
public function removeExtensionFromInstallQueue($extensionKey)
|
||||
{
|
||||
if (array_key_exists($extensionKey, $this->extensionInstallStorage)) {
|
||||
unset($this->extensionInstallStorage[$extensionKey]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an extension to the copy queue for later copying
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @param string $sourceFolder
|
||||
*/
|
||||
public function addExtensionToCopyQueue($extensionKey, $sourceFolder)
|
||||
{
|
||||
$this->extensionCopyStorage[$extensionKey] = $sourceFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an extension from extension copy storage
|
||||
*
|
||||
* @param $extensionKey
|
||||
*/
|
||||
public function removeExtensionFromCopyQueue($extensionKey)
|
||||
{
|
||||
if (array_key_exists($extensionKey, $this->extensionCopyStorage)) {
|
||||
unset($this->extensionCopyStorage[$extensionKey]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the extension installation queue
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExtensionInstallStorage()
|
||||
{
|
||||
return $this->extensionInstallStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the extension copy queue
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExtensionCopyStorage()
|
||||
{
|
||||
return $this->extensionCopyStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the queue contains extensions or not
|
||||
*
|
||||
* @param string $stack
|
||||
* @return bool
|
||||
*/
|
||||
public function isQueueEmpty($stack = 'download')
|
||||
{
|
||||
return empty($this->extensionStorage[$stack]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the copy queue contains extensions or not
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCopyQueueEmpty()
|
||||
{
|
||||
return empty($this->extensionCopyStorage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the install queue contains extensions or not
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInstallQueueEmpty()
|
||||
{
|
||||
return empty($this->extensionInstallStorage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the extension queue and returns old extensions
|
||||
*
|
||||
* @param string|null $stack if null, all stacks are reset
|
||||
* @return array
|
||||
*/
|
||||
public function resetExtensionQueue($stack = null)
|
||||
{
|
||||
$storage = [];
|
||||
if ($stack === null) {
|
||||
$storage = $this->extensionStorage;
|
||||
$this->extensionStorage = [];
|
||||
} elseif (isset($this->extensionStorage[$stack])) {
|
||||
$storage = $this->extensionStorage[$stack];
|
||||
$this->extensionStorage[$stack] = [];
|
||||
}
|
||||
|
||||
return $storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the copy queue and returns the old extensions
|
||||
* @return array
|
||||
*/
|
||||
public function resetExtensionCopyStorage()
|
||||
{
|
||||
$storage = $this->extensionCopyStorage;
|
||||
$this->extensionCopyStorage = [];
|
||||
|
||||
return $storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the install queue and returns the old extensions
|
||||
* @return array
|
||||
*/
|
||||
public function resetExtensionInstallStorage()
|
||||
{
|
||||
$storage = $this->extensionInstallStorage;
|
||||
$this->extensionInstallStorage = [];
|
||||
|
||||
return $storage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,602 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\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\Core\Core\Environment;
|
||||
|
||||
/**
|
||||
* Main extension model
|
||||
* @internal This class is a specific domain model implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class Extension extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
|
||||
{
|
||||
/**
|
||||
* Category index for distributions
|
||||
*/
|
||||
const DISTRIBUTION_CATEGORY = 10;
|
||||
|
||||
/**
|
||||
* Contains default categories.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $defaultCategories = [
|
||||
0 => 'be',
|
||||
1 => 'module',
|
||||
2 => 'fe',
|
||||
3 => 'plugin',
|
||||
4 => 'misc',
|
||||
5 => 'services',
|
||||
6 => 'templates',
|
||||
8 => 'doc',
|
||||
9 => 'example',
|
||||
self::DISTRIBUTION_CATEGORY => 'distribution'
|
||||
];
|
||||
|
||||
/**
|
||||
* Contains default states.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $defaultStates = [
|
||||
0 => 'alpha',
|
||||
1 => 'beta',
|
||||
2 => 'stable',
|
||||
3 => 'experimental',
|
||||
4 => 'test',
|
||||
5 => 'obsolete',
|
||||
6 => 'excludeFromUpdates',
|
||||
7 => 'deprecated',
|
||||
999 => 'n/a'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Object\ObjectManager
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $extensionKey = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $version = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $integerVersion = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $title = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $state = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $category = 0;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $lastUpdated;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $updateComment = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $authorName = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $authorEmail = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $currentVersion = false;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $md5hash = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $reviewState;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $alldownloadcounter;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $serializedDependencies = '';
|
||||
|
||||
/**
|
||||
* @var \SplObjectStorage<\TYPO3\CMS\Extensionmanager\Domain\Model\Dependency>
|
||||
*/
|
||||
protected $dependencies;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @var int
|
||||
*/
|
||||
protected $position = 0;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager
|
||||
*/
|
||||
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $authorEmail
|
||||
*/
|
||||
public function setAuthorEmail($authorEmail)
|
||||
{
|
||||
$this->authorEmail = $authorEmail;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAuthorEmail()
|
||||
{
|
||||
return $this->authorEmail;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $authorName
|
||||
*/
|
||||
public function setAuthorName($authorName)
|
||||
{
|
||||
$this->authorName = $authorName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAuthorName()
|
||||
{
|
||||
return $this->authorName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $category
|
||||
*/
|
||||
public function setCategory($category)
|
||||
{
|
||||
$this->category = $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCategory()
|
||||
{
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Category String
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCategoryString()
|
||||
{
|
||||
$categoryString = '';
|
||||
if (isset(self::$defaultCategories[$this->getCategory()])) {
|
||||
$categoryString = self::$defaultCategories[$this->getCategory()];
|
||||
}
|
||||
return $categoryString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns category index from a given string or an integer.
|
||||
* Fallback to 4 - 'misc' in case string is not found or integer ist out of range.
|
||||
*
|
||||
* @param string|int $category Category string or integer
|
||||
* @return int Valid category index
|
||||
*/
|
||||
public function getCategoryIndexFromStringOrNumber($category)
|
||||
{
|
||||
$categoryIndex = 4;
|
||||
if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($category)) {
|
||||
$categoryIndex = (int)$category;
|
||||
if ($categoryIndex < 0 || $categoryIndex > 10) {
|
||||
$categoryIndex = 4;
|
||||
}
|
||||
} elseif (is_string($category)) {
|
||||
$categoryIndex = array_search($category, self::$defaultCategories);
|
||||
if ($categoryIndex === false) {
|
||||
$categoryIndex = 4;
|
||||
}
|
||||
}
|
||||
return $categoryIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $description
|
||||
*/
|
||||
public function setDescription($description)
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
public function setExtensionKey($extensionKey)
|
||||
{
|
||||
$this->extensionKey = $extensionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getExtensionKey()
|
||||
{
|
||||
return $this->extensionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $lastUpdated
|
||||
*/
|
||||
public function setLastUpdated(\DateTime $lastUpdated)
|
||||
{
|
||||
$this->lastUpdated = $lastUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getLastUpdated()
|
||||
{
|
||||
return $this->lastUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $state
|
||||
*/
|
||||
public function setState($state)
|
||||
{
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get State string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStateString()
|
||||
{
|
||||
$stateString = '';
|
||||
if (isset(self::$defaultStates[$this->getState()])) {
|
||||
$stateString = self::$defaultStates[$this->getState()];
|
||||
}
|
||||
return $stateString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns either array with all default states or index/title
|
||||
* of a state entry.
|
||||
*
|
||||
* @param mixed $state state title or state index
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDefaultState($state = null)
|
||||
{
|
||||
$defaultState = '';
|
||||
if ($state === null) {
|
||||
$defaultState = self::$defaultStates;
|
||||
} else {
|
||||
if (is_string($state)) {
|
||||
$stateIndex = array_search(strtolower($state), self::$defaultStates);
|
||||
if ($stateIndex === false) {
|
||||
// default state
|
||||
$stateIndex = 999;
|
||||
}
|
||||
$defaultState = $stateIndex;
|
||||
} else {
|
||||
if (is_int($state) && $state >= 0) {
|
||||
if (array_key_exists($state, self::$defaultStates)) {
|
||||
$stateTitle = self::$defaultStates[$state];
|
||||
} else {
|
||||
// default state
|
||||
$stateTitle = 'n/a';
|
||||
}
|
||||
$defaultState = $stateTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $defaultState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $title
|
||||
*/
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $updateComment
|
||||
*/
|
||||
public function setUpdateComment($updateComment)
|
||||
{
|
||||
$this->updateComment = $updateComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUpdateComment()
|
||||
{
|
||||
return $this->updateComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $version
|
||||
*/
|
||||
public function setVersion($version)
|
||||
{
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $currentVersion
|
||||
*/
|
||||
public function setCurrentVersion($currentVersion)
|
||||
{
|
||||
$this->currentVersion = $currentVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getCurrentVersion()
|
||||
{
|
||||
return $this->currentVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $md5hash
|
||||
*/
|
||||
public function setMd5hash($md5hash)
|
||||
{
|
||||
$this->md5hash = $md5hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMd5hash()
|
||||
{
|
||||
return $this->md5hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Possible install paths
|
||||
*
|
||||
* @static
|
||||
* @return array
|
||||
*/
|
||||
public static function returnInstallPaths()
|
||||
{
|
||||
$installPaths = [
|
||||
'System' => Environment::getFrameworkBasePath() . '/',
|
||||
'Global' => Environment::getBackendPath() . '/ext/',
|
||||
'Local' => Environment::getExtensionsPath() . '/'
|
||||
];
|
||||
return $installPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allowed install paths
|
||||
*
|
||||
* @static
|
||||
* @return array
|
||||
*/
|
||||
public static function returnAllowedInstallPaths()
|
||||
{
|
||||
$installPaths = self::returnInstallPaths();
|
||||
if (empty($GLOBALS['TYPO3_CONF_VARS']['EXT']['allowGlobalInstall'])) {
|
||||
unset($installPaths['Global']);
|
||||
}
|
||||
if (empty($GLOBALS['TYPO3_CONF_VARS']['EXT']['allowLocalInstall'])) {
|
||||
unset($installPaths['Local']);
|
||||
}
|
||||
return $installPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allowed install names: System, Global, Local
|
||||
*
|
||||
* @static
|
||||
* @return array
|
||||
*/
|
||||
public static function returnAllowedInstallTypes()
|
||||
{
|
||||
$installPaths = self::returnAllowedInstallPaths();
|
||||
return array_keys($installPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dependencies
|
||||
*/
|
||||
public function setSerializedDependencies($dependencies)
|
||||
{
|
||||
$this->serializedDependencies = $dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSerializedDependencies()
|
||||
{
|
||||
return $this->serializedDependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \SplObjectStorage $dependencies
|
||||
*/
|
||||
public function setDependencies($dependencies)
|
||||
{
|
||||
$this->dependencies = $dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \SplObjectStorage
|
||||
*/
|
||||
public function getDependencies()
|
||||
{
|
||||
if (!is_object($this->dependencies)) {
|
||||
/** @var \TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility $extensionModelUtility */
|
||||
$extensionModelUtility = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility::class);
|
||||
$this->setDependencies($extensionModelUtility->convertDependenciesToObjects($this->getSerializedDependencies()));
|
||||
}
|
||||
return $this->dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency
|
||||
*/
|
||||
public function addDependency(\TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency)
|
||||
{
|
||||
$this->dependencies->attach($dependency);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $integerVersion
|
||||
*/
|
||||
public function setIntegerVersion($integerVersion)
|
||||
{
|
||||
$this->integerVersion = $integerVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getIntegerVersion()
|
||||
{
|
||||
return $this->integerVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $reviewState
|
||||
*/
|
||||
public function setReviewState($reviewState)
|
||||
{
|
||||
$this->reviewState = $reviewState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getReviewState()
|
||||
{
|
||||
return $this->reviewState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $position
|
||||
*/
|
||||
public function setPosition($position)
|
||||
{
|
||||
$this->position = $position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPosition()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $alldownloadcounter
|
||||
*/
|
||||
public function setAlldownloadcounter($alldownloadcounter)
|
||||
{
|
||||
$this->alldownloadcounter = $alldownloadcounter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getAlldownloadcounter()
|
||||
{
|
||||
return $this->alldownloadcounter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Repository mirrors object for extension manager.
|
||||
* @internal This class is a specific domain model implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class Mirrors extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
|
||||
{
|
||||
/**
|
||||
* Keeps mirrors.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $mirrors = [];
|
||||
|
||||
/**
|
||||
* Keeps currently select mirror.
|
||||
*
|
||||
* Is array index.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $currentMirror;
|
||||
|
||||
/**
|
||||
* Keeps information if a mirror should
|
||||
* be randomly selected.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $isRandomSelection = true;
|
||||
|
||||
/**
|
||||
* Method selects one specific mirror to be used.
|
||||
*
|
||||
* @param int $mirrorId number (>=1) of mirror or NULL for random selection
|
||||
* @see $currentMirror
|
||||
*/
|
||||
public function setSelect($mirrorId = null)
|
||||
{
|
||||
if ($mirrorId === null) {
|
||||
$this->isRandomSelection = true;
|
||||
} else {
|
||||
if (is_int($mirrorId) && $mirrorId >= 1 && $mirrorId <= count($this->mirrors)) {
|
||||
$this->currentMirror = $mirrorId - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns one mirror for use.
|
||||
*
|
||||
* Mirror has previously been selected or is chosen
|
||||
* randomly.
|
||||
*
|
||||
* @return array array of a mirror's properties or NULL in case of errors
|
||||
*/
|
||||
public function getMirror()
|
||||
{
|
||||
$sumMirrors = count($this->mirrors);
|
||||
if ($sumMirrors > 0) {
|
||||
if (!is_int($this->currentMirror)) {
|
||||
$this->currentMirror = rand(0, $sumMirrors - 1);
|
||||
}
|
||||
return $this->mirrors[$this->currentMirror];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mirror url from selected mirror
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMirrorUrl()
|
||||
{
|
||||
$mirror = $this->getMirror();
|
||||
$mirrorUrl = $mirror['host'] . $mirror['path'];
|
||||
return 'https://' . $mirrorUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns all available mirrors.
|
||||
*
|
||||
* @return array multidimensional array with mirrors and their properties
|
||||
* @see $mirrors, setMirrors()
|
||||
*/
|
||||
public function getMirrors()
|
||||
{
|
||||
return $this->mirrors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method sets available mirrors.
|
||||
*
|
||||
* @param array $mirrors multidimensional array with mirrors and their properties
|
||||
* @see $mirrors, getMirrors()
|
||||
*/
|
||||
public function setMirrors(array $mirrors)
|
||||
{
|
||||
if (count($mirrors) >= 1) {
|
||||
$this->mirrors = $mirrors;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Repository object for extension manager.
|
||||
* @internal This class is a specific domain model implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class Repository extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
|
||||
{
|
||||
/**
|
||||
* Keeps repository title.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* Keeps repository description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* Keeps mirror list URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $mirrorListUrl;
|
||||
|
||||
/**
|
||||
* Keeps repository mirrors object.
|
||||
*
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Model\Mirrors
|
||||
*/
|
||||
protected $mirrors;
|
||||
|
||||
/**
|
||||
* Keeps wsdl URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $wsdlUrl;
|
||||
|
||||
/**
|
||||
* Keeps last update.
|
||||
*
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $lastUpdate;
|
||||
|
||||
/**
|
||||
* Keeps extension count.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $extensionCount;
|
||||
|
||||
/**
|
||||
* Method returns title of a repository.
|
||||
*
|
||||
* @return string title of repository
|
||||
* @see $title, setTitle()
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method sets title of a repository.
|
||||
*
|
||||
* @param string $title title of repository to set
|
||||
* @see $title, getTitle()
|
||||
*/
|
||||
public function setTitle($title)
|
||||
{
|
||||
if (!empty($title) && is_string($title)) {
|
||||
$this->title = $title;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns description of a repository.
|
||||
*
|
||||
* @return string title of repository
|
||||
* @see $title, setTitle()
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method sets description of a repository.
|
||||
*
|
||||
* @param string $description title of repository to set
|
||||
*/
|
||||
public function setDescription($description)
|
||||
{
|
||||
if (!empty($description) && is_string($description)) {
|
||||
$this->description = $description;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns URL of a resource that contains repository mirrors.
|
||||
*
|
||||
* @return string URL of file that contains repository mirrors
|
||||
* @see $mirrorListUrl, getMirrorListUrl()
|
||||
*/
|
||||
public function getMirrorListUrl()
|
||||
{
|
||||
return $this->mirrorListUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method sets URL of a resource that contains repository mirrors.
|
||||
*
|
||||
* Parameter is typically a remote gzipped xml file.
|
||||
*
|
||||
* @param string $url URL of file that contains repository mirrors
|
||||
* @see $mirrorListUrl, getMirrorListUrl()
|
||||
*/
|
||||
public function setMirrorListUrl($url)
|
||||
{
|
||||
if (empty($url) || !empty($url) && \TYPO3\CMS\Core\Utility\GeneralUtility::isValidUrl($url)) {
|
||||
$this->mirrorListUrl = $url;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns URL of repository WSDL.
|
||||
*
|
||||
* @return string URL of repository WSDL
|
||||
* @see $wsdlUrl, setWsdlUrl()
|
||||
*/
|
||||
public function getWsdlUrl()
|
||||
{
|
||||
return $this->wsdlUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method sets URL of repository WSDL.
|
||||
*
|
||||
* @param string $url URL of repository WSDL
|
||||
* @see $wsdlUrl, getWsdlUrl()
|
||||
*/
|
||||
public function setWsdlUrl($url)
|
||||
{
|
||||
if (!empty($url) && \TYPO3\CMS\Core\Utility\GeneralUtility::isValidUrl($url)) {
|
||||
$this->wsdlUrl = $url;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns LastUpdate.
|
||||
*
|
||||
* @return \DateTime timestamp of last update
|
||||
*/
|
||||
public function getLastUpdate()
|
||||
{
|
||||
return $this->lastUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method sets LastUpdate.
|
||||
*
|
||||
* @param \DateTime $time URL of repository WSDL
|
||||
*/
|
||||
public function setLastUpdate(\DateTime $time)
|
||||
{
|
||||
$this->lastUpdate = $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns extension count
|
||||
*
|
||||
* @return int count of read extensions
|
||||
*/
|
||||
public function getExtensionCount()
|
||||
{
|
||||
return $this->extensionCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method sets extension count
|
||||
*
|
||||
* @param string $count count of read extensions
|
||||
*/
|
||||
public function setExtensionCount($count)
|
||||
{
|
||||
$this->extensionCount = $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method registers repository mirrors object.
|
||||
*
|
||||
* Repository mirrors object is passed by reference.
|
||||
*
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Model\Mirrors $mirrors mirror list
|
||||
* @see $mirrors, getMirrors(), hasMirrors(), removeMirrors()
|
||||
*/
|
||||
public function addMirrors(\TYPO3\CMS\Extensionmanager\Domain\Model\Mirrors $mirrors)
|
||||
{
|
||||
$this->mirrors = $mirrors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns information if a repository mirrors
|
||||
* object has been registered to this repository.
|
||||
*
|
||||
* @return bool TRUE, if a repository mirrors object has been registered, otherwise FALSE
|
||||
* @see $mirrors, addMirrors(), getMirrors(), removeMirrors()
|
||||
*/
|
||||
public function hasMirrors()
|
||||
{
|
||||
$hasMirrors = false;
|
||||
if (is_object($this->mirrors)) {
|
||||
$hasMirrors = true;
|
||||
}
|
||||
return $hasMirrors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns a repository mirrors object.
|
||||
*
|
||||
* @return \TYPO3\CMS\Extensionmanager\Domain\Model\Mirrors mirrors for repository
|
||||
* @see $mirrors, addMirrors(), hasMirrors(), removeMirrors()
|
||||
*/
|
||||
public function getMirrors()
|
||||
{
|
||||
return $this->hasMirrors() ? $this->mirrors : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method unregisters a repository mirrors object.
|
||||
*
|
||||
* @see $mirrors, addMirrors(), getMirrors(), hasMirrors()
|
||||
*/
|
||||
public function removeMirrors()
|
||||
{
|
||||
unset($this->mirrors);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,439 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\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!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Platform\PlatformInformation;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* A repository for extensions
|
||||
* @internal This class is a specific domain repository implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class ExtensionRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const TABLE_NAME = 'tx_extensionmanager_domain_model_extension';
|
||||
|
||||
/**
|
||||
* Do not include pid in queries
|
||||
*/
|
||||
public function initializeObject()
|
||||
{
|
||||
/** @var \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $defaultQuerySettings */
|
||||
$defaultQuerySettings = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface::class);
|
||||
$defaultQuerySettings->setRespectStoragePage(false);
|
||||
$this->setDefaultQuerySettings($defaultQuerySettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count all extensions
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countAll()
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$query = $this->addDefaultConstraints($query);
|
||||
return $query->execute()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all extensions
|
||||
*
|
||||
* @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
|
||||
*/
|
||||
public function findAll()
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$query = $this->addDefaultConstraints($query);
|
||||
$query->setOrderings(
|
||||
[
|
||||
'lastUpdated' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
|
||||
]
|
||||
);
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an extension by extension key ordered by version
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
|
||||
*/
|
||||
public function findByExtensionKeyOrderedByVersion($extensionKey)
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$query->matching($query->logicalAnd($query->equals('extensionKey', $extensionKey), $query->greaterThanOrEqual('reviewState', 0)));
|
||||
$query->setOrderings(['integerVersion' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING]);
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the current version by extension key
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
|
||||
*/
|
||||
public function findOneByCurrentVersionByExtensionKey($extensionKey)
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$query->matching(
|
||||
$query->logicalAnd(
|
||||
$query->equals('extensionKey', $extensionKey),
|
||||
$query->greaterThanOrEqual('reviewState', 0),
|
||||
$query->equals('currentVersion', 1)
|
||||
)
|
||||
);
|
||||
$query->setLimit(1);
|
||||
return $query->execute()->getFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find one extension by extension key and version
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @param string $version (example: 4.3.10)
|
||||
* @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
|
||||
*/
|
||||
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)
|
||||
));
|
||||
return $query->setLimit(1)->execute()->getFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an extension by title, author name or extension key
|
||||
* This is the function used by the TER search. It is using a
|
||||
* scoring for the matches to sort the extension with an
|
||||
* exact key match on top
|
||||
*
|
||||
* @param string $searchString The string to search for extensions
|
||||
* @return mixed
|
||||
*/
|
||||
public function findByTitleOrAuthorNameOrExtensionKey($searchString)
|
||||
{
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable(self::TABLE_NAME);
|
||||
|
||||
$searchPlaceholderForLike = '%' . $queryBuilder->escapeLikeWildcards($searchString) . '%';
|
||||
|
||||
$searchConstraints = [
|
||||
'extension_key' => $queryBuilder->expr()->eq(
|
||||
'extension_key',
|
||||
$queryBuilder->createNamedParameter($searchString, \PDO::PARAM_STR)
|
||||
),
|
||||
'extension_key_like' => $queryBuilder->expr()->like(
|
||||
'extension_key',
|
||||
$queryBuilder->createNamedParameter($searchPlaceholderForLike, \PDO::PARAM_STR)
|
||||
),
|
||||
'title' => $queryBuilder->expr()->like(
|
||||
'title',
|
||||
$queryBuilder->createNamedParameter($searchPlaceholderForLike, \PDO::PARAM_STR)
|
||||
),
|
||||
'description' => $queryBuilder->expr()->like(
|
||||
'description',
|
||||
$queryBuilder->createNamedParameter($searchPlaceholderForLike, \PDO::PARAM_STR)
|
||||
),
|
||||
'author_name' => $queryBuilder->expr()->like(
|
||||
'author_name',
|
||||
$queryBuilder->createNamedParameter($searchPlaceholderForLike, \PDO::PARAM_STR)
|
||||
),
|
||||
];
|
||||
|
||||
$caseStatement = 'CASE ' .
|
||||
'WHEN ' . $searchConstraints['extension_key'] . ' THEN 16 ' .
|
||||
'WHEN ' . $searchConstraints['extension_key_like'] . ' THEN 8 ' .
|
||||
'WHEN ' . $searchConstraints['title'] . ' THEN 4 ' .
|
||||
'WHEN ' . $searchConstraints['description'] . ' THEN 2 ' .
|
||||
'WHEN ' . $searchConstraints['author_name'] . ' THEN 1 ' .
|
||||
'END AS ' . $queryBuilder->quoteIdentifier('position');
|
||||
|
||||
$result = $queryBuilder
|
||||
->select('*')
|
||||
->addSelectLiteral($caseStatement)
|
||||
->from(self::TABLE_NAME)
|
||||
->where(
|
||||
$queryBuilder->expr()->orX(...array_values($searchConstraints)),
|
||||
$queryBuilder->expr()->eq('current_version', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->gte('review_state', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
|
||||
)
|
||||
->orderBy('position', 'DESC')
|
||||
->execute()
|
||||
->fetchAll();
|
||||
|
||||
$dataMapper = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper::class);
|
||||
return $dataMapper->map(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension::class, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an extension between a certain version range ordered by version number
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @param int $lowestVersion
|
||||
* @param int $highestVersion
|
||||
* @param bool $includeCurrentVersion
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
|
||||
*/
|
||||
public function findByVersionRangeAndExtensionKeyOrderedByVersion($extensionKey, $lowestVersion = 0, $highestVersion = 0, $includeCurrentVersion = true)
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$constraint = null;
|
||||
if ($lowestVersion !== 0 && $highestVersion !== 0) {
|
||||
if ($includeCurrentVersion) {
|
||||
$constraint = $query->logicalAnd($query->lessThanOrEqual('integerVersion', $highestVersion), $query->greaterThanOrEqual('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
|
||||
} else {
|
||||
$constraint = $query->logicalAnd($query->lessThanOrEqual('integerVersion', $highestVersion), $query->greaterThan('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
|
||||
}
|
||||
} elseif ($lowestVersion === 0 && $highestVersion !== 0) {
|
||||
if ($includeCurrentVersion) {
|
||||
$constraint = $query->logicalAnd($query->lessThanOrEqual('integerVersion', $highestVersion), $query->equals('extensionKey', $extensionKey));
|
||||
} else {
|
||||
$constraint = $query->logicalAnd($query->lessThan('integerVersion', $highestVersion), $query->equals('extensionKey', $extensionKey));
|
||||
}
|
||||
} elseif ($lowestVersion !== 0 && $highestVersion === 0) {
|
||||
if ($includeCurrentVersion) {
|
||||
$constraint = $query->logicalAnd($query->greaterThanOrEqual('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
|
||||
} else {
|
||||
$constraint = $query->logicalAnd($query->greaterThan('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
|
||||
}
|
||||
} elseif ($lowestVersion === 0 && $highestVersion === 0) {
|
||||
$constraint = $query->equals('extensionKey', $extensionKey);
|
||||
}
|
||||
if ($constraint) {
|
||||
$query->matching($query->logicalAnd($constraint, $query->greaterThanOrEqual('reviewState', 0)));
|
||||
}
|
||||
$query->setOrderings([
|
||||
'integerVersion' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
|
||||
]);
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all extensions with category "distribution" not published by the TYPO3 CMS Team
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
|
||||
*/
|
||||
public function findAllCommunityDistributions()
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$query->matching(
|
||||
$query->logicalAnd(
|
||||
$query->equals('category', \TYPO3\CMS\Extensionmanager\Domain\Model\Extension::DISTRIBUTION_CATEGORY),
|
||||
$query->logicalNot($query->equals('ownerusername', 'typo3v4'))
|
||||
)
|
||||
);
|
||||
|
||||
$query->setOrderings([
|
||||
'alldownloadcounter' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
|
||||
]);
|
||||
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all extensions with category "distribution" that are published by the TYPO3 CMS Team
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
|
||||
*/
|
||||
public function findAllOfficialDistributions()
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$query->matching(
|
||||
$query->logicalAnd(
|
||||
$query->equals('category', \TYPO3\CMS\Extensionmanager\Domain\Model\Extension::DISTRIBUTION_CATEGORY),
|
||||
$query->equals('ownerusername', 'typo3v4')
|
||||
)
|
||||
);
|
||||
|
||||
$query->setOrderings([
|
||||
'alldownloadcounter' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
|
||||
]);
|
||||
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count extensions with a certain key between a given version range
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @param int $lowestVersion
|
||||
* @param int $highestVersion
|
||||
* @return int
|
||||
*/
|
||||
public function countByVersionRangeAndExtensionKey($extensionKey, $lowestVersion = 0, $highestVersion = 0)
|
||||
{
|
||||
return $this->findByVersionRangeAndExtensionKeyOrderedByVersion($extensionKey, $lowestVersion, $highestVersion)->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find highest version available of an extension
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @return \TYPO3\CMS\Extensionmanager\Domain\Model\Extension
|
||||
*/
|
||||
public function findHighestAvailableVersion($extensionKey)
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$query->matching($query->logicalAnd($query->equals('extensionKey', $extensionKey), $query->greaterThanOrEqual('reviewState', 0)));
|
||||
$query->setOrderings([
|
||||
'integerVersion' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
|
||||
]);
|
||||
return $query->setLimit(1)->execute()->getFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current_version field after update.
|
||||
*
|
||||
* @param int $repositoryUid
|
||||
* @return int
|
||||
*/
|
||||
public function insertLastVersion($repositoryUid = 1)
|
||||
{
|
||||
$this->markExtensionWithMaximumVersionAsCurrent($repositoryUid);
|
||||
|
||||
return $this->getNumberOfCurrentExtensions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets current_version = 1 for all extensions where the extension version is maximal.
|
||||
*
|
||||
* For performance reasons, the "native" database connection is used here directly.
|
||||
*
|
||||
* @param int $repositoryUid
|
||||
*/
|
||||
protected function markExtensionWithMaximumVersionAsCurrent($repositoryUid)
|
||||
{
|
||||
$uidsOfCurrentVersion = $this->fetchMaximalVersionsForAllExtensions($repositoryUid);
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable(self::TABLE_NAME);
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getConnectionForTable(self::TABLE_NAME);
|
||||
$maxBindParameters = PlatformInformation::getMaxBindParameters(
|
||||
$connection->getDatabasePlatform()
|
||||
);
|
||||
|
||||
foreach (array_chunk($uidsOfCurrentVersion, $maxBindParameters - 10) as $chunk) {
|
||||
$queryBuilder
|
||||
->update(self::TABLE_NAME)
|
||||
->where(
|
||||
$queryBuilder->expr()->in(
|
||||
'uid',
|
||||
$queryBuilder->createNamedParameter($chunk, Connection::PARAM_INT_ARRAY)
|
||||
)
|
||||
)
|
||||
->set('current_version', 1)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the UIDs of all maximal versions for all extensions.
|
||||
* This is done by doing a LEFT JOIN to itself ("a" and "b") and comparing
|
||||
* both integer_version fields.
|
||||
*
|
||||
* @param int $repositoryUid
|
||||
* @return array
|
||||
*/
|
||||
protected function fetchMaximalVersionsForAllExtensions($repositoryUid)
|
||||
{
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable(self::TABLE_NAME);
|
||||
|
||||
$queryResult = $queryBuilder
|
||||
->select('a.uid AS uid')
|
||||
->from(self::TABLE_NAME, 'a')
|
||||
->leftJoin(
|
||||
'a',
|
||||
self::TABLE_NAME,
|
||||
'b',
|
||||
$queryBuilder->expr()->andX(
|
||||
$queryBuilder->expr()->eq('a.repository', $queryBuilder->quoteIdentifier('b.repository')),
|
||||
$queryBuilder->expr()->eq('a.extension_key', $queryBuilder->quoteIdentifier('b.extension_key')),
|
||||
$queryBuilder->expr()->lt('a.integer_version', $queryBuilder->quoteIdentifier('b.integer_version'))
|
||||
)
|
||||
)
|
||||
->where(
|
||||
$queryBuilder->expr()->eq(
|
||||
'a.repository',
|
||||
$queryBuilder->createNamedParameter($repositoryUid, \PDO::PARAM_INT)
|
||||
),
|
||||
$queryBuilder->expr()->isNull('b.extension_key')
|
||||
)
|
||||
->orderBy('a.uid')
|
||||
->execute();
|
||||
|
||||
$extensionUids = [];
|
||||
while ($row = $queryResult->fetch()) {
|
||||
$extensionUids[] = $row['uid'];
|
||||
}
|
||||
|
||||
return $extensionUids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of extensions that are current.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getNumberOfCurrentExtensions()
|
||||
{
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable(self::TABLE_NAME);
|
||||
|
||||
return (int)$queryBuilder
|
||||
->count('*')
|
||||
->from(self::TABLE_NAME)
|
||||
->where($queryBuilder->expr()->eq(
|
||||
'current_version',
|
||||
$queryBuilder->createNamedParameter(1, \PDO::PARAM_INT)
|
||||
))
|
||||
->execute()
|
||||
->fetchColumn(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds default constraints to the query - in this case it
|
||||
* enables us to always just search for the latest version of an extension
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Persistence\Generic\Query $query the query to adjust
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Query
|
||||
*/
|
||||
protected function addDefaultConstraints(\TYPO3\CMS\Extbase\Persistence\Generic\Query $query)
|
||||
{
|
||||
if ($query->getConstraint()) {
|
||||
$query->matching($query->logicalAnd(
|
||||
$query->getConstraint(),
|
||||
$query->equals('current_version', true),
|
||||
$query->greaterThanOrEqual('reviewState', 0)
|
||||
));
|
||||
} else {
|
||||
$query->matching($query->logicalAnd(
|
||||
$query->equals('current_version', true),
|
||||
$query->greaterThanOrEqual('reviewState', 0)
|
||||
));
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* A repository for extension repositories
|
||||
* @internal This class is a specific domain repository implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class RepositoryRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
|
||||
{
|
||||
/**
|
||||
* Do not include pid in queries
|
||||
*/
|
||||
public function initializeObject()
|
||||
{
|
||||
/** @var \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $defaultQuerySettings */
|
||||
$defaultQuerySettings = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface::class);
|
||||
$defaultQuerySettings->setRespectStoragePage(false);
|
||||
$this->setDefaultQuerySettings($defaultQuerySettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates ExtCount and lastUpdated in Repository eg after import
|
||||
*
|
||||
* @param int $extCount
|
||||
* @param int $uid
|
||||
*/
|
||||
public function updateRepositoryCount($extCount, $uid = 1)
|
||||
{
|
||||
$repository = $this->findByUid($uid);
|
||||
|
||||
$repository->setLastUpdate(new \DateTime());
|
||||
$repository->setExtensionCount((int)$extCount);
|
||||
|
||||
$this->update($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 \TYPO3\CMS\Extensionmanager\Domain\Model\Repository $repository */
|
||||
if ($repository->getTitle() === 'TYPO3.org Main Repository') {
|
||||
$typo3OrgRepository = $repository;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $typo3OrgRepository;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager;
|
||||
|
||||
/*
|
||||
* 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\Exception as CoreException;
|
||||
|
||||
/**
|
||||
* A generic Extensionmanager exception
|
||||
*/
|
||||
class Exception extends CoreException
|
||||
{
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Exception;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* An exception when dependency configuration could not be found after uploading a t3x file
|
||||
*/
|
||||
class DependencyConfigurationNotFoundException extends ExtensionManagerException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Exception;
|
||||
|
||||
/*
|
||||
* 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\Extensionmanager\Exception;
|
||||
|
||||
/**
|
||||
* An exception when something is wrong within the extension manager
|
||||
*/
|
||||
class ExtensionManagerException extends Exception
|
||||
{
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Exception;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* An exception when some dependency is unresolved
|
||||
*/
|
||||
class MissingExtensionDependencyException extends UnresolvedDependencyException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Exception;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* An exception when some dependency is unresolved
|
||||
*/
|
||||
class MissingVersionDependencyException extends UnresolvedDependencyException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Exception;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* An exception when some dependency is unresolved
|
||||
*/
|
||||
class UnresolvedDependencyException extends ExtensionManagerException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Exception;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* An exception when some dependency is unresolved
|
||||
*/
|
||||
class UnresolvedPhpDependencyException extends UnresolvedDependencyException
|
||||
{
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Exception;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* An exception when some dependency is unresolved
|
||||
*/
|
||||
class UnresolvedTypo3DependencyException extends UnresolvedDependencyException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Report;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Extension status reports
|
||||
* @internal This class is a specific EXT:reports implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository
|
||||
*/
|
||||
protected $repositoryRepository;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
|
||||
*/
|
||||
protected $listUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Localization\LanguageService
|
||||
*/
|
||||
protected $languageService;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
|
||||
$this->repositoryRepository = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository::class);
|
||||
$this->listUtility = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Utility\ListUtility::class);
|
||||
$this->languageService = $this->objectManager->get(\TYPO3\CMS\Core\Localization\LanguageService::class);
|
||||
$this->languageService->includeLLFile('EXT:extensionmanager/Resources/Private/Language/locallang.xlf');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines extension manager status
|
||||
*
|
||||
* @return array List of statuses
|
||||
*/
|
||||
public function getStatus()
|
||||
{
|
||||
$status = [];
|
||||
$status['mainRepositoryStatus'] = $this->getMainRepositoryStatus();
|
||||
|
||||
$extensionStatus = $this->getSecurityStatusOfExtensions();
|
||||
$status['extensionsSecurityStatusInstalled'] = $extensionStatus->loaded ?? [];
|
||||
$status['extensionsSecurityStatusNotInstalled'] = $extensionStatus->existing ?? [];
|
||||
$status['extensionsOutdatedStatusInstalled'] = $extensionStatus->loadedoutdated ?? [];
|
||||
$status['extensionsOutdatedStatusNotInstalled'] = $extensionStatus->existingoutdated ?? [];
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check main repository status: existence, has extensions, last update younger than 7 days
|
||||
*
|
||||
* @return \TYPO3\CMS\Reports\Status
|
||||
*/
|
||||
protected function getMainRepositoryStatus()
|
||||
{
|
||||
/** @var \TYPO3\CMS\Extensionmanager\Domain\Model\Repository $mainRepository */
|
||||
$mainRepository = $this->repositoryRepository->findOneTypo3OrgRepository();
|
||||
|
||||
if ($mainRepository === null) {
|
||||
$value = $this->languageService->getLL('report.status.mainRepository.notFound.value');
|
||||
$message = $this->languageService->getLL('report.status.mainRepository.notFound.message');
|
||||
$severity = \TYPO3\CMS\Reports\Status::ERROR;
|
||||
} elseif ($mainRepository->getLastUpdate()->getTimestamp() < $GLOBALS['EXEC_TIME'] - 24 * 60 * 60 * 7) {
|
||||
$value = $this->languageService->getLL('report.status.mainRepository.notUpToDate.value');
|
||||
$message = $this->languageService->getLL('report.status.mainRepository.notUpToDate.message');
|
||||
$severity = \TYPO3\CMS\Reports\Status::NOTICE;
|
||||
} else {
|
||||
$value = $this->languageService->getLL('report.status.mainRepository.upToDate.value');
|
||||
$message = '';
|
||||
$severity = \TYPO3\CMS\Reports\Status::OK;
|
||||
}
|
||||
|
||||
/** @var \TYPO3\CMS\Reports\Status $status */
|
||||
$status = $this->objectManager->get(
|
||||
\TYPO3\CMS\Reports\Status::class,
|
||||
$this->languageService->getLL('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 = [];
|
||||
$existingInsecure = [];
|
||||
$loadedOutdated = [];
|
||||
$existingOutdated = [];
|
||||
foreach ($extensionInformation as $extensionKey => $information) {
|
||||
if (
|
||||
array_key_exists('terObject', $information)
|
||||
&& $information['terObject'] instanceof \TYPO3\CMS\Extensionmanager\Domain\Model\Extension
|
||||
) {
|
||||
/** @var \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $terObject */
|
||||
$terObject = $information['terObject'];
|
||||
$insecureStatus = $terObject->getReviewState();
|
||||
if ($insecureStatus === -1) {
|
||||
if (
|
||||
array_key_exists('installed', $information)
|
||||
&& $information['installed'] === true
|
||||
) {
|
||||
$loadedInsecure[] = [
|
||||
'extensionKey' => $extensionKey,
|
||||
'version' => $terObject->getVersion(),
|
||||
];
|
||||
} else {
|
||||
$existingInsecure[] = [
|
||||
'extensionKey' => $extensionKey,
|
||||
'version' => $terObject->getVersion(),
|
||||
];
|
||||
}
|
||||
} elseif ($insecureStatus === -2) {
|
||||
if (
|
||||
array_key_exists('installed', $information)
|
||||
&& $information['installed'] === true
|
||||
) {
|
||||
$loadedOutdated[] = [
|
||||
'extensionKey' => $extensionKey,
|
||||
'version' => $terObject->getVersion(),
|
||||
];
|
||||
} else {
|
||||
$existingOutdated[] = [
|
||||
'extensionKey' => $extensionKey,
|
||||
'version' => $terObject->getVersion(),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = new \stdClass();
|
||||
|
||||
if (empty($loadedInsecure)) {
|
||||
$value = $this->languageService->getLL('report.status.loadedExtensions.noInsecureExtensionLoaded.value');
|
||||
$message = '';
|
||||
$severity = \TYPO3\CMS\Reports\Status::OK;
|
||||
} else {
|
||||
$value = sprintf(
|
||||
$this->languageService->getLL('report.status.loadedExtensions.insecureExtensionLoaded.value'),
|
||||
count($loadedInsecure)
|
||||
);
|
||||
$extensionList = [];
|
||||
foreach ($loadedInsecure as $insecureExtension) {
|
||||
$extensionList[] = sprintf(
|
||||
$this->languageService->getLL('report.status.loadedExtensions.insecureExtensionLoaded.message.extension'),
|
||||
$insecureExtension['extensionKey'],
|
||||
$insecureExtension['version']
|
||||
);
|
||||
}
|
||||
$message = sprintf(
|
||||
$this->languageService->getLL('report.status.loadedExtensions.insecureExtensionLoaded.message'),
|
||||
implode('', $extensionList)
|
||||
);
|
||||
$severity = \TYPO3\CMS\Reports\Status::ERROR;
|
||||
}
|
||||
$result->loaded = $this->objectManager->get(
|
||||
\TYPO3\CMS\Reports\Status::class,
|
||||
$this->languageService->getLL('report.status.loadedExtensions.title'),
|
||||
$value,
|
||||
$message,
|
||||
$severity
|
||||
);
|
||||
|
||||
if (empty($existingInsecure)) {
|
||||
$value = $this->languageService->getLL('report.status.existingExtensions.noInsecureExtensionExists.value');
|
||||
$message = '';
|
||||
$severity = \TYPO3\CMS\Reports\Status::OK;
|
||||
} else {
|
||||
$value = sprintf(
|
||||
$this->languageService->getLL('report.status.existingExtensions.insecureExtensionExists.value'),
|
||||
count($existingInsecure)
|
||||
);
|
||||
$extensionList = [];
|
||||
foreach ($existingInsecure as $insecureExtension) {
|
||||
$extensionList[] = sprintf(
|
||||
$this->languageService->getLL('report.status.existingExtensions.insecureExtensionExists.message.extension'),
|
||||
$insecureExtension['extensionKey'],
|
||||
$insecureExtension['version']
|
||||
);
|
||||
}
|
||||
$message = sprintf(
|
||||
$this->languageService->getLL('report.status.existingExtensions.insecureExtensionExists.message'),
|
||||
implode('', $extensionList)
|
||||
);
|
||||
$severity = \TYPO3\CMS\Reports\Status::WARNING;
|
||||
}
|
||||
$result->existing = $this->objectManager->get(
|
||||
\TYPO3\CMS\Reports\Status::class,
|
||||
$this->languageService->getLL('report.status.existingExtensions.title'),
|
||||
$value,
|
||||
$message,
|
||||
$severity
|
||||
);
|
||||
|
||||
if (empty($loadedOutdated)) {
|
||||
$value = $this->languageService->getLL('report.status.loadedOutdatedExtensions.noOutdatedExtensionLoaded.value');
|
||||
$message = '';
|
||||
$severity = \TYPO3\CMS\Reports\Status::OK;
|
||||
} else {
|
||||
$value = sprintf(
|
||||
$this->languageService->getLL('report.status.loadedOutdatedExtensions.outdatedExtensionLoaded.value'),
|
||||
count($loadedOutdated)
|
||||
);
|
||||
$extensionList = [];
|
||||
foreach ($loadedOutdated as $outdatedExtension) {
|
||||
$extensionList[] = sprintf(
|
||||
$this->languageService->getLL('report.status.loadedOutdatedExtensions.outdatedExtensionLoaded.message.extension'),
|
||||
$outdatedExtension['extensionKey'],
|
||||
$outdatedExtension['version']
|
||||
);
|
||||
}
|
||||
$message = sprintf(
|
||||
$this->languageService->getLL('report.status.loadedOutdatedExtensions.outdatedExtensionLoaded.message'),
|
||||
implode('', $extensionList)
|
||||
);
|
||||
$severity = \TYPO3\CMS\Reports\Status::WARNING;
|
||||
}
|
||||
$result->loadedoutdated = $this->objectManager->get(
|
||||
\TYPO3\CMS\Reports\Status::class,
|
||||
$this->languageService->getLL('report.status.loadedOutdatedExtensions.title'),
|
||||
$value,
|
||||
$message,
|
||||
$severity
|
||||
);
|
||||
|
||||
if (empty($existingOutdated)) {
|
||||
$value = $this->languageService->getLL('report.status.existingOutdatedExtensions.noOutdatedExtensionExists.value');
|
||||
$message = '';
|
||||
$severity = \TYPO3\CMS\Reports\Status::OK;
|
||||
} else {
|
||||
$value = sprintf(
|
||||
$this->languageService->getLL('report.status.existingOutdatedExtensions.outdatedExtensionExists.value'),
|
||||
count($existingOutdated)
|
||||
);
|
||||
$extensionList = [];
|
||||
foreach ($existingOutdated as $outdatedExtension) {
|
||||
$extensionList[] = sprintf(
|
||||
$this->languageService->getLL('report.status.existingOutdatedExtensions.outdatedExtensionExists.message.extension'),
|
||||
$outdatedExtension['extensionKey'],
|
||||
$outdatedExtension['version']
|
||||
);
|
||||
}
|
||||
$message = sprintf(
|
||||
$this->languageService->getLL('report.status.existingOutdatedExtensions.outdatedExtensionExists.message'),
|
||||
implode('', $extensionList)
|
||||
);
|
||||
$severity = \TYPO3\CMS\Reports\Status::WARNING;
|
||||
}
|
||||
$result->existingoutdated = $this->objectManager->get(
|
||||
\TYPO3\CMS\Reports\Status::class,
|
||||
$this->languageService->getLL('report.status.existingOutdatedExtensions.title'),
|
||||
$value,
|
||||
$message,
|
||||
$severity
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,458 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Service;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
|
||||
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
|
||||
|
||||
/**
|
||||
* Service class for managing multiple step processes (dependencies for example)
|
||||
*/
|
||||
class ExtensionManagementService implements \TYPO3\CMS\Core\SingletonInterface
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Model\DownloadQueue
|
||||
*/
|
||||
protected $downloadQueue;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility
|
||||
*/
|
||||
protected $dependencyUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\InstallUtility
|
||||
*/
|
||||
protected $installUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility
|
||||
*/
|
||||
protected $extensionModelUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\DownloadUtility
|
||||
*/
|
||||
protected $downloadUtility;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $automaticInstallationEnabled = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $skipDependencyCheck = false;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Model\DownloadQueue $downloadQueue
|
||||
*/
|
||||
public function injectDownloadQueue(\TYPO3\CMS\Extensionmanager\Domain\Model\DownloadQueue $downloadQueue)
|
||||
{
|
||||
$this->downloadQueue = $downloadQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility
|
||||
*/
|
||||
public function injectDependencyUtility(\TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility)
|
||||
{
|
||||
$this->dependencyUtility = $dependencyUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility
|
||||
*/
|
||||
public function injectInstallUtility(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility)
|
||||
{
|
||||
$this->installUtility = $installUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility $extensionModelUtility
|
||||
*/
|
||||
public function injectExtensionModelUtility(\TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility $extensionModelUtility)
|
||||
{
|
||||
$this->extensionModelUtility = $extensionModelUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\DownloadUtility $downloadUtility
|
||||
*/
|
||||
public function injectDownloadUtility(\TYPO3\CMS\Extensionmanager\Utility\DownloadUtility $downloadUtility)
|
||||
{
|
||||
$this->downloadUtility = $downloadUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
public function markExtensionForInstallation($extensionKey)
|
||||
{
|
||||
// We have to check for dependencies of the extension first, before marking it for installation
|
||||
// because this extension might have dependencies, which need to be installed first
|
||||
$this->installUtility->reloadAvailableExtensions();
|
||||
$extension = $this->getExtension($extensionKey);
|
||||
$this->dependencyUtility->checkDependencies($extension);
|
||||
$this->downloadQueue->addExtensionToInstallQueue($extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark an extension for copy
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @param string $sourceFolder
|
||||
*/
|
||||
public function markExtensionForCopy($extensionKey, $sourceFolder)
|
||||
{
|
||||
$this->downloadQueue->addExtensionToCopyQueue($extensionKey, $sourceFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark an extension for download
|
||||
*
|
||||
* @param Extension $extension
|
||||
*/
|
||||
public function markExtensionForDownload(Extension $extension)
|
||||
{
|
||||
// We have to check for dependencies of the extension first, before marking it for download
|
||||
// because this extension might have dependencies, which need to be downloaded and installed first
|
||||
$this->dependencyUtility->checkDependencies($extension);
|
||||
if (!$this->dependencyUtility->hasDependencyErrors()) {
|
||||
$this->downloadQueue->addExtensionToQueue($extension);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Extension $extension
|
||||
*/
|
||||
public function markExtensionForUpdate(Extension $extension)
|
||||
{
|
||||
// We have to check for dependencies of the extension first, before marking it for download
|
||||
// because this extension might have dependencies, which need to be downloaded and installed first
|
||||
$this->dependencyUtility->checkDependencies($extension);
|
||||
$this->downloadQueue->addExtensionToQueue($extension, 'update');
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the dependency check for system environment (PHP, TYPO3) before extension installation
|
||||
*
|
||||
* @param bool $skipDependencyCheck
|
||||
*/
|
||||
public function setSkipDependencyCheck($skipDependencyCheck)
|
||||
{
|
||||
$this->skipDependencyCheck = $skipDependencyCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $automaticInstallationEnabled
|
||||
*/
|
||||
public function setAutomaticInstallationEnabled($automaticInstallationEnabled)
|
||||
{
|
||||
$this->automaticInstallationEnabled = (bool)$automaticInstallationEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install the extension
|
||||
*
|
||||
* @param Extension $extension
|
||||
* @return bool|array Returns FALSE if dependencies cannot be resolved, otherwise array with installation information
|
||||
*/
|
||||
public function installExtension(Extension $extension)
|
||||
{
|
||||
$this->downloadExtension($extension);
|
||||
if (!$this->checkDependencies($extension)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$downloadedDependencies = [];
|
||||
$updatedDependencies = [];
|
||||
$installQueue = [];
|
||||
|
||||
// First resolve all dependencies and the sub-dependencies until all queues are empty as new extensions might be
|
||||
// added each time
|
||||
// Extensions have to be installed in reverse order. Extensions which were added at last are dependencies of
|
||||
// earlier ones and need to be available before
|
||||
while (!$this->downloadQueue->isCopyQueueEmpty()
|
||||
|| !$this->downloadQueue->isQueueEmpty('download')
|
||||
|| !$this->downloadQueue->isQueueEmpty('update')
|
||||
) {
|
||||
// First copy all available extension
|
||||
// This might change other queues again
|
||||
$copyQueue = $this->downloadQueue->resetExtensionCopyStorage();
|
||||
if (!empty($copyQueue)) {
|
||||
$this->copyDependencies($copyQueue);
|
||||
}
|
||||
$installQueue = array_merge($this->downloadQueue->resetExtensionInstallStorage(), $installQueue);
|
||||
// Get download and update information
|
||||
$queue = $this->downloadQueue->resetExtensionQueue();
|
||||
if (!empty($queue['download'])) {
|
||||
$downloadedDependencies = array_merge($downloadedDependencies, $this->downloadDependencies($queue['download']));
|
||||
}
|
||||
$installQueue = array_merge($this->downloadQueue->resetExtensionInstallStorage(), $installQueue);
|
||||
if ($this->automaticInstallationEnabled) {
|
||||
if (!empty($queue['update'])) {
|
||||
$this->downloadDependencies($queue['update']);
|
||||
$updatedDependencies = array_merge($updatedDependencies, $this->uninstallDependenciesToBeUpdated($queue['update']));
|
||||
}
|
||||
$installQueue = array_merge($this->downloadQueue->resetExtensionInstallStorage(), $installQueue);
|
||||
}
|
||||
}
|
||||
|
||||
// If there were any dependency errors we have to abort here
|
||||
if ($this->dependencyUtility->hasDependencyErrors()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attach extension to install queue
|
||||
$this->downloadQueue->addExtensionToInstallQueue($extension);
|
||||
$installQueue += $this->downloadQueue->resetExtensionInstallStorage();
|
||||
$installedDependencies = [];
|
||||
if ($this->automaticInstallationEnabled) {
|
||||
$installedDependencies = $this->installDependencies($installQueue);
|
||||
}
|
||||
|
||||
return array_merge($downloadedDependencies, $updatedDependencies, $installedDependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unresolved dependency errors
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDependencyErrors()
|
||||
{
|
||||
return $this->dependencyUtility->getDependencyErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extensionKey
|
||||
* @return Extension
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
|
||||
*/
|
||||
public function getExtension($extensionKey)
|
||||
{
|
||||
return $this->extensionModelUtility->mapExtensionArrayToModel(
|
||||
$this->installUtility->enrichExtensionWithDetails($extensionKey)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an extension is available in the system
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @return bool
|
||||
*/
|
||||
public function isAvailable($extensionKey)
|
||||
{
|
||||
return $this->installUtility->isAvailable($extensionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extensionKey
|
||||
* @throws \TYPO3\CMS\Core\Package\Exception\InvalidPackageStateException if the package isn't available
|
||||
* @throws \TYPO3\CMS\Core\Package\Exception\InvalidPackageKeyException if an invalid package key was passed
|
||||
* @throws \TYPO3\CMS\Core\Package\Exception\InvalidPackagePathException if an invalid package path was passed
|
||||
* @throws \TYPO3\CMS\Core\Package\Exception\InvalidPackageManifestException if no extension configuration file could be found
|
||||
*/
|
||||
public function reloadPackageInformation($extensionKey)
|
||||
{
|
||||
$this->installUtility->reloadPackageInformation($extensionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download an extension
|
||||
*
|
||||
* @param Extension $extension
|
||||
*/
|
||||
protected function downloadExtension(Extension $extension)
|
||||
{
|
||||
$this->downloadMainExtension($extension);
|
||||
$this->setInExtensionRepository($extension->getExtensionKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check dependencies for an extension and its required extensions
|
||||
*
|
||||
* @param Extension $extension
|
||||
* @return bool Returns TRUE if all dependencies can be resolved, otherwise FALSE
|
||||
*/
|
||||
protected function checkDependencies(Extension $extension)
|
||||
{
|
||||
$this->dependencyUtility->setSkipDependencyCheck($this->skipDependencyCheck);
|
||||
$this->dependencyUtility->checkDependencies($extension);
|
||||
|
||||
return !$this->dependencyUtility->hasDependencyErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to the repository in an extension
|
||||
* (Initialisation/Extensions) depending on the extension
|
||||
* that is currently installed
|
||||
*
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
protected function setInExtensionRepository($extensionKey)
|
||||
{
|
||||
$paths = Extension::returnInstallPaths();
|
||||
$path = $paths[$this->downloadUtility->getDownloadPath()];
|
||||
$localExtensionStorage = $path . $extensionKey . '/Initialisation/Extensions/';
|
||||
$this->dependencyUtility->setLocalExtensionStorage($localExtensionStorage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies locally provided extensions to typo3conf/ext
|
||||
*
|
||||
* @param array $copyQueue
|
||||
*/
|
||||
protected function copyDependencies(array $copyQueue)
|
||||
{
|
||||
$installPaths = Extension::returnAllowedInstallPaths();
|
||||
foreach ($copyQueue as $extensionKey => $sourceFolder) {
|
||||
$destination = $installPaths['Local'] . $extensionKey;
|
||||
GeneralUtility::mkdir($destination);
|
||||
GeneralUtility::copyDirectory($sourceFolder . $extensionKey, $destination);
|
||||
$this->markExtensionForInstallation($extensionKey);
|
||||
$this->downloadQueue->removeExtensionFromCopyQueue($extensionKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall extensions that will be updated
|
||||
* This is not strictly necessary but cleaner all in all
|
||||
*
|
||||
* @param Extension[] $updateQueue
|
||||
* @return array
|
||||
*/
|
||||
protected function uninstallDependenciesToBeUpdated(array $updateQueue)
|
||||
{
|
||||
$resolvedDependencies = [];
|
||||
foreach ($updateQueue as $extensionToUpdate) {
|
||||
$this->installUtility->uninstall($extensionToUpdate->getExtensionKey());
|
||||
$resolvedDependencies['updated'][$extensionToUpdate->getExtensionKey()] = $extensionToUpdate;
|
||||
}
|
||||
return $resolvedDependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install dependent extensions
|
||||
*
|
||||
* @param array $installQueue
|
||||
* @return array
|
||||
*/
|
||||
protected function installDependencies(array $installQueue)
|
||||
{
|
||||
if (empty($installQueue)) {
|
||||
return [];
|
||||
}
|
||||
$this->emitWillInstallExtensionsSignal($installQueue);
|
||||
$resolvedDependencies = [];
|
||||
$this->installUtility->install(...array_keys($installQueue));
|
||||
foreach ($installQueue as $extensionKey => $_) {
|
||||
$this->emitHasInstalledExtensionSignal($extensionKey);
|
||||
if (!isset($resolvedDependencies['installed']) || !is_array($resolvedDependencies['installed'])) {
|
||||
$resolvedDependencies['installed'] = [];
|
||||
}
|
||||
$resolvedDependencies['installed'][$extensionKey] = $extensionKey;
|
||||
}
|
||||
return $resolvedDependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download dependencies
|
||||
* expects an array of extension objects to download
|
||||
*
|
||||
* @param Extension[] $downloadQueue
|
||||
* @return array
|
||||
*/
|
||||
protected function downloadDependencies(array $downloadQueue)
|
||||
{
|
||||
$resolvedDependencies = [];
|
||||
foreach ($downloadQueue as $extensionToDownload) {
|
||||
$this->downloadUtility->download($extensionToDownload);
|
||||
$this->downloadQueue->removeExtensionFromQueue($extensionToDownload);
|
||||
$resolvedDependencies['downloaded'][$extensionToDownload->getExtensionKey()] = $extensionToDownload;
|
||||
$this->markExtensionForInstallation($extensionToDownload->getExtensionKey());
|
||||
}
|
||||
return $resolvedDependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and resolve dependencies
|
||||
*
|
||||
* @param Extension $extension
|
||||
* @return array
|
||||
*/
|
||||
public function getAndResolveDependencies(Extension $extension)
|
||||
{
|
||||
$this->dependencyUtility->setSkipDependencyCheck($this->skipDependencyCheck);
|
||||
$this->dependencyUtility->checkDependencies($extension);
|
||||
$installQueue = $this->downloadQueue->getExtensionInstallStorage();
|
||||
if (is_array($installQueue) && !empty($installQueue)) {
|
||||
$installQueue = ['install' => $installQueue];
|
||||
}
|
||||
return array_merge($this->downloadQueue->getExtensionQueue(), $installQueue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the extension the user wants to install
|
||||
* This is separated from downloading the dependencies
|
||||
* as an extension is able to provide it's own dependencies
|
||||
*
|
||||
* @param Extension $extension
|
||||
*/
|
||||
public function downloadMainExtension(Extension $extension)
|
||||
{
|
||||
// The extension object has a uid if the extension is not present in the system
|
||||
// or an update of a present extension is triggered.
|
||||
if ($extension->getUid()) {
|
||||
$this->downloadUtility->download($extension);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $installQueue
|
||||
*/
|
||||
protected function emitWillInstallExtensionsSignal(array $installQueue)
|
||||
{
|
||||
$this->getSignalSlotDispatcher()->dispatch(__CLASS__, 'willInstallExtensions', [$installQueue]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extensionKey
|
||||
* @deprecated since TYPO3 v9, will be removed with TYPO3v10
|
||||
*/
|
||||
protected function emitHasInstalledExtensionSignal($extensionKey)
|
||||
{
|
||||
$this->getSignalSlotDispatcher()->dispatch(__CLASS__, 'hasInstalledExtensions', [$extensionKey]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SignalSlot dispatcher
|
||||
*
|
||||
* @return Dispatcher
|
||||
*/
|
||||
protected function getSignalSlotDispatcher()
|
||||
{
|
||||
if (!isset($this->signalSlotDispatcher)) {
|
||||
$this->signalSlotDispatcher = GeneralUtility::makeInstance(ObjectManager::class)
|
||||
->get(Dispatcher::class);
|
||||
}
|
||||
return $this->signalSlotDispatcher;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Task;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Update extension list from TER task
|
||||
* @internal This class is a specific EXT:scheduler task implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class UpdateExtensionListTask extends \TYPO3\CMS\Scheduler\Task\AbstractTask
|
||||
{
|
||||
/**
|
||||
* Public method, called by scheduler.
|
||||
*
|
||||
* @return bool TRUE on success
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
// Throws exceptions if something went wrong
|
||||
$this->updateExtensionList();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update extension list
|
||||
*
|
||||
* @TODO: Adapt to multiple repositories if the Helper can handle this
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
|
||||
*/
|
||||
protected function updateExtensionList()
|
||||
{
|
||||
/** @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager */
|
||||
$objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
|
||||
|
||||
/** @var \TYPO3\CMS\Extensionmanager\Utility\Repository\Helper $repositoryHelper */
|
||||
$repositoryHelper = $objectManager->get(\TYPO3\CMS\Extensionmanager\Utility\Repository\Helper::class);
|
||||
$repositoryHelper->updateExtList();
|
||||
|
||||
/** @var \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager $persistenceManager */
|
||||
$persistenceManager = $objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class);
|
||||
$persistenceManager->persistAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility\Connection;
|
||||
|
||||
/*
|
||||
* 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\Configuration\ExtensionConfiguration;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
|
||||
|
||||
/**
|
||||
* TER2 connection handling class for the TYPO3 Extension Manager.
|
||||
*
|
||||
* It contains methods for downloading and uploading extensions and related code
|
||||
* @internal This class is a specific TER implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class TerUtility
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $wsdlUrl;
|
||||
|
||||
/**
|
||||
* Fetches an extension from the given mirror
|
||||
*
|
||||
* @param string $extensionKey Extension Key
|
||||
* @param string $version Version to install
|
||||
* @param string $expectedMd5 Expected MD5 hash of extension file
|
||||
* @param string $mirrorUrl URL of mirror to use
|
||||
* @throws ExtensionManagerException
|
||||
* @return array T3X data
|
||||
*/
|
||||
public function fetchExtension($extensionKey, $version, $expectedMd5, $mirrorUrl)
|
||||
{
|
||||
if (
|
||||
(bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('extensionmanager', 'offlineMode')
|
||||
|| Environment::isComposerMode()
|
||||
) {
|
||||
throw new ExtensionManagerException('Extension Manager is in offline mode. No TER connection available.', 1437078620);
|
||||
}
|
||||
$extensionPath = strtolower($extensionKey);
|
||||
$mirrorUrl .= $extensionPath[0] . '/' . $extensionPath[1] . '/' . $extensionPath . '_' . $version . '.t3x';
|
||||
$t3x = \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl($mirrorUrl);
|
||||
$md5 = md5($t3x);
|
||||
if ($t3x === false) {
|
||||
throw new ExtensionManagerException(sprintf('The T3X file "%s" could not be fetched. Possible reasons: network problems, allow_url_fopen is off, cURL is not enabled in Install Tool.', $mirrorUrl), 1334426097);
|
||||
}
|
||||
if ($md5 === $expectedMd5) {
|
||||
// Fetch and return:
|
||||
$extensionData = $this->decodeExchangeData($t3x);
|
||||
} else {
|
||||
throw new ExtensionManagerException('Error: MD5 hash of downloaded file not as expected: ' . $md5 . ' != ' . $expectedMd5, 1334426098);
|
||||
}
|
||||
return $extensionData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode server data
|
||||
* This is information like the extension list, extension
|
||||
* information etc., return data after uploads (new em_conf)
|
||||
* On success, returns an array with data array and stats
|
||||
* array as key 0 and 1.
|
||||
*
|
||||
* @param string $externalData Data stream from remove server
|
||||
* @throws ExtensionManagerException
|
||||
* @return array $externalData
|
||||
* @see fetchServerData(), processRepositoryReturnData()
|
||||
*/
|
||||
public function decodeServerData($externalData)
|
||||
{
|
||||
$parts = explode(':', $externalData, 4);
|
||||
$dat = base64_decode($parts[2]);
|
||||
gzuncompress($dat);
|
||||
// compare hashes ignoring any leading whitespace. See bug #0000365.
|
||||
if (ltrim($parts[0]) == md5($dat)) {
|
||||
if ($parts[1] === 'gzcompress') {
|
||||
if (function_exists('gzuncompress')) {
|
||||
$dat = gzuncompress($dat);
|
||||
} else {
|
||||
throw new ExtensionManagerException('Decoding Error: No decompressor available for compressed content. gzuncompress() function is not available!', 1342859463);
|
||||
}
|
||||
}
|
||||
$listArr = unserialize($dat, ['allowed_classes' => false]);
|
||||
if (!is_array($listArr)) {
|
||||
throw new ExtensionManagerException('Error: Unserialized information was not an array - strange!', 1342859489);
|
||||
}
|
||||
} else {
|
||||
throw new ExtensionManagerException('Error: MD5 hashes in T3X data did not match!', 1342859505);
|
||||
}
|
||||
return $listArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes extension upload array.
|
||||
* This kind of data is when an extension is uploaded to TER
|
||||
*
|
||||
* @param string $stream Data stream
|
||||
* @throws ExtensionManagerException
|
||||
* @return array Array with result on success, otherwise an error string.
|
||||
*/
|
||||
public function decodeExchangeData($stream)
|
||||
{
|
||||
$parts = explode(':', $stream, 3);
|
||||
if ($parts[1] === 'gzcompress') {
|
||||
if (function_exists('gzuncompress')) {
|
||||
$parts[2] = gzuncompress($parts[2]);
|
||||
} else {
|
||||
throw new ExtensionManagerException('Decoding Error: No decompressor available for compressed content. gzcompress()/gzuncompress() ' . 'functions are not available!', 1344761814);
|
||||
}
|
||||
}
|
||||
if (hash_equals($parts[0], md5($parts[2]))) {
|
||||
$output = unserialize($parts[2], ['allowed_classes' => false]);
|
||||
if (!is_array($output)) {
|
||||
throw new ExtensionManagerException('Error: Content could not be unserialized to an array. Strange (since MD5 hashes match!)', 1344761938);
|
||||
}
|
||||
} else {
|
||||
throw new ExtensionManagerException('Error: MD5 mismatch. Maybe the extension file was downloaded and saved as a text file by the ' . 'browser and thereby corrupted!? (Always select "All" filetype when saving extensions)', 1344761991);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,664 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility;
|
||||
|
||||
/*
|
||||
* 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\ExtensionManagementUtility;
|
||||
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
|
||||
use TYPO3\CMS\Extensionmanager\Domain\Model\Dependency;
|
||||
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
|
||||
use TYPO3\CMS\Extensionmanager\Exception;
|
||||
|
||||
/**
|
||||
* Utility for dealing with dependencies
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class DependencyUtility implements \TYPO3\CMS\Core\SingletonInterface
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Object\ObjectManager
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
|
||||
*/
|
||||
protected $extensionRepository;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
|
||||
*/
|
||||
protected $listUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\EmConfUtility
|
||||
*/
|
||||
protected $emConfUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService
|
||||
*/
|
||||
protected $managementService;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $availableExtensions = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $localExtensionStorage = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $dependencyErrors = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $skipDependencyCheck = false;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager
|
||||
*/
|
||||
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository
|
||||
*/
|
||||
public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
|
||||
{
|
||||
$this->extensionRepository = $extensionRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility
|
||||
*/
|
||||
public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
|
||||
{
|
||||
$this->listUtility = $listUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility
|
||||
*/
|
||||
public function injectEmConfUtility(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility)
|
||||
{
|
||||
$this->emConfUtility = $emConfUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService
|
||||
*/
|
||||
public function injectManagementService(\TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService)
|
||||
{
|
||||
$this->managementService = $managementService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $localExtensionStorage
|
||||
*/
|
||||
public function setLocalExtensionStorage($localExtensionStorage)
|
||||
{
|
||||
$this->localExtensionStorage = $localExtensionStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for available extensions
|
||||
* gets available extensions from list utility if not already done
|
||||
*/
|
||||
protected function setAvailableExtensions()
|
||||
{
|
||||
$this->availableExtensions = $this->listUtility->getAvailableExtensions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $skipDependencyCheck
|
||||
*/
|
||||
public function setSkipDependencyCheck($skipDependencyCheck)
|
||||
{
|
||||
$this->skipDependencyCheck = $skipDependencyCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks dependencies for special cases (currently typo3 and php)
|
||||
*
|
||||
* @param Extension $extension
|
||||
*/
|
||||
public function checkDependencies(Extension $extension)
|
||||
{
|
||||
$this->dependencyErrors = [];
|
||||
$dependencies = $extension->getDependencies();
|
||||
foreach ($dependencies as $dependency) {
|
||||
/** @var Dependency $dependency */
|
||||
$identifier = strtolower($dependency->getIdentifier());
|
||||
try {
|
||||
if (in_array($identifier, Dependency::$specialDependencies)) {
|
||||
if (!$this->skipDependencyCheck) {
|
||||
$methodName = 'check' . ucfirst($identifier) . 'Dependency';
|
||||
$this->{$methodName}($dependency);
|
||||
}
|
||||
} else {
|
||||
if ($dependency->getType() === 'depends') {
|
||||
$this->checkExtensionDependency($dependency);
|
||||
}
|
||||
}
|
||||
} catch (Exception\UnresolvedDependencyException $e) {
|
||||
if (in_array($identifier, Dependency::$specialDependencies)) {
|
||||
$extensionKey = $extension->getExtensionKey();
|
||||
} else {
|
||||
$extensionKey = $identifier;
|
||||
}
|
||||
if (!isset($this->dependencyErrors[$extensionKey])) {
|
||||
$this->dependencyErrors[$extensionKey] = [];
|
||||
}
|
||||
$this->dependencyErrors[$extensionKey][] = [
|
||||
'code' => $e->getCode(),
|
||||
'message' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if a dependency error was found
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDependencyErrors()
|
||||
{
|
||||
return !empty($this->dependencyErrors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dependency errors
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDependencyErrors()
|
||||
{
|
||||
return $this->dependencyErrors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if current TYPO3 version fulfills extension requirements
|
||||
*
|
||||
* @param Dependency $dependency
|
||||
* @throws Exception\UnresolvedTypo3DependencyException
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkTypo3Dependency(Dependency $dependency)
|
||||
{
|
||||
$lowerCaseIdentifier = strtolower($dependency->getIdentifier());
|
||||
if ($lowerCaseIdentifier === 'typo3') {
|
||||
if (!($dependency->getLowestVersion() === '') && version_compare(VersionNumberUtility::getNumericTypo3Version(), $dependency->getLowestVersion()) === -1) {
|
||||
throw new Exception\UnresolvedTypo3DependencyException(
|
||||
'Your TYPO3 version is lower than this extension requires. It requires TYPO3 versions ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
|
||||
1399144499
|
||||
);
|
||||
}
|
||||
if (!($dependency->getHighestVersion() === '') && version_compare($dependency->getHighestVersion(), VersionNumberUtility::getNumericTypo3Version()) === -1) {
|
||||
throw new Exception\UnresolvedTypo3DependencyException(
|
||||
'Your TYPO3 version is higher than this extension requires. It requires TYPO3 versions ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
|
||||
1399144521
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Exception\UnresolvedTypo3DependencyException(
|
||||
'checkTypo3Dependency can only check TYPO3 dependencies. Found dependency with identifier "' . $dependency->getIdentifier() . '"',
|
||||
1399144551
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if current php version fulfills extension requirements
|
||||
*
|
||||
* @param Dependency $dependency
|
||||
* @throws Exception\UnresolvedPhpDependencyException
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkPhpDependency(Dependency $dependency)
|
||||
{
|
||||
$lowerCaseIdentifier = strtolower($dependency->getIdentifier());
|
||||
if ($lowerCaseIdentifier === 'php') {
|
||||
if (!($dependency->getLowestVersion() === '') && version_compare(PHP_VERSION, $dependency->getLowestVersion()) === -1) {
|
||||
throw new Exception\UnresolvedPhpDependencyException(
|
||||
'Your PHP version is lower than necessary. You need at least PHP version ' . $dependency->getLowestVersion(),
|
||||
1377977857
|
||||
);
|
||||
}
|
||||
if (!($dependency->getHighestVersion() === '') && version_compare($dependency->getHighestVersion(), PHP_VERSION) === -1) {
|
||||
throw new Exception\UnresolvedPhpDependencyException(
|
||||
'Your PHP version is higher than allowed. You can use PHP versions ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
|
||||
1377977856
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Exception\UnresolvedPhpDependencyException(
|
||||
'checkPhpDependency can only check PHP dependencies. Found dependency with identifier "' . $dependency->getIdentifier() . '"',
|
||||
1377977858
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main controlling function for checking dependencies
|
||||
* Dependency check is done in the following way:
|
||||
* - installed extension in matching version ? - return true
|
||||
* - available extension in matching version ? - mark for installation
|
||||
* - remote (TER) extension in matching version? - mark for download
|
||||
*
|
||||
* @todo handle exceptions / markForUpload
|
||||
* @param Dependency $dependency
|
||||
* @throws Exception\MissingVersionDependencyException
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkExtensionDependency(Dependency $dependency)
|
||||
{
|
||||
$extensionKey = $dependency->getIdentifier();
|
||||
$extensionIsLoaded = $this->isDependentExtensionLoaded($extensionKey);
|
||||
if ($extensionIsLoaded === true) {
|
||||
$isLoadedVersionCompatible = $this->isLoadedVersionCompatible($dependency);
|
||||
if ($isLoadedVersionCompatible === true || $this->skipDependencyCheck) {
|
||||
return true;
|
||||
}
|
||||
$extension = $this->listUtility->getExtension($extensionKey);
|
||||
$loadedVersion = $extension->getPackageMetaData()->getVersion();
|
||||
if (version_compare($loadedVersion, $dependency->getHighestVersion()) === -1) {
|
||||
try {
|
||||
$this->getExtensionFromRepository($extensionKey, $dependency);
|
||||
} catch (Exception\UnresolvedDependencyException $e) {
|
||||
throw new Exception\MissingVersionDependencyException(
|
||||
'The extension ' . $extensionKey . ' is installed in version ' . $loadedVersion
|
||||
. ' but needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion() . ' and could not be fetched from TER',
|
||||
1396302624
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Exception\MissingVersionDependencyException(
|
||||
'The extension ' . $extensionKey . ' is installed in version ' . $loadedVersion .
|
||||
' but needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
|
||||
1430561927
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$extensionIsAvailable = $this->isDependentExtensionAvailable($extensionKey);
|
||||
if ($extensionIsAvailable === true) {
|
||||
$isAvailableVersionCompatible = $this->isAvailableVersionCompatible($dependency);
|
||||
if ($isAvailableVersionCompatible) {
|
||||
$unresolvedDependencyErrors = $this->dependencyErrors;
|
||||
$this->managementService->markExtensionForInstallation($extensionKey);
|
||||
$this->dependencyErrors = array_merge($unresolvedDependencyErrors, $this->dependencyErrors);
|
||||
} else {
|
||||
$extension = $this->listUtility->getExtension($extensionKey);
|
||||
$availableVersion = $extension->getPackageMetaData()->getVersion();
|
||||
if (version_compare($availableVersion, $dependency->getHighestVersion()) === -1) {
|
||||
try {
|
||||
$this->getExtensionFromRepository($extensionKey, $dependency);
|
||||
} catch (Exception\MissingExtensionDependencyException $e) {
|
||||
if (!$this->skipDependencyCheck) {
|
||||
throw new Exception\MissingVersionDependencyException(
|
||||
'The extension ' . $extensionKey . ' is available in version ' . $availableVersion
|
||||
. ' but is needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion() . ' and could not be fetched from TER',
|
||||
1430560390
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!$this->skipDependencyCheck) {
|
||||
throw new Exception\MissingVersionDependencyException(
|
||||
'The extension ' . $extensionKey . ' is available in version ' . $availableVersion
|
||||
. ' but is needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
|
||||
1430562374
|
||||
);
|
||||
}
|
||||
// Dependency check is skipped and the local version has to be installed
|
||||
$this->managementService->markExtensionForInstallation($extensionKey);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$unresolvedDependencyErrors = $this->dependencyErrors;
|
||||
$this->getExtensionFromRepository($extensionKey, $dependency);
|
||||
$this->dependencyErrors = array_merge($unresolvedDependencyErrors, $this->dependencyErrors);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an extension from a repository
|
||||
* (might be in the extension itself or the TER)
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @param Dependency $dependency
|
||||
* @throws Exception\UnresolvedDependencyException
|
||||
*/
|
||||
protected function getExtensionFromRepository($extensionKey, Dependency $dependency)
|
||||
{
|
||||
if (!$this->getExtensionFromInExtensionRepository($extensionKey)) {
|
||||
$this->getExtensionFromTer($extensionKey, $dependency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an extension from the in extension repository
|
||||
* (the local extension storage)
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @return bool
|
||||
*/
|
||||
protected function getExtensionFromInExtensionRepository($extensionKey)
|
||||
{
|
||||
if ($this->localExtensionStorage !== '' && is_dir($this->localExtensionStorage)) {
|
||||
$extList = \TYPO3\CMS\Core\Utility\GeneralUtility::get_dirs($this->localExtensionStorage);
|
||||
if (in_array($extensionKey, $extList)) {
|
||||
$this->managementService->markExtensionForCopy($extensionKey, $this->localExtensionStorage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles checks to find a compatible extension version from TER to fulfill given dependency
|
||||
*
|
||||
* @todo unit tests
|
||||
* @param string $extensionKey
|
||||
* @param Dependency $dependency
|
||||
* @throws Exception\UnresolvedDependencyException
|
||||
*/
|
||||
protected function getExtensionFromTer($extensionKey, Dependency $dependency)
|
||||
{
|
||||
$isExtensionDownloadableFromTer = $this->isExtensionDownloadableFromTer($extensionKey);
|
||||
if (!$isExtensionDownloadableFromTer) {
|
||||
if (!$this->skipDependencyCheck) {
|
||||
if ($this->extensionRepository->countAll() > 0) {
|
||||
throw new Exception\MissingExtensionDependencyException(
|
||||
'The extension ' . $extensionKey . ' is not available from TER.',
|
||||
1399161266
|
||||
);
|
||||
}
|
||||
throw new Exception\MissingExtensionDependencyException(
|
||||
'The extension ' . $extensionKey . ' could not be checked. Please update your Extension-List from TYPO3 Extension Repository (TER).',
|
||||
1430580308
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$isDownloadableVersionCompatible = $this->isDownloadableVersionCompatible($dependency);
|
||||
if (!$isDownloadableVersionCompatible) {
|
||||
if (!$this->skipDependencyCheck) {
|
||||
throw new Exception\MissingVersionDependencyException(
|
||||
'No compatible version found for extension ' . $extensionKey,
|
||||
1399161284
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$latestCompatibleExtensionByIntegerVersionDependency = $this->getLatestCompatibleExtensionByIntegerVersionDependency($dependency);
|
||||
if (!$latestCompatibleExtensionByIntegerVersionDependency instanceof Extension) {
|
||||
if (!$this->skipDependencyCheck) {
|
||||
throw new Exception\MissingExtensionDependencyException(
|
||||
'Could not resolve dependency for "' . $dependency->getIdentifier() . '"',
|
||||
1399161302
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isDependentExtensionLoaded($extensionKey)) {
|
||||
$this->managementService->markExtensionForUpdate($latestCompatibleExtensionByIntegerVersionDependency);
|
||||
} else {
|
||||
$this->managementService->markExtensionForDownload($latestCompatibleExtensionByIntegerVersionDependency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extensionKey
|
||||
* @return bool
|
||||
*/
|
||||
protected function isDependentExtensionLoaded($extensionKey)
|
||||
{
|
||||
return ExtensionManagementUtility::isLoaded($extensionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dependency $dependency
|
||||
* @return bool
|
||||
*/
|
||||
protected function isLoadedVersionCompatible(Dependency $dependency)
|
||||
{
|
||||
$extensionVersion = ExtensionManagementUtility::getExtensionVersion($dependency->getIdentifier());
|
||||
return $this->isVersionCompatible($extensionVersion, $dependency);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $version
|
||||
* @param Dependency $dependency
|
||||
* @return bool
|
||||
*/
|
||||
protected function isVersionCompatible($version, Dependency $dependency)
|
||||
{
|
||||
if (!($dependency->getLowestVersion() === '') && version_compare($version, $dependency->getLowestVersion()) === -1) {
|
||||
return false;
|
||||
}
|
||||
if (!($dependency->getHighestVersion() === '') && version_compare($dependency->getHighestVersion(), $version) === -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the needed extension is available
|
||||
* (not necessarily installed, but present in system)
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @return bool
|
||||
*/
|
||||
protected function isDependentExtensionAvailable($extensionKey)
|
||||
{
|
||||
$this->setAvailableExtensions();
|
||||
return array_key_exists($extensionKey, $this->availableExtensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the available version is compatible
|
||||
*
|
||||
* @param Dependency $dependency
|
||||
* @return bool
|
||||
*/
|
||||
protected function isAvailableVersionCompatible(Dependency $dependency)
|
||||
{
|
||||
$this->setAvailableExtensions();
|
||||
$extensionData = $this->emConfUtility->includeEmConf($this->availableExtensions[$dependency->getIdentifier()]);
|
||||
return $this->isVersionCompatible($extensionData['version'], $dependency);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a ter extension with $extensionKey exists
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @return bool
|
||||
*/
|
||||
protected function isExtensionDownloadableFromTer($extensionKey)
|
||||
{
|
||||
return $this->extensionRepository->countByExtensionKey($extensionKey) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a compatible version of the extension exists in TER
|
||||
*
|
||||
* @param Dependency $dependency
|
||||
* @return bool
|
||||
*/
|
||||
protected function isDownloadableVersionCompatible(Dependency $dependency)
|
||||
{
|
||||
$versions = $this->getLowestAndHighestIntegerVersions($dependency);
|
||||
$count = $this->extensionRepository->countByVersionRangeAndExtensionKey(
|
||||
$dependency->getIdentifier(),
|
||||
$versions['lowestIntegerVersion'],
|
||||
$versions['highestIntegerVersion']
|
||||
);
|
||||
return !empty($count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest compatible version of an extension that's
|
||||
* compatible with the current core and PHP version.
|
||||
*
|
||||
* @param iterable $extensions
|
||||
* @return Extension|null
|
||||
*/
|
||||
protected function getCompatibleExtension(iterable $extensions): ?Extension
|
||||
{
|
||||
foreach ($extensions as $extension) {
|
||||
/** @var Extension $extension */
|
||||
$this->checkDependencies($extension);
|
||||
$extensionKey = $extension->getExtensionKey();
|
||||
|
||||
if (isset($this->dependencyErrors[$extensionKey])) {
|
||||
// reset dependencyErrors and continue with next version
|
||||
unset($this->dependencyErrors[$extensionKey]);
|
||||
continue;
|
||||
}
|
||||
|
||||
return $extension;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest compatible version of an extension that
|
||||
* fulfills the given dependency from TER
|
||||
*
|
||||
* @param Dependency $dependency
|
||||
* @return Extension
|
||||
*/
|
||||
protected function getLatestCompatibleExtensionByIntegerVersionDependency(Dependency $dependency)
|
||||
{
|
||||
$versions = $this->getLowestAndHighestIntegerVersions($dependency);
|
||||
$compatibleDataSets = $this->extensionRepository->findByVersionRangeAndExtensionKeyOrderedByVersion(
|
||||
$dependency->getIdentifier(),
|
||||
$versions['lowestIntegerVersion'],
|
||||
$versions['highestIntegerVersion']
|
||||
);
|
||||
return $this->getCompatibleExtension($compatibleDataSets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of lowest and highest version of dependency as integer
|
||||
*
|
||||
* @param Dependency $dependency
|
||||
* @return array
|
||||
*/
|
||||
protected function getLowestAndHighestIntegerVersions(Dependency $dependency)
|
||||
{
|
||||
$lowestVersion = $dependency->getLowestVersion();
|
||||
$lowestVersionInteger = $lowestVersion ? VersionNumberUtility::convertVersionNumberToInteger($lowestVersion) : 0;
|
||||
$highestVersion = $dependency->getHighestVersion();
|
||||
$highestVersionInteger = $highestVersion ? VersionNumberUtility::convertVersionNumberToInteger($highestVersion) : 0;
|
||||
return [
|
||||
'lowestIntegerVersion' => $lowestVersionInteger,
|
||||
'highestIntegerVersion' => $highestVersionInteger
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extensionKey
|
||||
* @return array
|
||||
*/
|
||||
public function findInstalledExtensionsThatDependOnMe($extensionKey)
|
||||
{
|
||||
$availableAndInstalledExtensions = $this->listUtility->getAvailableAndInstalledExtensionsWithAdditionalInformation();
|
||||
$dependentExtensions = [];
|
||||
foreach ($availableAndInstalledExtensions as $availableAndInstalledExtensionKey => $availableAndInstalledExtension) {
|
||||
if (isset($availableAndInstalledExtension['installed']) && $availableAndInstalledExtension['installed'] === true) {
|
||||
if (is_array($availableAndInstalledExtension['constraints']) && is_array($availableAndInstalledExtension['constraints']['depends']) && array_key_exists($extensionKey, $availableAndInstalledExtension['constraints']['depends'])) {
|
||||
$dependentExtensions[] = $availableAndInstalledExtensionKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $dependentExtensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extensions (out of a given list) that are suitable for the current TYPO3 version
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array $extensions List of extensions to check
|
||||
* @return array List of extensions suitable for current TYPO3 version
|
||||
*/
|
||||
public function getExtensionsSuitableForTypo3Version($extensions)
|
||||
{
|
||||
$suitableExtensions = [];
|
||||
/** @var Extension $extension */
|
||||
foreach ($extensions as $extension) {
|
||||
/** @var Dependency $dependency */
|
||||
foreach ($extension->getDependencies() as $dependency) {
|
||||
if ($dependency->getIdentifier() === 'typo3') {
|
||||
try {
|
||||
if ($this->checkTypo3Dependency($dependency)) {
|
||||
$suitableExtensions[] = $extension;
|
||||
}
|
||||
} catch (Exception\UnresolvedTypo3DependencyException $e) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $suitableExtensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of various extensions in various versions and returns
|
||||
* a filtered list containing the extension-version combination with
|
||||
* the highest version number.
|
||||
*
|
||||
* @param Extension[] $extensions
|
||||
* @param bool $showUnsuitable
|
||||
*
|
||||
* @return \TYPO3\CMS\Extensionmanager\Domain\Model\Extension[]
|
||||
*/
|
||||
public function filterYoungestVersionOfExtensionList(array $extensions, $showUnsuitable)
|
||||
{
|
||||
if (!$showUnsuitable) {
|
||||
$extensions = $this->getExtensionsSuitableForTypo3Version($extensions);
|
||||
}
|
||||
$filteredExtensions = [];
|
||||
foreach ($extensions as $extension) {
|
||||
$extensionKey = $extension->getExtensionKey();
|
||||
if (!array_key_exists($extensionKey, $filteredExtensions)) {
|
||||
$filteredExtensions[$extensionKey] = $extension;
|
||||
continue;
|
||||
}
|
||||
$currentVersion = $filteredExtensions[$extensionKey]->getVersion();
|
||||
$newVersion = $extension->getVersion();
|
||||
if (version_compare($newVersion, $currentVersion, '>')) {
|
||||
$filteredExtensions[$extensionKey] = $extension;
|
||||
}
|
||||
}
|
||||
return $filteredExtensions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Utility for Downloading Extensions
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class DownloadUtility implements \TYPO3\CMS\Core\SingletonInterface
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility
|
||||
*/
|
||||
protected $terUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\Repository\Helper
|
||||
*/
|
||||
protected $repositoryHelper;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $downloadPath = 'Local';
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
|
||||
*/
|
||||
protected $fileHandlingUtility;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility $terUtility
|
||||
*/
|
||||
public function injectTerUtility(\TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility $terUtility)
|
||||
{
|
||||
$this->terUtility = $terUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\Repository\Helper $repositoryHelper
|
||||
*/
|
||||
public function injectRepositoryHelper(\TYPO3\CMS\Extensionmanager\Utility\Repository\Helper $repositoryHelper)
|
||||
{
|
||||
$this->repositoryHelper = $repositoryHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility
|
||||
*/
|
||||
public function injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility)
|
||||
{
|
||||
$this->fileHandlingUtility = $fileHandlingUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download an extension
|
||||
*
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension
|
||||
*/
|
||||
public function download(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension)
|
||||
{
|
||||
$mirrorUrl = $this->repositoryHelper->getMirrors()->getMirrorUrl();
|
||||
$fetchedExtension = $this->terUtility->fetchExtension($extension->getExtensionKey(), $extension->getVersion(), $extension->getMd5hash(), $mirrorUrl);
|
||||
if (isset($fetchedExtension['extKey']) && !empty($fetchedExtension['extKey']) && is_string($fetchedExtension['extKey'])) {
|
||||
$this->fileHandlingUtility->unpackExtensionFromExtensionDataArray($fetchedExtension, $extension, $this->getDownloadPath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the download path
|
||||
*
|
||||
* @param string $downloadPath
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
|
||||
*/
|
||||
public function setDownloadPath($downloadPath)
|
||||
{
|
||||
if (!in_array($downloadPath, \TYPO3\CMS\Extensionmanager\Domain\Model\Extension::returnAllowedInstallTypes())) {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException($downloadPath . ' not in allowed download paths', 1344766387);
|
||||
}
|
||||
$this->downloadPath = $downloadPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the download path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDownloadPath()
|
||||
{
|
||||
return $this->downloadPath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility;
|
||||
|
||||
/*
|
||||
* 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\Core\Environment;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
|
||||
/**
|
||||
* Utility for dealing with ext_emconf
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class EmConfUtility implements SingletonInterface
|
||||
{
|
||||
/**
|
||||
* Returns the $EM_CONF array from an extensions ext_emconf.php file
|
||||
*
|
||||
* @param array $extension Extension information array
|
||||
* @return array|bool EMconf array values or false if no ext_emconf.php found.
|
||||
*/
|
||||
public function includeEmConf(array $extension)
|
||||
{
|
||||
$_EXTKEY = $extension['key'];
|
||||
$path = Environment::getPublicPath() . '/' . $extension['siteRelPath'] . 'ext_emconf.php';
|
||||
$EM_CONF = null;
|
||||
if (file_exists($path)) {
|
||||
include $path;
|
||||
if (is_array($EM_CONF[$_EXTKEY])) {
|
||||
return $EM_CONF[$_EXTKEY];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the content for the ext_emconf.php file
|
||||
* Sets dependencies from TER data if any
|
||||
*
|
||||
* @param array $extensionData
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension Extension object from TER data
|
||||
* @return string
|
||||
* @internal
|
||||
*/
|
||||
public function constructEmConf(array $extensionData, \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension = null)
|
||||
{
|
||||
if (is_object($extension) && empty($extensionData['EM_CONF']['constraints'])) {
|
||||
$extensionData['EM_CONF']['constraints'] = unserialize($extension->getSerializedDependencies(), ['allowed_classes' => false]);
|
||||
}
|
||||
$emConf = $this->fixEmConf($extensionData['EM_CONF']);
|
||||
$emConf = var_export($emConf, true);
|
||||
$code = '<?php
|
||||
|
||||
/***************************************************************
|
||||
* Extension Manager/Repository config file for ext "' . $extensionData['extKey'] . '".
|
||||
*
|
||||
* Auto generated ' . date('d-m-Y H:i') . '
|
||||
*
|
||||
* Manual updates:
|
||||
* Only the data in the array - everything else is removed by next
|
||||
* writing. "version" and "dependencies" must not be touched!
|
||||
***************************************************************/
|
||||
|
||||
$EM_CONF[$_EXTKEY] = ' . $emConf . ';
|
||||
|
||||
';
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix the em conf - Converts old / ter em_conf format to new format
|
||||
*
|
||||
* @param array $emConf
|
||||
* @return array
|
||||
*/
|
||||
public function fixEmConf(array $emConf)
|
||||
{
|
||||
if (
|
||||
!isset($emConf['constraints']) || !isset($emConf['constraints']['depends'])
|
||||
|| !isset($emConf['constraints']['conflicts']) || !isset($emConf['constraints']['suggests'])
|
||||
) {
|
||||
if (!isset($emConf['constraints']) || !isset($emConf['constraints']['depends'])) {
|
||||
$emConf['constraints']['depends'] = $this->stringToDependency($emConf['dependencies'] ?? '');
|
||||
if (isset($emConf['PHP_version']) && (string)$emConf['PHP_version'] !== '') {
|
||||
$emConf['constraints']['depends']['php'] = $emConf['PHP_version'];
|
||||
}
|
||||
if (isset($emConf['TYPO3_version']) && (string)$emConf['TYPO3_version'] !== '') {
|
||||
$emConf['constraints']['depends']['typo3'] = $emConf['TYPO3_version'];
|
||||
}
|
||||
}
|
||||
if (!isset($emConf['constraints']) || !isset($emConf['constraints']['conflicts'])) {
|
||||
$emConf['constraints']['conflicts'] = $this->stringToDependency($emConf['conflicts'] ?? '');
|
||||
}
|
||||
if (!isset($emConf['constraints']) || !isset($emConf['constraints']['suggests'])) {
|
||||
$emConf['constraints']['suggests'] = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Remove TER v1-style entries
|
||||
unset($emConf['dependencies']);
|
||||
unset($emConf['conflicts']);
|
||||
unset($emConf['suggests']);
|
||||
unset($emConf['private']);
|
||||
unset($emConf['download_password']);
|
||||
unset($emConf['TYPO3_version']);
|
||||
unset($emConf['PHP_version']);
|
||||
unset($emConf['internal']);
|
||||
unset($emConf['module']);
|
||||
unset($emConf['loadOrder']);
|
||||
unset($emConf['lockType']);
|
||||
unset($emConf['shy']);
|
||||
unset($emConf['priority']);
|
||||
unset($emConf['modify_tables']);
|
||||
unset($emConf['CGLcompliance']);
|
||||
unset($emConf['CGLcompliance_note']);
|
||||
|
||||
return $emConf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the passed dependency is TER-style (string)or
|
||||
* TER2-style (array)and returns a single string for displaying the
|
||||
* dependencies.
|
||||
*
|
||||
* It leaves out all version numbers and the "php" and "typo3" dependencies,
|
||||
* as they are implicit and of no interest without the version number.
|
||||
*
|
||||
* @param mixed $dependency Either a string or an array listing dependencies.
|
||||
* @return array A simple dependency list for display
|
||||
*/
|
||||
public function stringToDependency($dependency)
|
||||
{
|
||||
$constraint = [];
|
||||
if (is_string($dependency) && $dependency !== '') {
|
||||
$dependency = explode(',', $dependency);
|
||||
foreach ($dependency as $v) {
|
||||
$constraint[$v] = '';
|
||||
}
|
||||
}
|
||||
return $constraint;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Utility for dealing with extension model related helper functions
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class ExtensionModelUtility
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Object\ObjectManager
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager
|
||||
*/
|
||||
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a legacy extension array to an object
|
||||
*
|
||||
* @param array $extensionArray
|
||||
* @return \TYPO3\CMS\Extensionmanager\Domain\Model\Extension
|
||||
*/
|
||||
public function mapExtensionArrayToModel(array $extensionArray)
|
||||
{
|
||||
/** @var \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension */
|
||||
$extension = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension::class);
|
||||
$extension->setExtensionKey($extensionArray['key']);
|
||||
if (isset($extensionArray['version'])) {
|
||||
$extension->setVersion($extensionArray['version']);
|
||||
}
|
||||
if (isset($extensionArray['constraints'])) {
|
||||
$extension->setDependencies($this->convertDependenciesToObjects(serialize($extensionArray['constraints'])));
|
||||
}
|
||||
return $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts string dependencies to an object storage of dependencies
|
||||
*
|
||||
* @param string $dependencies
|
||||
* @return \SplObjectStorage
|
||||
*/
|
||||
public function convertDependenciesToObjects($dependencies)
|
||||
{
|
||||
$dependenciesObject = new \SplObjectStorage();
|
||||
$unserializedDependencies = unserialize($dependencies, ['allowed_classes' => false]);
|
||||
if (!is_array($unserializedDependencies)) {
|
||||
return $dependenciesObject;
|
||||
}
|
||||
foreach ($unserializedDependencies as $dependencyType => $dependencyValues) {
|
||||
// Dependencies might be given as empty string, e.g. conflicts => ''
|
||||
if (!is_array($dependencyValues)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($dependencyValues as $dependency => $versions) {
|
||||
if ($dependencyType && $dependency) {
|
||||
$versionNumbers = \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionsStringToVersionNumbers($versions);
|
||||
$lowest = $versionNumbers[0];
|
||||
if (count($versionNumbers) === 2) {
|
||||
$highest = $versionNumbers[1];
|
||||
} else {
|
||||
$highest = '';
|
||||
}
|
||||
/** @var \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependencyObject */
|
||||
$dependencyObject = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Domain\Model\Dependency::class);
|
||||
$dependencyObject->setType($dependencyType);
|
||||
// dynamically migrate 'cms' dependency to 'core' dependency
|
||||
// see also \TYPO3\CMS\Core\Package\Package::getPackageMetaData
|
||||
$dependencyObject->setIdentifier($dependency === 'cms' ? 'core' : $dependency);
|
||||
$dependencyObject->setLowestVersion($lowest);
|
||||
$dependencyObject->setHighestVersion($highest);
|
||||
$dependenciesObject->attach($dependencyObject);
|
||||
unset($dependencyObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $dependenciesObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,523 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility;
|
||||
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Exception\Archive\ExtractException;
|
||||
use TYPO3\CMS\Core\Service\Archive\ZipService;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\PathUtility;
|
||||
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
|
||||
use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Utility for dealing with files and folders
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\EmConfUtility
|
||||
*/
|
||||
protected $emConfUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\InstallUtility
|
||||
*/
|
||||
protected $installUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Localization\LanguageService
|
||||
*/
|
||||
protected $languageService;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility
|
||||
*/
|
||||
public function injectEmConfUtility(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility)
|
||||
{
|
||||
$this->emConfUtility = $emConfUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility
|
||||
*/
|
||||
public function injectInstallUtility(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility)
|
||||
{
|
||||
$this->installUtility = $installUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Core\Localization\LanguageService $languageService
|
||||
*/
|
||||
public function injectLanguageService(\TYPO3\CMS\Core\Localization\LanguageService $languageService)
|
||||
{
|
||||
$this->languageService = $languageService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize method - loads language file
|
||||
*/
|
||||
public function initializeObject()
|
||||
{
|
||||
$this->languageService->includeLLFile('EXT:extensionmanager/Resources/Private/Language/locallang.xlf');
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack an extension in t3x data format and write files
|
||||
*
|
||||
* @param array $extensionData
|
||||
* @param Extension $extension
|
||||
* @param string $pathType
|
||||
*/
|
||||
public function unpackExtensionFromExtensionDataArray(array $extensionData, Extension $extension = null, $pathType = 'Local')
|
||||
{
|
||||
$extensionDir = $this->makeAndClearExtensionDir($extensionData['extKey'], $pathType);
|
||||
$files = $this->extractFilesArrayFromExtensionData($extensionData);
|
||||
$directories = $this->extractDirectoriesFromExtensionData($files);
|
||||
$files = array_diff_key($files, array_flip($directories));
|
||||
$this->createDirectoriesForExtensionFiles($directories, $extensionDir);
|
||||
$this->writeExtensionFiles($files, $extensionDir);
|
||||
$this->writeEmConfToFile($extensionData, $extensionDir, $extension);
|
||||
$this->reloadPackageInformation($extensionData['extKey']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract needed directories from given extensionDataFilesArray
|
||||
*
|
||||
* @param array $files
|
||||
* @return array
|
||||
*/
|
||||
protected function extractDirectoriesFromExtensionData(array $files)
|
||||
{
|
||||
$directories = [];
|
||||
foreach ($files as $filePath => $file) {
|
||||
preg_match('/(.*)\\//', $filePath, $matches);
|
||||
if (!empty($matches[0])) {
|
||||
$directories[] = $matches[0];
|
||||
}
|
||||
}
|
||||
return array_unique($directories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "FILES" part from the data array
|
||||
*
|
||||
* @param array $extensionData
|
||||
* @return mixed
|
||||
*/
|
||||
protected function extractFilesArrayFromExtensionData(array $extensionData)
|
||||
{
|
||||
return $extensionData['FILES'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops over an array of directories and creates them in the given root path
|
||||
* It also creates nested directory structures
|
||||
*
|
||||
* @param array $directories
|
||||
* @param string $rootPath
|
||||
*/
|
||||
protected function createDirectoriesForExtensionFiles(array $directories, $rootPath)
|
||||
{
|
||||
foreach ($directories as $directory) {
|
||||
$this->createNestedDirectory($rootPath . $directory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for utility method to create directory recusively
|
||||
*
|
||||
* @param string $directory Absolute path
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
protected function createNestedDirectory($directory)
|
||||
{
|
||||
try {
|
||||
GeneralUtility::mkdir_deep($directory);
|
||||
} catch (\RuntimeException $exception) {
|
||||
throw new ExtensionManagerException(
|
||||
sprintf($this->languageService->getLL('fileHandling.couldNotCreateDirectory'), $this->getRelativePath($directory)),
|
||||
1337280416
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops over an array of files and writes them to the given rootPath
|
||||
*
|
||||
* @param array $files
|
||||
* @param string $rootPath
|
||||
*/
|
||||
protected function writeExtensionFiles(array $files, $rootPath)
|
||||
{
|
||||
foreach ($files as $file) {
|
||||
GeneralUtility::writeFile($rootPath . $file['name'], $file['content']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the current extension of $type and creates the base folder for
|
||||
* the new one (which is going to be imported)
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @param string $pathType Extension installation scope (Local,Global,System)
|
||||
* @throws ExtensionManagerException
|
||||
* @return string
|
||||
*/
|
||||
protected function makeAndClearExtensionDir($extensionKey, $pathType = 'Local')
|
||||
{
|
||||
$extDirPath = $this->getExtensionDir($extensionKey, $pathType);
|
||||
if (is_dir($extDirPath)) {
|
||||
$this->removeDirectory($extDirPath);
|
||||
}
|
||||
$this->addDirectory($extDirPath);
|
||||
|
||||
return $extDirPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the installation directory for an extension depending on the installation scope
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @param string $pathType Extension installation scope (Local,Global,System)
|
||||
* @return string
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
public function getExtensionDir($extensionKey, $pathType = 'Local')
|
||||
{
|
||||
$paths = Extension::returnInstallPaths();
|
||||
$path = $paths[$pathType] ?? '';
|
||||
if (!$path || !is_dir($path) || !$extensionKey) {
|
||||
throw new ExtensionManagerException(
|
||||
sprintf($this->languageService->getLL('fileHandling.installPathWasNoDirectory'), $this->getRelativePath($path)),
|
||||
1337280417
|
||||
);
|
||||
}
|
||||
|
||||
return $path . $extensionKey . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add specified directory
|
||||
*
|
||||
* @param string $extDirPath
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
protected function addDirectory($extDirPath)
|
||||
{
|
||||
GeneralUtility::mkdir($extDirPath);
|
||||
if (!is_dir($extDirPath)) {
|
||||
throw new ExtensionManagerException(
|
||||
sprintf($this->languageService->getLL('fileHandling.couldNotCreateDirectory'), $this->getRelativePath($extDirPath)),
|
||||
1337280418
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates directories configured in ext_emconf.php if not already present
|
||||
*
|
||||
* @param array $extension
|
||||
*/
|
||||
public function ensureConfiguredDirectoriesExist(array $extension)
|
||||
{
|
||||
foreach ($this->getAbsolutePathsToConfiguredDirectories($extension) as $directory) {
|
||||
if (!$this->directoryExists($directory)) {
|
||||
$this->createNestedDirectory($directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper method for directory existence check
|
||||
*
|
||||
* @param string $directory
|
||||
* @return bool
|
||||
*/
|
||||
protected function directoryExists($directory)
|
||||
{
|
||||
return is_dir($directory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks configuration and returns an array of absolute paths that should be created
|
||||
*
|
||||
* @param array $extension
|
||||
* @return array
|
||||
*/
|
||||
protected function getAbsolutePathsToConfiguredDirectories(array $extension)
|
||||
{
|
||||
$requestedDirectories = [];
|
||||
$requestUploadFolder = isset($extension['uploadfolder']) ? (bool)$extension['uploadfolder'] : false;
|
||||
if ($requestUploadFolder) {
|
||||
$requestedDirectories[] = $this->getAbsolutePath($this->getPathToUploadFolder($extension));
|
||||
}
|
||||
|
||||
$requestCreateDirectories = empty($extension['createDirs']) ? false : (string)$extension['createDirs'];
|
||||
if ($requestCreateDirectories) {
|
||||
foreach (GeneralUtility::trimExplode(',', $extension['createDirs']) as $directoryToCreate) {
|
||||
$requestedDirectories[] = $this->getAbsolutePath($directoryToCreate);
|
||||
}
|
||||
}
|
||||
|
||||
return $requestedDirectories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload folders always reside in “uploads/tx_[extKey-with-no-underscore]”
|
||||
*
|
||||
* @param array $extension
|
||||
* @return string
|
||||
*/
|
||||
protected function getPathToUploadFolder($extension)
|
||||
{
|
||||
return 'uploads/tx_' . str_replace('_', '', $extension['key']) . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove specified directory
|
||||
*
|
||||
* @param string $extDirPath
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
public function removeDirectory($extDirPath)
|
||||
{
|
||||
$extDirPath = GeneralUtility::fixWindowsFilePath($extDirPath);
|
||||
$extensionPathWithoutTrailingSlash = rtrim($extDirPath, '/');
|
||||
if (is_link($extensionPathWithoutTrailingSlash) && !Environment::isWindows()) {
|
||||
$result = unlink($extensionPathWithoutTrailingSlash);
|
||||
} else {
|
||||
$result = GeneralUtility::rmdir($extDirPath, true);
|
||||
}
|
||||
if ($result === false) {
|
||||
throw new ExtensionManagerException(
|
||||
sprintf($this->languageService->getLL('fileHandling.couldNotRemoveDirectory'), $this->getRelativePath($extDirPath)),
|
||||
1337280415
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs emConf and writes it to corresponding file
|
||||
* In case the file has been extracted already, the properties of the meta data take precedence but are merged with the present ext_emconf.php
|
||||
*
|
||||
* @param array $extensionData
|
||||
* @param string $rootPath
|
||||
* @param Extension $extension
|
||||
*/
|
||||
protected function writeEmConfToFile(array $extensionData, $rootPath, Extension $extension = null)
|
||||
{
|
||||
$emConfFileData = [];
|
||||
if (file_exists($rootPath . 'ext_emconf.php')) {
|
||||
$emConfFileData = $this->emConfUtility->includeEmConf(
|
||||
[
|
||||
'key' => $extensionData['extKey'],
|
||||
'siteRelPath' => PathUtility::stripPathSitePrefix($rootPath)
|
||||
]
|
||||
);
|
||||
}
|
||||
$extensionData['EM_CONF'] = array_replace_recursive($emConfFileData, $extensionData['EM_CONF']);
|
||||
$emConfContent = $this->emConfUtility->constructEmConf($extensionData, $extension);
|
||||
GeneralUtility::writeFile($rootPath . 'ext_emconf.php', $emConfContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the given path a valid path for extension installation
|
||||
*
|
||||
* @param string $path the absolute (!) path in question
|
||||
* @return bool
|
||||
*/
|
||||
public function isValidExtensionPath($path)
|
||||
{
|
||||
$allowedPaths = Extension::returnAllowedInstallPaths();
|
||||
foreach ($allowedPaths as $allowedPath) {
|
||||
if (GeneralUtility::isFirstPartOfStr($path, $allowedPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns absolute path
|
||||
*
|
||||
* @param string $relativePath
|
||||
* @throws ExtensionManagerException
|
||||
* @return string
|
||||
*/
|
||||
protected function getAbsolutePath($relativePath)
|
||||
{
|
||||
$absolutePath = GeneralUtility::getFileAbsFileName(GeneralUtility::resolveBackPath(Environment::getPublicPath() . '/' . $relativePath));
|
||||
if (empty($absolutePath)) {
|
||||
throw new ExtensionManagerException('Illegal relative path given', 1350742864);
|
||||
}
|
||||
return $absolutePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns relative path
|
||||
*
|
||||
* @param string $absolutePath
|
||||
* @return string
|
||||
*/
|
||||
protected function getRelativePath($absolutePath)
|
||||
{
|
||||
return PathUtility::stripPathSitePrefix($absolutePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extension path for an available or installed extension
|
||||
*
|
||||
* @param string $extension
|
||||
* @return string
|
||||
*/
|
||||
public function getAbsoluteExtensionPath($extension)
|
||||
{
|
||||
$extension = $this->installUtility->enrichExtensionWithDetails($extension);
|
||||
$absolutePath = $this->getAbsolutePath($extension['siteRelPath']);
|
||||
return $absolutePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version of an available or installed extension
|
||||
*
|
||||
* @param string $extension
|
||||
* @return string
|
||||
*/
|
||||
public function getExtensionVersion($extension)
|
||||
{
|
||||
$extensionData = $this->installUtility->enrichExtensionWithDetails($extension);
|
||||
$version = $extensionData['version'];
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a zip file from an extension
|
||||
*
|
||||
* @param array $extension
|
||||
* @return string Name and path of create zip file
|
||||
*/
|
||||
public function createZipFileFromExtension($extension)
|
||||
{
|
||||
$extensionPath = $this->getAbsoluteExtensionPath($extension);
|
||||
|
||||
// Add trailing slash to the extension path, getAllFilesAndFoldersInPath explicitly requires that.
|
||||
$extensionPath = PathUtility::sanitizeTrailingSeparator($extensionPath);
|
||||
|
||||
$version = $this->getExtensionVersion($extension);
|
||||
if (empty($version)) {
|
||||
$version = '0.0.0';
|
||||
}
|
||||
|
||||
$temporaryPath = Environment::getVarPath() . '/transient/';
|
||||
if (!@is_dir($temporaryPath)) {
|
||||
GeneralUtility::mkdir($temporaryPath);
|
||||
}
|
||||
$fileName = $temporaryPath . $extension . '_' . $version . '_' . date('YmdHi', $GLOBALS['EXEC_TIME']) . '.zip';
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
$zip->open($fileName, \ZipArchive::CREATE);
|
||||
|
||||
$excludePattern = $GLOBALS['TYPO3_CONF_VARS']['EXT']['excludeForPackaging'];
|
||||
|
||||
// Get all the files of the extension, but exclude the ones specified in the excludePattern
|
||||
$files = GeneralUtility::getAllFilesAndFoldersInPath(
|
||||
[], // No files pre-added
|
||||
$extensionPath, // Start from here
|
||||
'', // Do not filter files by extension
|
||||
true, // Include subdirectories
|
||||
PHP_INT_MAX, // Recursion level
|
||||
$excludePattern // Files and directories to exclude.
|
||||
);
|
||||
|
||||
// Make paths relative to extension root directory.
|
||||
$files = GeneralUtility::removePrefixPathFromList($files, $extensionPath);
|
||||
|
||||
// Remove the one empty path that is the extension dir itself.
|
||||
$files = array_filter($files);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$fullPath = $extensionPath . $file;
|
||||
// Distinguish between files and directories, as creation of the archive
|
||||
// fails on Windows when trying to add a directory with "addFile".
|
||||
if (is_dir($fullPath)) {
|
||||
$zip->addEmptyDir($file);
|
||||
} else {
|
||||
$zip->addFile($fullPath, $file);
|
||||
}
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unzip an extension.zip.
|
||||
*
|
||||
* @param string $file path to zip file
|
||||
* @param string $fileName file name
|
||||
* @param string $pathType path type (Local, Global, System)
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
public function unzipExtensionFromFile($file, $fileName, $pathType = 'Local')
|
||||
{
|
||||
$extensionDir = $this->makeAndClearExtensionDir($fileName, $pathType);
|
||||
|
||||
try {
|
||||
$zipService = GeneralUtility::makeInstance(ZipService::class);
|
||||
if ($zipService->verify($file)) {
|
||||
$zipService->extract($file, $extensionDir);
|
||||
}
|
||||
} catch (ExtractException $e) {
|
||||
$this->logger->error('Extracting the extension archive failed', ['exception' => $e]);
|
||||
throw new ExtensionManagerException('Extracting the extension archive failed: ' . $e->getMessage(), 1565777179, $e);
|
||||
}
|
||||
|
||||
GeneralUtility::fixPermissions($extensionDir, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a zip file to the browser and deletes it afterwards
|
||||
*
|
||||
* @param string $fileName
|
||||
* @param string $downloadName
|
||||
*/
|
||||
public function sendZipFileToBrowserAndDelete($fileName, $downloadName = '')
|
||||
{
|
||||
if ($downloadName === '') {
|
||||
$downloadName = PathUtility::basename($fileName);
|
||||
}
|
||||
header('Content-Type: application/zip');
|
||||
header('Content-Length: ' . filesize($fileName));
|
||||
header('Content-Disposition: attachment; filename="' . $downloadName . '"');
|
||||
readfile($fileName);
|
||||
unlink($fileName);
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
protected function reloadPackageInformation($extensionKey)
|
||||
{
|
||||
$this->installUtility->reloadPackageInformation($extensionKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility\Importer;
|
||||
|
||||
/*
|
||||
* 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\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Platform\PlatformInformation;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
|
||||
use TYPO3\CMS\Extensionmanager\Utility\Parser\AbstractExtensionXmlParser;
|
||||
|
||||
/**
|
||||
* Importer object for extension list
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class ExtensionListUtility implements \SplObserver
|
||||
{
|
||||
/**
|
||||
* Keeps instance of a XML parser.
|
||||
*
|
||||
* @var AbstractExtensionXmlParser
|
||||
*/
|
||||
protected $parser;
|
||||
|
||||
/**
|
||||
* Keeps number of processed version records.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $sumRecords = 0;
|
||||
|
||||
/**
|
||||
* Keeps record values to be inserted into database.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arrRows = [];
|
||||
|
||||
/**
|
||||
* Keeps fieldnames of tx_extensionmanager_domain_model_extension table.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $fieldNames = [
|
||||
'extension_key',
|
||||
'version',
|
||||
'integer_version',
|
||||
'current_version',
|
||||
'alldownloadcounter',
|
||||
'downloadcounter',
|
||||
'title',
|
||||
'ownerusername',
|
||||
'author_name',
|
||||
'author_email',
|
||||
'authorcompany',
|
||||
'last_updated',
|
||||
'md5hash',
|
||||
'repository',
|
||||
'state',
|
||||
'review_state',
|
||||
'category',
|
||||
'description',
|
||||
'serialized_dependencies',
|
||||
'update_comment'
|
||||
];
|
||||
|
||||
/**
|
||||
* Table name to be used to store extension models.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $tableName = 'tx_extensionmanager_domain_model_extension';
|
||||
|
||||
/**
|
||||
* Maximum of rows that can be used in a bulk insert for the current
|
||||
* database platform.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $maxRowsPerChunk = 50;
|
||||
|
||||
/**
|
||||
* Keeps indexes of fields that should not be quoted.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $fieldIndicesNoQuote = [2, 3, 5, 11, 13, 14, 15, 16];
|
||||
|
||||
/**
|
||||
* Keeps repository UID.
|
||||
*
|
||||
* The UID is necessary for inserting records.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $repositoryUid = 1;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository
|
||||
*/
|
||||
protected $repositoryRepository;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
|
||||
*/
|
||||
protected $extensionRepository;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Model\Extension
|
||||
*/
|
||||
protected $extensionModel;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Object\ObjectManager
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* Only import extensions newer than this date (timestamp),
|
||||
* see constructor
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $minimumDateToImport;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* Method retrieves and initializes extension XML parser instance.
|
||||
*
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
/** @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager */
|
||||
$this->objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
|
||||
$this->repositoryRepository = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository::class);
|
||||
$this->extensionRepository = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository::class);
|
||||
$this->extensionModel = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension::class);
|
||||
// @todo catch parser exception
|
||||
$this->parser = \TYPO3\CMS\Extensionmanager\Utility\Parser\XmlParserFactory::getParserInstance('extension');
|
||||
if (is_object($this->parser)) {
|
||||
$this->parser->attach($this);
|
||||
} else {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(
|
||||
static::class . ': No XML parser available.',
|
||||
1476108717
|
||||
);
|
||||
}
|
||||
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getConnectionForTable(self::$tableName);
|
||||
$maxBindParameters = PlatformInformation::getMaxBindParameters(
|
||||
$connection->getDatabasePlatform()
|
||||
);
|
||||
$countOfBindParamsPerRow = count(self::$fieldNames);
|
||||
// flush at least chunks of 50 elements - in case the currently used
|
||||
// database platform does not support that, the threshold is lowered
|
||||
$this->maxRowsPerChunk = min(
|
||||
$this->maxRowsPerChunk,
|
||||
floor($maxBindParameters / $countOfBindParamsPerRow)
|
||||
);
|
||||
// Only import extensions that are compatible with 7.6 or higher.
|
||||
// TER only allows to publish extensions with compatibility if the TYPO3 version has been released
|
||||
// And 7.6 was released on 10th of November 2015.
|
||||
// This effectively reduces the number of extensions imported into this TYPO3 installation
|
||||
// by more than 70%. As long as the extensions.xml from TER includes these files, we need to "hack" this
|
||||
// within TYPO3 Core.
|
||||
// For TYPO3 v10.0, this date could be set to 2017-04-04 (8 LTS release).
|
||||
// Also see https://decisions.typo3.org/t/reduce-size-of-extension-manager-db-table/329/
|
||||
$this->minimumDateToImport = strtotime('2015-11-10T00:00:00+00:00');
|
||||
}
|
||||
|
||||
/**
|
||||
* Method initializes parsing of extension.xml.gz file.
|
||||
*
|
||||
* @param string $localExtensionListFile absolute path to extension list xml.gz
|
||||
* @param int $repositoryUid UID of repository when inserting records into DB
|
||||
* @return int total number of imported extension versions
|
||||
*/
|
||||
public function import($localExtensionListFile, $repositoryUid = null)
|
||||
{
|
||||
if ($repositoryUid !== null && is_int($repositoryUid)) {
|
||||
$this->repositoryUid = $repositoryUid;
|
||||
}
|
||||
$zlibStream = 'compress.zlib://';
|
||||
$this->sumRecords = 0;
|
||||
$this->parser->parseXml($zlibStream . $localExtensionListFile);
|
||||
// flush last rows to database if existing
|
||||
if (!empty($this->arrRows)) {
|
||||
GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getConnectionForTable('tx_extensionmanager_domain_model_extension')
|
||||
->bulkInsert(
|
||||
'tx_extensionmanager_domain_model_extension',
|
||||
$this->arrRows,
|
||||
self::$fieldNames
|
||||
);
|
||||
}
|
||||
$extensions = $this->extensionRepository->insertLastVersion($this->repositoryUid);
|
||||
$this->repositoryRepository->updateRepositoryCount($extensions, $this->repositoryUid);
|
||||
return $this->sumRecords;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method collects and stores extension version details into the database.
|
||||
*
|
||||
* @param AbstractExtensionXmlParser $subject a subject notifying this observer
|
||||
*/
|
||||
protected function loadIntoDatabase(AbstractExtensionXmlParser &$subject)
|
||||
{
|
||||
if ($this->sumRecords !== 0 && $this->sumRecords % $this->maxRowsPerChunk === 0) {
|
||||
GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getConnectionForTable(self::$tableName)
|
||||
->bulkInsert(
|
||||
self::$tableName,
|
||||
$this->arrRows,
|
||||
self::$fieldNames
|
||||
);
|
||||
$this->arrRows = [];
|
||||
}
|
||||
$versionRepresentations = VersionNumberUtility::convertVersionStringToArray($subject->getVersion());
|
||||
// order must match that of self::$fieldNames!
|
||||
$this->arrRows[] = [
|
||||
$subject->getExtkey(),
|
||||
$subject->getVersion(),
|
||||
$versionRepresentations['version_int'],
|
||||
// initialize current_version, correct value computed later:
|
||||
0,
|
||||
(int)$subject->getAlldownloadcounter(),
|
||||
(int)$subject->getDownloadcounter(),
|
||||
$subject->getTitle() !== null ? $subject->getTitle() : '',
|
||||
$subject->getOwnerusername(),
|
||||
$subject->getAuthorname() !== null ? $subject->getAuthorname() : '',
|
||||
$subject->getAuthoremail() !== null ? $subject->getAuthoremail() : '',
|
||||
$subject->getAuthorcompany() !== null ? $subject->getAuthorcompany() : '',
|
||||
(int)$subject->getLastuploaddate(),
|
||||
$subject->getT3xfilemd5(),
|
||||
$this->repositoryUid,
|
||||
$this->extensionModel->getDefaultState($subject->getState() ?: ''),
|
||||
(int)$subject->getReviewstate(),
|
||||
$this->extensionModel->getCategoryIndexFromStringOrNumber($subject->getCategory() ?: ''),
|
||||
$subject->getDescription() ?: '',
|
||||
$subject->getDependencies() ?: '',
|
||||
$subject->getUploadcomment() ?: ''
|
||||
];
|
||||
++$this->sumRecords;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method receives an update from a subject.
|
||||
*
|
||||
* @param \SplSubject $subject a subject notifying this observer
|
||||
*/
|
||||
public function update(\SplSubject $subject)
|
||||
{
|
||||
if (is_subclass_of($subject, AbstractExtensionXmlParser::class)) {
|
||||
if ((int)$subject->getLastuploaddate() > $this->minimumDateToImport) {
|
||||
$this->loadIntoDatabase($subject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility\Importer;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Importer object for mirror list.
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class MirrorListUtility implements \SplObserver
|
||||
{
|
||||
/**
|
||||
* Keeps instance of a XML parser.
|
||||
*
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\Parser\AbstractMirrorXmlParser
|
||||
*/
|
||||
protected $parser;
|
||||
|
||||
/**
|
||||
* Keeps mirrors' details.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arrTmpMirrors = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* Method retrieves and initializes extension XML parser instance
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// @todo catch parser exception
|
||||
$this->parser = \TYPO3\CMS\Extensionmanager\Utility\Parser\XmlParserFactory::getParserInstance('mirror');
|
||||
if (is_object($this->parser)) {
|
||||
$this->parser->attach($this);
|
||||
} else {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(
|
||||
static::class . ': No XML parser available.',
|
||||
1476108687
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method collects mirrors' details and returns instance of
|
||||
* \TYPO3\CMS\Extensionmanager\Domain\Model\Mirrors with retrieved details.
|
||||
*
|
||||
* @param string $localMirrorListFile absolute path to local mirror xml.gz file
|
||||
* @return \TYPO3\CMS\Extensionmanager\Domain\Model\Mirrors
|
||||
*/
|
||||
public function getMirrors($localMirrorListFile)
|
||||
{
|
||||
$zlibStream = 'compress.zlib://';
|
||||
$this->parser->parseXml($zlibStream . $localMirrorListFile);
|
||||
/** @var \TYPO3\CMS\Extensionmanager\Domain\Model\Mirrors $objRepositoryMirrors */
|
||||
$objRepositoryMirrors = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extensionmanager\Domain\Model\Mirrors::class);
|
||||
$objRepositoryMirrors->setMirrors($this->arrTmpMirrors);
|
||||
$this->arrTmpMirrors = [];
|
||||
return $objRepositoryMirrors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method receives an update from a subject.
|
||||
*
|
||||
* @param \SplSubject $subject a subject notifying this observer
|
||||
*/
|
||||
public function update(\SplSubject $subject)
|
||||
{
|
||||
// @todo mirrorxml_abstract_parser
|
||||
if (is_subclass_of($subject, \TYPO3\CMS\Extensionmanager\Utility\Parser\AbstractXmlParser::class)) {
|
||||
$this->arrTmpMirrors[] = $subject->getAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,710 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility;
|
||||
|
||||
/*
|
||||
* 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\Configuration\ExtensionConfiguration;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Database\Schema\SchemaMigrator;
|
||||
use TYPO3\CMS\Core\Database\Schema\SqlReader;
|
||||
use TYPO3\CMS\Core\Service\OpcodeCacheService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
|
||||
use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
|
||||
use TYPO3\CMS\Impexp\Utility\ImportExportUtility;
|
||||
|
||||
/**
|
||||
* Extension Manager Install Utility
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class InstallUtility implements \TYPO3\CMS\Core\SingletonInterface
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Object\ObjectManager
|
||||
*/
|
||||
public $objectManager;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility
|
||||
*/
|
||||
protected $dependencyUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
|
||||
*/
|
||||
protected $fileHandlingUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
|
||||
*/
|
||||
protected $listUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
|
||||
*/
|
||||
public $extensionRepository;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Package\PackageManager
|
||||
*/
|
||||
protected $packageManager;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Cache\CacheManager
|
||||
*/
|
||||
protected $cacheManager;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
|
||||
*/
|
||||
protected $signalSlotDispatcher;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Registry
|
||||
*/
|
||||
protected $registry;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager
|
||||
*/
|
||||
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility
|
||||
*/
|
||||
public function injectDependencyUtility(\TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility)
|
||||
{
|
||||
$this->dependencyUtility = $dependencyUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility
|
||||
*/
|
||||
public function injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility)
|
||||
{
|
||||
$this->fileHandlingUtility = $fileHandlingUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility
|
||||
*/
|
||||
public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
|
||||
{
|
||||
$this->listUtility = $listUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository
|
||||
*/
|
||||
public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
|
||||
{
|
||||
$this->extensionRepository = $extensionRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Core\Package\PackageManager $packageManager
|
||||
*/
|
||||
public function injectPackageManager(\TYPO3\CMS\Core\Package\PackageManager $packageManager)
|
||||
{
|
||||
$this->packageManager = $packageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Core\Cache\CacheManager $cacheManager
|
||||
*/
|
||||
public function injectCacheManager(\TYPO3\CMS\Core\Cache\CacheManager $cacheManager)
|
||||
{
|
||||
$this->cacheManager = $cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher
|
||||
*/
|
||||
public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
|
||||
{
|
||||
$this->signalSlotDispatcher = $signalSlotDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Core\Registry $registry
|
||||
*/
|
||||
public function injectRegistry(\TYPO3\CMS\Core\Registry $registry)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to install an extension
|
||||
* also processes db updates and clears the cache if the extension asks for it
|
||||
*
|
||||
* @param array $extensionKeys
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
public function install(...$extensionKeys)
|
||||
{
|
||||
$flushCaches = false;
|
||||
foreach ($extensionKeys as $extensionKey) {
|
||||
$this->loadExtension($extensionKey);
|
||||
$extension = $this->enrichExtensionWithDetails($extensionKey, false);
|
||||
$this->saveDefaultConfiguration($extensionKey);
|
||||
if (!empty($extension['clearcacheonload']) || !empty($extension['clearCacheOnLoad'])) {
|
||||
$flushCaches = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($flushCaches) {
|
||||
$this->cacheManager->flushCaches();
|
||||
} else {
|
||||
$this->cacheManager->flushCachesInGroup('system');
|
||||
}
|
||||
$this->reloadCaches();
|
||||
$this->updateDatabase($extensionKeys);
|
||||
|
||||
foreach ($extensionKeys as $extensionKey) {
|
||||
$this->processExtensionSetup($extensionKey);
|
||||
$this->emitAfterExtensionInstallSignal($extensionKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
public function processExtensionSetup($extensionKey)
|
||||
{
|
||||
$extension = $this->enrichExtensionWithDetails($extensionKey, false);
|
||||
$this->ensureConfiguredDirectoriesExist($extension);
|
||||
$this->importInitialFiles($extension['siteRelPath'] ?? '', $extensionKey);
|
||||
$this->importStaticSqlFile($extension['siteRelPath']);
|
||||
$this->importT3DFile($extension['siteRelPath']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to uninstall an extension
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
public function uninstall($extensionKey)
|
||||
{
|
||||
$dependentExtensions = $this->dependencyUtility->findInstalledExtensionsThatDependOnMe($extensionKey);
|
||||
if (is_array($dependentExtensions) && !empty($dependentExtensions)) {
|
||||
throw new ExtensionManagerException(
|
||||
\TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate(
|
||||
'extensionList.uninstall.dependencyError',
|
||||
'extensionmanager',
|
||||
[$extensionKey, implode(',', $dependentExtensions)]
|
||||
),
|
||||
1342554622
|
||||
);
|
||||
}
|
||||
$this->unloadExtension($extensionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function to check for loaded extensions
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @return bool TRUE if extension is loaded
|
||||
*/
|
||||
public function isLoaded($extensionKey)
|
||||
{
|
||||
return $this->packageManager->isPackageActive($extensionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset and reload the available extensions
|
||||
*/
|
||||
public function reloadAvailableExtensions()
|
||||
{
|
||||
$this->listUtility->reloadAvailableExtensions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for loading extensions
|
||||
*
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
protected function loadExtension($extensionKey)
|
||||
{
|
||||
$this->packageManager->activatePackage($extensionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for unloading extensions
|
||||
*
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
protected function unloadExtension($extensionKey)
|
||||
{
|
||||
$this->packageManager->deactivatePackage($extensionKey);
|
||||
$this->emitAfterExtensionUninstallSignal($extensionKey);
|
||||
$this->cacheManager->flushCachesInGroup('system');
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a signal after an extension has been installed
|
||||
*
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
protected function emitAfterExtensionInstallSignal($extensionKey)
|
||||
{
|
||||
$this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionInstall', [$extensionKey, $this]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a signal after an extension has been uninstalled
|
||||
*
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
protected function emitAfterExtensionUninstallSignal($extensionKey)
|
||||
{
|
||||
$this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionUninstall', [$extensionKey, $this]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an extension is available in the system
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @return bool
|
||||
*/
|
||||
public function isAvailable($extensionKey)
|
||||
{
|
||||
return $this->packageManager->isPackageAvailable($extensionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the package information, if the package is already registered
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @throws \TYPO3\CMS\Core\Package\Exception\InvalidPackageStateException if the package isn't available
|
||||
* @throws \TYPO3\CMS\Core\Package\Exception\InvalidPackageKeyException if an invalid package key was passed
|
||||
* @throws \TYPO3\CMS\Core\Package\Exception\InvalidPackagePathException if an invalid package path was passed
|
||||
* @throws \TYPO3\CMS\Core\Package\Exception\InvalidPackageManifestException if no extension configuration file could be found
|
||||
*/
|
||||
public function reloadPackageInformation($extensionKey)
|
||||
{
|
||||
if ($this->packageManager->isPackageAvailable($extensionKey)) {
|
||||
$this->reloadOpcache();
|
||||
$this->packageManager->reloadPackageInformation($extensionKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch additional information for an extension key
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @param bool $loadTerInformation
|
||||
* @internal
|
||||
* @return array
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
public function enrichExtensionWithDetails($extensionKey, $loadTerInformation = true)
|
||||
{
|
||||
$extension = $this->getExtensionArray($extensionKey);
|
||||
if (!$loadTerInformation) {
|
||||
$availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfInformation([$extensionKey => $extension]);
|
||||
} else {
|
||||
$availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfAndTerInformation([$extensionKey => $extension]);
|
||||
}
|
||||
|
||||
if (!isset($availableAndInstalledExtensions[$extensionKey])) {
|
||||
throw new ExtensionManagerException(
|
||||
'Please check your uploaded extension "' . $extensionKey . '". The configuration file "ext_emconf.php" seems to be invalid.',
|
||||
1391432222
|
||||
);
|
||||
}
|
||||
|
||||
return $availableAndInstalledExtensions[$extensionKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extensionKey
|
||||
* @return array
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
protected function getExtensionArray($extensionKey)
|
||||
{
|
||||
$availableExtensions = $this->listUtility->getAvailableExtensions();
|
||||
if (isset($availableExtensions[$extensionKey])) {
|
||||
return $availableExtensions[$extensionKey];
|
||||
}
|
||||
throw new ExtensionManagerException('Extension ' . $extensionKey . ' is not available', 1342864081);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates directories as requested in ext_emconf.php
|
||||
*
|
||||
* @param array $extension
|
||||
*/
|
||||
protected function ensureConfiguredDirectoriesExist(array $extension)
|
||||
{
|
||||
$this->fileHandlingUtility->ensureConfiguredDirectoriesExist($extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content of the ext_tables.sql and ext_tables_static+adt.sql files
|
||||
* Additionally adds the table definitions for the cache tables
|
||||
*
|
||||
* @param array $extension
|
||||
* @deprecated since TYPO3 v9, will be removed with TYPO3v10
|
||||
*/
|
||||
public function processDatabaseUpdates(array $extension)
|
||||
{
|
||||
trigger_error('This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
|
||||
$extTablesSqlFile = Environment::getPublicPath() . '/' . $extension['siteRelPath'] . 'ext_tables.sql';
|
||||
$extTablesSqlContent = '';
|
||||
if (file_exists($extTablesSqlFile)) {
|
||||
$extTablesSqlContent .= file_get_contents($extTablesSqlFile);
|
||||
}
|
||||
if ($extTablesSqlContent !== '') {
|
||||
try {
|
||||
$this->updateDbWithExtTablesSql($extTablesSqlContent);
|
||||
} catch (\TYPO3\CMS\Core\Database\Schema\Exception\StatementException $e) {
|
||||
throw new ExtensionManagerException(
|
||||
$e->getMessage(),
|
||||
1476340371
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->importStaticSqlFile($extension['siteRelPath']);
|
||||
$this->importT3DFile($extension['siteRelPath']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a signal to manipulate the tables definitions
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @throws ExtensionManagerException
|
||||
* @return mixed
|
||||
* @deprecated since TYPO3 v9, will be removed with TYPO3v10
|
||||
* @see \TYPO3\CMS\Core\Database\Schema\SqlReader::emitTablesDefinitionIsBeingBuiltSignal
|
||||
*/
|
||||
protected function emitTablesDefinitionIsBeingBuiltSignal($extensionKey)
|
||||
{
|
||||
$signalReturn = $this->signalSlotDispatcher->dispatch(__CLASS__, 'tablesDefinitionIsBeingBuilt', [[], $extensionKey]);
|
||||
// This is important to support old associated returns
|
||||
$signalReturn = array_values($signalReturn);
|
||||
$sqlString = $signalReturn[0];
|
||||
if (!is_array($sqlString)) {
|
||||
throw new ExtensionManagerException(
|
||||
sprintf(
|
||||
'The signal %s of class %s returned a value of type %s, but array was expected.',
|
||||
'tablesDefinitionIsBeingBuilt',
|
||||
__CLASS__,
|
||||
gettype($sqlString)
|
||||
),
|
||||
1382360258
|
||||
);
|
||||
}
|
||||
if (!empty($sqlString)) {
|
||||
trigger_error(
|
||||
sprintf(
|
||||
'The signal %s of class %s is deprecated and will be removed in TYPO3 v10.0.',
|
||||
'tablesDefinitionIsBeingBuilt',
|
||||
__CLASS__
|
||||
),
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
return $sqlString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload Cache files and Typo3LoadedExtensions
|
||||
*/
|
||||
public function reloadCaches()
|
||||
{
|
||||
$this->reloadOpcache();
|
||||
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::loadExtLocalconf(false);
|
||||
\TYPO3\CMS\Core\Core\Bootstrap::loadBaseTca(false);
|
||||
\TYPO3\CMS\Core\Core\Bootstrap::loadExtTables(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads PHP opcache
|
||||
*/
|
||||
protected function reloadOpcache()
|
||||
{
|
||||
GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes all safe database statements.
|
||||
* Tables and fields are created and altered. Nothing gets deleted or renamed here.
|
||||
*
|
||||
* @param array $extensionKeys
|
||||
*/
|
||||
protected function updateDatabase(array $extensionKeys)
|
||||
{
|
||||
$sqlReader = GeneralUtility::makeInstance(SqlReader::class);
|
||||
$schemaMigrator = GeneralUtility::makeInstance(SchemaMigrator::class);
|
||||
$sqlStatements = [];
|
||||
$sqlStatements[] = $sqlReader->getTablesDefinitionString();
|
||||
foreach ($extensionKeys as $extensionKey) {
|
||||
$sqlStatements += $this->emitTablesDefinitionIsBeingBuiltSignal($extensionKey);
|
||||
}
|
||||
$sqlStatements = $sqlReader->getCreateTableStatementArray(implode(LF . LF, array_filter($sqlStatements)));
|
||||
$updateStatements = $schemaMigrator->getUpdateSuggestions($sqlStatements);
|
||||
|
||||
$updateStatements = array_merge_recursive(...array_values($updateStatements));
|
||||
$selectedStatements = [];
|
||||
foreach (['add', 'change', 'create_table', 'change_table'] as $action) {
|
||||
if (empty($updateStatements[$action])) {
|
||||
continue;
|
||||
}
|
||||
$selectedStatements = array_merge(
|
||||
$selectedStatements,
|
||||
array_combine(array_keys($updateStatements[$action]), array_fill(0, count($updateStatements[$action]), true))
|
||||
);
|
||||
}
|
||||
|
||||
$schemaMigrator->migrate($sqlStatements, $selectedStatements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save default configuration of an extension
|
||||
*
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
protected function saveDefaultConfiguration($extensionKey)
|
||||
{
|
||||
$extensionConfiguration = $this->objectManager->get(ExtensionConfiguration::class);
|
||||
$extensionConfiguration->synchronizeExtConfTemplateWithLocalConfiguration($extensionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update database / process db updates from ext_tables
|
||||
*
|
||||
* @param string $rawDefinitions The raw SQL statements from ext_tables.sql
|
||||
* @deprecated since TYPO3 v9, will be removed with TYPO3v10
|
||||
*/
|
||||
public function updateDbWithExtTablesSql($rawDefinitions)
|
||||
{
|
||||
trigger_error('This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
|
||||
$sqlReader = GeneralUtility::makeInstance(SqlReader::class);
|
||||
$statements = $sqlReader->getCreateTableStatementArray($rawDefinitions);
|
||||
if (count($statements) !== 0) {
|
||||
$schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
|
||||
$schemaMigrationService->install($statements);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import static SQL data (normally used for ext_tables_static+adt.sql)
|
||||
*
|
||||
* @param string $rawDefinitions
|
||||
*/
|
||||
public function importStaticSql($rawDefinitions)
|
||||
{
|
||||
$sqlReader = GeneralUtility::makeInstance(SqlReader::class);
|
||||
$statements = $sqlReader->getStatementArray($rawDefinitions);
|
||||
|
||||
$schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
|
||||
$schemaMigrationService->importStaticData($statements, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an extension (delete the directory)
|
||||
*
|
||||
* @param string $extension
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
public function removeExtension($extension)
|
||||
{
|
||||
$absolutePath = $this->fileHandlingUtility->getAbsoluteExtensionPath($extension);
|
||||
if ($this->fileHandlingUtility->isValidExtensionPath($absolutePath)) {
|
||||
if ($this->packageManager->isPackageAvailable($extension)) {
|
||||
// Package manager deletes the extension and removes the entry from PackageStates.php
|
||||
$this->packageManager->deletePackage($extension);
|
||||
} else {
|
||||
// The extension is not listed in PackageStates.php, we can safely remove it
|
||||
$this->fileHandlingUtility->removeDirectory($absolutePath);
|
||||
}
|
||||
} else {
|
||||
throw new ExtensionManagerException('No valid extension path given.', 1342875724);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an update for an extension is available which also resolves dependencies.
|
||||
*
|
||||
* @internal
|
||||
* @param Extension $extensionData
|
||||
* @return bool
|
||||
*/
|
||||
public function isUpdateAvailable(Extension $extensionData)
|
||||
{
|
||||
return (bool)$this->getUpdateableVersion($extensionData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the updateable version for an extension which also resolves dependencies.
|
||||
*
|
||||
* @internal
|
||||
* @param Extension $extensionData
|
||||
* @return bool|Extension FALSE if no update available otherwise latest possible update
|
||||
*/
|
||||
public function getUpdateableVersion(Extension $extensionData)
|
||||
{
|
||||
// Only check for update for TER extensions
|
||||
$version = $extensionData->getIntegerVersion();
|
||||
|
||||
/** @var $extensionUpdates[] \TYPO3\CMS\Extensionmanager\Domain\Model\Extension */
|
||||
$extensionUpdates = $this->extensionRepository->findByVersionRangeAndExtensionKeyOrderedByVersion(
|
||||
$extensionData->getExtensionKey(),
|
||||
$version,
|
||||
0,
|
||||
false
|
||||
);
|
||||
if ($extensionUpdates->count() > 0) {
|
||||
foreach ($extensionUpdates as $extensionUpdate) {
|
||||
try {
|
||||
$this->dependencyUtility->checkDependencies($extensionUpdate);
|
||||
if (!$this->dependencyUtility->hasDependencyErrors()) {
|
||||
return $extensionUpdate;
|
||||
}
|
||||
} catch (ExtensionManagerException $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the export import extension to import a T3D or XML file to PID 0
|
||||
* Execution state is saved in the this->registry, so it only happens once
|
||||
*
|
||||
* @param string $extensionSiteRelPath
|
||||
*/
|
||||
protected function importT3DFile($extensionSiteRelPath)
|
||||
{
|
||||
$registryKeysToCheck = [
|
||||
$extensionSiteRelPath . 'Initialisation/data.t3d',
|
||||
$extensionSiteRelPath . 'Initialisation/dataImported',
|
||||
];
|
||||
foreach ($registryKeysToCheck as $registryKeyToCheck) {
|
||||
if ($this->registry->get('extensionDataImport', $registryKeyToCheck)) {
|
||||
// Data was imported before => early return
|
||||
return;
|
||||
}
|
||||
}
|
||||
$importFileToUse = null;
|
||||
$possibleImportFiles = [
|
||||
$extensionSiteRelPath . 'Initialisation/data.t3d',
|
||||
$extensionSiteRelPath . 'Initialisation/data.xml'
|
||||
];
|
||||
foreach ($possibleImportFiles as $possibleImportFile) {
|
||||
if (!file_exists(Environment::getPublicPath() . '/' . $possibleImportFile)) {
|
||||
continue;
|
||||
}
|
||||
$importFileToUse = $possibleImportFile;
|
||||
}
|
||||
if ($importFileToUse !== null) {
|
||||
/** @var ImportExportUtility $importExportUtility */
|
||||
$importExportUtility = $this->objectManager->get(ImportExportUtility::class);
|
||||
try {
|
||||
$importResult = $importExportUtility->importT3DFile(Environment::getPublicPath() . '/' . $importFileToUse, 0);
|
||||
$this->registry->set('extensionDataImport', $extensionSiteRelPath . 'Initialisation/dataImported', 1);
|
||||
$this->emitAfterExtensionT3DImportSignal($importFileToUse, $importResult);
|
||||
} catch (\ErrorException $e) {
|
||||
$logger = $this->objectManager->get(\TYPO3\CMS\Core\Log\LogManager::class)->getLogger(__CLASS__);
|
||||
$logger->log(\TYPO3\CMS\Core\Log\LogLevel::WARNING, $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a signal after an t3d file was imported
|
||||
*
|
||||
* @param string $importFileToUse
|
||||
* @param int $importResult
|
||||
*/
|
||||
protected function emitAfterExtensionT3DImportSignal($importFileToUse, $importResult)
|
||||
{
|
||||
$this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionT3DImport', [$importFileToUse, $importResult, $this]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a static tables SQL File (ext_tables_static+adt)
|
||||
* Execution state is saved in the this->registry, so it only happens once
|
||||
*
|
||||
* @param string $extensionSiteRelPath
|
||||
*/
|
||||
protected function importStaticSqlFile($extensionSiteRelPath)
|
||||
{
|
||||
$extTablesStaticSqlRelFile = $extensionSiteRelPath . 'ext_tables_static+adt.sql';
|
||||
if (!$this->registry->get('extensionDataImport', $extTablesStaticSqlRelFile)) {
|
||||
$extTablesStaticSqlFile = Environment::getPublicPath() . '/' . $extTablesStaticSqlRelFile;
|
||||
$shortFileHash = '';
|
||||
if (file_exists($extTablesStaticSqlFile)) {
|
||||
$extTablesStaticSqlContent = file_get_contents($extTablesStaticSqlFile);
|
||||
$shortFileHash = md5($extTablesStaticSqlContent);
|
||||
$this->importStaticSql($extTablesStaticSqlContent);
|
||||
}
|
||||
$this->registry->set('extensionDataImport', $extTablesStaticSqlRelFile, $shortFileHash);
|
||||
$this->emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a signal after a static sql file was imported
|
||||
*
|
||||
* @param string $extTablesStaticSqlRelFile
|
||||
*/
|
||||
protected function emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile)
|
||||
{
|
||||
$this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionStaticSqlImport', [$extTablesStaticSqlRelFile, $this]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports files from Initialisation/Files to fileadmin
|
||||
* via lowlevel copy directory method
|
||||
*
|
||||
* @param string $extensionSiteRelPath relative path to extension dir
|
||||
* @param string $extensionKey
|
||||
*/
|
||||
protected function importInitialFiles($extensionSiteRelPath, $extensionKey)
|
||||
{
|
||||
$importRelFolder = $extensionSiteRelPath . 'Initialisation/Files';
|
||||
if (!$this->registry->get('extensionDataImport', $importRelFolder)) {
|
||||
$importFolder = Environment::getPublicPath() . '/' . $importRelFolder;
|
||||
if (file_exists($importFolder)) {
|
||||
$destinationRelPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . $extensionKey;
|
||||
$destinationAbsolutePath = Environment::getPublicPath() . '/' . $destinationRelPath;
|
||||
if (!file_exists($destinationAbsolutePath) &&
|
||||
GeneralUtility::isAllowedAbsPath($destinationAbsolutePath)
|
||||
) {
|
||||
GeneralUtility::mkdir($destinationAbsolutePath);
|
||||
}
|
||||
GeneralUtility::copyDirectory($importRelFolder, $destinationRelPath);
|
||||
$this->registry->set('extensionDataImport', $importRelFolder, 1);
|
||||
$this->emitAfterExtensionFileImportSignal($destinationAbsolutePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a signal after extension files were imported
|
||||
*
|
||||
* @param string $destinationAbsolutePath
|
||||
*/
|
||||
protected function emitAfterExtensionFileImportSignal($destinationAbsolutePath)
|
||||
{
|
||||
$this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionFileImport', [$destinationAbsolutePath, $this]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility;
|
||||
|
||||
/*
|
||||
* 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\Core\Environment;
|
||||
use TYPO3\CMS\Core\Package\PackageInterface;
|
||||
use TYPO3\CMS\Core\Type\File\ImageInfo;
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
|
||||
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
|
||||
|
||||
/**
|
||||
* 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?
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class ListUtility implements \TYPO3\CMS\Core\SingletonInterface
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\EmConfUtility
|
||||
*/
|
||||
protected $emConfUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
|
||||
*/
|
||||
protected $extensionRepository;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extensionmanager\Utility\InstallUtility
|
||||
*/
|
||||
protected $installUtility;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Package\PackageManager
|
||||
*/
|
||||
protected $packageManager;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
|
||||
*/
|
||||
protected $signalSlotDispatcher;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $availableExtensions;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility
|
||||
*/
|
||||
public function injectEmConfUtility(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility)
|
||||
{
|
||||
$this->emConfUtility = $emConfUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository
|
||||
*/
|
||||
public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
|
||||
{
|
||||
$this->extensionRepository = $extensionRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility
|
||||
*/
|
||||
public function injectInstallUtility(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility)
|
||||
{
|
||||
$this->installUtility = $installUtility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Core\Package\PackageManager $packageManager
|
||||
*/
|
||||
public function injectPackageManager(\TYPO3\CMS\Core\Package\PackageManager $packageManager)
|
||||
{
|
||||
$this->packageManager = $packageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher
|
||||
*/
|
||||
public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
|
||||
{
|
||||
$this->signalSlotDispatcher = $signalSlotDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of available, but not necessarily loaded extensions
|
||||
*
|
||||
* @return array[] All extensions with info
|
||||
*/
|
||||
public function getAvailableExtensions()
|
||||
{
|
||||
if ($this->availableExtensions === null) {
|
||||
$this->availableExtensions = [];
|
||||
$this->emitPackagesMayHaveChangedSignal();
|
||||
foreach ($this->packageManager->getAvailablePackages() as $package) {
|
||||
$installationType = $this->getInstallTypeForPackage($package);
|
||||
$this->availableExtensions[$package->getPackageKey()] = [
|
||||
'siteRelPath' => str_replace(Environment::getPublicPath() . '/', '', $package->getPackagePath()),
|
||||
'type' => $installationType,
|
||||
'key' => $package->getPackageKey(),
|
||||
'ext_icon' => ExtensionManagementUtility::getExtensionIcon($package->getPackagePath()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->availableExtensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset and reload the available extensions
|
||||
*/
|
||||
public function reloadAvailableExtensions()
|
||||
{
|
||||
$this->availableExtensions = null;
|
||||
$this->packageManager->scanAvailablePackages();
|
||||
$this->getAvailableExtensions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extensionKey
|
||||
* @return \TYPO3\CMS\Core\Package\PackageInterface
|
||||
* @throws \TYPO3\CMS\Core\Package\Exception\UnknownPackageException if the specified package is unknown
|
||||
*/
|
||||
public function getExtension($extensionKey)
|
||||
{
|
||||
return $this->packageManager->getPackage($extensionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits packages may have changed signal
|
||||
*/
|
||||
protected function emitPackagesMayHaveChangedSignal()
|
||||
{
|
||||
$this->signalSlotDispatcher->dispatch('PackageManagement', 'packagesMayHaveChanged');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "System", "Global" or "Local" based on extension position in filesystem.
|
||||
*
|
||||
* @param PackageInterface $package
|
||||
* @return string
|
||||
*/
|
||||
protected function getInstallTypeForPackage(PackageInterface $package)
|
||||
{
|
||||
foreach (Extension::returnInstallPaths() as $installType => $installPath) {
|
||||
if (GeneralUtility::isFirstPartOfStr($package->getPackagePath(), $installPath)) {
|
||||
return $installType;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Enrich the output of getAvailableExtensions() with an array key installed = 1 if an extension is loaded.
|
||||
*
|
||||
* @param array $availableExtensions
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableAndInstalledExtensions(array $availableExtensions)
|
||||
{
|
||||
foreach ($this->packageManager->getActivePackages() as $extKey => $_) {
|
||||
if (isset($availableExtensions[$extKey])) {
|
||||
$availableExtensions[$extKey]['installed'] = true;
|
||||
}
|
||||
}
|
||||
return $availableExtensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the information from the emconf array to the extension information
|
||||
*
|
||||
* @param array $extensions
|
||||
* @return array
|
||||
*/
|
||||
public function enrichExtensionsWithEmConfInformation(array $extensions)
|
||||
{
|
||||
foreach ($extensions as $extensionKey => $properties) {
|
||||
$emconf = $this->emConfUtility->includeEmConf($properties);
|
||||
if ($emconf) {
|
||||
$extensions[$extensionKey] = array_merge($emconf, $properties);
|
||||
} else {
|
||||
unset($extensions[$extensionKey]);
|
||||
}
|
||||
}
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the information from the emconf array and TER to the extension information
|
||||
*
|
||||
* @param array $extensions
|
||||
* @return array
|
||||
*/
|
||||
public function enrichExtensionsWithEmConfAndTerInformation(array $extensions)
|
||||
{
|
||||
$extensions = $this->enrichExtensionsWithEmConfInformation($extensions);
|
||||
foreach ($extensions as $extensionKey => $properties) {
|
||||
$terObject = $this->getExtensionTerData($extensionKey, $extensions[$extensionKey]['version'] ?? '');
|
||||
if ($terObject !== null) {
|
||||
$extensions[$extensionKey]['terObject'] = $terObject;
|
||||
$extensions[$extensionKey]['updateAvailable'] = false;
|
||||
$extensions[$extensionKey]['updateToVersion'] = null;
|
||||
$extensionToUpdate = $this->installUtility->getUpdateableVersion($terObject);
|
||||
if ($extensionToUpdate !== false) {
|
||||
$extensions[$extensionKey]['updateAvailable'] = true;
|
||||
$extensions[$extensionKey]['updateToVersion'] = $extensionToUpdate;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find given extension with given version in TER data.
|
||||
* If extension is found but not the given version, we return TER data from highest version with version data set to
|
||||
* given one.
|
||||
*
|
||||
* @param string $extensionKey Key of the extension
|
||||
* @param string $version String representation of version number
|
||||
* @return Extension|null Extension TER object or NULL if nothing found
|
||||
*/
|
||||
protected function getExtensionTerData($extensionKey, $version)
|
||||
{
|
||||
$terObject = $this->extensionRepository->findOneByExtensionKeyAndVersion($extensionKey, $version);
|
||||
if (!$terObject instanceof Extension) {
|
||||
// Version unknown in TER data, try to find extension
|
||||
$terObject = $this->extensionRepository->findHighestAvailableVersion($extensionKey);
|
||||
if ($terObject instanceof Extension) {
|
||||
// Found in TER now, set version information to the known ones, so we can look if there is a newer one
|
||||
// Use a cloned object, otherwise wrong information is stored in persistenceManager
|
||||
$terObject = clone $terObject;
|
||||
$terObject->setVersion($version);
|
||||
$terObject->setIntegerVersion(
|
||||
VersionNumberUtility::convertVersionNumberToInteger($terObject->getVersion())
|
||||
);
|
||||
} else {
|
||||
$terObject = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $terObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information about icon size to the extension information
|
||||
*
|
||||
* @param array $extensions
|
||||
* @return array
|
||||
*/
|
||||
public function enrichExtensionsWithIconInformation(array $extensions)
|
||||
{
|
||||
foreach ($extensions as &$properties) {
|
||||
$extIconPath = Environment::getPublicPath() . '/' . $properties['siteRelPath'] . $properties['ext_icon'];
|
||||
if (@is_file($extIconPath)) {
|
||||
$imageInfo = GeneralUtility::makeInstance(ImageInfo::class, $extIconPath);
|
||||
$properties['ext_icon_width'] = $imageInfo->getWidth();
|
||||
$properties['ext_icon_height'] = $imageInfo->getHeight();
|
||||
} else {
|
||||
$properties['ext_icon_width'] = 0;
|
||||
$properties['ext_icon_height'] = 0;
|
||||
}
|
||||
}
|
||||
unset($properties);
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all available and installed extension with additional information
|
||||
* from em_conf and TER (if available)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableAndInstalledExtensionsWithAdditionalInformation()
|
||||
{
|
||||
$availableExtensions = $this->getAvailableExtensions();
|
||||
$availableAndInstalledExtensions = $this->getAvailableAndInstalledExtensions($availableExtensions);
|
||||
$availableAndInstalledExtensions = $this->enrichExtensionsWithIconInformation($availableAndInstalledExtensions);
|
||||
return $this->enrichExtensionsWithEmConfAndTerInformation($availableAndInstalledExtensions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract parser for TYPO3's extension.xml file.
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
abstract class AbstractExtensionXmlParser extends AbstractXmlParser
|
||||
{
|
||||
/**
|
||||
* Keeps current author company of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $authorcompany;
|
||||
|
||||
/**
|
||||
* Keeps current author mail address of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $authoremail;
|
||||
|
||||
/**
|
||||
* Keeps current author name of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $authorname;
|
||||
|
||||
/**
|
||||
* Keeps current category of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $category;
|
||||
|
||||
/**
|
||||
* Keeps current dependencies of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $dependencies;
|
||||
|
||||
/**
|
||||
* Keeps current description of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* Keeps current download number sum of all extension's versions.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $extensionDownloadCounter;
|
||||
|
||||
/**
|
||||
* Keeps current key of an extension.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $extensionKey;
|
||||
|
||||
/**
|
||||
* Keeps current upload date of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $lastuploaddate;
|
||||
|
||||
/**
|
||||
* Keeps current owner username of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $ownerusername;
|
||||
|
||||
/**
|
||||
* Keeps current reviewstate of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reviewstate;
|
||||
|
||||
/**
|
||||
* Keeps current state of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* Keeps current t3x file hash of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $t3xfilemd5;
|
||||
|
||||
/**
|
||||
* Keeps current title of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* Keeps current upload comment of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $uploadcomment;
|
||||
|
||||
/**
|
||||
* Keeps current version number.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
/**
|
||||
* Keeps current download number of an extension's version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $versionDownloadCounter;
|
||||
|
||||
/**
|
||||
* Returns an assoziative array of all extension version properties.
|
||||
*
|
||||
* Valid array keys of returned array are:
|
||||
* extkey, version, alldownloadcounter, downloadcounter, title, description,
|
||||
* state, reviewstate, category, lastuploaddate, uploadcomment, dependencies,
|
||||
* authorname, authoremail, authorcompany, ownerusername, t3xfilemd5
|
||||
*
|
||||
* @see $extensionKey, $version, $extensionDownloadCounter,
|
||||
* @return array assoziative array of an extension version's properties
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
$versionProperties = [];
|
||||
$versionProperties['extkey'] = $this->extensionKey;
|
||||
$versionProperties['version'] = $this->version;
|
||||
$versionProperties['alldownloadcounter'] = $this->extensionDownloadCounter;
|
||||
$versionProperties['downloadcounter'] = $this->versionDownloadCounter;
|
||||
$versionProperties['title'] = $this->title;
|
||||
$versionProperties['description'] = $this->description;
|
||||
$versionProperties['state'] = $this->state;
|
||||
$versionProperties['reviewstate'] = $this->reviewstate;
|
||||
$versionProperties['category'] = $this->category;
|
||||
$versionProperties['lastuploaddate'] = $this->lastuploaddate;
|
||||
$versionProperties['uploadcomment'] = $this->uploadcomment;
|
||||
$versionProperties['dependencies'] = $this->dependencies;
|
||||
$versionProperties['authorname'] = $this->authorname;
|
||||
$versionProperties['authoremail'] = $this->authoremail;
|
||||
$versionProperties['authorcompany'] = $this->authorcompany;
|
||||
$versionProperties['ownerusername'] = $this->ownerusername;
|
||||
$versionProperties['t3xfilemd5'] = $this->t3xfilemd5;
|
||||
return $versionProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns download number sum of all extension's versions.
|
||||
*
|
||||
* @return string download number sum
|
||||
* @see $extensionDLCounter, getAll()
|
||||
*/
|
||||
public function getAlldownloadcounter()
|
||||
{
|
||||
return $this->extensionDownloadCounter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns company name of extension author.
|
||||
*
|
||||
* @return string company name of extension author
|
||||
* @see $authorcompany, getAll()
|
||||
*/
|
||||
public function getAuthorcompany()
|
||||
{
|
||||
return $this->authorcompany;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns e-mail address of extension author.
|
||||
*
|
||||
* @return string e-mail address of extension author
|
||||
* @see $authoremail, getAll()
|
||||
*/
|
||||
public function getAuthoremail()
|
||||
{
|
||||
return $this->authoremail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of extension author.
|
||||
*
|
||||
* @return string name of extension author
|
||||
* @see $authorname, getAll()
|
||||
*/
|
||||
public function getAuthorname()
|
||||
{
|
||||
return $this->authorname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns category of an extension.
|
||||
*
|
||||
* @return string extension category
|
||||
* @see $category, getAll()
|
||||
*/
|
||||
public function getCategory()
|
||||
{
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns dependencies of an extension's version.
|
||||
*
|
||||
* @return string extension dependencies
|
||||
* @see $dependencies, getAll()
|
||||
*/
|
||||
public function getDependencies()
|
||||
{
|
||||
return $this->dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of an extension's version.
|
||||
*
|
||||
* @return string extension description
|
||||
* @see $description, getAll()
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns download number of an extension's version.
|
||||
*
|
||||
* @return string download number
|
||||
* @see $versionDLCounter, getAll()
|
||||
*/
|
||||
public function getDownloadcounter()
|
||||
{
|
||||
return $this->versionDownloadCounter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns key of an extension.
|
||||
*
|
||||
* @return string extension key
|
||||
* @see $extensionKey, getAll()
|
||||
*/
|
||||
public function getExtkey()
|
||||
{
|
||||
return $this->extensionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns last uploaddate of an extension's version.
|
||||
*
|
||||
* @return string last upload date of an extension's version
|
||||
* @see $lastuploaddate, getAll()
|
||||
*/
|
||||
public function getLastuploaddate()
|
||||
{
|
||||
return $this->lastuploaddate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns username of extension owner.
|
||||
*
|
||||
* @return string extension owner's username
|
||||
* @see $ownerusername, getAll()
|
||||
*/
|
||||
public function getOwnerusername()
|
||||
{
|
||||
return $this->ownerusername;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns review state of an extension's version.
|
||||
*
|
||||
* @return string extension review state
|
||||
* @see $reviewstate, getAll()
|
||||
*/
|
||||
public function getReviewstate()
|
||||
{
|
||||
return $this->reviewstate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns state of an extension's version.
|
||||
*
|
||||
* @return string extension state
|
||||
* @see $state, getAll()
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns t3x file hash of an extension's version.
|
||||
*
|
||||
* @return string t3x file hash
|
||||
* @see $t3xfilemd5, getAll()
|
||||
*/
|
||||
public function getT3xfilemd5()
|
||||
{
|
||||
return $this->t3xfilemd5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns title of an extension's version.
|
||||
*
|
||||
* @return string extension title
|
||||
* @see $title, getAll()
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns extension upload comment.
|
||||
*
|
||||
* @return string extension upload comment
|
||||
* @see $uploadcomment, getAll()
|
||||
*/
|
||||
public function getUploadcomment()
|
||||
{
|
||||
return $this->uploadcomment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns version number.
|
||||
*
|
||||
* @return string version number
|
||||
* @see $version, getAll()
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method resets version class properties.
|
||||
*
|
||||
* @param bool $resetAll If TRUE, additionally extension properties are reset
|
||||
* @see $extensionKey, $version, $extensionDLCounter, $versionDLCounter,
|
||||
*/
|
||||
protected function resetProperties($resetAll = false)
|
||||
{
|
||||
// resetting at least class property "version" is mandatory
|
||||
// as we need to do some magic in regards to
|
||||
// an extension's and version's child node "downloadcounter"
|
||||
$this->version = $this->title = $this->versionDownloadCounter = $this->description = $this->state = $this->reviewstate = $this->category = $this->lastuploaddate = $this->uploadcomment = $this->dependencies = $this->authorname = $this->authoremail = $this->authorcompany = $this->ownerusername = $this->t3xfilemd5 = null;
|
||||
if ($resetAll) {
|
||||
$this->extensionKey = $this->extensionDownloadCounter = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert dependencies from TER format to EM_CONF format
|
||||
*
|
||||
* @param string $dependencies serialized dependency array
|
||||
* @return string
|
||||
*/
|
||||
protected function convertDependencies($dependencies)
|
||||
{
|
||||
$newDependencies = [];
|
||||
$dependenciesArray = unserialize($dependencies, ['allowed_classes' => false]);
|
||||
if (is_array($dependenciesArray)) {
|
||||
foreach ($dependenciesArray as $version) {
|
||||
if (!empty($version['kind']) && !empty($version['extensionKey'])) {
|
||||
$newDependencies[$version['kind']][$version['extensionKey']] = $version['versionRange'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return serialize($newDependencies);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract parser for TYPO3's mirror.xml file.
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
abstract class AbstractMirrorXmlParser extends AbstractXmlParser
|
||||
{
|
||||
/**
|
||||
* Keeps country of currently processed mirror.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $country;
|
||||
|
||||
/**
|
||||
* Keeps hostname of currently processed mirror.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* Keeps path to mirrored TER of currently processed mirror.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Keeps sponsor link of currently processed mirror.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $sponsorlink;
|
||||
|
||||
/**
|
||||
* Keeps sponsor logo location of currently processed mirror.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $sponsorlogo;
|
||||
|
||||
/**
|
||||
* Keeps sponsor name of currently processed mirror.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $sponsorname;
|
||||
|
||||
/**
|
||||
* Keeps title of currently processed mirror.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* Returns an associative array of all mirror properties.
|
||||
*
|
||||
* Valid array keys of returned array are:
|
||||
* country, host, path, sponsorlink, sponsorlogo, sponsorname, title
|
||||
*
|
||||
* @return array assoziative array of a mirror's properties
|
||||
* @see $country, $host, $path, $sponsorlink, $sponsorlogo, $sponsorname, $title
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
$mirrorProperties = [];
|
||||
$mirrorProperties['title'] = $this->title;
|
||||
$mirrorProperties['host'] = $this->host;
|
||||
$mirrorProperties['path'] = $this->path;
|
||||
$mirrorProperties['country'] = $this->country;
|
||||
$mirrorProperties['sponsorname'] = $this->sponsorname;
|
||||
$mirrorProperties['sponsorlink'] = $this->sponsorlink;
|
||||
$mirrorProperties['sponsorlogo'] = $this->sponsorlogo;
|
||||
return $mirrorProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns country of currently processed mirror.
|
||||
*
|
||||
* @return string name of country a mirror is located in
|
||||
* @see $country, getAll()
|
||||
*/
|
||||
public function getCountry()
|
||||
{
|
||||
return $this->country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns host of currently processed mirror.
|
||||
*
|
||||
* @return string host name
|
||||
* @see $host, getAll()
|
||||
*/
|
||||
public function getHost()
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to mirrored TER of currently processed mirror.
|
||||
*
|
||||
* @return string path name
|
||||
* @see $path, getAll()
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sponsor link of currently processed mirror.
|
||||
*
|
||||
* @return string URL of a sponsor's website
|
||||
* @see $sponsorlink, getAll()
|
||||
*/
|
||||
public function getSponsorlink()
|
||||
{
|
||||
return $this->sponsorlink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sponsor logo location of currently processed mirror.
|
||||
*
|
||||
* @return string a sponsor's logo location
|
||||
* @see $sponsorlogo, getAll()
|
||||
*/
|
||||
public function getSponsorlogo()
|
||||
{
|
||||
return $this->sponsorlogo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sponsor name of currently processed mirror.
|
||||
*
|
||||
* @return string name of sponsor
|
||||
* @see $sponsorname, getAll()
|
||||
*/
|
||||
public function getSponsorname()
|
||||
{
|
||||
return $this->sponsorname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns title of currently processed mirror.
|
||||
*
|
||||
* @return string title of mirror
|
||||
* @see $title, getAll()
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method resets version class properties.
|
||||
*
|
||||
* @see $country, $host, $path, $sponsorlink, $sponsorlogo, $sponsorname, $title
|
||||
*/
|
||||
protected function resetProperties()
|
||||
{
|
||||
$this->title = $this->host = $this->path = $this->country
|
||||
= $this->sponsorname = $this->sponsorlink = $this->sponsorlogo = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract parser for EM related TYPO3 xml files.
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
abstract class AbstractXmlParser implements \SplSubject
|
||||
{
|
||||
/**
|
||||
* Keeps XML parser instance.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $objXml;
|
||||
|
||||
/**
|
||||
* Keeps name of required PHP extension
|
||||
* for this class to work properly.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $requiredPhpExtensions;
|
||||
|
||||
/**
|
||||
* Keeps list of attached observers.
|
||||
*
|
||||
* @var \SplObserver[]
|
||||
*/
|
||||
protected $observers = [];
|
||||
|
||||
/**
|
||||
* Method attaches an observer.
|
||||
*
|
||||
* @param \SplObserver $observer an observer to attach
|
||||
* @see $observers, detach(), notify()
|
||||
*/
|
||||
public function attach(\SplObserver $observer)
|
||||
{
|
||||
$this->observers[] = $observer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method detaches an attached observer
|
||||
*
|
||||
* @param \SplObserver $observer an observer to detach
|
||||
* @see $observers, attach(), notify()
|
||||
*/
|
||||
public function detach(\SplObserver $observer)
|
||||
{
|
||||
$key = array_search($observer, $this->observers, true);
|
||||
if ($key !== false) {
|
||||
unset($this->observers[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method notifies attached observers.
|
||||
*
|
||||
* @see $observers, attach(), detach()
|
||||
*/
|
||||
public function notify()
|
||||
{
|
||||
foreach ($this->observers as $observer) {
|
||||
$observer->update($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method determines if a necessary PHP extension is available.
|
||||
*
|
||||
* Method tries to load the extension if necessary and possible.
|
||||
*
|
||||
* @return bool TRUE, if PHP extension is available, otherwise FALSE
|
||||
*/
|
||||
public function isAvailable()
|
||||
{
|
||||
$isAvailable = true;
|
||||
if (!extension_loaded($this->requiredPhpExtensions)) {
|
||||
$prefix = PHP_SHLIB_SUFFIX === 'dll' ? 'php_' : '';
|
||||
if (!(((bool)ini_get('enable_dl') && !(bool)ini_get('safe_mode')) && function_exists('dl') && dl($prefix . $this->requiredPhpExtensions . PHP_SHLIB_SUFFIX))) {
|
||||
$isAvailable = false;
|
||||
}
|
||||
}
|
||||
return $isAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method parses an XML file.
|
||||
*
|
||||
* @param string $file GZIP stream resource
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException in case of XML parser errors
|
||||
*/
|
||||
abstract public function parseXml($file);
|
||||
|
||||
/**
|
||||
* Create required parser
|
||||
*/
|
||||
abstract protected function createParser();
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parser for TYPO3's extension.xml file.
|
||||
*
|
||||
* Depends on PHP ext/xmlreader which should be available
|
||||
* with PHP >= 5.1.0.
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class ExtensionXmlPullParser extends AbstractExtensionXmlParser
|
||||
{
|
||||
/**
|
||||
* Class constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->requiredPhpExtensions = 'xmlreader';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create required parser
|
||||
*/
|
||||
protected function createParser()
|
||||
{
|
||||
$this->objXml = new \XMLReader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method parses an extensions.xml file.
|
||||
*
|
||||
* @param string $file GZIP stream resource
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException in case of parser error
|
||||
*/
|
||||
public function parseXml($file)
|
||||
{
|
||||
$this->createParser();
|
||||
if (!(is_object($this->objXml) && get_class($this->objXml) === 'XMLReader')) {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Unable to create XML parser.', 1342640540);
|
||||
}
|
||||
if ($this->objXml->open($file, 'utf-8') === false) {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(
|
||||
sprintf('Unable to open file resource %s.', $file),
|
||||
1476108651
|
||||
);
|
||||
}
|
||||
while ($this->objXml->read()) {
|
||||
if ($this->objXml->nodeType == \XMLReader::ELEMENT) {
|
||||
$this->startElement($this->objXml->name);
|
||||
} else {
|
||||
if ($this->objXml->nodeType == \XMLReader::END_ELEMENT) {
|
||||
$this->endElement($this->objXml->name);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->objXml->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method is invoked when parser accesses start tag of an element.
|
||||
*
|
||||
* @param string $elementName element name at parser's current position
|
||||
*/
|
||||
protected function startElement($elementName)
|
||||
{
|
||||
switch ($elementName) {
|
||||
case 'extension':
|
||||
$this->extensionKey = $this->objXml->getAttribute('extensionkey');
|
||||
break;
|
||||
case 'version':
|
||||
$this->version = $this->objXml->getAttribute('version');
|
||||
break;
|
||||
case 'downloadcounter':
|
||||
// downloadcounter could be a child node of
|
||||
// extension or version
|
||||
if ($this->version == null) {
|
||||
$this->extensionDownloadCounter = $this->getElementValue($elementName);
|
||||
} else {
|
||||
$this->versionDownloadCounter = $this->getElementValue($elementName);
|
||||
}
|
||||
break;
|
||||
case 'title':
|
||||
$this->title = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'description':
|
||||
$this->description = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'state':
|
||||
$this->state = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'reviewstate':
|
||||
$this->reviewstate = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'category':
|
||||
$this->category = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'lastuploaddate':
|
||||
$this->lastuploaddate = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'uploadcomment':
|
||||
$this->uploadcomment = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'dependencies':
|
||||
$this->dependencies = $this->convertDependencies($this->getElementValue($elementName));
|
||||
break;
|
||||
case 'authorname':
|
||||
$this->authorname = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'authoremail':
|
||||
$this->authoremail = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'authorcompany':
|
||||
$this->authorcompany = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'ownerusername':
|
||||
$this->ownerusername = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 't3xfilemd5':
|
||||
$this->t3xfilemd5 = $this->getElementValue($elementName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method is invoked when parser accesses end tag of an element.
|
||||
*
|
||||
* @param string $elementName: element name at parser's current position
|
||||
*/
|
||||
protected function endElement($elementName)
|
||||
{
|
||||
switch ($elementName) {
|
||||
case 'extension':
|
||||
$this->resetProperties(true);
|
||||
break;
|
||||
case 'version':
|
||||
$this->notify();
|
||||
$this->resetProperties();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns the value of an element at XMLReader's current
|
||||
* position.
|
||||
*
|
||||
* Method will read until it finds the end of the given element.
|
||||
* If element has no value, method returns NULL.
|
||||
*
|
||||
* @param string $elementName: name of element to retrieve it's value from
|
||||
* @return string an element's value if it has a value, otherwise NULL
|
||||
*/
|
||||
protected function getElementValue(&$elementName)
|
||||
{
|
||||
$value = null;
|
||||
if (!$this->objXml->isEmptyElement) {
|
||||
$value = '';
|
||||
while ($this->objXml->read()) {
|
||||
if ($this->objXml->nodeType == \XMLReader::TEXT || $this->objXml->nodeType == \XMLReader::CDATA || $this->objXml->nodeType == \XMLReader::WHITESPACE || $this->objXml->nodeType == \XMLReader::SIGNIFICANT_WHITESPACE) {
|
||||
$value .= $this->objXml->value;
|
||||
} else {
|
||||
if ($this->objXml->nodeType == \XMLReader::END_ELEMENT && $this->objXml->name === $elementName) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parser for TYPO3's extension.xml file.
|
||||
*
|
||||
* Depends on PHP ext/xml which should be available
|
||||
* with PHP 4+. This is the parser used in TYPO3
|
||||
* Core <= 4.3 (without the "collect all data in one
|
||||
* array" behaviour).
|
||||
* Notice: ext/xml has proven to be buggy with entities.
|
||||
* Use at least PHP 5.2.9+ and libxml2 2.7.3+!
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class ExtensionXmlPushParser extends AbstractExtensionXmlParser
|
||||
{
|
||||
/**
|
||||
* Keeps current data of element to process.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $elementData = '';
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->requiredPhpExtensions = 'xml';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create required parser
|
||||
*/
|
||||
protected function createParser()
|
||||
{
|
||||
$this->objXml = xml_parser_create();
|
||||
xml_set_object($this->objXml, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method parses an extensions.xml file.
|
||||
*
|
||||
* @param string $file GZIP stream resource
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException in case of parse errors
|
||||
*/
|
||||
public function parseXml($file)
|
||||
{
|
||||
$this->createParser();
|
||||
if (!is_resource($this->objXml)) {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Unable to create XML parser.', 1342640663);
|
||||
}
|
||||
// Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
|
||||
$previousValueOfEntityLoader = libxml_disable_entity_loader(true);
|
||||
// keep original character case of XML document
|
||||
xml_parser_set_option($this->objXml, XML_OPTION_CASE_FOLDING, false);
|
||||
xml_parser_set_option($this->objXml, XML_OPTION_SKIP_WHITE, false);
|
||||
xml_parser_set_option($this->objXml, XML_OPTION_TARGET_ENCODING, 'utf-8');
|
||||
xml_set_element_handler($this->objXml, 'startElement', 'endElement');
|
||||
xml_set_character_data_handler($this->objXml, 'characterData');
|
||||
if (!($fp = fopen($file, 'r'))) {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('Unable to open file resource %s.', $file), 1342640689);
|
||||
}
|
||||
while ($data = fread($fp, 4096)) {
|
||||
if (!xml_parse($this->objXml, $data, feof($fp))) {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('XML error %s in line %u of file resource %s.', xml_error_string(xml_get_error_code($this->objXml)), xml_get_current_line_number($this->objXml), $file), 1342640703);
|
||||
}
|
||||
}
|
||||
libxml_disable_entity_loader($previousValueOfEntityLoader);
|
||||
xml_parser_free($this->objXml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method is invoked when parser accesses start tag of an element.
|
||||
*
|
||||
* @param resource $parser parser resource
|
||||
* @param string $elementName element name at parser's current position
|
||||
* @param array $attrs array of an element's attributes if available
|
||||
*/
|
||||
protected function startElement($parser, $elementName, $attrs)
|
||||
{
|
||||
switch ($elementName) {
|
||||
case 'extension':
|
||||
$this->extensionKey = $attrs['extensionkey'];
|
||||
break;
|
||||
case 'version':
|
||||
$this->version = $attrs['version'];
|
||||
break;
|
||||
default:
|
||||
$this->elementData = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method is invoked when parser accesses end tag of an element.
|
||||
*
|
||||
* @param resource $parser parser resource
|
||||
* @param string $elementName Element name at parser's current position
|
||||
*/
|
||||
protected function endElement($parser, $elementName)
|
||||
{
|
||||
switch ($elementName) {
|
||||
case 'extension':
|
||||
$this->resetProperties(true);
|
||||
break;
|
||||
case 'version':
|
||||
$this->notify();
|
||||
$this->resetProperties();
|
||||
break;
|
||||
case 'downloadcounter':
|
||||
// downloadcounter could be a child node of
|
||||
// extension or version
|
||||
if ($this->version == null) {
|
||||
$this->extensionDownloadCounter = $this->elementData;
|
||||
} else {
|
||||
$this->versionDownloadCounter = $this->elementData;
|
||||
}
|
||||
break;
|
||||
case 'title':
|
||||
$this->title = $this->elementData;
|
||||
break;
|
||||
case 'description':
|
||||
$this->description = $this->elementData;
|
||||
break;
|
||||
case 'state':
|
||||
$this->state = $this->elementData;
|
||||
break;
|
||||
case 'reviewstate':
|
||||
$this->reviewstate = $this->elementData;
|
||||
break;
|
||||
case 'category':
|
||||
$this->category = $this->elementData;
|
||||
break;
|
||||
case 'lastuploaddate':
|
||||
$this->lastuploaddate = $this->elementData;
|
||||
break;
|
||||
case 'uploadcomment':
|
||||
$this->uploadcomment = $this->elementData;
|
||||
break;
|
||||
case 'dependencies':
|
||||
$this->dependencies = $this->convertDependencies($this->elementData);
|
||||
break;
|
||||
case 'authorname':
|
||||
$this->authorname = $this->elementData;
|
||||
break;
|
||||
case 'authoremail':
|
||||
$this->authoremail = $this->elementData;
|
||||
break;
|
||||
case 'authorcompany':
|
||||
$this->authorcompany = $this->elementData;
|
||||
break;
|
||||
case 'ownerusername':
|
||||
$this->ownerusername = $this->elementData;
|
||||
break;
|
||||
case 't3xfilemd5':
|
||||
$this->t3xfilemd5 = $this->elementData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method is invoked when parser accesses any character other than elements.
|
||||
*
|
||||
* @param resource $parser parser resource
|
||||
* @param string $data An element's value
|
||||
*/
|
||||
protected function characterData($parser, $data)
|
||||
{
|
||||
$this->elementData .= $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parser for TYPO3's mirrors.xml file.
|
||||
*
|
||||
* Depends on PHP ext/xmlreader which should be available
|
||||
* with PHP >= 5.1.0.
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class MirrorXmlPullParser extends AbstractMirrorXmlParser
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->requiredPhpExtensions = 'xmlreader';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create required parser
|
||||
*/
|
||||
protected function createParser()
|
||||
{
|
||||
$this->objXml = new \XMLReader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method parses an extensions.xml file.
|
||||
*
|
||||
* @param string $file file resource, typically a stream
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException in case of XML parser errors
|
||||
*/
|
||||
public function parseXml($file)
|
||||
{
|
||||
$this->createParser();
|
||||
if (!(is_object($this->objXml) && get_class($this->objXml) === 'XMLReader')) {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Unable to create XML parser.', 1342640820);
|
||||
}
|
||||
if ($this->objXml->open($file, 'utf-8') === false) {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('Unable to open file resource %s.', $file), 1342640893);
|
||||
}
|
||||
while ($this->objXml->read()) {
|
||||
if ($this->objXml->nodeType == \XMLReader::ELEMENT) {
|
||||
$this->startElement($this->objXml->name);
|
||||
} else {
|
||||
if ($this->objXml->nodeType == \XMLReader::END_ELEMENT) {
|
||||
$this->endElement($this->objXml->name);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->objXml->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method is invoked when parser accesses start tag of an element.
|
||||
*
|
||||
* @param string $elementName element name at parser's current position
|
||||
* @see endElement()
|
||||
*/
|
||||
protected function startElement($elementName)
|
||||
{
|
||||
switch ($elementName) {
|
||||
case 'title':
|
||||
$this->title = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'host':
|
||||
$this->host = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'path':
|
||||
$this->path = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'country':
|
||||
$this->country = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'name':
|
||||
$this->sponsorname = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'link':
|
||||
$this->sponsorlink = $this->getElementValue($elementName);
|
||||
break;
|
||||
case 'logo':
|
||||
$this->sponsorlogo = $this->getElementValue($elementName);
|
||||
break;
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method is invoked when parser accesses end tag of an element.
|
||||
*
|
||||
* @param string $elementName element name at parser's current position
|
||||
* @see startElement()
|
||||
*/
|
||||
protected function endElement($elementName)
|
||||
{
|
||||
switch ($elementName) {
|
||||
case 'mirror':
|
||||
$this->notify();
|
||||
$this->resetProperties();
|
||||
break;
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns the value of an element at XMLReader's current
|
||||
* position.
|
||||
*
|
||||
* Method will read until it finds the end of the given element.
|
||||
* If element has no value, method returns NULL.
|
||||
*
|
||||
* @param string &$elementName name of element to retrieve it's value from
|
||||
* @return string an element's value if it has a value, otherwise NULL
|
||||
*/
|
||||
protected function getElementValue(&$elementName)
|
||||
{
|
||||
$value = null;
|
||||
if (!$this->objXml->isEmptyElement) {
|
||||
$value = '';
|
||||
while ($this->objXml->read()) {
|
||||
if ($this->objXml->nodeType == \XMLReader::TEXT || $this->objXml->nodeType == \XMLReader::CDATA || $this->objXml->nodeType == \XMLReader::WHITESPACE || $this->objXml->nodeType == \XMLReader::SIGNIFICANT_WHITESPACE) {
|
||||
$value .= $this->objXml->value;
|
||||
} else {
|
||||
if ($this->objXml->nodeType == \XMLReader::END_ELEMENT && $this->objXml->name === $elementName) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parser for TYPO3's mirrors.xml file.
|
||||
*
|
||||
* Depends on PHP ext/xml which should be available
|
||||
* with PHP 4+. This is the parser used in TYPO3
|
||||
* Core <= 4.3 (without the "collect all data in one
|
||||
* array" behaviour).
|
||||
* Notice: ext/xml has proven to be buggy with entities.
|
||||
* Use at least PHP 5.2.9+ and libxml2 2.7.3+!
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class MirrorXmlPushParser extends AbstractMirrorXmlParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $element;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->requiredPhpExtensions = 'xml';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create required parser
|
||||
*/
|
||||
protected function createParser()
|
||||
{
|
||||
$this->objXml = xml_parser_create();
|
||||
xml_set_object($this->objXml, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method parses a mirror.xml file.
|
||||
*
|
||||
* @param string $file GZIP stream resource
|
||||
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException in case of XML parser errors
|
||||
*/
|
||||
public function parseXml($file)
|
||||
{
|
||||
$this->createParser();
|
||||
if (!is_resource($this->objXml)) {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Unable to create XML parser.', 1342641009);
|
||||
}
|
||||
// Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
|
||||
$previousValueOfEntityLoader = libxml_disable_entity_loader(true);
|
||||
// keep original character case of XML document
|
||||
xml_parser_set_option($this->objXml, XML_OPTION_CASE_FOLDING, false);
|
||||
xml_parser_set_option($this->objXml, XML_OPTION_SKIP_WHITE, false);
|
||||
xml_parser_set_option($this->objXml, XML_OPTION_TARGET_ENCODING, 'utf-8');
|
||||
xml_set_element_handler($this->objXml, 'startElement', 'endElement');
|
||||
xml_set_character_data_handler($this->objXml, 'characterData');
|
||||
if (!($fp = fopen($file, 'r'))) {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('Unable to open file resource %s.', $file), 1342641010);
|
||||
}
|
||||
while ($data = fread($fp, 4096)) {
|
||||
if (!xml_parse($this->objXml, $data, feof($fp))) {
|
||||
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('XML error %s in line %u of file resource %s.', xml_error_string(xml_get_error_code($this->objXml)), xml_get_current_line_number($this->objXml), $file), 1342641011);
|
||||
}
|
||||
}
|
||||
libxml_disable_entity_loader($previousValueOfEntityLoader);
|
||||
xml_parser_free($this->objXml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method is invoked when parser accesses start tag of an element.
|
||||
*
|
||||
* @param resource $parser parser resource
|
||||
* @param string $elementName element name at parser's current position
|
||||
* @param array $attrs array of an element's attributes if available
|
||||
*/
|
||||
protected function startElement($parser, $elementName, $attrs)
|
||||
{
|
||||
switch ($elementName) {
|
||||
default:
|
||||
$this->element = $elementName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method is invoked when parser accesses end tag of an element.
|
||||
* Although the first parameter seems unused, it needs to be there for
|
||||
* adherence to the API of xml_set_element_handler
|
||||
*
|
||||
* @see xml_set_element_handler
|
||||
* @param resource $parser parser resource
|
||||
* @param string $elementName element name at parser's current position
|
||||
*/
|
||||
protected function endElement($parser, $elementName)
|
||||
{
|
||||
switch ($elementName) {
|
||||
case 'mirror':
|
||||
$this->notify();
|
||||
$this->resetProperties();
|
||||
break;
|
||||
default:
|
||||
$this->element = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method is invoked when parser accesses any character other than elements.
|
||||
* Although the first parameter seems unused, it needs to be there for
|
||||
* adherence to the API of xml_set_character_data_handler
|
||||
*
|
||||
* @see xml_set_character_data_handler
|
||||
* @param resource $parser parser resource
|
||||
* @param string $data an element's value
|
||||
*/
|
||||
protected function characterData($parser, $data)
|
||||
{
|
||||
if (isset($this->element)) {
|
||||
switch ($this->element) {
|
||||
case 'title':
|
||||
$this->title = $data;
|
||||
break;
|
||||
case 'host':
|
||||
$this->host = $data;
|
||||
break;
|
||||
case 'path':
|
||||
$this->path = $data;
|
||||
break;
|
||||
case 'country':
|
||||
$this->country = $data;
|
||||
break;
|
||||
case 'name':
|
||||
$this->sponsorname = $data;
|
||||
break;
|
||||
case 'link':
|
||||
$this->sponsorlink = $data;
|
||||
break;
|
||||
case 'logo':
|
||||
$this->sponsorlogo = $data;
|
||||
break;
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Factory for XML parsers.
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class XmlParserFactory
|
||||
{
|
||||
/**
|
||||
* An array with instances of xml parsers.
|
||||
* This member is set in the getParserInstance() function.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $instance = [];
|
||||
|
||||
/**
|
||||
* Keeps array of all available parsers.
|
||||
*
|
||||
* @todo This would better be moved to a global configuration array like
|
||||
* $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']. (might require EM to be moved in a sysext)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $parsers = [
|
||||
'extension' => [
|
||||
\TYPO3\CMS\Extensionmanager\Utility\Parser\ExtensionXmlPushParser::class => 'ExtensionXmlPushParser.php',
|
||||
\TYPO3\CMS\Extensionmanager\Utility\Parser\ExtensionXmlPullParser::class => 'ExtensionXmlPullParser.php',
|
||||
],
|
||||
'mirror' => [
|
||||
\TYPO3\CMS\Extensionmanager\Utility\Parser\MirrorXmlPushParser::class => 'MirrorXmlPushParser.php',
|
||||
\TYPO3\CMS\Extensionmanager\Utility\Parser\MirrorXmlPullParser::class=> 'MirrorXmlPullParser.php',
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* Obtains a xml parser instance.
|
||||
*
|
||||
* This function will return an instance of a class that implements
|
||||
* \TYPO3\CMS\Extensionmanager\Utility\Parser\AbstractExtensionXmlParser
|
||||
*
|
||||
* @param string $parserType type of parser, one of extension and mirror
|
||||
* @param string $excludeClassNames (optional) comma-separated list of class names
|
||||
* @return \TYPO3\CMS\Extensionmanager\Utility\Parser\AbstractExtensionXmlParser an instance of an extension.xml parser
|
||||
*/
|
||||
public static function getParserInstance($parserType, $excludeClassNames = '')
|
||||
{
|
||||
if (!isset(self::$instance[$parserType]) || !is_object(self::$instance[$parserType]) || !empty($excludeClassNames)) {
|
||||
// reset instance
|
||||
self::$instance[$parserType] = ($objParser = null);
|
||||
foreach (self::$parsers[$parserType] as $className => $file) {
|
||||
if (!\TYPO3\CMS\Core\Utility\GeneralUtility::inList($excludeClassNames, $className)) {
|
||||
$objParser = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance($className);
|
||||
if ($objParser->isAvailable()) {
|
||||
self::$instance[$parserType] = &$objParser;
|
||||
break;
|
||||
}
|
||||
$objParser = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return self::$instance[$parserType];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility\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!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
use TYPO3\CMS\Extensionmanager\Domain\Model\Repository;
|
||||
use TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository;
|
||||
use TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository;
|
||||
use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
|
||||
use TYPO3\CMS\Extensionmanager\Utility\Importer\ExtensionListUtility;
|
||||
use TYPO3\CMS\Extensionmanager\Utility\Importer\MirrorListUtility;
|
||||
|
||||
/**
|
||||
* Central utility class for repository handling.
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class Helper implements \TYPO3\CMS\Core\SingletonInterface
|
||||
{
|
||||
/**
|
||||
* ##########################################
|
||||
* Problem constants - to be used in bitmasks
|
||||
* ##########################################
|
||||
*/
|
||||
|
||||
/**
|
||||
* Type of problem: extension file not existing in file system.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const PROBLEM_EXTENSION_FILE_NOT_EXISTING = 1;
|
||||
/**
|
||||
* Type of problem: wrong hash indicates outdated extension file.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const PROBLEM_EXTENSION_HASH_CHANGED = 2;
|
||||
|
||||
/**
|
||||
* Type of problem: no version records in database.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const PROBLEM_NO_VERSIONS_IN_DATABASE = 4;
|
||||
|
||||
/**
|
||||
* Keeps instance of repository class.
|
||||
*
|
||||
* @var Repository
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* @var RepositoryRepository
|
||||
*/
|
||||
protected $repositoryRepository;
|
||||
|
||||
/**
|
||||
* @var ExtensionRepository
|
||||
*/
|
||||
protected $extensionRepository;
|
||||
|
||||
/**
|
||||
* @var ObjectManager
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
||||
$repositoryRepository = $this->objectManager->get(RepositoryRepository::class);
|
||||
$this->extensionRepository = $this->objectManager->get(ExtensionRepository::class);
|
||||
/** @var Repository $repository */
|
||||
$repository = $repositoryRepository->findOneTypo3OrgRepository();
|
||||
if (is_object($repository)) {
|
||||
$this->setRepository($repository);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method registers required repository instance to work with.
|
||||
*
|
||||
* Repository instance is passed by reference.
|
||||
*
|
||||
* @param Repository $repository
|
||||
* @see $repository
|
||||
*/
|
||||
public function setRepository(Repository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method fetches extension list file from remote server.
|
||||
*
|
||||
* Delegates to {@link fetchFile()}.
|
||||
*
|
||||
* @throws ExtensionManagerException
|
||||
* @see fetchFile()
|
||||
*/
|
||||
public function fetchExtListFile()
|
||||
{
|
||||
$this->fetchFile($this->getRemoteExtListFile(), $this->getLocalExtListFile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method fetches mirror list file from remote server.
|
||||
*
|
||||
* Delegates to {@link fetchFile()}.
|
||||
*
|
||||
* @throws ExtensionManagerException
|
||||
* @see fetchFile()
|
||||
*/
|
||||
public function fetchMirrorListFile()
|
||||
{
|
||||
$this->fetchFile($this->getRemoteMirrorListFile(), $this->getLocalMirrorListFile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method fetches contents from remote server and
|
||||
* writes them into a file in the local file system.
|
||||
*
|
||||
* @param string $remoteResource remote resource to read contents from
|
||||
* @param string $localResource local resource (absolute file path) to store retrieved contents to (must be within typo3temp/)
|
||||
* @see \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl(), \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile()
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
protected function fetchFile($remoteResource, $localResource)
|
||||
{
|
||||
$isOffline = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('extensionmanager', 'offlineMode');
|
||||
if ($isOffline) {
|
||||
throw new ExtensionManagerException('Extension Manager is in offline mode. No TER connection available.', 1437078780);
|
||||
}
|
||||
if (is_string($remoteResource) && is_string($localResource) && !empty($remoteResource) && !empty($localResource)) {
|
||||
$fileContent = GeneralUtility::getUrl($remoteResource);
|
||||
if ($fileContent !== false) {
|
||||
if (GeneralUtility::writeFileToTypo3tempDir($localResource, $fileContent) !== null) {
|
||||
throw new ExtensionManagerException(sprintf('Could not write to file %s.', $localResource), 1342635378);
|
||||
}
|
||||
} else {
|
||||
throw new ExtensionManagerException(sprintf('Could not access remote resource %s.', $remoteResource), 1342635425);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns location of local extension list file.
|
||||
*
|
||||
* @return string local location of file
|
||||
* @see getRemoteExtListFile()
|
||||
*/
|
||||
public function getLocalExtListFile()
|
||||
{
|
||||
return Environment::getVarPath() . '/extensionmanager/' . (int)$this->repository->getUid() . '.extensions.xml.gz';
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns location of remote extension list file.
|
||||
*
|
||||
* @return string remote location of file
|
||||
* @see getLocalExtListFile()
|
||||
*/
|
||||
public function getRemoteExtListFile()
|
||||
{
|
||||
$mirror = $this->getMirrors(true)->getMirror();
|
||||
$filePath = 'https://' . $mirror['host'] . $mirror['path'] . 'extensions.xml.gz';
|
||||
return $filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns location of remote file containing
|
||||
* the extension checksum hash.
|
||||
*
|
||||
* @return string remote location of file
|
||||
*/
|
||||
public function getRemoteExtHashFile()
|
||||
{
|
||||
$mirror = $this->getMirrors(true)->getMirror();
|
||||
$filePath = 'https://' . $mirror['host'] . $mirror['path'] . 'extensions.md5';
|
||||
return $filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns location of local mirror list file.
|
||||
*
|
||||
* @return string local location of file
|
||||
* @see getRemoteMirrorListFile()
|
||||
*/
|
||||
public function getLocalMirrorListFile()
|
||||
{
|
||||
return Environment::getVarPath() . '/extensionmanager/' . (int)$this->repository->getUid() . '.mirrors.xml.gz';
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns location of remote mirror list file.
|
||||
*
|
||||
* @return string remote location of file
|
||||
* @see getLocalMirrorListFile()
|
||||
*/
|
||||
public function getRemoteMirrorListFile()
|
||||
{
|
||||
$filePath = $this->repository->getMirrorListUrl();
|
||||
return $filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns available mirrors for registered repository.
|
||||
*
|
||||
* If there are no mirrors registered to the repository,
|
||||
* the method will retrieve them from file system or remote
|
||||
* server.
|
||||
*
|
||||
* @param bool $forcedUpdateFromRemote if boolean TRUE, mirror configuration will always retrieved from remote server
|
||||
* @return \TYPO3\CMS\Extensionmanager\Domain\Model\Mirrors instance of repository mirrors class
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
public function getMirrors($forcedUpdateFromRemote = true)
|
||||
{
|
||||
$assignedMirror = $this->repository->getMirrors();
|
||||
if ($forcedUpdateFromRemote || $assignedMirror === null || !is_object($assignedMirror)) {
|
||||
if ($forcedUpdateFromRemote || !is_file($this->getLocalMirrorListFile())) {
|
||||
$this->fetchMirrorListFile();
|
||||
}
|
||||
$objMirrorListImporter = GeneralUtility::makeInstance(MirrorListUtility::class);
|
||||
$this->repository->addMirrors($objMirrorListImporter->getMirrors($this->getLocalMirrorListFile()));
|
||||
}
|
||||
return $this->repository->getMirrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns information if currently available
|
||||
* extension list might be outdated.
|
||||
*
|
||||
* @see \TYPO3\CMS\Extensionmanager\Utility\Repository\Helper::PROBLEM_NO_VERSIONS_IN_DATABASE,
|
||||
* @throws ExtensionManagerException
|
||||
* @return int "0" if everything is perfect, otherwise bitmask with problems
|
||||
*/
|
||||
public function isExtListUpdateNecessary()
|
||||
{
|
||||
if ($this->repository === null) {
|
||||
throw new ExtensionManagerException('No extension repository was found.', 1500060252);
|
||||
}
|
||||
$updateNecessity = 0;
|
||||
if ($this->extensionRepository->countByRepository($this->repository->getUid()) <= 0) {
|
||||
$updateNecessity |= self::PROBLEM_NO_VERSIONS_IN_DATABASE;
|
||||
}
|
||||
if (!is_file($this->getLocalExtListFile())) {
|
||||
$updateNecessity |= self::PROBLEM_EXTENSION_FILE_NOT_EXISTING;
|
||||
} else {
|
||||
$remotemd5 = GeneralUtility::getUrl($this->getRemoteExtHashFile());
|
||||
if ($remotemd5 !== false) {
|
||||
$localmd5 = md5_file($this->getLocalExtListFile());
|
||||
if ($remotemd5 !== $localmd5) {
|
||||
$updateNecessity |= self::PROBLEM_EXTENSION_HASH_CHANGED;
|
||||
}
|
||||
} else {
|
||||
throw new ExtensionManagerException('Could not retrieve extension hash file from remote server.', 1342635016);
|
||||
}
|
||||
}
|
||||
return $updateNecessity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method updates TYPO3 database with up-to-date
|
||||
* extension version records.
|
||||
*
|
||||
* @return bool TRUE if the extension list was successfully update, FALSE if no update necessary
|
||||
* @throws ExtensionManagerException
|
||||
* @see isExtListUpdateNecessary()
|
||||
*/
|
||||
public function updateExtList()
|
||||
{
|
||||
$updated = false;
|
||||
$updateNecessity = $this->isExtListUpdateNecessary();
|
||||
if ($updateNecessity !== 0) {
|
||||
// retrieval of file necessary
|
||||
$tmpBitmask = self::PROBLEM_EXTENSION_FILE_NOT_EXISTING | self::PROBLEM_EXTENSION_HASH_CHANGED;
|
||||
if (($tmpBitmask & $updateNecessity) > 0) {
|
||||
$this->fetchExtListFile();
|
||||
$updateNecessity &= ~$tmpBitmask;
|
||||
}
|
||||
// database table cleanup
|
||||
if ($updateNecessity & self::PROBLEM_NO_VERSIONS_IN_DATABASE) {
|
||||
$updateNecessity &= ~self::PROBLEM_NO_VERSIONS_IN_DATABASE;
|
||||
} else {
|
||||
// Use straight query as extbase "remove" is too slow here
|
||||
// This truncates the whole table. It would be more correct to remove only rows of a specific
|
||||
// repository, but multiple repository handling is not implemented, and truncate is quicker.
|
||||
GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getConnectionForTable('tx_extensionmanager_domain_model_extension')
|
||||
->truncate('tx_extensionmanager_domain_model_extension');
|
||||
}
|
||||
// no further problems - start of import process
|
||||
if ($updateNecessity === 0) {
|
||||
$uid = $this->repository->getUid();
|
||||
$objExtListImporter = $this->objectManager->get(ExtensionListUtility::class);
|
||||
$objExtListImporter->import($this->getLocalExtListFile(), $uid);
|
||||
$updated = true;
|
||||
}
|
||||
}
|
||||
return $updated;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\Utility;
|
||||
|
||||
/*
|
||||
* 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\Core\Environment;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
|
||||
|
||||
/**
|
||||
* Utility to find and execute class.ext_update.php scripts of extensions
|
||||
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
class UpdateScriptUtility
|
||||
{
|
||||
/**
|
||||
* Returns true, if ext_update class says it wants to run.
|
||||
*
|
||||
* @param string $extensionKey extension key
|
||||
* @return mixed NULL, if update is not available, else update script return
|
||||
*/
|
||||
public function executeUpdateIfNeeded($extensionKey)
|
||||
{
|
||||
$className = $this->requireUpdateScript($extensionKey);
|
||||
$scriptObject = GeneralUtility::makeInstance($className);
|
||||
|
||||
// old em always assumed the method exist, we do so too.
|
||||
// @TODO: Make this smart, let scripts implement interfaces
|
||||
// @TODO: With current ext_update construct it is impossible to enforce some type of return
|
||||
return $scriptObject->access() ? $scriptObject->main() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require update script.
|
||||
* Throws exception if update script does not exist, so checkUpdateScriptExists()
|
||||
* should be called before
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @return string Class name of update script
|
||||
* @throws ExtensionManagerException
|
||||
*/
|
||||
protected function requireUpdateScript($extensionKey)
|
||||
{
|
||||
if (class_exists('ext_update', false)) {
|
||||
throw new ExtensionManagerException(
|
||||
'class ext_update for this run does already exist, requiring impossible',
|
||||
1359748085
|
||||
);
|
||||
}
|
||||
|
||||
$className = $this->determineUpdateClassName($extensionKey);
|
||||
if ($className === '') {
|
||||
throw new ExtensionManagerException(
|
||||
'Requested update script of extension does not exist',
|
||||
1359747976
|
||||
);
|
||||
}
|
||||
return $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an update class file exists.
|
||||
*
|
||||
* Does not check if some update is needed.
|
||||
*
|
||||
* @param string $extensionKey Extension key
|
||||
* @return bool TRUE, if there is some update script and it needs to be executed
|
||||
*/
|
||||
public function checkUpdateScriptExists($extensionKey)
|
||||
{
|
||||
$className = $this->determineUpdateClassName($extensionKey);
|
||||
if ($className !== '') {
|
||||
$updater = GeneralUtility::makeInstance($className);
|
||||
return $updater->access();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the real class name to use
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @return string Returns the final class name if an update script is present, otherwise empty string
|
||||
* @throws ExtensionManagerException If an update script is present but no ext_update class can be loaded
|
||||
*/
|
||||
protected function determineUpdateClassName($extensionKey)
|
||||
{
|
||||
$updateScript = GeneralUtility::getFileAbsFileName('EXT:' . $extensionKey . '/class.ext_update.php');
|
||||
if (!file_exists($updateScript)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// get script contents
|
||||
$scriptSourceCode = GeneralUtility::getUrl($updateScript);
|
||||
// check if it has a namespace
|
||||
if (!preg_match('/<\?php.*namespace\s+([^;]+);.*class/is', $scriptSourceCode, $matches)) {
|
||||
// if no, rename the class with a unique name
|
||||
$className = 'ext_update' . md5($extensionKey . $scriptSourceCode);
|
||||
$temporaryFileName = Environment::getVarPath() . '/transient/' . $className . '.php';
|
||||
if (!file_exists(GeneralUtility::getFileAbsFileName($temporaryFileName))) {
|
||||
$scriptSourceCode = preg_replace('/^\s*class\s+ext_update\s+/m', 'class ' . $className . ' ', $scriptSourceCode);
|
||||
GeneralUtility::writeFileToTypo3tempDir($temporaryFileName, $scriptSourceCode);
|
||||
}
|
||||
$updateScript = $temporaryFileName;
|
||||
} else {
|
||||
$className = $matches[1] . '\ext_update';
|
||||
}
|
||||
include_once $updateScript;
|
||||
if (!class_exists($className, false)) {
|
||||
throw new ExtensionManagerException(
|
||||
sprintf('class.ext_update.php of extension "%s" did not declare ext_update class', $extensionKey),
|
||||
1428176468
|
||||
);
|
||||
}
|
||||
|
||||
return $className;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\ViewHelpers\Be;
|
||||
|
||||
/*
|
||||
* 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\Extensionmanager\Controller\AbstractController;
|
||||
|
||||
/**
|
||||
* Special ViewHelper for the BE module of the Extension Manager. Loads JS code for triggering
|
||||
* refresh events.
|
||||
*
|
||||
* = Examples =
|
||||
*
|
||||
* <code title="Simple">
|
||||
* <em:be.container triggers="{triggers}" />
|
||||
* </code>
|
||||
* <output>
|
||||
* Writes some JS inline code
|
||||
* </output>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class TriggerViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Be\AbstractBackendViewHelper
|
||||
{
|
||||
/**
|
||||
* Initializes the arguments
|
||||
*/
|
||||
public function initializeArguments()
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('triggers', 'array', 'Defined triggers to be forwarded to client (e.g. refreshing backend widgets)', false, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads some JS inline code based on a list of triggers. This is used to reload the main
|
||||
* menu when modules are loaded/unloaded.
|
||||
*
|
||||
* @return string This ViewHelper does not return any content
|
||||
* @see \TYPO3\CMS\Backend\Template\DocumentTemplate
|
||||
* @see \TYPO3\CMS\Core\Page\PageRenderer
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$pageRenderer = $this->getPageRenderer();
|
||||
// Handle triggers
|
||||
if (!empty($this->arguments['triggers'][AbstractController::TRIGGER_RefreshModuleMenu])) {
|
||||
$pageRenderer->addJsInlineCode(
|
||||
AbstractController::TRIGGER_RefreshModuleMenu,
|
||||
'if (top && top.TYPO3.ModuleMenu.App) { top.TYPO3.ModuleMenu.App.refreshMenu(); }'
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($this->arguments['triggers'][AbstractController::TRIGGER_RefreshTopbar])) {
|
||||
$pageRenderer->addJsInlineCode(
|
||||
AbstractController::TRIGGER_RefreshTopbar,
|
||||
'if (top && top.TYPO3.Backend && top.TYPO3.Backend.Topbar) { top.TYPO3.Backend.Topbar.refresh(); }'
|
||||
);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\ViewHelpers;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
use TYPO3\CMS\Core\Utility\PathUtility;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
|
||||
|
||||
/**
|
||||
* Renders the distribution image
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class DistributionImageViewHelper extends AbstractTagBasedViewHelper
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $tagName = 'img';
|
||||
|
||||
/**
|
||||
* Initialize arguments.
|
||||
*/
|
||||
public function initializeArguments()
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('extensionkey', 'string', '', true);
|
||||
$this->registerArgument('width', 'int', 'width of the image');
|
||||
$this->registerArgument('height', 'int', 'height of the image');
|
||||
$this->registerUniversalTagAttributes();
|
||||
$this->registerTagAttribute('alt', 'string', 'Specifies an alternate text for an image', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the destribution preview image for the given extension
|
||||
*
|
||||
* @return string rendered tag.
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$extensionKey = $this->arguments['extensionkey'];
|
||||
$width = $this->arguments['width'];
|
||||
$height = $this->arguments['height'];
|
||||
$content = '';
|
||||
|
||||
$src = $this->findImage($extensionKey);
|
||||
$uri = $this->getImageUri($src);
|
||||
|
||||
if (!empty($uri)) {
|
||||
if ($width) {
|
||||
$this->tag->addAttribute('width', (int)$width);
|
||||
}
|
||||
if ($height) {
|
||||
$this->tag->addAttribute('height', (int)$height);
|
||||
}
|
||||
$this->tag->addAttribute('src', $uri);
|
||||
$content = $this->tag->render();
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the distrubution image
|
||||
*
|
||||
* @param string $extensionKey
|
||||
* @return string
|
||||
*/
|
||||
protected function findImage($extensionKey)
|
||||
{
|
||||
$paths = [];
|
||||
$paths[] = 'EXT:' . $extensionKey . '/Resources/Public/Images/Distribution.svg';
|
||||
$paths[] = 'EXT:' . $extensionKey . '/Resources/Public/Images/Distribution.png';
|
||||
$paths[] = 'EXT:extensionmanager/Resources/Public/Images/Distribution.svg';
|
||||
|
||||
foreach ($paths as $path) {
|
||||
$absFileName = GeneralUtility::getFileAbsFileName($path);
|
||||
if (file_exists($absFileName)) {
|
||||
return $absFileName;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get image uri
|
||||
*
|
||||
* @param string $src
|
||||
* @return string
|
||||
*/
|
||||
protected function getImageUri($src)
|
||||
{
|
||||
$uri = GeneralUtility::getFileAbsFileName($src);
|
||||
if ($uri !== '' && !file_exists($uri)) {
|
||||
$uri = '';
|
||||
}
|
||||
if ($uri !== '') {
|
||||
$uri = '../' . PathUtility::stripPathSitePrefix($uri);
|
||||
}
|
||||
return $uri;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\ViewHelpers;
|
||||
|
||||
/*
|
||||
* 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\Configuration\ExtensionConfiguration;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
|
||||
|
||||
/**
|
||||
* ViewHelper
|
||||
* @internal
|
||||
*/
|
||||
class DownloadExtensionViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormViewHelper
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $tagName = 'form';
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Service\ExtensionService
|
||||
*/
|
||||
protected $extensionService;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Service\ExtensionService $extensionService
|
||||
*/
|
||||
public function injectExtensionService(\TYPO3\CMS\Extbase\Service\ExtensionService $extensionService)
|
||||
{
|
||||
$this->extensionService = $extensionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize arguments.
|
||||
*/
|
||||
public function initializeArguments()
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('extension', Extension::class, '', true);
|
||||
$this->registerTagAttribute('enctype', 'string', 'MIME type with which the form is submitted');
|
||||
$this->registerTagAttribute('method', 'string', 'Transfer type (GET or POST)');
|
||||
$this->registerTagAttribute('name', 'string', 'Name of form');
|
||||
$this->registerTagAttribute('onreset', 'string', 'JavaScript: On reset of the form');
|
||||
$this->registerTagAttribute('onsubmit', 'string', 'JavaScript: On submit of the form');
|
||||
$this->registerUniversalTagAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a download link
|
||||
*
|
||||
* @return string the rendered a tag
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
/** @var Extension $extension */
|
||||
$extension = $this->arguments['extension'];
|
||||
$installPaths = Extension::returnAllowedInstallPaths();
|
||||
if (empty($installPaths)) {
|
||||
return '';
|
||||
}
|
||||
$pathSelector = '<ul class="is-hidden">';
|
||||
foreach ($installPaths as $installPathType => $installPath) {
|
||||
$pathSelector .= '<li>
|
||||
<input type="radio" id="' . htmlspecialchars($extension->getExtensionKey()) . '-downloadPath-' . htmlspecialchars($installPathType) . '" name="' . htmlspecialchars($this->getFieldNamePrefix()) . '[downloadPath]" class="downloadPath" value="' . htmlspecialchars($installPathType) . '" ' . ($installPathType === 'Local' ? 'checked="checked"' : '') . ' />
|
||||
<label for="' . htmlspecialchars($extension->getExtensionKey()) . '-downloadPath-' . htmlspecialchars($installPathType) . '">' . htmlspecialchars($installPathType) . '</label>
|
||||
</li>';
|
||||
}
|
||||
$pathSelector .= '</ul>';
|
||||
$uriBuilder = $this->renderingContext->getControllerContext()->getUriBuilder();
|
||||
$action = 'checkDependencies';
|
||||
$uriBuilder->reset();
|
||||
$uriBuilder->setFormat('json');
|
||||
$uri = $uriBuilder->uriFor($action, [
|
||||
'extension' => (int)$extension->getUid()
|
||||
], 'Download');
|
||||
$this->tag->addAttribute('data-href', $uri);
|
||||
|
||||
$automaticInstallation = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('extensionmanager', 'automaticInstallation');
|
||||
$labelKeySuffix = $automaticInstallation ? '' : '.downloadOnly';
|
||||
$label = '
|
||||
<div class="btn-group">
|
||||
<button
|
||||
title="' . LocalizationUtility::translate('extensionList.downloadViewHelper.submit' . $labelKeySuffix, 'extensionmanager') . '"
|
||||
type="submit"
|
||||
class="btn btn-default"
|
||||
value="' . LocalizationUtility::translate('extensionList.downloadViewHelper.submit' . $labelKeySuffix, 'extensionmanager') . '"
|
||||
>
|
||||
<span class="t3-icon fa fa-cloud-download"></span>
|
||||
</button>
|
||||
</div>';
|
||||
|
||||
$this->tag->setContent($label . $pathSelector);
|
||||
$this->tag->addAttribute('class', 'download');
|
||||
return '<div id="' . htmlspecialchars($extension->getExtensionKey()) . '-downloadFromTer" class="downloadFromTer">' . $this->tag->render() . '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field name prefix
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getFieldNamePrefix()
|
||||
{
|
||||
if ($this->hasArgument('fieldNamePrefix')) {
|
||||
return $this->arguments['fieldNamePrefix'];
|
||||
}
|
||||
return $this->getDefaultFieldNamePrefix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the default field name prefix for this form
|
||||
*
|
||||
* @return string default field name prefix
|
||||
*/
|
||||
protected function getDefaultFieldNamePrefix()
|
||||
{
|
||||
$request = $this->renderingContext->getControllerContext()->getRequest();
|
||||
if ($this->hasArgument('extensionName')) {
|
||||
$extensionName = $this->arguments['extensionName'];
|
||||
} else {
|
||||
$extensionName = $request->getControllerExtensionName();
|
||||
}
|
||||
if ($this->hasArgument('pluginName')) {
|
||||
$pluginName = $this->arguments['pluginName'];
|
||||
} else {
|
||||
$pluginName = $request->getPluginName();
|
||||
}
|
||||
if ($extensionName !== null && $pluginName != null) {
|
||||
return $this->extensionService->getPluginNamespace($extensionName, $pluginName);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\ViewHelpers;
|
||||
|
||||
/*
|
||||
* 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 TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
|
||||
|
||||
/**
|
||||
* Returns a string meant to be used as css class stating whether an extension is
|
||||
* available or installed
|
||||
* @internal
|
||||
*/
|
||||
class InstallationStateCssClassViewHelper extends AbstractViewHelper
|
||||
{
|
||||
use CompileWithRenderStatic;
|
||||
|
||||
/**
|
||||
* Initialize arguments
|
||||
*/
|
||||
public function initializeArguments()
|
||||
{
|
||||
$this->registerArgument('needle', 'string', '', true);
|
||||
$this->registerArgument('haystack', 'array', '', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string meant to be used as css class
|
||||
* 'installed' => if an extension is installed
|
||||
* 'available' => if an extension is available in the system
|
||||
* '' (empty string) => if neither installed nor available
|
||||
*
|
||||
* @param array $arguments
|
||||
* @param \Closure $renderChildrenClosure
|
||||
* @param RenderingContextInterface $renderingContext
|
||||
*
|
||||
* @return string the rendered a tag
|
||||
*/
|
||||
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
|
||||
{
|
||||
$needle = $arguments['needle'];
|
||||
$haystack = $arguments['haystack'];
|
||||
if (array_key_exists($needle, $haystack)) {
|
||||
if (isset($haystack[$needle]['installed']) && $haystack[$needle]['installed'] === true) {
|
||||
return 'installed';
|
||||
}
|
||||
return 'available';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\ViewHelpers;
|
||||
|
||||
/*
|
||||
* 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 TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
|
||||
|
||||
/**
|
||||
* ViewHelper to let 3rd-party extensions process the list of available
|
||||
* actions for a given extension.
|
||||
* @internal
|
||||
*/
|
||||
class ProcessAvailableActionsViewHelper extends AbstractTagBasedViewHelper
|
||||
{
|
||||
const SIGNAL_ProcessActions = 'processActions';
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
|
||||
*/
|
||||
protected $signalSlotDispatcher;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher
|
||||
*/
|
||||
public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
|
||||
{
|
||||
$this->signalSlotDispatcher = $signalSlotDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize arguments
|
||||
*/
|
||||
public function initializeArguments()
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('extension', 'string', '', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the list of actions.
|
||||
*
|
||||
* @return string the rendered list of actions
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$extension = $this->arguments['extension'];
|
||||
$html = $this->renderChildren();
|
||||
$actions = preg_split('#\\n\\s*#s', trim($html));
|
||||
|
||||
$actions = $this->emitProcessActionsSignal($extension, $actions);
|
||||
|
||||
return implode(' ', $actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a signal after the list of actions is processed
|
||||
*
|
||||
* @param string $extension
|
||||
* @param array $actions
|
||||
* @return array Modified action array
|
||||
*/
|
||||
protected function emitProcessActionsSignal($extension, array $actions)
|
||||
{
|
||||
$this->signalSlotDispatcher->dispatch(
|
||||
__CLASS__,
|
||||
static::SIGNAL_ProcessActions,
|
||||
[
|
||||
$extension,
|
||||
&$actions,
|
||||
]
|
||||
);
|
||||
return $actions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\ViewHelpers;
|
||||
|
||||
/*
|
||||
* 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\Core\Environment;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Registry;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
use TYPO3\CMS\Fluid\ViewHelpers\Link\ActionViewHelper;
|
||||
|
||||
/**
|
||||
* ViewHelper for update script link
|
||||
* @internal
|
||||
*/
|
||||
class ReloadSqlDataViewHelper extends ActionViewHelper
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected static $registryNamespace = 'extensionDataImport';
|
||||
|
||||
public function initializeArguments()
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('extension', 'array', 'Extension key', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a link to re-import the static SQL data of an extension
|
||||
*
|
||||
* @return string The rendered a tag
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$extension = $this->arguments['extension'];
|
||||
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
|
||||
|
||||
$staticSqlDataFile = $extension['siteRelPath'] . 'ext_tables_static+adt.sql';
|
||||
if (!file_exists(Environment::getPublicPath() . '/' . $staticSqlDataFile)) {
|
||||
return '<span class="btn btn-default disabled">' . $iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>';
|
||||
}
|
||||
|
||||
$registry = GeneralUtility::makeInstance(Registry::class);
|
||||
$oldMd5Hash = $registry->get(static::$registryNamespace, $staticSqlDataFile);
|
||||
|
||||
$md5HashIsEqual = true;
|
||||
// We used to only store "1" in the database when data was imported
|
||||
// No need to compare file content here and just show the reload icon
|
||||
if (!empty($oldMd5Hash) && $oldMd5Hash !== 1) {
|
||||
$currentMd5Hash = md5_file(Environment::getPublicPath() . '/' . $staticSqlDataFile);
|
||||
$md5HashIsEqual = $oldMd5Hash === $currentMd5Hash;
|
||||
}
|
||||
|
||||
if ($md5HashIsEqual) {
|
||||
$iconIdentifier = 'actions-database-reload';
|
||||
$languageKey = 'extensionList.databaseReload';
|
||||
} else {
|
||||
$iconIdentifier = 'actions-database-import';
|
||||
$languageKey = 'extensionList.databaseImport';
|
||||
}
|
||||
|
||||
$uriBuilder = $this->renderingContext->getControllerContext()->getUriBuilder();
|
||||
$uriBuilder->reset();
|
||||
$uri = $uriBuilder->uriFor('reloadExtensionData', ['extension' => $extension['key']], 'Action');
|
||||
$this->tag->addAttribute('href', $uri);
|
||||
$this->tag->addAttribute('title', LocalizationUtility::translate($languageKey, 'extensionmanager'));
|
||||
$this->tag->setContent($iconFactory->getIcon($iconIdentifier, Icon::SIZE_SMALL)->render());
|
||||
|
||||
return $this->tag->render();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\ViewHelpers;
|
||||
|
||||
/*
|
||||
* 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\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Fluid\ViewHelpers\Link\ActionViewHelper;
|
||||
|
||||
/**
|
||||
* ViewHelper for displaying a remove extension link
|
||||
* @internal
|
||||
*/
|
||||
class RemoveExtensionViewHelper extends ActionViewHelper
|
||||
{
|
||||
/**
|
||||
* Initialize arguments
|
||||
*
|
||||
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
|
||||
*/
|
||||
public function initializeArguments()
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('extension', 'array', '', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an install link
|
||||
*
|
||||
* @return string the rendered a tag
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$extension = $this->arguments['extension'];
|
||||
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
|
||||
if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded($extension['key'])) {
|
||||
return '<span class="btn btn-default disabled">' . $iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>';
|
||||
}
|
||||
if (
|
||||
!in_array($extension['type'], \TYPO3\CMS\Extensionmanager\Domain\Model\Extension::returnAllowedInstallTypes()) ||
|
||||
$extension['type'] === 'System'
|
||||
) {
|
||||
return '<span class="btn btn-default disabled">' . $iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>';
|
||||
}
|
||||
$uriBuilder = $this->renderingContext->getControllerContext()->getUriBuilder();
|
||||
$action = 'removeExtension';
|
||||
$uriBuilder->reset();
|
||||
$uriBuilder->setFormat('json');
|
||||
$uri = $uriBuilder->uriFor($action, [
|
||||
'extension' => $extension['key']
|
||||
], 'Action');
|
||||
$this->tag->addAttribute('href', $uri);
|
||||
$cssClass = 'removeExtension btn btn-default';
|
||||
$this->tag->addAttribute('class', $cssClass);
|
||||
$this->tag->addAttribute('title', \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('extensionList.remove', 'extensionmanager'));
|
||||
$this->tag->setContent($iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL)->render());
|
||||
return $this->tag->render();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\ViewHelpers;
|
||||
|
||||
/*
|
||||
* 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\Extensionmanager\Domain\Model\Extension;
|
||||
use TYPO3\CMS\Fluid\ViewHelpers\Link\ActionViewHelper;
|
||||
|
||||
/**
|
||||
* Display a link to show all versions of an extension
|
||||
* @internal
|
||||
*/
|
||||
class ShowExtensionVersionsViewHelper extends ActionViewHelper
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $tagName = 'a';
|
||||
|
||||
/**
|
||||
* Initialize arguments
|
||||
*/
|
||||
public function initializeArguments()
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('extension', Extension::class, '', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an install link
|
||||
*
|
||||
* @return string the rendered a tag
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
/** @var Extension $extension */
|
||||
$extension = $this->arguments['extension'];
|
||||
|
||||
$uriBuilder = $this->renderingContext->getControllerContext()->getUriBuilder();
|
||||
$action = 'showAllVersions';
|
||||
$uri = $uriBuilder->reset()->uriFor($action, [
|
||||
'extensionKey' => $extension->getExtensionKey(),
|
||||
], 'List');
|
||||
$this->tag->addAttribute('href', $uri);
|
||||
|
||||
// Set class
|
||||
$this->tag->addAttribute('class', 'versions-all ui-icon ui-icon-triangle-1-s');
|
||||
|
||||
$label = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('extensionList.showAllVersions.label', 'extensionmanager');
|
||||
$this->tag->addAttribute('title', $label);
|
||||
$this->tag->setContent($label);
|
||||
return $this->tag->render();
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\ViewHelpers;
|
||||
|
||||
/*
|
||||
* 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\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Package\PackageManager;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
use TYPO3\CMS\Fluid\ViewHelpers\Link\ActionViewHelper;
|
||||
|
||||
/**
|
||||
* Display a deactivate / activate link
|
||||
* @internal
|
||||
*/
|
||||
class ToggleExtensionInstallationStateViewHelper extends ActionViewHelper
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $tagName = 'a';
|
||||
|
||||
/** @var \TYPO3\CMS\Extbase\Object\ObjectManager */
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
|
||||
*/
|
||||
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize arguments
|
||||
*/
|
||||
public function initializeArguments()
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('extension', 'array', '', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an install link
|
||||
*
|
||||
* @return string the rendered a tag
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$extension = $this->arguments['extension'];
|
||||
// Early return if package is protected or is a runtime activated package and can not be unloaded
|
||||
/** @var \TYPO3\CMS\Core\Package\PackageManager $packageManager */
|
||||
$packageManager = $this->objectManager->get(PackageManager::class);
|
||||
$package = $packageManager->getPackage($extension['key']);
|
||||
if ($package->isProtected() || in_array($extension['key'], $GLOBALS['TYPO3_CONF_VARS']['EXT']['runtimeActivatedPackages'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$uriBuilder = $this->renderingContext->getControllerContext()->getUriBuilder();
|
||||
$action = 'toggleExtensionInstallationState';
|
||||
$uri = $uriBuilder->reset()->uriFor($action, [
|
||||
'extensionKey' => $extension['key']
|
||||
], 'Action');
|
||||
$this->tag->addAttribute('href', $uri);
|
||||
$label = $extension['installed'] ? 'deactivate' : 'activate';
|
||||
$this->tag->addAttribute('title', LocalizationUtility::translate('extensionList.' . $label, 'extensionmanager'));
|
||||
$icon = $extension['installed'] ? 'uninstall' : 'install';
|
||||
$this->tag->addAttribute('class', 'onClickMaskExtensionManager btn btn-default');
|
||||
|
||||
/** @var IconFactory $iconFactory */
|
||||
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
|
||||
$this->tag->setContent($iconFactory->getIcon('actions-system-extension-' . $icon, Icon::SIZE_SMALL)->render());
|
||||
return $this->tag->render();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\ViewHelpers;
|
||||
|
||||
/*
|
||||
* 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\MathUtility;
|
||||
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
|
||||
use TYPO3\CMS\Extensionmanager\Domain\Model\Dependency;
|
||||
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
|
||||
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
|
||||
|
||||
/**
|
||||
* Shows the version numbers of the TYPO3 dependency, if any
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Typo3DependencyViewHelper extends AbstractViewHelper
|
||||
{
|
||||
use CompileWithRenderStatic;
|
||||
|
||||
/**
|
||||
* As this ViewHelper renders HTML, the output must not be escaped.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $escapeOutput = false;
|
||||
|
||||
/**
|
||||
* Initialize arguments
|
||||
*
|
||||
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
|
||||
*/
|
||||
public function initializeArguments()
|
||||
{
|
||||
$this->registerArgument('extension', Extension::class, '', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns the suitable TYPO3 versions of an extension
|
||||
*
|
||||
* @param array $arguments
|
||||
* @param \Closure $renderChildrenClosure
|
||||
* @param RenderingContextInterface $renderingContext
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
|
||||
{
|
||||
$extension = $arguments['extension'];
|
||||
/** @var Dependency $dependency */
|
||||
foreach ($extension->getDependencies() as $dependency) {
|
||||
if ($dependency->getIdentifier() === 'typo3') {
|
||||
$lowestVersion = $dependency->getLowestVersion();
|
||||
$highestVersion = $dependency->getHighestVersion();
|
||||
$cssClass = self::isVersionSuitable($lowestVersion, $highestVersion) ? 'success' : 'default';
|
||||
return
|
||||
'<span class="label label-' . $cssClass . '">'
|
||||
. htmlspecialchars($lowestVersion) . ' - ' . htmlspecialchars($highestVersion)
|
||||
. '</span>';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current TYPO3 version is suitable for the extension
|
||||
*
|
||||
* @param string $lowestVersion
|
||||
* @param string $highestVersion
|
||||
* @return bool
|
||||
*/
|
||||
protected static function isVersionSuitable($lowestVersion, $highestVersion)
|
||||
{
|
||||
$numericTypo3Version = VersionNumberUtility::convertVersionNumberToInteger(VersionNumberUtility::getNumericTypo3Version());
|
||||
$numericLowestVersion = VersionNumberUtility::convertVersionNumberToInteger($lowestVersion);
|
||||
$numericHighestVersion = VersionNumberUtility::convertVersionNumberToInteger($highestVersion);
|
||||
return MathUtility::isIntegerInRange($numericTypo3Version, $numericLowestVersion, $numericHighestVersion);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extensionmanager\ViewHelpers;
|
||||
|
||||
/*
|
||||
* 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\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Fluid\ViewHelpers\Link\ActionViewHelper;
|
||||
|
||||
/**
|
||||
* ViewHelper for update script link
|
||||
* @internal
|
||||
*/
|
||||
class UpdateScriptViewHelper extends ActionViewHelper
|
||||
{
|
||||
|
||||
/** @var \TYPO3\CMS\Extbase\Object\ObjectManager */
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
|
||||
*/
|
||||
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize arguments
|
||||
*/
|
||||
public function initializeArguments()
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('extensionKey', 'string', 'Extension key', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a link to the update script screen if the extension has one
|
||||
*
|
||||
* @return string The rendered a tag
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$extensionKey = $this->arguments['extensionKey'];
|
||||
|
||||
// If the "class.ext_update.php" file exists, build link to the update script screen
|
||||
/** @var \TYPO3\CMS\Extensionmanager\Utility\UpdateScriptUtility $updateScriptUtility */
|
||||
$updateScriptUtility = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Utility\UpdateScriptUtility::class);
|
||||
/** @var IconFactory $iconFactory */
|
||||
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
|
||||
if ($updateScriptUtility->checkUpdateScriptExists($extensionKey)) {
|
||||
$uriBuilder = $this->renderingContext->getControllerContext()->getUriBuilder();
|
||||
$action = 'show';
|
||||
$uri = $uriBuilder->reset()->uriFor(
|
||||
$action,
|
||||
['extensionKey' => $extensionKey],
|
||||
'UpdateScript'
|
||||
);
|
||||
$this->tag->addAttribute('href', $uri);
|
||||
$this->tag->addAttribute('title', \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('extensionList.update.script', 'extensionmanager'));
|
||||
$this->tag->setContent($iconFactory->getIcon('actions-refresh', Icon::SIZE_SMALL)->render());
|
||||
$tag = $this->tag->render();
|
||||
} else {
|
||||
return '<span class="btn btn-default disabled">' . $iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>';
|
||||
}
|
||||
return $tag;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* Commands to be executed by typo3, where the key of the array
|
||||
* is the name of the command (to be called as the first argument after typo3).
|
||||
* Required parameter is the "class" of the command which needs to be a subclass
|
||||
* of Symfony/Console/Command.
|
||||
*/
|
||||
return [
|
||||
'extension:activate' => [
|
||||
'class' => \TYPO3\CMS\Extensionmanager\Command\ActivateExtensionCommand::class,
|
||||
'schedulable' => false,
|
||||
],
|
||||
'extension:deactivate' => [
|
||||
'class' => \TYPO3\CMS\Extensionmanager\Command\DeactivateExtensionCommand::class,
|
||||
'schedulable' => false,
|
||||
],
|
||||
];
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
return [
|
||||
'ctrl' => [
|
||||
'title' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension',
|
||||
'label' => 'uid',
|
||||
'default_sortby' => '',
|
||||
'hideTable' => true,
|
||||
'rootLevel' => 1,
|
||||
'adminOnly' => true,
|
||||
'typeicon_classes' => [
|
||||
'default' => 'empty-icon'
|
||||
]
|
||||
],
|
||||
'interface' => [
|
||||
'showRecordFieldList' => 'extension_key,version,integer_version,title,description,state,category,last_updated,update_comment,author_name,author_email,md5hash,serialized_dependencies'
|
||||
],
|
||||
'columns' => [
|
||||
'extension_key' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.extensionkey',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30
|
||||
]
|
||||
],
|
||||
'version' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.version',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30
|
||||
]
|
||||
],
|
||||
'alldownloadcounter' => [
|
||||
'config' => [
|
||||
'type' => 'passthrough'
|
||||
]
|
||||
],
|
||||
'integer_version' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.integerversion',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30
|
||||
]
|
||||
],
|
||||
'title' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.title',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30
|
||||
]
|
||||
],
|
||||
'description' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.description',
|
||||
'config' => [
|
||||
'type' => 'text',
|
||||
'cols' => 30,
|
||||
'rows' => 5
|
||||
]
|
||||
],
|
||||
'state' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.state',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30,
|
||||
'range' => ['lower' => 0, 'upper' => 1000],
|
||||
'eval' => 'int'
|
||||
]
|
||||
],
|
||||
'category' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.category',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30,
|
||||
'range' => ['lower' => 0, 'upper' => 1000],
|
||||
'eval' => 'int'
|
||||
]
|
||||
],
|
||||
'last_updated' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.lastupdated',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'renderType' => 'inputDateTime',
|
||||
'eval' => 'datetime,int'
|
||||
]
|
||||
],
|
||||
'update_comment' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.updatecomment',
|
||||
'config' => [
|
||||
'type' => 'text',
|
||||
'cols' => 30,
|
||||
'rows' => 5
|
||||
]
|
||||
],
|
||||
'author_name' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.authorname',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30
|
||||
]
|
||||
],
|
||||
'author_email' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.authoremail',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30
|
||||
]
|
||||
],
|
||||
'current_version' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.currentversion',
|
||||
'config' => [
|
||||
'type' => 'check',
|
||||
'size' => 1
|
||||
]
|
||||
],
|
||||
'review_state' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.reviewstate',
|
||||
'config' => [
|
||||
'type' => 'check',
|
||||
'size' => 1
|
||||
]
|
||||
],
|
||||
'md5hash' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.md5hash',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 1,
|
||||
],
|
||||
],
|
||||
'serialized_dependencies' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_extension.serializedDependencies',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30,
|
||||
],
|
||||
],
|
||||
],
|
||||
'types' => [
|
||||
'0' => ['showitem' => 'extensionkey, version, integer_version, title, description, state, category, last_updated, update_comment, author_name, author_email, review_state, md5hash, serialized_dependencies']
|
||||
],
|
||||
'palettes' => [
|
||||
'1' => ['showitem' => '']
|
||||
]
|
||||
];
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
return [
|
||||
'ctrl' => [
|
||||
'title' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_repository',
|
||||
'label' => 'uid',
|
||||
'default_sortby' => '',
|
||||
'hideTable' => true,
|
||||
'rootLevel' => 1,
|
||||
'adminOnly' => true,
|
||||
'typeicon_classes' => [
|
||||
'default' => 'empty-icon'
|
||||
]
|
||||
],
|
||||
'interface' => [
|
||||
'showRecordFieldList' => 'title,description,wsdl_url_mirror_list_url,last_update,extension_count'
|
||||
],
|
||||
'columns' => [
|
||||
'title' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_repository.title',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30
|
||||
],
|
||||
],
|
||||
'description' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_repository.description',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30
|
||||
],
|
||||
],
|
||||
'wsdl_url' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_repository.wsdlUrl',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30
|
||||
],
|
||||
],
|
||||
'mirror_list_url' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_repository.mirrorListUrl',
|
||||
'config' => [
|
||||
'type' => 'text',
|
||||
'cols' => 30,
|
||||
],
|
||||
],
|
||||
'last_update' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_repository.lastUpdate',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30,
|
||||
],
|
||||
],
|
||||
'extension_count' => [
|
||||
'label' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf:tx_extensionmanager_domain_model_repository.extensionCount',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30,
|
||||
],
|
||||
],
|
||||
],
|
||||
'types' => [
|
||||
'0' => ['showitem' => 'title, description, wsdl_url, mirror_list_url, last_update, extension_count'],
|
||||
],
|
||||
'palettes' => [
|
||||
'1' => ['showitem' => ''],
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,345 @@
|
||||
Some icons used in the TYPO3 project are retrieved from the "Silk" icon set of
|
||||
Mark James, which can be found at http://famfamfam.com/lab/icons/silk/. This
|
||||
set is distributed under a Creative Commons Attribution 2.5 License. The
|
||||
license can be found at http://creativecommons.org/licenses/by/2.5/.
|
||||
---------------------------------
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -0,0 +1,529 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff version="1.0" xmlns:t3="http://typo3.org/schemas/xliff">
|
||||
<file t3:id="1415814818" source-language="en" datatype="plaintext" original="EXT:extensionmanager/Resources/Private/Language/locallang.xlf" date="2011-10-17T20:22:37Z" product-name="workspaces">
|
||||
<header/>
|
||||
<body>
|
||||
<trans-unit id="extensionManager" resname="extensionManager">
|
||||
<source>Extension Manager</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="manageExtensions" resname="manageExtensions">
|
||||
<source>Manage Extensions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="installExtension" resname="installExtension">
|
||||
<source>Install Extension</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="composerMode.title" resname="composerMode.title">
|
||||
<source>Composer mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="composerMode.message" resname="composerMode.message">
|
||||
<source>The system is set to composer mode. Please notice that it is not possible to add an extension with the Extension Manager. You have to use composer to add further extensions to your system.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="installedExtensions" resname="installedExtensions">
|
||||
<source>Installed Extensions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="getExtensions" resname="getExtensions">
|
||||
<source>Get Extensions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="showAllVersions" resname="showAllVersions">
|
||||
<source>Show all versions of</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extConfTemplate.backToList" resname="extConfTemplate.backToList">
|
||||
<source>Back to list</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="updateScript.headline" resname="updateScript.headline">
|
||||
<source>Update script for %s</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="updateScript.none" resname="updateScript.none">
|
||||
<source>No update necessary.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="button.cancel" resname="button.cancel">
|
||||
<source>Cancel</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="button.details" resname="button.details">
|
||||
<source>Show Details</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="button.remove" resname="button.remove">
|
||||
<source>Remove extension</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="button.updateExtension" resname="button.updateExtension">
|
||||
<source>Update extension</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="button.resolveDependencies" resname="button.resolveDependencies">
|
||||
<source>Resolve dependencies</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.resolveDependenciesEnableButton" resname="label.resolveDependenciesEnableButton">
|
||||
<source>I understand what this means and I'm able to investigate on possibly occurring issues by myself.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="button.resolveDependenciesIgnore" resname="button.resolveDependenciesIgnore">
|
||||
<source>I know what I'm doing, continue anyway</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.update.script" resname="extensionList.update.script">
|
||||
<source>Execute the update script</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.downloadsql" resname="extensionList.downloadsql">
|
||||
<source>Download SQL Dump</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.remove" resname="extensionList.remove">
|
||||
<source>Remove</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.update" resname="extensionList.update">
|
||||
<source>Update</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateToVersion" resname="extensionList.updateToVersion">
|
||||
<source>Update to version %s</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateDisabled" resname="extensionList.updateDisabled">
|
||||
<source>An update is available but this operation is currently disabled</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.deactivate" resname="extensionList.deactivate">
|
||||
<source>Deactivate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.activate" resname="extensionList.activate">
|
||||
<source>Activate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.downloadzip" resname="extensionList.downloadzip">
|
||||
<source>Download as zip</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.uploadExtension" resname="extensionList.uploadExtension">
|
||||
<source>Upload Extension .t3x/.zip</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.overwriteExtension" resname="extensionList.overwriteExtension">
|
||||
<source>Overwrite</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.overwritingDisabled" resname="extensionList.overwritingDisabled">
|
||||
<source>Extension is already available and overwriting was not enabled</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.loading" resname="extensionList.loading">
|
||||
<source>Loading form...</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.update" resname="extensionList.header.update">
|
||||
<source>Upd.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.title.update" resname="extensionList.header.title.update">
|
||||
<source>Update</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.activate" resname="extensionList.header.activate">
|
||||
<source>A/D</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.title.activate" resname="extensionList.header.title.activate">
|
||||
<source>Activate / Deactivate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.extensionName" resname="extensionList.header.extensionName">
|
||||
<source>Extension</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.version" resname="extensionList.header.version">
|
||||
<source>Version</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.extensionKey" resname="extensionList.header.extensionKey">
|
||||
<source>Key</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.extensionVersion" resname="extensionList.header.extensionVersion">
|
||||
<source>Version</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.extensionActions" resname="extensionList.header.extensionActions">
|
||||
<source>Actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.extensionState" resname="extensionList.header.extensionState">
|
||||
<source>State</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.extensionType" resname="extensionList.header.extensionType">
|
||||
<source>Type</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.uploadComment" resname="extensionList.header.uploadComment">
|
||||
<source>Upload Comment</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.lastUpdate" resname="extensionList.header.lastUpdate">
|
||||
<source>Last Updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.description" resname="extensionList.header.description">
|
||||
<source>Description</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.header.author" resname="extensionList.header.author">
|
||||
<source>Author</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="searchTemplate.searchExtensions" resname="searchTemplate.searchExtensions">
|
||||
<source>Search extensions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.search" resname="extensionList.search">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="searchTemplate.submitButton" resname="searchTemplate.submitButton">
|
||||
<source>Go</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="searchTemplate.resetSearchButton" resname="searchTemplate.resetSearchButton">
|
||||
<source>Reset Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="searchTemplate.yourSearch" resname="searchTemplate.yourSearch">
|
||||
<source>Your search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="searchTemplate.searchAgain" resname="searchTemplate.searchAgain">
|
||||
<source>Search again</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="uploadTemplate.uploadButton" resname="uploadTemplate.uploadButton">
|
||||
<source>Upload!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="uploadTemplate.extensionLabel" resname="uploadTemplate.extensionLabel">
|
||||
<source>Extension</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.removalConfirmation.title" resname="extensionList.removalConfirmation.title">
|
||||
<source>Extension Removal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.removalConfirmation.message" resname="extensionList.removalConfirmation.message">
|
||||
<source>The extension is currently installed. Uninstall extension?</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateConfirmation.title" resname="extensionList.updateConfirmation.title">
|
||||
<source>Update?</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateConfirmation.message" resname="extensionList.updateConfirmation.message">
|
||||
<source>Update Comments</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.databaseImport" resname="extensionList.databaseImport">
|
||||
<source>The static database data has changed. You should re-import the data.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.databaseReload" resname="extensionList.databaseReload">
|
||||
<source>Nothing has changed since last import. You might want to reload static database data.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateConfirmation.questionVersionComments" resname="extensionList.updateConfirmation.questionVersionComments">
|
||||
<source>Version Comments</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateFlashMessage.title" resname="extensionList.updateFlashMessage.title">
|
||||
<source>Extension Update</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateFlashMessage.body" resname="extensionList.updateFlashMessage.body">
|
||||
<source>%s was updated!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.removalConfirmation.question" resname="extensionList.removalConfirmation.question">
|
||||
<source>Are you sure you want to remove the extension?</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="dependencyCheck.unresolvedDependencies.title" resname="dependencyCheck.unresolvedDependencies.title">
|
||||
<source>Please read this carefully</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="dependencyCheck.unresolvedDependencies.message" resname="dependencyCheck.unresolvedDependencies.message">
|
||||
<source><![CDATA[
|
||||
If you continue installing the extension, all dependency checks will be turned off.
|
||||
<ul>
|
||||
<li>Required extensions are tried to be fetched from TER (if they do not yet exist in the system)</li>
|
||||
<li>Version dependency checks are skipped</li>
|
||||
</ul>
|
||||
<strong>
|
||||
Be aware that an installation without dependency checks may turn your installation unusable.<br />
|
||||
In such a case manual intervention is required.
|
||||
</strong>
|
||||
]]></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="dependencyCheck.unresolvedDependencies.question" resname="dependencyCheck.unresolvedDependencies.question">
|
||||
<source>Are you sure you want to proceed?</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="dependencyCheck.unresolvedDependencies.proceed" resname="dependencyCheck.unresolvedDependencies.proceed">
|
||||
<source>I know what I'm doing, install the extension!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="dependencyCheck.headline" resname="dependencyCheck.headline">
|
||||
<source>The following errors were found while trying to install "%s"</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="dependencyCheck.requiredExtension" resname="dependencyCheck.requiredExtension">
|
||||
<source>Required extension "%s"</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="downloadExtension.dependencies.headline" resname="downloadExtension.dependencies.headline">
|
||||
<source>The following dependencies have to be resolved before installation:
|
||||
|
||||
|
||||
</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="downloadExtension.dependencies.extensionWithVersion" resname="downloadExtension.dependencies.extensionWithVersion">
|
||||
<source>%1$s (new version %2$s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="downloadExtension.dependencies.typeHeadline" resname="downloadExtension.dependencies.typeHeadline" xml:space="preserve">
|
||||
<source><h3>Extensions marked for %s:</h3>
|
||||
%s
|
||||
|
||||
|
||||
</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="downloadExtension.dependencies.resolveAutomatically" resname="downloadExtension.dependencies.resolveAutomatically">
|
||||
<source>Shall these dependencies be resolved automatically?</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="downloadExtension.dependencies.errorTitle" resname="downloadExtension.dependencies.errorTitle">
|
||||
<source>Dependencies could not be resolved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="downloadExtension.dependencies.errorMessage" resname="downloadExtension.dependencies.errorMessage">
|
||||
<source>The dependencies of the following Extensions could not be resolved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="downloadExtension.dependencyType.download" resname="downloadExtension.dependencyType.download">
|
||||
<source>download</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="downloadExtension.dependencyType.install" resname="downloadExtension.dependencyType.install">
|
||||
<source>install</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="downloadExtension.dependencyType.update" resname="downloadExtension.dependencyType.update">
|
||||
<source>update</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="downloadExtension.updateExtension.error" resname="downloadExtension.updateExtension.error">
|
||||
<source>Error while updating extension</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.dependenciesResolveDownloadError.title" resname="extensionList.dependenciesResolveDownloadError.title">
|
||||
<source>Download Error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.dependenciesResolveDownloadSuccess.message" resname="extensionList.dependenciesResolveDownloadSuccess.message">
|
||||
<source>Your installation of {0} was successful.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.dependenciesResolveDownloadSuccess.message.downloadOnly" resname="extensionList.dependenciesResolveDownloadSuccess.message.downloadOnly">
|
||||
<source>Your download of {0} was successful.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.dependenciesResolveDownloadSuccess.header" resname="extensionList.dependenciesResolveDownloadSuccess.header">
|
||||
<source>Log</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.dependenciesResolveDownloadSuccess.item" resname="extensionList.dependenciesResolveDownloadSuccess.item">
|
||||
<source>Extensions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.dependenciesResolveFlashMessage.title" resname="extensionList.dependenciesResolveFlashMessage.title">
|
||||
<source>{0} installed.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.dependenciesResolveFlashMessage.title.downloadOnly" resname="extensionList.dependenciesResolveFlashMessage.title.downloadOnly">
|
||||
<source>{0} downloaded.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.uploadFlashMessage.title" resname="extensionList.uploadFlashMessage.title">
|
||||
<source>Extension Upload</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.uploadFlashMessage.message" resname="extensionList.uploadFlashMessage.message">
|
||||
<source>%s was uploaded!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.installedFlashMessage.message" resname="extensionList.installedFlashMessage.message">
|
||||
<source>%s was installed!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateFromTerFlashMessage.title" resname="extensionList.updateFromTerFlashMessage.title">
|
||||
<source>Update Extension List</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.downloadViewHelper.submit" resname="extensionList.downloadViewHelper.submit">
|
||||
<source>Import and Install</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.downloadViewHelper.submit.downloadOnly" resname="extensionList.downloadViewHelper.submit.downloadOnly">
|
||||
<source>Import</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.showAllVersions.label" resname="extensionList.showAllVersions.label">
|
||||
<source>Show all versions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.showAllVersions.extensionKey" resname="extensionList.showAllVersions.extensionKey">
|
||||
<source>Extension key</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.showAllVersions.version" resname="extensionList.showAllVersions.version">
|
||||
<source>Version</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.showAllVersions.lastUploaded" resname="extensionList.showAllVersions.lastUploaded">
|
||||
<source>Last uploaded</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.showAllVersions.downloads" resname="extensionList.showAllVersions.downloads">
|
||||
<source>Downloads</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.showAllVersions.category" resname="extensionList.showAllVersions.category">
|
||||
<source>Category</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.showAllVersions.manual" resname="extensionList.showAllVersions.manual">
|
||||
<source>Manual</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.showAllVersions.author" resname="extensionList.showAllVersions.author">
|
||||
<source>Author</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.showAllVersions.readOnline" resname="extensionList.showAllVersions.readOnline">
|
||||
<source>Read online</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.remove.message" resname="extensionList.remove.message">
|
||||
<source>Extension "%s" was removed successfully!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.distribution.key" resname="extensionList.distribution.key">
|
||||
<source>Key</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.distribution.author" resname="extensionList.distribution.author">
|
||||
<source>Author</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.distribution.title" resname="extensionList.distribution.title">
|
||||
<source>Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.distribution.version" resname="extensionList.distribution.version">
|
||||
<source>Version</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.distribution.downloads" resname="extensionList.distribution.downloads">
|
||||
<source>Downloads</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.distribution.lastUpdated" resname="extensionList.distribution.lastUpdated">
|
||||
<source>Last Update</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.showUnsuitableDistributions" resname="extensionList.showUnsuitableDistributions">
|
||||
<source>Show also distributions that are not suitable for this TYPO3 version</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.officialDistribution" resname="extensionList.officialDistribution">
|
||||
<source>Official distribution</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="distributions" resname="distributions">
|
||||
<source>Get preconfigured distribution</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.installDistribution" resname="extensionList.installDistribution">
|
||||
<source>Install</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.installImpexp" resname="extensionList.installImpexp">
|
||||
<source>Please install the Import/Export (impexp) extension to enable distribution support.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="distribution.installImpexp" resname="distribution.installImpexp">
|
||||
<source>Install impexp</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateFromTer.label" resname="extensionList.updateFromTer.label">
|
||||
<source>Retrieving extension list from TYPO3 Extension Repository (TER)...</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateFromTer.buttonLabel" resname="extensionList.updateFromTer.buttonLabel">
|
||||
<source>Update now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateFromTer.lastUpdate.label" resname="extensionList.updateFromTer.lastUpdate.label">
|
||||
<source>Last update</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateFromTer.lastUpdate.timeSinceLastUpdateLabel" resname="extensionList.updateFromTer.lastUpdate.timeSinceLastUpdateLabel">
|
||||
<source>Time since last update</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateFromTer.lastUpdate.noInfo" resname="extensionList.updateFromTer.lastUpdate.noInfo">
|
||||
<source>unknown</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateFromTer.lastUpdate.fullTimeFormat" resname="extensionList.updateFromTer.lastUpdate.fullTimeFormat">
|
||||
<source>Y-m-d H:i:s</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateFromTer.lastUpdate.timeOfLastUpdate" resname="extensionList.updateFromTer.lastUpdate.timeOfLastUpdate">
|
||||
<source>Time of last update: </source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateFromTer.lastUpdate.noTimeOfLastUpdate" resname="extensionList.updateFromTer.lastUpdate.noTimeOfLastUpdate">
|
||||
<source>No last update time known.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.updateFromTer.never" resname="extensionList.updateFromTer.never">
|
||||
<source>Never</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="extensionList.uninstall.dependencyError" resname="extensionList.uninstall.dependencyError">
|
||||
<source>Cannot deactivate extension '%s' - The extension(s) '%s' depend on it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="distribution.error.headline" resname="distribution.error.headline">
|
||||
<source>Could not install distribution '%s'</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="distribution.welcome.headline" resname="distribution.welcome.headline">
|
||||
<source>Congratulations...</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="distribution.welcome.message" resname="distribution.welcome.message">
|
||||
<source>You successfully installed the distribution '%s'</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="distribution.welcome.nextSteps" resname="distribution.welcome.nextSteps">
|
||||
<source>Now you can</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="distribution.welcome.openViewModule" resname="distribution.welcome.openViewModule">
|
||||
<source>View your website</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="distribution.welcome.openPageModule" resname="distribution.welcome.openPageModule">
|
||||
<source>Start editing your website</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="distribution.dependency.headline" resname="distribution.dependency.headline">
|
||||
<source>Dependencies, conflicts and suggestions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="distribution.dependency.identifier" resname="distribution.dependency.identifier">
|
||||
<source>Identifier</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="distribution.dependency.type" resname="distribution.dependency.type">
|
||||
<source>Type</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="distribution.dependency.version" resname="distribution.dependency.version">
|
||||
<source>Version</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="task.updateExtensionListTask.name" resname="task.updateExtensionListTask.name">
|
||||
<source>Update extension list</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="task.updateExtensionListTask.description" resname="task.updateExtensionListTask.description">
|
||||
<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" resname="report.status.mainRepository.title">
|
||||
<source>Update status of typo3.org main repository extension list</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.mainRepository.notFound.value" resname="report.status.mainRepository.notFound.value">
|
||||
<source>Error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.mainRepository.notFound.message" resname="report.status.mainRepository.notFound.message">
|
||||
<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" resname="report.status.mainRepository.notUpToDate.value">
|
||||
<source>Extension list is not up to date!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.mainRepository.notUpToDate.message" resname="report.status.mainRepository.notUpToDate.message">
|
||||
<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" resname="report.status.mainRepository.upToDate.value">
|
||||
<source>OK</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.loadedExtensions.title" resname="report.status.loadedExtensions.title">
|
||||
<source>Security status of loaded extensions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.loadedExtensions.noInsecureExtensionLoaded.value" resname="report.status.loadedExtensions.noInsecureExtensionLoaded.value">
|
||||
<source>OK</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.loadedExtensions.insecureExtensionLoaded.value" resname="report.status.loadedExtensions.insecureExtensionLoaded.value">
|
||||
<source>%s insecure extension(s) found</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.loadedExtensions.insecureExtensionLoaded.message" resname="report.status.loadedExtensions.insecureExtensionLoaded.message">
|
||||
<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:<br><br>%s</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.loadedExtensions.insecureExtensionLoaded.message.extension" resname="report.status.loadedExtensions.insecureExtensionLoaded.message.extension">
|
||||
<source><strong>%1$s</strong> (version %2$s)<br></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.existingExtensions.title" resname="report.status.existingExtensions.title">
|
||||
<source>Security status of existing, but not loaded extensions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.existingExtensions.noInsecureExtensionExists.value" resname="report.status.existingExtensions.noInsecureExtensionExists.value">
|
||||
<source>OK</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.existingExtensions.insecureExtensionExists.value" resname="report.status.existingExtensions.insecureExtensionExists.value">
|
||||
<source>%s insecure extension(s) found</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.existingExtensions.insecureExtensionExists.message" resname="report.status.existingExtensions.insecureExtensionExists.message">
|
||||
<source>The following extensions were found on your system, but are currently not installed. Please delete the extensions using the extension manager:<br><br>%s</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.existingExtensions.insecureExtensionExists.message.extension" resname="report.status.existingExtensions.insecureExtensionExists.message.extension">
|
||||
<source><strong>%1$s</strong> (version %2$s)<br></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.loadedOutdatedExtensions.title" resname="report.status.loadedOutdatedExtensions.title">
|
||||
<source>Outdated status of loaded extensions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.loadedOutdatedExtensions.noOutdatedExtensionLoaded.value" resname="report.status.loadedOutdatedExtensions.noOutdatedExtensionLoaded.value">
|
||||
<source>OK</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.loadedOutdatedExtensions.outdatedExtensionLoaded.value" resname="report.status.loadedOutdatedExtensions.outdatedExtensionLoaded.value">
|
||||
<source>%s outdated extension(s) found</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.loadedOutdatedExtensions.outdatedExtensionLoaded.message" resname="report.status.loadedOutdatedExtensions.outdatedExtensionLoaded.message">
|
||||
<source>The following extensions are outdated and may not be compatible with your installation. Please make sure these extensions are compatible and ask the extension author to publish updates:<br><br>%s</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.loadedOutdatedExtensions.outdatedExtensionLoaded.message.extension" resname="report.status.loadedOutdatedExtensions.outdatedExtensionLoaded.message.extension">
|
||||
<source><strong>%1$s</strong> (version %2$s)<br></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.existingOutdatedExtensions.title" resname="report.status.existingOutdatedExtensions.title">
|
||||
<source>Outdated status of existing, but not loaded extensions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.existingOutdatedExtensions.noOutdatedExtensionExists.value" resname="report.status.existingOutdatedExtensions.noOutdatedExtensionExists.value">
|
||||
<source>OK</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.existingOutdatedExtensions.outdatedExtensionExists.value" resname="report.status.existingOutdatedExtensions.outdatedExtensionExists.value">
|
||||
<source>%s outdated extension(s) found</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.existingOutdatedExtensions.outdatedExtensionExists.message" resname="report.status.existingOutdatedExtensions.outdatedExtensionExists.message">
|
||||
<source>The following extensions were found on your system, but are currently not installed. Please make sure they are compatible with your system before installing them:<br><br>%s</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="report.status.existingOutdatedExtensions.outdatedExtensionExists.message.extension" resname="report.status.existingOutdatedExtensions.outdatedExtensionExists.message.extension">
|
||||
<source><strong>%1$s</strong> (version %2$s)<br></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="fileHandling.couldNotRemoveDirectory" resname="fileHandling.couldNotRemoveDirectory">
|
||||
<source>ERROR: Could not remove extension directory "%s". Reasons</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="fileHandling.couldNotCreateDirectory" resname="fileHandling.couldNotCreateDirectory">
|
||||
<source>ERROR: Could not create extension directory "%s"!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="fileHandling.installPathWasNoDirectory" resname="fileHandling.installPathWasNoDirectory">
|
||||
<source>ERROR: The extension install path "%s" was no directory!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="config.automaticInstallation" resname="config.automaticInstallation">
|
||||
<source>Install extensions automatically after download from TER or file upload</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="config.offlineMode" resname="config.offlineMode">
|
||||
<source>Disable TER connection and hide menu items</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<xliff version="1.0" xmlns:t3="http://typo3.org/schemas/xliff">
|
||||
<file t3:id="1435679883" source-language="en" datatype="plaintext" original="EXT:extensionmanager/Resources/Private/Language/locallang_db.xlf" date="2015-06-30T15:55:11Z" product-name="">
|
||||
<header/>
|
||||
<body>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension" resname="tx_extensionmanager_domain_model_extension">
|
||||
<source>Extension</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.extensionkey" resname="tx_extensionmanager_domain_model_extension.extensionkey">
|
||||
<source>Extension Key</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.version" resname="tx_extensionmanager_domain_model_extension.version">
|
||||
<source>Version</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.title" resname="tx_extensionmanager_domain_model_extension.title">
|
||||
<source>Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.description" resname="tx_extensionmanager_domain_model_extension.description">
|
||||
<source>Description</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.state" resname="tx_extensionmanager_domain_model_extension.state">
|
||||
<source>State</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.category" resname="tx_extensionmanager_domain_model_extension.category">
|
||||
<source>Category</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.lastupdated" resname="tx_extensionmanager_domain_model_extension.lastupdated">
|
||||
<source>Last Updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.updatecomment" resname="tx_extensionmanager_domain_model_extension.updatecomment">
|
||||
<source>Update Comment</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.authorname" resname="tx_extensionmanager_domain_model_extension.authorname">
|
||||
<source>Author Name</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.authoremail" resname="tx_extensionmanager_domain_model_extension.authoremail">
|
||||
<source>Author Email</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.md5hash" resname="tx_extensionmanager_domain_model_extension.md5hash">
|
||||
<source>Md5 hash</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.reviewstate" resname="tx_extensionmanager_domain_model_extension.reviewstate">
|
||||
<source>Review State</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.integerversion" resname="tx_extensionmanager_domain_model_extension.integerversion">
|
||||
<source>Integer Version</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.currentversion" resname="tx_extensionmanager_domain_model_extension.currentversion">
|
||||
<source>Current Version</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_extension.serializedDependencies" resname="tx_extensionmanager_domain_model_extension.serializedDependencies">
|
||||
<source>Serialized dependencies</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_repository" resname="tx_extensionmanager_domain_model_repository">
|
||||
<source>Repository</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_repository.title" resname="tx_extensionmanager_domain_model_repository.title">
|
||||
<source>Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_repository.description" resname="tx_extensionmanager_domain_model_repository.description">
|
||||
<source>Description</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_repository.wsdlUrl" resname="tx_extensionmanager_domain_model_repository.wsdlUrl">
|
||||
<source>WSDL URL</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_repository.mirrorListUrl" resname="tx_extensionmanager_domain_model_repository.mirrorListUrl">
|
||||
<source>Mirror list URL</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_repository.lastUpdate" resname="tx_extensionmanager_domain_model_repository.lastUpdate">
|
||||
<source>Last update</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_extensionmanager_domain_model_repository.extensionCount" resname="tx_extensionmanager_domain_model_repository.extensionCount">
|
||||
<source>Extension count</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff version="1.0" xmlns:t3="http://typo3.org/schemas/xliff">
|
||||
<file t3:id="1415814819" source-language="en" datatype="plaintext" original="EXT:extensionmanager/Resources/Private/Language/locallang_mod.xlf" date="2011-10-17T20:22:38Z" product-name="workspaces">
|
||||
<header/>
|
||||
<body>
|
||||
<trans-unit id="mlang_tabs_tab" resname="mlang_tabs_tab">
|
||||
<source>Extensions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="mlang_labels_tablabel" resname="mlang_labels_tablabel">
|
||||
<source>TYPO3 Extension Manager</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="mlang_labels_tabdescr" resname="mlang_labels_tabdescr">
|
||||
<source>Manages TYPO3 extensions from a central repository. TYPO3 extensions include plugins, modules, class extensions, configuration code, etc. <br /><em>Access for 'admin' users only!</em></source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -0,0 +1,13 @@
|
||||
{namespace em=TYPO3\CMS\Extensionmanager\ViewHelpers}
|
||||
|
||||
<f:be.pageRenderer
|
||||
includeRequireJsModules="{
|
||||
0:'TYPO3/CMS/Extensionmanager/Main'
|
||||
}"
|
||||
/>
|
||||
|
||||
<em:be.trigger triggers="{triggers}" />
|
||||
|
||||
<f:render section="headline" />
|
||||
|
||||
<f:render section="content" />
|
||||
@@ -0,0 +1,50 @@
|
||||
{namespace em=TYPO3\CMS\Extensionmanager\ViewHelpers}
|
||||
<div class="card card-size-fixed-small">
|
||||
|
||||
<div class="card-image">
|
||||
<f:link.action action="show" controller="Distribution" arguments="{extension:distribution}">
|
||||
<em:distributionImage extensionkey="{distribution.extensionKey}" alt="{distribution.title}" height="225" width="300" />
|
||||
</f:link.action>
|
||||
<f:if condition="{distribution.dependencies}">
|
||||
<div class="card-image-badge">
|
||||
<em:typo3Dependency extension="{distribution}" />
|
||||
</div>
|
||||
</f:if>
|
||||
<f:if condition="{official}">
|
||||
<div class="distribution-official-badge">
|
||||
<f:image
|
||||
src="EXT:extensionmanager/Resources/Public/Images/OfficialBadge.svg" alt="{distribution.title}"
|
||||
height="64"
|
||||
width="64"
|
||||
/>
|
||||
</div>
|
||||
</f:if>
|
||||
</div>
|
||||
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<f:link.action action="show" controller="Distribution" arguments="{extension:distribution}">
|
||||
{distribution.title}
|
||||
</f:link.action>
|
||||
</h3>
|
||||
<span class="card-subtitle">
|
||||
{distribution.extensionKey}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p class="card-text">
|
||||
{distribution.description -> f:format.crop(maxCharacters: 150) -> f:format.raw()}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<f:link.action action="installDistribution" controller="Download" arguments="{extension:distribution}" class="btn btn-default t3-button-action-installdistribution">
|
||||
<core:icon identifier="actions-system-extension-import" /> <f:translate key="extensionList.installDistribution">Install</f:translate>
|
||||
</f:link.action>
|
||||
<f:link.action action="show" controller="Distribution" arguments="{extension:distribution}" class="btn btn-link">
|
||||
<f:translate key="button.details">Show Details</f:translate>
|
||||
</f:link.action>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
<f:be.widget.paginate objects="{extensions}" as="paginatedExtensions" configuration="{itemsPerPage: 10, maximumNumberOfLinks: 2}">
|
||||
<table id="terTable" class="table table-striped table-hover typo3-extension-list">
|
||||
<f:if condition="{showVersionList}">
|
||||
<f:then>
|
||||
<f:render partial="List/TerShowVersionsTable.html" arguments="{paginatedExtensions:paginatedExtensions}" />
|
||||
</f:then>
|
||||
<f:else>
|
||||
<f:render partial="List/TerTable.html" arguments="{paginatedExtensions:paginatedExtensions, availableAndInstalled:availableAndInstalled}" />
|
||||
</f:else>
|
||||
</f:if>
|
||||
</table>
|
||||
</f:be.widget.paginate>
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{namespace em=TYPO3\CMS\Extensionmanager\ViewHelpers}
|
||||
<td>
|
||||
<em:downloadExtension extension="{extension}" />
|
||||
</td>
|
||||
<td>
|
||||
{extension.title}
|
||||
</td>
|
||||
<td>
|
||||
{extension.version}
|
||||
</td>
|
||||
<td>
|
||||
<f:format.nl2br>{extension.updateComment}</f:format.nl2br>
|
||||
</td>
|
||||
<td>
|
||||
<f:format.date>{extension.lastUpdated}</f:format.date>
|
||||
</td>
|
||||
<td class="{extension.stateString}">
|
||||
{extension.stateString}
|
||||
</td>
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
{namespace em=TYPO3\CMS\Extensionmanager\ViewHelpers}
|
||||
<thead>
|
||||
<tr>
|
||||
<th><f:translate key="extensionList.header.extensionActions"/></th>
|
||||
<th><f:translate key="extensionList.header.extensionName"/></th>
|
||||
<th><f:translate key="extensionList.header.version"/></th>
|
||||
<th><f:translate key="extensionList.header.uploadComment"/></th>
|
||||
<th><f:translate key="extensionList.header.lastUpdate"/></th>
|
||||
<th><f:translate key="extensionList.header.extensionState"/></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<f:for each="{extensions}" as="extension">
|
||||
<f:cycle values="{0: 'odd', 1: 'even'}" as="zebraClass">
|
||||
<tr class="{zebraClass}">
|
||||
</f:cycle>
|
||||
<f:render partial="List/TerShowVersionsSingleLine" arguments="{extension:extension}" />
|
||||
</tr>
|
||||
</f:for>
|
||||
</tbody>
|
||||
@@ -0,0 +1,36 @@
|
||||
{namespace em=TYPO3\CMS\Extensionmanager\ViewHelpers}
|
||||
|
||||
<td>
|
||||
<em:downloadExtension extension="{extension}" />
|
||||
</td>
|
||||
<td>
|
||||
<f:link.action action="showAllVersions" arguments="{extensionKey: extension.extensionKey}" title="{f:translate(key:'extensionList.showAllVersions.label')}">
|
||||
{extension.title}
|
||||
</f:link.action>
|
||||
<em:showExtensionVersions extension="{extension}" />
|
||||
</td>
|
||||
<td>
|
||||
<f:link.action action="showAllVersions" arguments="{extensionKey: extension.extensionKey}" title="{f:translate(key:'extensionList.showAllVersions.label')}">
|
||||
{extension.extensionKey}
|
||||
</f:link.action>
|
||||
</td>
|
||||
<td>
|
||||
<f:link.action action="showAllVersions" arguments="{extensionKey: extension.extensionKey}" title="{f:translate(key:'extensionList.showAllVersions.label')}">
|
||||
{extension.version}
|
||||
</f:link.action>
|
||||
<em:showExtensionVersions extension="{extension}" />
|
||||
</td>
|
||||
<td>
|
||||
<f:format.date>{extension.lastUpdated}</f:format.date>
|
||||
</td>
|
||||
<td title="{extension.description}">
|
||||
<f:format.crop maxCharacters="50" respectWordBoundaries="true" respectHtml="false">{extension.description}</f:format.crop>
|
||||
|
||||
<div class="author">
|
||||
<div class="author-name">{extension.authorName}</div>
|
||||
<div class="author-email">{extension.authorEmail}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-{extension.stateString}">{extension.stateString}</span>
|
||||
</td>
|
||||
@@ -0,0 +1,19 @@
|
||||
{namespace em=TYPO3\CMS\Extensionmanager\ViewHelpers}
|
||||
<thead>
|
||||
<tr>
|
||||
<th><f:translate key="extensionList.header.extensionActions"/></th>
|
||||
<th><f:translate key="extensionList.header.extensionName"/></th>
|
||||
<th><f:translate key="extensionList.header.extensionKey"/></th>
|
||||
<th><f:translate key="extensionList.header.version"/></th>
|
||||
<th><f:translate key="extensionList.header.lastUpdate"/></th>
|
||||
<th><f:translate key="extensionList.header.description"/></th>
|
||||
<th><f:translate key="extensionList.header.extensionState"/></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<f:for each="{paginatedExtensions}" as="extension">
|
||||
<tr class="{em:installationStateCssClass(needle:extension.extensionKey, haystack:availableAndInstalled)}">
|
||||
<f:render partial="List/TerSingleLine.html" arguments="{extension:extension}" />
|
||||
</tr>
|
||||
</f:for>
|
||||
</tbody>
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<p><f:translate key="dependencyCheck.headline" arguments="{0: extension.extensionKey}" />:</p>
|
||||
<ul>
|
||||
<f:for each="{unresolvedDependencies}" key="key" as="messages">
|
||||
<f:if condition="{key} == {extension.extensionKey}">
|
||||
<f:for each="{messages}" as="message">
|
||||
<li>{message.message -> f:format.htmlspecialchars()}</li>
|
||||
</f:for>
|
||||
</f:if>
|
||||
</f:for>
|
||||
<f:for each="{unresolvedDependencies}" key="key" as="messages">
|
||||
<f:if condition="{key} != {extension.extensionKey}">
|
||||
<li>
|
||||
<strong>{f:translate(key: 'dependencyCheck.requiredExtension', arguments: '{key: key}') -> f:format.htmlspecialchars()}</strong>
|
||||
<ul>
|
||||
<f:for each="{messages}" as="message">
|
||||
<li>{message.message -> f:format.htmlspecialchars()}</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</li>
|
||||
</f:if>
|
||||
</f:for>
|
||||
</ul>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<f:render partial="List/UnresolvedDependencies.html" arguments="{_all}" />
|
||||
|
||||
<f:be.infobox title="{f:translate(key: 'dependencyCheck.unresolvedDependencies.title')}" state="1">
|
||||
<f:render partial="List/UnresolvedDependenciesMessage.html" />
|
||||
<p><strong><f:translate key="dependencyCheck.unresolvedDependencies.question" /></strong></p>
|
||||
</f:be.infobox>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<p>{f:translate(key: 'dependencyCheck.unresolvedDependencies.message') -> f:format.raw()}</p>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="unlockDependencyIgnoreButton"> <f:translate key="label.resolveDependenciesEnableButton" />
|
||||
</label>
|
||||
</div>
|
||||
@@ -0,0 +1,15 @@
|
||||
<f:form
|
||||
action="updateExtensionListFromTer"
|
||||
controller="UpdateFromTer"
|
||||
format="json"
|
||||
class="update-from-ter"
|
||||
>
|
||||
{f:translate(key: 'extensionList.updateFromTer.lastUpdate.timeSinceLastUpdateLabel')}
|
||||
<span class="time-since-last-update" title="{f:translate(key: 'extensionList.updateFromTer.lastUpdate.noTimeOfLastUpdate')}">
|
||||
{f:translate(key: 'extensionList.updateFromTer.lastUpdate.noInfo')}
|
||||
</span>
|
||||
<input
|
||||
class="btn btn-default"
|
||||
type="submit"
|
||||
value="{f:translate(key: 'extensionList.updateFromTer.buttonLabel')}" />
|
||||
</f:form>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div class="uploadForm" style="display: none;">
|
||||
<i class="fa fa-circle-o-notch fa-spin"></i>
|
||||
<f:translate key="extensionList.loading"/>
|
||||
</div>
|
||||
@@ -0,0 +1,83 @@
|
||||
{namespace em=TYPO3\CMS\Extensionmanager\ViewHelpers}
|
||||
<f:layout name="main" />
|
||||
|
||||
<f:section name="headline">
|
||||
</f:section>
|
||||
|
||||
<f:section name="content">
|
||||
<div class="distribution-detail">
|
||||
<div class="distribution-detail-previewpane">
|
||||
<em:distributionImage extensionkey="{extension.extensionKey}" alt="" height="225" width="300" />
|
||||
</div>
|
||||
<div class="distribution-detail-body">
|
||||
<div class="distribution-detail-header">
|
||||
<h1>{extension.title}</h1>
|
||||
<p>{extension.description}</p>
|
||||
</div>
|
||||
|
||||
<ul class="distribution-detail-actions">
|
||||
<f:if condition="{distributionActive}">
|
||||
<f:else>
|
||||
<li>
|
||||
<f:link.action action="installDistribution" controller="Download" arguments="{extension:extension}" class="btn btn-default t3-button-action-installdistribution">
|
||||
<core:icon identifier="actions-system-extension-import" /> <f:translate key="extensionList.installDistribution">Install</f:translate>
|
||||
</f:link.action>
|
||||
</li>
|
||||
</f:else>
|
||||
<f:then>
|
||||
<li>
|
||||
<button class="btn btn-default distribution-openViewModule" onclick="top.goToModule('web_ViewpageView');">
|
||||
<core:icon identifier="actions-document-view" /> <f:translate key="distribution.welcome.openViewModule" />
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-default distribution-openPageModule" onclick="top.goToModule('web_layout');">
|
||||
<core:icon identifier="actions-open" /> <f:translate key="distribution.welcome.openPageModule" />
|
||||
</button>
|
||||
</li>
|
||||
</f:then>
|
||||
</f:if>
|
||||
</ul>
|
||||
|
||||
<dl class="dl-horizontal dl-horizontal-wide distribution-detail-summary">
|
||||
<dt><f:translate key="extensionList.distribution.title" /></dt>
|
||||
<dd>{extension.title}</dd>
|
||||
<dt><f:translate key="extensionList.distribution.key" /></dt>
|
||||
<dd>{extension.extensionKey}</dd>
|
||||
<dt><f:translate key="extensionList.distribution.version" /></dt>
|
||||
<dd>{extension.version} (<f:format.date format="d.m.Y">{extension.lastUpdated}</f:format.date>)<br><span class="{extension.stateString}">{extension.stateString}</span></dd>
|
||||
<dt><f:translate key="extensionList.distribution.author" /></dt>
|
||||
<dd>{extension.authorName}</dd>
|
||||
<dt><f:translate key="extensionList.distribution.downloads" /></dt>
|
||||
<dd>{extension.alldownloadcounter}</dd>
|
||||
</dl>
|
||||
|
||||
<f:if condition="{extension.dependencies}">
|
||||
<h2><f:translate key="distribution.dependency.headline" /></h2>
|
||||
<div class="table-fit">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><f:translate key="distribution.dependency.identifier" /></td>
|
||||
<td><f:translate key="distribution.dependency.type" /></td>
|
||||
<td><f:translate key="distribution.dependency.version" /></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<f:for each="{extension.dependencies}" as="dependency">
|
||||
<tr>
|
||||
<td>{dependency.identifier}</td>
|
||||
<td>{dependency.type}</td>
|
||||
<td>
|
||||
{dependency.lowestVersion}<f:if condition="{dependency.highestVersion}">-{dependency.highestVersion}</f:if>
|
||||
</td>
|
||||
</tr>
|
||||
</f:for>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</f:if>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</f:section>
|
||||
@@ -0,0 +1,9 @@
|
||||
<f:format.raw><f:format.json value="{
|
||||
result: result,
|
||||
extension: extension.extensionKey,
|
||||
installationTypeLanguageKey: installationTypeLanguageKey,
|
||||
errorCount: '{f:count(subject: unresolvedDependencies)}',
|
||||
errorTitle: '{f:translate(key: \'downloadExtension.dependencies.errorTitle\')}',
|
||||
errorMessage: '{f:render(partial: \'List/UnresolvedDependencies\', arguments: \'{_all}\')}',
|
||||
skipDependencyUri: '{f:uri.action(action: \'installExtensionWithoutSystemDependencyCheck\', format: \'json\', arguments: \'{extension: extension}\')}'
|
||||
}" /></f:format.raw>
|
||||
@@ -0,0 +1,41 @@
|
||||
{namespace em=TYPO3\CMS\Extensionmanager\ViewHelpers}
|
||||
|
||||
<f:layout name="main"/>
|
||||
|
||||
<f:section name="headline">
|
||||
<h1><f:translate key="distributions">Get Distributions</f:translate></h1>
|
||||
</f:section>
|
||||
|
||||
<f:section name="content">
|
||||
<f:if condition="{enableDistributionsView}">
|
||||
<f:then>
|
||||
<f:if condition="{showUnsuitableDistributions} == 0">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<f:form class="typo3-extensionmanager-unsuitable-dist" action="distributions" arguments="{showUnsuitableDistributions: 1}" >
|
||||
<div class="input-group">
|
||||
<span class="input-group-btn">
|
||||
<f:form.submit class="btn btn-default" value="{f:translate(key:'extensionList.showUnsuitableDistributions')}"/>
|
||||
</span>
|
||||
</div>
|
||||
</f:form>
|
||||
</div>
|
||||
</div>
|
||||
</f:if>
|
||||
<div class="card-container">
|
||||
<f:for each="{officialDistributions}" as="distribution">
|
||||
<f:render partial="List/Distribution" arguments="{distribution: distribution, official: 1}" />
|
||||
</f:for>
|
||||
<f:for each="{communityDistributions}" as="distribution">
|
||||
<f:render partial="List/Distribution" arguments="{distribution: distribution, official: 0}" />
|
||||
</f:for>
|
||||
</div>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<p>{f:translate(key: 'extensionList.installImpexp')}</p>
|
||||
<f:link.action action="toggleExtensionInstallationState" controller="Action" arguments="{extensionKey: 'impexp'}" class="btn btn-default t3-button-action-installdistribution">
|
||||
<span class="fa fa-cloud-download"> </span><f:translate key="distribution.installImpexp">Install impexp</f:translate>
|
||||
</f:link.action>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:section>
|
||||
@@ -0,0 +1,103 @@
|
||||
{namespace em=TYPO3\CMS\Extensionmanager\ViewHelpers}
|
||||
<f:layout name="main" />
|
||||
|
||||
<f:section name="headline">
|
||||
<h1><f:translate key="installedExtensions" /></h1>
|
||||
</f:section>
|
||||
|
||||
<f:section name="content">
|
||||
<f:render partial="List/UploadForm" />
|
||||
|
||||
<form class="form-inline">
|
||||
<div class="form-group">
|
||||
<f:form.textfield name="Tx_Extensionmanager_extensionkey" placeholder="{f:translate(key:'extensionList.search')}" id="Tx_Extensionmanager_extensionkey" value="{search}" class="form-control" />
|
||||
</div>
|
||||
</form>
|
||||
<br>
|
||||
<div class="table-fit">
|
||||
<table id="typo3-extension-list" class="table table-striped table-hover typo3-extension-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th title="{f:translate(key:'extensionList.header.title.update')}"><f:translate key="extensionList.header.update"/></th>
|
||||
<th title="{f:translate(key:'extensionList.header.title.activate')}"><f:translate key="extensionList.header.activate"/></th>
|
||||
<th><f:translate key="extensionList.header.extensionName"/></th>
|
||||
<th><f:translate key="extensionList.header.extensionKey"/></th>
|
||||
<th><f:translate key="extensionList.header.extensionVersion"/></th>
|
||||
<th><f:translate key="extensionList.header.extensionState"/></th>
|
||||
<th><f:translate key="extensionList.header.extensionType"/></th>
|
||||
<th><f:translate key="extensionList.header.extensionActions"/></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<f:for each="{extensions}" as="extension" key="extensionKey">
|
||||
<f:if condition="{extension.terObject}">
|
||||
<f:then>
|
||||
<tr id="{extensionKey}" class="{f:if(condition:'{extension.terObject.reviewState} == -1', then:'insecure')}{f:if(condition:'{extension.terObject.reviewState} == -2', then:'outdated')} {f:if(condition:'{extension.installed}', then: '', else: 'inactive')}">
|
||||
</f:then>
|
||||
<f:else>
|
||||
<tr id="{extensionKey}" class="{f:if(condition:'{extension.installed}', then: '', else: 'inactive')}">
|
||||
</f:else>
|
||||
</f:if>
|
||||
<td>
|
||||
<f:if condition="{extension.updateAvailable}">
|
||||
<f:if condition="{isComposerMode} || {settings.offlineMode} || {extension.state} == 'excludeFromUpdates'">
|
||||
<f:then>
|
||||
<span class="btn btn-default disabled" title="{f:translate(key:'extensionList.updateDisabled')}">
|
||||
<core:icon identifier="actions-system-extension-update" />
|
||||
</span>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<f:link.action action="updateCommentForUpdatableVersions"
|
||||
class="btn btn-default"
|
||||
additionalAttributes="{data-action:'update-extension'}"
|
||||
format="json" controller="Download"
|
||||
arguments="{extension:extension.key, integerVersionStart: extension.terObject.integerVersion, integerVersionStop: extension.updateToVersion.integerVersion}"
|
||||
title="{f:translate(key:'extensionList.updateToVersion', arguments:'{0:extension.updateToVersion.version}')}"
|
||||
>
|
||||
<core:icon identifier="actions-system-extension-update" />
|
||||
</f:link.action>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:if>
|
||||
</td>
|
||||
<td data-order="{f:if(condition: extension.installed, then: 'active', else: 'inactive')}">
|
||||
<em:toggleExtensionInstallationState extension="{extension}" />
|
||||
</td>
|
||||
<td>
|
||||
<f:if condition="{extension.ext_icon}">
|
||||
<img class="ext-icon" src="../{extension.siteRelPath}{extension.ext_icon}" alt="" />
|
||||
</f:if>
|
||||
<span title="{extension.description}">{extension.title}</span>
|
||||
</td>
|
||||
<td>
|
||||
{extensionKey}
|
||||
</td>
|
||||
<td>
|
||||
{extension.version}
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-{extension.state}">{extension.state}</span>
|
||||
</td>
|
||||
<td>
|
||||
{extension.type}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
<em:processAvailableActions extension="{extension}">
|
||||
<em:updateScript class="btn btn-default" extensionKey="{extension.key}" />
|
||||
<f:if condition="!{isComposerMode}">
|
||||
<em:removeExtension class="btn btn-default" extension="{extension}" />
|
||||
</f:if>
|
||||
<f:link.action action="downloadExtensionZip" controller="Action" arguments="{extension:extension.key}" title="{f:translate(key:'extensionList.downloadzip')}" class="btn btn-default">
|
||||
<core:icon identifier="actions-system-extension-download" />
|
||||
</f:link.action>
|
||||
<em:reloadSqlData class="btn btn-default" extension="{extension}" />
|
||||
</em:processAvailableActions>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</f:for>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</f:section>
|
||||
@@ -0,0 +1,67 @@
|
||||
{namespace em=TYPO3\CMS\Extensionmanager\ViewHelpers}
|
||||
<f:layout name="main" />
|
||||
|
||||
<f:section name="docheader-buttons">
|
||||
<a href="{f:uri.action(action: 'ter')}" title="{f:translate(key:'extConfTemplate.backToList')}">
|
||||
<core:icon identifier="actions-view-go-back" />
|
||||
</a>
|
||||
</f:section>
|
||||
|
||||
<f:section name="headline">
|
||||
<h1><f:translate key="showAllVersions" /> {extensionKey}</h1>
|
||||
</f:section>
|
||||
|
||||
<f:section name="content">
|
||||
<f:if condition="{currentVersion}">
|
||||
<p><f:format.nl2br>{currentVersion.description}</f:format.nl2br></p>
|
||||
|
||||
<table cellpadding="0" cellspacing="0" class="currentVersionInfo">
|
||||
<tr class="ter-ext-single-info-key">
|
||||
<th><f:translate key="extensionList.showAllVersions.extensionKey" /></th>
|
||||
<td><strong>{currentVersion.extensionKey}</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><f:translate key="extensionList.showAllVersions.version" /></th>
|
||||
<td>
|
||||
{currentVersion.version}
|
||||
<span class="ter-ext-state ter-ext-state-{currentVersion.stateString}">{currentVersion.stateString}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><f:translate key="extensionList.showAllVersions.lastUploaded" /></th>
|
||||
<td>
|
||||
<f:format.date>{currentVersion.lastUpdated}</f:format.date></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><f:translate key="extensionList.showAllVersions.downloads" /></th>
|
||||
<td>
|
||||
<f:format.number decimals="0">
|
||||
{currentVersion.alldownloadcounter}
|
||||
</f:format.number>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><f:translate key="extensionList.showAllVersions.category" /></th>
|
||||
<td>{currentVersion.categoryString}</td>
|
||||
</tr>
|
||||
<tr class="ter-ext-single-info-manual">
|
||||
<th><f:translate key="extensionList.showAllVersions.manual" /></th>
|
||||
<td>
|
||||
<a href="https://docs.typo3.org/typo3cms/extensions/{currentVersion.extensionKey}/" target="_blank" rel="noopener noreferrer">
|
||||
<f:translate key="extensionList.showAllVersions.readOnline" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><f:translate key="extensionList.showAllVersions.author" /></th>
|
||||
<td>{currentVersion.authorName}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</f:if>
|
||||
|
||||
<table id="terVersionTable" class="table table-striped table-hover typo3-extension-list">
|
||||
<f:render partial="List/TerShowVersionsTable" arguments="{extensions: extensions}" />
|
||||
</table>
|
||||
</f:section>
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
{namespace em=TYPO3\CMS\Extensionmanager\ViewHelpers}
|
||||
|
||||
<f:layout name="main" />
|
||||
|
||||
<f:section name="headline">
|
||||
<h1><f:translate key="getExtensions">Get Extensions</f:translate></h1>
|
||||
</f:section>
|
||||
|
||||
<f:section name="content">
|
||||
<f:render partial="List/UploadForm" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<f:form class="typo3-extensionmanager-searchTerForm form-inline" action="ter">
|
||||
<div class="input-group">
|
||||
<f:form.textfield name="search" value="{search}" class="form-control" />
|
||||
<span class="input-group-btn">
|
||||
<f:form.submit class="btn btn-default" value="{f:translate(key:'searchTemplate.submitButton')}"/>
|
||||
</span>
|
||||
</div>
|
||||
</f:form>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="pull-right">
|
||||
<f:render partial="List/UpdateFromTer" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="terTableWrapper">
|
||||
<f:if condition="{search}">
|
||||
<f:then>
|
||||
<table id="terSearchTable" class="table table-striped table-hover typo3-extension-list">
|
||||
<f:render partial="List/TerTable" arguments="{paginatedExtensions:extensions, availableAndInstalled:availableAndInstalled}" />
|
||||
</table>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<f:render partial="List/TerPaginator" arguments="{extensions:extensions, showVersionList:showVersionList, availableAndInstalled:availableAndInstalled}" />
|
||||
</f:else>
|
||||
</f:if>
|
||||
<div class="splash-receivedata x-mask-loading"><core:icon identifier="spinner-circle-dark" /> <f:translate key="extensionList.updateFromTer.label"/></div>
|
||||
</div>
|
||||
|
||||
</f:section>
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
{namespace em=TYPO3\CMS\Extensionmanager\ViewHelpers}
|
||||
|
||||
<f:layout name="main" />
|
||||
|
||||
<f:section name="headline">
|
||||
<h1><f:translate key="installExtension">Install extension</f:translate> <small>{f:translate(key: 'downloadExtension.dependencies.errorTitle')}</small></h1>
|
||||
</f:section>
|
||||
|
||||
<f:section name="content">
|
||||
<f:render partial="List/UnresolvedDependencies" arguments="{_all}" />
|
||||
<f:be.infobox title="{f:translate(key: 'dependencyCheck.unresolvedDependencies.title')}" state="1">
|
||||
<f:render partial="List/UnresolvedDependenciesMessage" />
|
||||
</f:be.infobox>
|
||||
<f:link.action action="index" class="btn btn-default onClickMaskExtensionManager">
|
||||
Go back
|
||||
</f:link.action>
|
||||
<f:link.action action="installExtensionWithoutSystemDependencyCheck" controller="Action" arguments="{extensionKey: extension.extensionKey}" class="btn btn-danger t3js-dependencies disabled onClickMaskExtensionManager">
|
||||
<f:translate key="dependencyCheck.unresolvedDependencies.proceed" />
|
||||
</f:link.action>
|
||||
</f:section>
|
||||
@@ -0,0 +1,20 @@
|
||||
{namespace em=TYPO3\CMS\Extensionmanager\ViewHelpers}
|
||||
|
||||
<f:layout name="main" />
|
||||
|
||||
<f:section name="headline">
|
||||
<h1><f:translate key="updateScript.headline" arguments="{0: extensionKey}" /></h1>
|
||||
</f:section>
|
||||
|
||||
<f:section name="content">
|
||||
<div class="update-script">
|
||||
<f:if condition="{updateScriptResult}">
|
||||
<f:then>
|
||||
<f:format.raw>{updateScriptResult}</f:format.raw>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<f:translate key="updateScript.none" />
|
||||
</f:else>
|
||||
</f:if>
|
||||
</div>
|
||||
</f:section>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<f:form enctype="multipart/form-data" id="extensionUploadForm" method="post" controller="UploadExtensionFile" action="extract" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label for="extensionFile" class="col-sm-2 control-label">
|
||||
<f:translate key="uploadTemplate.extensionLabel" />
|
||||
</label>
|
||||
<div class="col-sm-4">
|
||||
<f:form.upload name="extensionFile" id="extensionFile" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-10 col-sm-offset-2">
|
||||
<div class="checkbox">
|
||||
<label for="extensionOverwrite">
|
||||
<f:form.checkbox name="overwrite" id="extensionOverwrite" value="1" />
|
||||
{f:translate(key:'extensionList.overwriteExtension')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<f:form.submit value="{f:translate(key:'uploadTemplate.uploadButton')}" id="extensionUpload" class="btn btn-primary"/>
|
||||
</div>
|
||||
</div>
|
||||
</f:form>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 808 B |
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"><title>module-extensionmanager</title><g fill="none" fill-rule="evenodd"><path d="M0 0h64v64H0z" fill="#F08D34"/><path d="M33.018 27.928l10.897-3.8A1 1 0 1 1 44.618 26L34 29.703V42.93c0 .589-.448 1.066-1 1.066s-1-.477-1-1.066V29.687l-10.67-3.815a1 1 0 1 1 .702-1.873l10.986 3.929zm.454-12a1.83 1.83 0 0 0-.944 0L16.472 21.05a.712.712 0 0 0-.472.643v20.633c0 .271.215.56.472.642l16.056 5.122c.26.083.687.082.944 0l16.056-5.122a.712.712 0 0 0 .472-.642V21.692a.723.723 0 0 0-.472-.643l-16.056-5.12z" fill="#FFF"/></g></svg>
|
||||
|
After Width: | Height: | Size: 581 B |
@@ -0,0 +1,23 @@
|
||||
<svg width="600" height="450" viewBox="0 0 600 450" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="1.5">
|
||||
<path fill="url(#a)" d="M0 0h600v450H0z"/>
|
||||
<path d="M374 384.829L629 290l-130-53-312 126" fill="url(#b)"/>
|
||||
<path d="M374 72v312.829L413 362V96l-39-24z" fill="#f7f7f7"/>
|
||||
<path d="M187 96l187-23v311.829L187 363V96z" fill="#fff"/>
|
||||
<path d="M187 96v267l187 21.829L413 362V96l-39-23-187 23z" fill="none" stroke="#969696" stroke-width="2"/>
|
||||
<path d="M291.466 305.869v-22.591l-22.591-2.759v22.591l22.591 2.759zm27.335-122.653c0-41.306-18.567-41.314-40.212-40.637-8.189.255-19.428 1.886-25.302 2.524v16.492c7.003-.5 18.299-1.38 27.561-.249 11.295 1.379 18.75 6.808 18.75 21.492 0 14.684-.903 16.155-15.587 31.757l-13.555 14.384v27.335l19.202 2.345v-23.687l12.199-12.969c15.136-15.998 16.944-21.166 16.944-38.787z" fill="#ccc" fill-rule="nonzero"/>
|
||||
<g fill-rule="nonzero">
|
||||
<path d="M388.788 360.247v8.369l-.813.47v-8.37l-1.44.831v-1.019l3.692-2.132v1.019l-1.439.832zm4.362 1.491v4.36l-.842.486v-4.36l-1.672-4.064.897-.517 1.233 3.082 1.234-4.507.851-.491-1.701 6.011zm4.224-1.62l-.785.453v3.541l-.814.47v-9.389s.805-.559 1.581-1.007c1.42-.82 1.831.204 1.831 1.853 0 2.039-.486 3.313-1.813 4.079m.093-5.03a7.66 7.66 0 0 0-.878.588v3.915l.878-.507c.524-.303.889-1.009.889-2.471 0-1.342-.226-1.908-.889-1.525m4.655 5.923c-1.542.89-1.953-.482-1.953-3.767 0-3.153.412-4.92 1.953-5.81 1.542-.89 1.953.402 1.953 3.555 0 3.285-.411 5.132-1.953 6.022m0-8.598c-.84.485-1.121 1.466-1.121 4.417 0 2.883.281 3.688 1.121 3.203.841-.486 1.121-1.615 1.121-4.497 0-2.951-.28-3.608-1.121-3.123m4.299 6.116c-.458.264-1.139.457-1.225.478v-.978c.225-.063.804-.259 1.216-.497.476-.274.785-1.033.785-2.067 0-1.219-.139-1.783-.803-1.399l-.767.442v-.98l.672-.388c.759-.438.794-1.572.794-2.107 0-1.061-.232-1.356-.699-1.086-.411.237-.88.655-1.15.892v-.979c.102-.086.655-.606 1.13-.881.963-.556 1.515-.284 1.515 1.715 0 .912-.225 1.846-.721 2.388.571-.275.832.472.832 1.786 0 1.999-.542 3.062-1.579 3.661"/>
|
||||
<path d="M382.618 368.303a1.666 1.666 0 0 1-.375.293c-1.13.652-2.789-4.056-2.789-5.942 0-.694.115-.991.276-1.284-1.382 1.03-3.042 2.717-3.572 3.949-.115.298-.184.702-.184 1.165 0 2.943 2.189 8.359 3.733 7.468.715-.413 1.92-2.794 2.911-5.649m-.721-8.381c1.428-.825 2.858-1.32 2.858-.162 0 2.348-1.038 5.793-1.567 6.099-.945.545-2.121-2.548-2.121-4.433 0-.86.231-1.158.83-1.504" fill="#ff8700"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="a" x2="1" gradientUnits="userSpaceOnUse" gradientTransform="rotate(36.9) scale(750)">
|
||||
<stop offset="0" stop-color="#fff"/>
|
||||
<stop offset="1" stop-color="#e6e6e6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" x2="1" gradientUnits="userSpaceOnUse" gradientTransform="rotate(53.626 -3.606 595.823) scale(98.3078)">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity="0"/>
|
||||
<stop offset=".48" stop-color="#3b3b3b" stop-opacity=".039"/>
|
||||
<stop offset="1" stop-opacity=".051"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
|
||||
<g>
|
||||
<circle fill="#FF8700" cx="32" cy="32" r="32"/>
|
||||
<circle fill="#FFFFFF" cx="32" cy="32" r="18"/>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M10.9,28.7c-0.1,0.4-0.2,0.8-0.4,1.2c-0.2,0.3-0.5,0.6-0.8,0.8c-0.3,0.2-0.7,0.4-1.2,0.4
|
||||
c-0.5,0.1-0.9,0.1-1.5,0c-0.5-0.1-1-0.2-1.4-0.4c-0.4-0.2-0.7-0.5-1-0.8c-0.2-0.3-0.4-0.7-0.5-1.1C4,28.5,4,28.1,4,27.6
|
||||
c0.1-0.4,0.2-0.8,0.4-1.2c0.2-0.3,0.5-0.6,0.8-0.8c0.3-0.2,0.7-0.4,1.2-0.4c0.4-0.1,0.9-0.1,1.5,0c0.5,0.1,1,0.2,1.4,0.4
|
||||
c0.4,0.2,0.7,0.5,1,0.8c0.3,0.3,0.4,0.7,0.5,1.1C11,27.8,11,28.2,10.9,28.7z M9.6,28.5c0.1-0.5-0.1-0.8-0.4-1.2
|
||||
c-0.3-0.3-0.9-0.5-1.5-0.6c-0.7-0.1-1.2-0.1-1.6,0.2c-0.4,0.2-0.7,0.5-0.7,1c-0.1,0.5,0.1,0.8,0.4,1.2c0.3,0.3,0.8,0.5,1.5,0.6
|
||||
c0.7,0.1,1.2,0.1,1.7-0.1S9.6,28.9,9.6,28.5z"/>
|
||||
<path fill="#FFFFFF" d="M11.7,24.9l-6.1-2.8l1.7-3.9l1.2,0.5l-1.1,2.5l1.4,0.6l1-2.1l1.2,0.5l-1,2.1l2.4,1.1L11.7,24.9z"/>
|
||||
<path fill="#FFFFFF" d="M13.8,20.4l-5.3-4.1l2.6-3.4l1,0.8l-1.7,2.2l1.2,1l1.4-1.8l1,0.8l-1.4,1.8l2.1,1.6L13.8,20.4z"/>
|
||||
<path fill="#FFFFFF" d="M17.1,16.5l-4.5-5l1.1-1l4.5,5L17.1,16.5z"/>
|
||||
<path fill="#FFFFFF" d="M21.6,13.3c-0.4,0.2-0.8,0.4-1.2,0.4c-0.4,0.1-0.8,0-1.2-0.1c-0.4-0.1-0.8-0.3-1.1-0.6
|
||||
c-0.4-0.3-0.7-0.7-1-1.2c-0.3-0.5-0.5-0.9-0.5-1.4c-0.1-0.5-0.1-0.9,0-1.3c0.1-0.4,0.3-0.8,0.5-1.1c0.3-0.3,0.6-0.6,0.9-0.8
|
||||
c0.4-0.2,0.8-0.3,1.1-0.4c0.4,0,0.7,0,1,0.1l-0.2,1.2c-0.2,0-0.4-0.1-0.7-0.1c-0.2,0-0.4,0.1-0.6,0.2c-0.2,0.1-0.4,0.3-0.5,0.5
|
||||
s-0.2,0.4-0.2,0.6c0,0.2,0,0.5,0,0.8c0.1,0.3,0.2,0.6,0.4,0.8c0.4,0.6,0.8,1,1.2,1.2s0.9,0.1,1.3-0.1c0.2-0.1,0.4-0.3,0.5-0.5
|
||||
c0.1-0.2,0.2-0.4,0.3-0.7l1.2,0.4C22.9,12.2,22.4,12.9,21.6,13.3z"/>
|
||||
<path fill="#FFFFFF" d="M24.1,12l-2.3-6.3l1.4-0.5l2.3,6.3L24.1,12z"/>
|
||||
<path fill="#FFFFFF" d="M25.9,11.2l1.1-7L28.8,4l3.1,6.3l-1.6,0.2L29.6,9l-2,0.3L27.4,11L25.9,11.2z M27.8,7.5l-0.1,0.6l1.4-0.2
|
||||
l-0.2-0.6c-0.1-0.3-0.3-0.7-0.4-1c-0.1-0.4-0.3-0.7-0.4-1l0,0c0,0.4-0.1,0.7-0.1,1.1C27.9,6.7,27.8,7.1,27.8,7.5z"/>
|
||||
<path fill="#FFFFFF" d="M32.2,10.4l0.6-6.7l1.5,0.1l-0.5,5.4l2.7,0.2l-0.1,1.3L32.2,10.4z"/>
|
||||
<path fill="#FFFFFF" d="M40.1,12.1l2.2-5l-1.7-0.8l0.5-1.2l4.8,2.1l-0.5,1.2l-1.7-0.8l-2.2,5L40.1,12.1z"/>
|
||||
<path fill="#FFFFFF" d="M44.5,14.5l1.5-1.9l1-4.7l1.3,1l-0.5,1.6c-0.1,0.3-0.2,0.5-0.2,0.8c-0.1,0.2-0.2,0.5-0.2,0.8l0,0
|
||||
c0.2-0.1,0.5-0.3,0.7-0.4c0.2-0.1,0.4-0.3,0.7-0.4l1.4-0.9l1.3,1l-4.2,2.2l-1.5,1.9L44.5,14.5z"/>
|
||||
<path fill="#FFFFFF" d="M47.5,16.8l5.3-4.2l1.4,1.8c0.2,0.3,0.4,0.6,0.6,0.9c0.1,0.3,0.2,0.6,0.2,0.9c0,0.3,0,0.6-0.2,0.9
|
||||
c-0.1,0.3-0.4,0.6-0.7,0.8c-0.3,0.2-0.6,0.4-0.9,0.5c-0.3,0.1-0.6,0.1-0.9,0c-0.3-0.1-0.6-0.2-0.8-0.4c-0.3-0.2-0.5-0.4-0.7-0.7
|
||||
l-0.5-0.7L48.4,18L47.5,16.8z M51.1,15.9l0.5,0.6c0.5,0.6,1.1,0.7,1.6,0.3c0.3-0.2,0.4-0.5,0.4-0.7s-0.2-0.6-0.4-0.9l-0.4-0.5
|
||||
L51.1,15.9z"/>
|
||||
<path fill="#FFFFFF" d="M51.8,23.9c-0.2-0.4-0.3-0.8-0.2-1.2s0.1-0.8,0.3-1.2s0.4-0.7,0.8-1c0.3-0.3,0.8-0.6,1.3-0.8
|
||||
c0.5-0.2,1-0.3,1.4-0.4c0.5,0,0.9,0,1.2,0.2s0.7,0.4,1,0.6s0.5,0.6,0.7,1.1c0.2,0.4,0.3,0.8,0.3,1.2c0,0.4-0.1,0.8-0.3,1.1
|
||||
c-0.2,0.4-0.4,0.7-0.8,1c-0.3,0.3-0.8,0.6-1.3,0.8c-0.5,0.2-1,0.3-1.4,0.4c-0.5,0-0.9,0-1.3-0.2s-0.7-0.3-1-0.6
|
||||
S52,24.3,51.8,23.9z M53,23.4c0.2,0.4,0.5,0.7,1,0.8c0.5,0.1,1,0,1.6-0.2c0.6-0.3,1.1-0.6,1.3-1c0.2-0.4,0.3-0.8,0.1-1.2
|
||||
c-0.2-0.4-0.5-0.7-0.9-0.8c-0.5-0.1-1,0-1.6,0.2c-0.6,0.3-1.1,0.6-1.3,1S52.9,23,53,23.4z"/>
|
||||
<path fill="#FFFFFF" d="M53.2,29.2c-0.1-0.5,0-1,0.1-1.4c0.1-0.4,0.3-0.7,0.6-1l1,0.6c-0.2,0.2-0.3,0.5-0.4,0.7
|
||||
c-0.1,0.3-0.1,0.5-0.1,0.8c0,0.3,0.1,0.6,0.3,0.7c0.2,0.2,0.4,0.2,0.6,0.2c0.1,0,0.3-0.1,0.4-0.1c0.1-0.1,0.2-0.2,0.3-0.3
|
||||
c0.1-0.1,0.1-0.3,0.1-0.6c0-0.2,0-0.5,0-0.9l1.1-0.1c0,0.3,0.1,0.5,0.1,0.7c0.1,0.2,0.1,0.3,0.2,0.5s0.2,0.2,0.3,0.2
|
||||
c0.1,0,0.2,0,0.4,0c0.2,0,0.4-0.1,0.5-0.3c0.1-0.2,0.1-0.4,0.1-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.1-0.2-0.3-0.4-0.5-0.5l0.8-0.9
|
||||
c0.3,0.3,0.5,0.6,0.7,0.9s0.3,0.7,0.4,1.1c0,0.3,0,0.6,0,0.9c0,0.3-0.1,0.5-0.3,0.7s-0.3,0.4-0.5,0.5c-0.2,0.1-0.5,0.2-0.7,0.3
|
||||
c-0.3,0-0.6,0-0.9-0.2c-0.3-0.2-0.5-0.4-0.7-0.7l0,0c-0.1,0.4-0.2,0.7-0.4,1s-0.6,0.4-1,0.5c-0.3,0-0.6,0-0.8-0.1
|
||||
c-0.3-0.1-0.5-0.3-0.7-0.5s-0.3-0.4-0.5-0.7C53.4,29.9,53.3,29.6,53.2,29.2z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M53.4,35.8l6.4,1.9l-0.5,1.8c-0.1,0.5-0.3,0.9-0.6,1.3c-0.2,0.4-0.5,0.6-0.9,0.8C57.4,41.9,57,42,56.6,42
|
||||
s-0.9,0-1.5-0.2c-0.6-0.2-1-0.4-1.4-0.7c-0.4-0.3-0.6-0.6-0.8-0.9c-0.2-0.4-0.3-0.7-0.3-1.2s0-0.9,0.2-1.3L53.4,35.8z M54.1,37.6
|
||||
L54,37.9c-0.1,0.3-0.1,0.5-0.1,0.8c0,0.2,0.1,0.5,0.2,0.7c0.1,0.2,0.3,0.4,0.5,0.6c0.2,0.2,0.6,0.3,0.9,0.4
|
||||
c0.4,0.1,0.7,0.2,1,0.2c0.3,0,0.5-0.1,0.7-0.2c0.2-0.1,0.4-0.3,0.5-0.5s0.2-0.4,0.3-0.7l0.1-0.3L54.1,37.6z"/>
|
||||
<path fill="#FFFFFF" d="M51.3,41.6l5.9,3.2l-0.7,1.3L50.6,43L51.3,41.6z"/>
|
||||
<path fill="#FFFFFF" d="M48.6,45.6c0.3-0.3,0.6-0.6,1-0.9c0.4-0.2,0.8-0.4,1.3-0.4l0.3,1.3c-0.3,0.1-0.6,0.2-0.9,0.3
|
||||
c-0.3,0.1-0.5,0.3-0.7,0.5c-0.2,0.2-0.3,0.5-0.3,0.7c0,0.2,0.1,0.4,0.2,0.5c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3,0
|
||||
s0.2-0.1,0.3-0.1c0.1-0.1,0.2-0.1,0.4-0.2l0.8-0.4c0.2-0.1,0.3-0.2,0.5-0.2s0.4-0.1,0.6-0.1c0.2,0,0.4,0,0.6,0.1s0.4,0.2,0.6,0.3
|
||||
c0.2,0.2,0.4,0.4,0.5,0.6c0.1,0.2,0.2,0.5,0.2,0.8c0,0.3,0,0.6-0.2,0.9s-0.3,0.6-0.5,0.8c-0.3,0.3-0.6,0.6-0.9,0.8
|
||||
s-0.7,0.3-1.1,0.4L51.5,50c0.3-0.1,0.5-0.1,0.7-0.3c0.2-0.1,0.4-0.3,0.6-0.5c0.2-0.2,0.3-0.4,0.3-0.6c0-0.2-0.1-0.3-0.2-0.5
|
||||
c-0.1-0.1-0.2-0.1-0.3-0.1c-0.1,0-0.2,0-0.3,0c-0.1,0-0.2,0.1-0.3,0.1c-0.1,0.1-0.2,0.1-0.4,0.2L50.8,49
|
||||
c-0.4,0.2-0.8,0.3-1.1,0.3c-0.4,0-0.7-0.2-1.1-0.5c-0.2-0.2-0.4-0.4-0.5-0.6c-0.1-0.2-0.2-0.5-0.2-0.8c0-0.3,0-0.6,0.2-0.9
|
||||
C48.1,46.2,48.3,45.9,48.6,45.6z"/>
|
||||
<path fill="#FFFFFF" d="M45.6,48.7l3.3,4.4l1.5-1.1l0.8,1l-4.2,3.1l-0.8-1l1.5-1.1l-3.3-4.4L45.6,48.7z"/>
|
||||
<path fill="#FFFFFF" d="M42.3,51l2.5,6.2l-2.2,0.9c-0.3,0.1-0.7,0.2-1,0.3c-0.3,0.1-0.6,0.1-0.9,0c-0.3-0.1-0.5-0.2-0.8-0.4
|
||||
s-0.4-0.5-0.6-0.8c-0.2-0.5-0.2-0.9-0.2-1.3c0.1-0.4,0.3-0.7,0.5-1l-2.4-1.9l1.6-0.6l2.1,1.8l0.8-0.3l-0.9-2.2L42.3,51z M42.3,55
|
||||
l-0.7,0.3c-0.4,0.2-0.6,0.3-0.8,0.6c-0.1,0.2-0.1,0.5,0,0.8c0.1,0.3,0.3,0.5,0.6,0.5c0.2,0,0.5,0,0.9-0.2l0.7-0.3L42.3,55z"/>
|
||||
<path fill="#FFFFFF" d="M36.7,53l1.2,6.6l-1.5,0.3l-1.2-6.6L36.7,53z"/>
|
||||
<path fill="#FFFFFF" d="M34,53.5l-0.1,6.7l-2.3,0c-0.4,0-0.7,0-1-0.1c-0.3-0.1-0.6-0.2-0.8-0.3c-0.2-0.1-0.4-0.3-0.5-0.5
|
||||
c-0.1-0.2-0.2-0.5-0.2-0.8c0-0.2,0-0.3,0.1-0.5s0.1-0.3,0.2-0.4c0.1-0.1,0.2-0.2,0.3-0.3s0.2-0.2,0.4-0.2v0
|
||||
c-0.2,0-0.3-0.1-0.5-0.2c-0.1-0.1-0.3-0.2-0.4-0.3C29.1,56.4,29,56.2,29,56c-0.1-0.2-0.1-0.4-0.1-0.6c0-0.3,0.1-0.6,0.2-0.9
|
||||
c0.1-0.2,0.3-0.4,0.6-0.6c0.2-0.2,0.5-0.3,0.8-0.3c0.3-0.1,0.7-0.1,1-0.1L34,53.5z M32.5,54.7l-0.9,0c-0.8,0-1.3,0.3-1.3,0.9
|
||||
c0,0.3,0.1,0.5,0.3,0.6c0.2,0.1,0.5,0.2,0.9,0.2l0.9,0L32.5,54.7z M32.4,57.6l-0.7,0c-0.4,0-0.6,0.1-0.8,0.2
|
||||
c-0.2,0.1-0.3,0.3-0.3,0.6c0,0.3,0.1,0.4,0.2,0.6c0.2,0.1,0.4,0.2,0.8,0.2l0.7,0L32.4,57.6z"/>
|
||||
<path fill="#FFFFFF" d="M25.7,52.4c0.8,0.3,1.4,0.7,1.7,1.3s0.3,1.5-0.1,2.6l-1.1,3.5l-1.5-0.4l1.1-3.6c0.2-0.6,0.2-1.1,0.1-1.4
|
||||
c-0.1-0.3-0.4-0.5-0.7-0.6s-0.7-0.1-1,0.1c-0.3,0.2-0.5,0.6-0.7,1.2l-1.1,3.6l-1.4-0.4l1.1-3.5c0.3-1.1,0.8-1.8,1.4-2.2
|
||||
C24.2,52.2,24.9,52.2,25.7,52.4z"/>
|
||||
<path fill="#FFFFFF" d="M21.1,50.5l-2.9,4.6l1.6,1L19,57.2l-4.4-2.8l0.7-1.1l1.6,1l2.9-4.6L21.1,50.5z"/>
|
||||
<path fill="#FFFFFF" d="M17.8,48.1L13.2,53l-1.1-1l4.6-4.9L17.8,48.1z"/>
|
||||
<path fill="#FFFFFF" d="M14.3,44c0.3,0.4,0.4,0.8,0.5,1.2c0.1,0.4,0.1,0.8,0,1.2c-0.1,0.4-0.3,0.8-0.6,1.1
|
||||
c-0.3,0.4-0.6,0.7-1.1,1c-0.5,0.3-0.9,0.5-1.3,0.6c-0.4,0.1-0.9,0.1-1.2,0.1c-0.4-0.1-0.8-0.2-1.1-0.4s-0.6-0.5-0.9-0.9
|
||||
s-0.4-0.8-0.5-1.2C8,46.3,8,46,8.1,45.6c0.1-0.4,0.3-0.8,0.6-1.1c0.3-0.4,0.6-0.7,1.1-1c0.5-0.3,0.9-0.5,1.3-0.6
|
||||
c0.4-0.1,0.9-0.2,1.3-0.1c0.4,0.1,0.8,0.2,1.1,0.4C13.8,43.3,14.1,43.6,14.3,44z M13.2,44.7c-0.3-0.4-0.6-0.6-1.1-0.6
|
||||
c-0.5,0-1,0.2-1.6,0.6c-0.6,0.4-0.9,0.8-1.1,1.2c-0.2,0.4-0.1,0.8,0.1,1.2c0.3,0.4,0.6,0.6,1.1,0.6c0.5,0,1-0.2,1.5-0.6
|
||||
c0.6-0.4,0.9-0.8,1.1-1.2C13.5,45.5,13.5,45.1,13.2,44.7z"/>
|
||||
<path fill="#FFFFFF" d="M12.2,40.8l-6.4,2l-0.5-1.5L8,38.6l1.2-1.1l0,0c-0.3,0.1-0.7,0.3-1.1,0.5c-0.4,0.2-0.8,0.3-1.2,0.4
|
||||
l-2.3,0.7l-0.4-1.4l6.4-2l0.5,1.5l-2.7,2.7L7.2,41l0,0c0.3-0.1,0.7-0.3,1.1-0.5s0.8-0.3,1.2-0.4l2.3-0.7L12.2,40.8z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FF8700" d="M38.4,36.4c-0.3,0.1-0.6,0.1-1,0.1c-3,0-7.3-10.3-7.3-13.8c0-1.3,0.3-1.7,0.7-2.1c-3.6,0.4-8,1.8-9.4,3.4
|
||||
c-0.3,0.4-0.5,1.1-0.5,1.9c0,5.4,5.7,17.6,9.8,17.6C32.7,43.6,35.8,40.6,38.4,36.4"/>
|
||||
<path fill="#FF8700" d="M36.5,20.4c3.7,0,7.5,0.6,7.5,2.7c0,4.3-2.7,9.5-4.1,9.5c-2.5,0-5.6-6.9-5.6-10.3
|
||||
C34.3,20.7,34.9,20.4,36.5,20.4"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.7 KiB |
@@ -0,0 +1,755 @@
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
/**
|
||||
* Module: TYPO3/CMS/Extensionmanager/Main
|
||||
* main logic holding everything together, consists of multiple parts
|
||||
* ExtensionManager => Various functions for displaying the extension list / sorting
|
||||
* Repository => Various AJAX functions for TER downloads
|
||||
* ExtensionManager.Update => Various AJAX functions to display updates
|
||||
* ExtensionManager.uploadForm => helper to show the upload form
|
||||
*/
|
||||
define([
|
||||
'jquery',
|
||||
'nprogress',
|
||||
'TYPO3/CMS/Backend/Modal',
|
||||
'TYPO3/CMS/Backend/SplitButtons',
|
||||
'TYPO3/CMS/Backend/Tooltip',
|
||||
'TYPO3/CMS/Backend/Notification',
|
||||
'TYPO3/CMS/Backend/Severity',
|
||||
'TYPO3/CMS/Core/SecurityUtility',
|
||||
'datatables',
|
||||
'TYPO3/CMS/Backend/jquery.clearable'
|
||||
], function($, NProgress, Modal, SplitButtons, Tooltip, Notification, Severity, SecurityUtility) {
|
||||
|
||||
var securityUtility = new SecurityUtility();
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {{identifier: {extensionlist: string, searchField: string, extensionManager: string}}}
|
||||
* @exports TYPO3/CMS/Extensionmanager/Main
|
||||
*/
|
||||
var ExtensionManager = {
|
||||
identifier: {
|
||||
extensionlist: '#typo3-extension-list',
|
||||
searchField: '#Tx_Extensionmanager_extensionkey'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
ExtensionManager.manageExtensionListing = function() {
|
||||
var $searchField = $(this.identifier.searchField),
|
||||
dataTable = $(this.identifier.extensionlist).DataTable({
|
||||
paging: false,
|
||||
dom: 'lrtip',
|
||||
lengthChange: false,
|
||||
pageLength: 15,
|
||||
stateSave: true,
|
||||
drawCallback: this.bindExtensionListActions,
|
||||
columns: [
|
||||
null,
|
||||
null,
|
||||
{
|
||||
type: 'extension'
|
||||
},
|
||||
null,
|
||||
{
|
||||
type: 'version'
|
||||
}, {
|
||||
orderable: false
|
||||
},
|
||||
null,
|
||||
null
|
||||
]
|
||||
});
|
||||
|
||||
$searchField.parents('form').on('submit', function() {
|
||||
return false;
|
||||
});
|
||||
|
||||
var getVars = ExtensionManager.getUrlVars();
|
||||
|
||||
// restore filter
|
||||
var currentSearch = (getVars['search'] ? getVars['search'] : dataTable.search());
|
||||
$searchField.val(currentSearch);
|
||||
|
||||
$searchField.on('input', function(e) {
|
||||
dataTable.search($(this).val()).draw();
|
||||
});
|
||||
|
||||
return dataTable;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
ExtensionManager.bindExtensionListActions = function() {
|
||||
$('.removeExtension').not('.transformed').each(function() {
|
||||
var $me = $(this);
|
||||
$me.data('href', $me.attr('href'));
|
||||
$me.attr('href', '#');
|
||||
$me.addClass('transformed');
|
||||
$me.click(function() {
|
||||
Modal.confirm(
|
||||
TYPO3.lang['extensionList.removalConfirmation.title'],
|
||||
TYPO3.lang['extensionList.removalConfirmation.question'],
|
||||
Severity.error,
|
||||
[
|
||||
{
|
||||
text: TYPO3.lang['button.cancel'],
|
||||
active: true,
|
||||
btnClass: 'btn-default',
|
||||
trigger: function() {
|
||||
Modal.dismiss();
|
||||
}
|
||||
}, {
|
||||
text: TYPO3.lang['button.remove'],
|
||||
btnClass: 'btn-danger',
|
||||
trigger: function() {
|
||||
ExtensionManager.removeExtensionFromDisk($me);
|
||||
Modal.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} $extension
|
||||
*/
|
||||
ExtensionManager.removeExtensionFromDisk = function($extension) {
|
||||
$.ajax({
|
||||
url: $extension.data('href'),
|
||||
beforeSend: function() {
|
||||
NProgress.start();
|
||||
},
|
||||
success: function() {
|
||||
location.reload();
|
||||
},
|
||||
complete: function() {
|
||||
NProgress.done();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
ExtensionManager.getUrlVars = function() {
|
||||
var vars = [], hash;
|
||||
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
|
||||
for (var i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i].split('=');
|
||||
vars.push(hash[0]);
|
||||
vars[hash[0]] = hash[1];
|
||||
}
|
||||
return vars;
|
||||
};
|
||||
|
||||
$.fn.dataTableExt.oSort['extension-asc'] = function(a, b) {
|
||||
return ExtensionManager.extensionCompare(a, b);
|
||||
};
|
||||
|
||||
$.fn.dataTableExt.oSort['extension-desc'] = function(a, b) {
|
||||
var result = ExtensionManager.extensionCompare(a, b);
|
||||
return result * -1;
|
||||
};
|
||||
|
||||
$.fn.dataTableExt.oSort['version-asc'] = function(a, b) {
|
||||
var result = ExtensionManager.versionCompare(a, b);
|
||||
return result * -1;
|
||||
};
|
||||
|
||||
$.fn.dataTableExt.oSort['version-desc'] = function(a, b) {
|
||||
return ExtensionManager.versionCompare(a, b);
|
||||
};
|
||||
|
||||
/**
|
||||
* Special sorting for the extension version column
|
||||
*
|
||||
* @param {String} a
|
||||
* @param {String} b
|
||||
* @returns {Number}
|
||||
*/
|
||||
ExtensionManager.versionCompare = function(a, b) {
|
||||
if (a === b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var a_components = a.split(".");
|
||||
var b_components = b.split(".");
|
||||
|
||||
var len = Math.min(a_components.length, b_components.length);
|
||||
|
||||
// loop while the components are equal
|
||||
for (var i = 0; i < len; i++) {
|
||||
// A bigger than B
|
||||
if (parseInt(a_components[i]) > parseInt(b_components[i])) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// B bigger than A
|
||||
if (parseInt(a_components[i]) < parseInt(b_components[i])) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// If one's a prefix of the other, the longer one is greaRepository.
|
||||
if (a_components.length > b_components.length) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (a_components.length < b_components.length) {
|
||||
return -1;
|
||||
}
|
||||
// Otherwise they are the same.
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* The extension name column can contain various forms of HTML that
|
||||
* break a direct comparison of values
|
||||
*
|
||||
* @param {String} a
|
||||
* @param {String} b
|
||||
* @returns {Number}
|
||||
*/
|
||||
ExtensionManager.extensionCompare = function(a, b) {
|
||||
var div = document.createElement("div");
|
||||
div.innerHTML = a;
|
||||
var aStr = div.textContent || div.innerText || a;
|
||||
|
||||
div.innerHTML = b;
|
||||
var bStr = div.textContent || div.innerText || b;
|
||||
|
||||
return aStr.trim().localeCompare(bStr.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} data
|
||||
*/
|
||||
ExtensionManager.updateExtension = function(data) {
|
||||
var i = 0;
|
||||
var $form = $('<form>');
|
||||
$.each(data.updateComments, function(version, comment) {
|
||||
var $input = $('<input>').attr({type: 'radio', name: 'version'}).val(version);
|
||||
if (i === 0) {
|
||||
$input.attr('checked', 'checked');
|
||||
}
|
||||
$form.append([
|
||||
$('<h3>').append([
|
||||
$input,
|
||||
' ' + securityUtility.encodeHtml(version)
|
||||
]),
|
||||
$('<div>')
|
||||
.append(
|
||||
comment
|
||||
.replace(/(\r\n|\n\r|\r|\n)/g, '\n')
|
||||
.split(/\n/).map(function(line) {
|
||||
return securityUtility.encodeHtml(line);
|
||||
})
|
||||
.join('<br>')
|
||||
)
|
||||
]);
|
||||
i++;
|
||||
});
|
||||
var $container = $('<div>').append([
|
||||
$('<h1>').text(TYPO3.lang['extensionList.updateConfirmation.title']),
|
||||
$('<h2>').text(TYPO3.lang['extensionList.updateConfirmation.message']),
|
||||
$form
|
||||
]);
|
||||
|
||||
NProgress.done();
|
||||
|
||||
Modal.confirm(
|
||||
TYPO3.lang['extensionList.updateConfirmation.questionVersionComments'],
|
||||
$container,
|
||||
Severity.warning,
|
||||
[
|
||||
{
|
||||
text: TYPO3.lang['button.cancel'],
|
||||
active: true,
|
||||
btnClass: 'btn-default',
|
||||
trigger: function() {
|
||||
Modal.dismiss();
|
||||
}
|
||||
}, {
|
||||
text: TYPO3.lang['button.updateExtension'],
|
||||
btnClass: 'btn-warning',
|
||||
trigger: function() {
|
||||
$.ajax({
|
||||
url: data.url,
|
||||
data: {
|
||||
tx_extensionmanager_tools_extensionmanagerextensionmanager: {
|
||||
version: $('input:radio[name=version]:checked', Modal.currentModal).val()
|
||||
}
|
||||
},
|
||||
dataType: 'json',
|
||||
beforeSend: function() {
|
||||
NProgress.start();
|
||||
},
|
||||
complete: function() {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
Modal.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {{downloadPath: string}}
|
||||
*/
|
||||
var Repository = {
|
||||
downloadPath: ''
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
Repository.initDom = function() {
|
||||
NProgress.configure({parent: '.module-loading-indicator', showSpinner: false});
|
||||
|
||||
$('#terTable').DataTable({
|
||||
lengthChange: false,
|
||||
pageLength: 15,
|
||||
stateSave: false,
|
||||
info: false,
|
||||
paging: false,
|
||||
searching: false,
|
||||
ordering: false,
|
||||
drawCallback: Repository.bindDownload
|
||||
});
|
||||
|
||||
$('#terVersionTable').DataTable({
|
||||
lengthChange: false,
|
||||
pageLength: 15,
|
||||
stateSave: false,
|
||||
info: false,
|
||||
paging: false,
|
||||
searching: false,
|
||||
drawCallback: Repository.bindDownload,
|
||||
order: [
|
||||
[2, 'asc']
|
||||
],
|
||||
columns: [
|
||||
{orderable: false},
|
||||
null,
|
||||
{type: 'version'},
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
});
|
||||
|
||||
$('#terSearchTable').DataTable({
|
||||
paging: false,
|
||||
lengthChange: false,
|
||||
stateSave: false,
|
||||
searching: false,
|
||||
language: {
|
||||
search: 'Filter results:'
|
||||
},
|
||||
ordering: false,
|
||||
drawCallback: Repository.bindDownload
|
||||
});
|
||||
|
||||
Repository.bindDownload();
|
||||
Repository.bindSearchFieldResetter();
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
Repository.bindDownload = function() {
|
||||
var installButtons = $('.downloadFromTer form.download button[type=submit]');
|
||||
installButtons.off('click');
|
||||
installButtons.on('click', function(event) {
|
||||
event.preventDefault();
|
||||
var url = $(event.currentTarget.form).attr('data-href');
|
||||
Repository.downloadPath = $(event.currentTarget.form).find('input.downloadPath:checked').val();
|
||||
$.ajax({
|
||||
url: url,
|
||||
dataType: 'json',
|
||||
beforeSend: function() {
|
||||
NProgress.start();
|
||||
},
|
||||
success: Repository.getDependencies
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
Repository.getDependencies = function(data) {
|
||||
NProgress.done();
|
||||
if (data.hasDependencies) {
|
||||
Modal.confirm(data.title, $(data.message), Severity.info, [
|
||||
{
|
||||
text: TYPO3.lang['button.cancel'],
|
||||
active: true,
|
||||
btnClass: 'btn-default',
|
||||
trigger: function() {
|
||||
Modal.dismiss();
|
||||
}
|
||||
}, {
|
||||
text: TYPO3.lang['button.resolveDependencies'],
|
||||
btnClass: 'btn-info',
|
||||
trigger: function() {
|
||||
Repository.getResolveDependenciesAndInstallResult(data.url + '&tx_extensionmanager_tools_extensionmanagerextensionmanager[downloadPath]=' + Repository.downloadPath);
|
||||
Modal.dismiss();
|
||||
}
|
||||
}
|
||||
]);
|
||||
} else {
|
||||
if (data.hasErrors) {
|
||||
Notification.error(data.title, data.message, 15);
|
||||
} else {
|
||||
Repository.getResolveDependenciesAndInstallResult(data.url + '&tx_extensionmanager_tools_extensionmanagerextensionmanager[downloadPath]=' + Repository.downloadPath);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} url
|
||||
*/
|
||||
Repository.getResolveDependenciesAndInstallResult = function(url) {
|
||||
$.ajax({
|
||||
url: url,
|
||||
dataType: 'json',
|
||||
beforeSend: function() {
|
||||
NProgress.start();
|
||||
},
|
||||
success: function(data) {
|
||||
if (data.errorCount > 0) {
|
||||
Modal.confirm(data.errorTitle, $(data.errorMessage), Severity.error, [
|
||||
{
|
||||
text: TYPO3.lang['button.cancel'],
|
||||
active: true,
|
||||
btnClass: 'btn-default',
|
||||
trigger: function() {
|
||||
Modal.dismiss();
|
||||
}
|
||||
}, {
|
||||
text: TYPO3.lang['button.resolveDependenciesIgnore'],
|
||||
btnClass: 'btn-danger disabled t3js-dependencies',
|
||||
trigger: function() {
|
||||
if (!$(this).hasClass('disabled')) {
|
||||
Repository.getResolveDependenciesAndInstallResult(data.skipDependencyUri);
|
||||
Modal.dismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
Modal.currentModal.on('shown.bs.modal', function() {
|
||||
var $actionButton = Modal.currentModal.find('.t3js-dependencies');
|
||||
$('input[name="unlockDependencyIgnoreButton"]', Modal.currentModal).on('change', function() {
|
||||
$actionButton.toggleClass('disabled', !$(this).prop('checked'));
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var successMessage = TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.message' + data.installationTypeLanguageKey].replace(/\{0\}/g, data.extension);
|
||||
|
||||
successMessage += '\n' + TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.header'] + ': ';
|
||||
$.each(data.result, function(index, value) {
|
||||
successMessage += '\n\n' + TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.item'] + ' ' + index + ': ';
|
||||
$.each(value, function(extkey) {
|
||||
successMessage += '\n* ' + extkey
|
||||
});
|
||||
});
|
||||
Notification.info(TYPO3.lang['extensionList.dependenciesResolveFlashMessage.title' + data.installationTypeLanguageKey].replace(/\{0\}/g, data.extension), successMessage, 15);
|
||||
top.TYPO3.ModuleMenu.App.refreshMenu();
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
NProgress.done();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
Repository.bindSearchFieldResetter = function() {
|
||||
var $searchFields = $('.typo3-extensionmanager-searchTerForm input[type="text"]');
|
||||
var searchResultShown = ('' !== $searchFields.first().val());
|
||||
|
||||
$searchFields.clearable(
|
||||
{
|
||||
onClear: function() {
|
||||
if (searchResultShown) {
|
||||
$(this).closest('form').submit();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {{identifier: {extensionTable: string, terUpdateAction: string, pagination: string, splashscreen: string, terTableWrapper: string, terTableDataTableWrapper: string}}}
|
||||
*/
|
||||
ExtensionManager.Update = {
|
||||
identifier: {
|
||||
extensionTable: '#terTable',
|
||||
terUpdateAction: '.update-from-ter',
|
||||
pagination: '.pagination-wrap',
|
||||
splashscreen: '.splash-receivedata',
|
||||
terTableWrapper: '#terTableWrapper',
|
||||
terTableDataTableWrapper: '#terTableWrapper .dataTables_wrapper'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Register "update from ter" action
|
||||
*/
|
||||
ExtensionManager.Update.initializeEvents = function() {
|
||||
$(ExtensionManager.Update.identifier.terUpdateAction).each(function() {
|
||||
|
||||
// "this" is the form which updates the extension list from
|
||||
// TER on submit
|
||||
var $me = $(this),
|
||||
updateURL = $(this).attr('action');
|
||||
|
||||
$me.attr('action', '#');
|
||||
$me.submit(function() {
|
||||
// Force update on click.
|
||||
ExtensionManager.Update.updateFromTer(updateURL, true);
|
||||
|
||||
// Prevent normal submit action.
|
||||
return false;
|
||||
});
|
||||
|
||||
// This might give problems when there are more "update"-buttons,
|
||||
// each one would trigger a TER-ExtensionManager.Update.
|
||||
ExtensionManager.Update.updateFromTer(updateURL, false);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} url
|
||||
* @param {Boolean} forceUpdate
|
||||
*/
|
||||
ExtensionManager.Update.updateFromTer = function(url, forceUpdate) {
|
||||
if (forceUpdate) {
|
||||
url = url + '&tx_extensionmanager_tools_extensionmanagerextensionmanager%5BforceUpdateCheck%5D=1';
|
||||
}
|
||||
|
||||
// Hide triggers for TER update
|
||||
$(ExtensionManager.Update.identifier.terUpdateAction).addClass('is-hidden');
|
||||
|
||||
// Hide extension table
|
||||
$(ExtensionManager.Update.identifier.extensionTable).hide();
|
||||
|
||||
// Show loaders
|
||||
$(ExtensionManager.Update.identifier.splashscreen).addClass('is-shown');
|
||||
$(ExtensionManager.Update.identifier.terTableDataTableWrapper).addClass('is-loading');
|
||||
$(ExtensionManager.Update.identifier.pagination).addClass('is-loading');
|
||||
|
||||
var reload = false;
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
beforeSend: function() {
|
||||
NProgress.start();
|
||||
},
|
||||
success: function(data) {
|
||||
// Something went wrong, show message
|
||||
if (data.errorMessage.length) {
|
||||
Notification.error(TYPO3.lang['extensionList.updateFromTerFlashMessage.title'], data.errorMessage, 10);
|
||||
}
|
||||
|
||||
// Message with latest updates
|
||||
var $lastUpdate = $(ExtensionManager.Update.identifier.terUpdateAction + ' .time-since-last-update');
|
||||
$lastUpdate.text(data.timeSinceLastUpdate);
|
||||
$lastUpdate.attr(
|
||||
'title',
|
||||
TYPO3.lang['extensionList.updateFromTer.lastUpdate.timeOfLastUpdate'] + data.lastUpdateTime
|
||||
);
|
||||
|
||||
if (data.updated) {
|
||||
// Reload page
|
||||
reload = true;
|
||||
window.location.replace(window.location.href);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
// Create an error message with diagnosis info.
|
||||
var errorMessage = textStatus + '(' + errorThrown + '): ' + jqXHR.responseText;
|
||||
|
||||
Notification.warning(
|
||||
TYPO3.lang['extensionList.updateFromTerFlashMessage.title'],
|
||||
errorMessage,
|
||||
10
|
||||
);
|
||||
},
|
||||
complete: function() {
|
||||
NProgress.done();
|
||||
|
||||
if (!reload) {
|
||||
// Hide loaders
|
||||
$(ExtensionManager.Update.identifier.splashscreen).removeClass('is-shown');
|
||||
$(ExtensionManager.Update.identifier.terTableDataTableWrapper).removeClass('is-loading');
|
||||
$(ExtensionManager.Update.identifier.pagination).removeClass('is-loading');
|
||||
|
||||
// Show triggers for TER-update
|
||||
$(ExtensionManager.Update.identifier.terUpdateAction).removeClass('is-hidden');
|
||||
|
||||
// Show extension table
|
||||
$(ExtensionManager.Update.identifier.extensionTable).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
ExtensionManager.Update.transformPaginatorToAjax = function() {
|
||||
$(ExtensionManager.Update.identifier.pagination + ' a').each(function() {
|
||||
var $me = $(this);
|
||||
$me.data('href', $(this).attr('href'));
|
||||
$me.attr('href', '#');
|
||||
$me.click(function() {
|
||||
var $terTableWrapper = $(ExtensionManager.Update.identifier.terTableWrapper);
|
||||
NProgress.start();
|
||||
$.ajax({
|
||||
url: $(this).data('href'),
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
$terTableWrapper.html(data);
|
||||
ExtensionManager.Update.transformPaginatorToAjax();
|
||||
},
|
||||
complete: function() {
|
||||
NProgress.done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* show the uploading form
|
||||
*/
|
||||
ExtensionManager.UploadForm = {
|
||||
expandedUploadFormClass: 'transformed'
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
ExtensionManager.UploadForm.initializeEvents = function() {
|
||||
// Show upload form
|
||||
$(document).on('click', '.t3js-upload', function(event) {
|
||||
var $me = $(this),
|
||||
$uploadForm = $('.uploadForm');
|
||||
|
||||
event.preventDefault();
|
||||
if ($me.hasClass(ExtensionManager.UploadForm.expandedUploadFormClass)) {
|
||||
$uploadForm.stop().slideUp();
|
||||
$me.removeClass(ExtensionManager.UploadForm.expandedUploadFormClass);
|
||||
} else {
|
||||
$me.addClass(ExtensionManager.UploadForm.expandedUploadFormClass);
|
||||
$uploadForm.stop().slideDown();
|
||||
|
||||
$.ajax({
|
||||
url: $me.attr('href'),
|
||||
dataType: 'html',
|
||||
success: function(data) {
|
||||
$uploadForm.html(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
var dataTable = ExtensionManager.manageExtensionListing();
|
||||
|
||||
$(document).on('click', '.onClickMaskExtensionManager', function() {
|
||||
NProgress.start();
|
||||
}).on('click', 'a[data-action=update-extension]', function(e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: $(this).attr('href'),
|
||||
dataType: 'json',
|
||||
beforeSend: function() {
|
||||
NProgress.start();
|
||||
},
|
||||
success: ExtensionManager.updateExtension
|
||||
});
|
||||
}).on('change', 'input[name=unlockDependencyIgnoreButton]', function() {
|
||||
var $actionButton = $('.t3js-dependencies');
|
||||
$actionButton.toggleClass('disabled', !$(this).prop('checked'));
|
||||
});
|
||||
|
||||
$(ExtensionManager.identifier.searchField).clearable({
|
||||
onClear: function() {
|
||||
dataTable.search('').draw();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '.t3-button-action-installdistribution', function() {
|
||||
NProgress.start();
|
||||
});
|
||||
|
||||
SplitButtons.addPreSubmitCallback(function(e) {
|
||||
if ($(e.target).hasClass('t3js-save-close')) {
|
||||
$('#configurationform').append($('<input />', {
|
||||
type: 'hidden',
|
||||
name: 'tx_extensionmanager_tools_extensionmanagerextensionmanager[action]',
|
||||
value: 'saveAndClose'
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
// initialize the repository
|
||||
Repository.initDom();
|
||||
|
||||
ExtensionManager.Update.initializeEvents();
|
||||
ExtensionManager.UploadForm.initializeEvents();
|
||||
|
||||
Tooltip.initialize('#typo3-extension-list [title]', {
|
||||
delay: {
|
||||
show: 500,
|
||||
hide: 100
|
||||
},
|
||||
trigger: 'hover',
|
||||
container: 'body'
|
||||
});
|
||||
});
|
||||
|
||||
if (typeof TYPO3.ExtensionManager === 'undefined') {
|
||||
TYPO3.ExtensionManager = ExtensionManager;
|
||||
}
|
||||
|
||||
return ExtensionManager;
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "typo3/cms-extensionmanager",
|
||||
"type": "typo3-cms-framework",
|
||||
"description": "TYPO3 Extension Manager",
|
||||
"homepage": "https://typo3.org",
|
||||
"license": ["GPL-2.0-or-later"],
|
||||
"authors": [{
|
||||
"name": "TYPO3 Core Team",
|
||||
"email": "typo3cms@typo3.org",
|
||||
"role": "Developer"
|
||||
}],
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"require": {
|
||||
"typo3/cms-core": "9.5.31"
|
||||
},
|
||||
"conflict": {
|
||||
"typo3/cms": "*"
|
||||
},
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"Package": {
|
||||
"protected": true,
|
||||
"partOfFactoryDefault": true,
|
||||
"partOfMinimalUsableSystem": true
|
||||
},
|
||||
"extension-key": "extensionmanager"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"TYPO3\\CMS\\Extensionmanager\\": "Classes/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"TYPO3\\CMS\\Extensionmanager\\Tests\\": "Tests/"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# cat=basic/enable; type=boolean; label=LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:config.automaticInstallation
|
||||
automaticInstallation = 1
|
||||
|
||||
# cat=basic/enable; type=boolean; label=LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:config.offlineMode
|
||||
offlineMode = 0
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
$EM_CONF[$_EXTKEY] = [
|
||||
'title' => 'Extension Manager',
|
||||
'description' => 'TYPO3 Extension Manager',
|
||||
'category' => 'module',
|
||||
'state' => 'stable',
|
||||
'uploadfolder' => 0,
|
||||
'createDirs' => '',
|
||||
'clearCacheOnLoad' => 0,
|
||||
'author' => 'TYPO3 Core Team',
|
||||
'author_email' => 'typo3cms@typo3.org',
|
||||
'author_company' => '',
|
||||
'version' => '9.5.31',
|
||||
'constraints' => [
|
||||
'depends' => [
|
||||
'typo3' => '9.5.31',
|
||||
],
|
||||
'conflicts' => [],
|
||||
'suggests' => [],
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
defined('TYPO3_MODE') or die();
|
||||
|
||||
// Register extension list update task
|
||||
$offlineMode = (bool)\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
|
||||
\TYPO3\CMS\Core\Configuration\ExtensionConfiguration::class
|
||||
)->get('extensionmanager', 'offlineMode');
|
||||
if (!$offlineMode) {
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Extensionmanager\Task\UpdateExtensionListTask::class] = [
|
||||
'extension' => 'extensionmanager',
|
||||
'title' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:task.updateExtensionListTask.name',
|
||||
'description' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang.xlf:task.updateExtensionListTask.description',
|
||||
'additionalFields' => '',
|
||||
];
|
||||
}
|
||||
unset($offlineMode);
|
||||
|
||||
if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_BE) {
|
||||
$signalSlotDispatcher = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class);
|
||||
$signalSlotDispatcher->connect(
|
||||
\TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService::class,
|
||||
'willInstallExtensions',
|
||||
\TYPO3\CMS\Core\Package\PackageManager::class,
|
||||
'scanAvailablePackages'
|
||||
);
|
||||
unset($signalSlotDispatcher);
|
||||
}
|
||||
|
||||
// Register extension status report system
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['reports']['tx_reports']['status']['providers']['Extension Manager'][] =
|
||||
\TYPO3\CMS\Extensionmanager\Report\ExtensionStatus::class;
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
defined('TYPO3_MODE') or die();
|
||||
|
||||
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
|
||||
'TYPO3.CMS.Extensionmanager',
|
||||
'tools',
|
||||
'extensionmanager',
|
||||
'',
|
||||
[
|
||||
'List' => 'index,unresolvedDependencies,ter,showAllVersions,distributions',
|
||||
'Action' => 'toggleExtensionInstallationState,installExtensionWithoutSystemDependencyCheck,removeExtension,downloadExtensionZip,reloadExtensionData',
|
||||
'Configuration' => 'showConfigurationForm,save,saveAndClose',
|
||||
'Download' => 'checkDependencies,installFromTer,installExtensionWithoutSystemDependencyCheck,installDistribution,updateExtension,updateCommentForUpdatableVersions',
|
||||
'UpdateScript' => 'show',
|
||||
'UpdateFromTer' => 'updateExtensionListFromTer',
|
||||
'UploadExtensionFile' => 'form,extract',
|
||||
'Distribution' => 'show'
|
||||
],
|
||||
[
|
||||
'access' => 'systemMaintainer',
|
||||
'icon' => 'EXT:extensionmanager/Resources/Public/Icons/module-extensionmanager.svg',
|
||||
'labels' => 'LLL:EXT:extensionmanager/Resources/Private/Language/locallang_mod.xlf',
|
||||
]
|
||||
);
|
||||
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# Table structure for table 'tx_extensionmanager_domain_model_repository'
|
||||
#
|
||||
CREATE TABLE tx_extensionmanager_domain_model_repository (
|
||||
title varchar(150) NOT NULL default '',
|
||||
description mediumtext,
|
||||
wsdl_url varchar(100) NOT NULL default '',
|
||||
mirror_list_url varchar(100) NOT NULL default '',
|
||||
last_update int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
extension_count int(11) DEFAULT '0' NOT NULL
|
||||
);
|
||||
|
||||
#
|
||||
# Table structure for table 'tx_extensionmanager_domain_model_extension'
|
||||
#
|
||||
CREATE TABLE tx_extensionmanager_domain_model_extension (
|
||||
extension_key varchar(60) NOT NULL default '',
|
||||
repository int(11) unsigned NOT NULL default '1',
|
||||
version varchar(15) NOT NULL default '',
|
||||
alldownloadcounter int(11) unsigned NOT NULL default '0',
|
||||
downloadcounter int(11) unsigned NOT NULL default '0',
|
||||
title varchar(150) NOT NULL default '',
|
||||
description mediumtext,
|
||||
state int(4) NOT NULL default '0',
|
||||
review_state int(4) NOT NULL default '0',
|
||||
category int(4) NOT NULL default '0',
|
||||
last_updated int(11) unsigned NOT NULL default '0',
|
||||
serialized_dependencies mediumtext,
|
||||
author_name varchar(255) NOT NULL default '',
|
||||
author_email varchar(255) NOT NULL default '',
|
||||
ownerusername varchar(50) NOT NULL default '',
|
||||
md5hash varchar(35) NOT NULL default '',
|
||||
update_comment mediumtext,
|
||||
authorcompany varchar(255) NOT NULL default '',
|
||||
integer_version int(11) NOT NULL default '0',
|
||||
current_version int(3) NOT NULL default '0',
|
||||
lastreviewedversion int(3) NOT NULL default '0',
|
||||
|
||||
KEY index_extrepo (extension_key,repository),
|
||||
KEY index_versionrepo (integer_version,repository,extension_key),
|
||||
KEY index_currentversions (current_version,review_state),
|
||||
UNIQUE versionextrepo (extension_key,version,repository)
|
||||
);
|
||||
Loaded 100 of 12716 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user