2 namespace TYPO3\CMS\Extensionmanager\Utility
;
5 * This file is part of the TYPO3 CMS project.
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
18 * Extension Manager Install Utility
20 * @author Susanne Moog <susanne.moog@typo3.org>
22 class InstallUtility
implements \TYPO3\CMS\Core\SingletonInterface
{
25 * @var \TYPO3\CMS\Extbase\Object\ObjectManager
28 public $objectManager;
31 * @var \TYPO3\CMS\Install\Service\SqlSchemaMigrationService
34 public $installToolSqlParser;
37 * @var \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility
40 protected $dependencyUtility;
43 * @var \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
46 protected $fileHandlingUtility;
49 * @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
52 protected $listUtility;
55 * @var \TYPO3\CMS\Extensionmanager\Utility\DatabaseUtility
58 protected $databaseUtility;
61 * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
64 public $extensionRepository;
67 * @var \TYPO3\CMS\Core\Package\PackageManager
70 protected $packageManager;
73 * @var \TYPO3\CMS\Core\Cache\CacheManager
76 protected $cacheManager;
79 * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
82 protected $signalSlotDispatcher;
85 * @var \TYPO3\CMS\Core\Registry
91 * Helper function to install an extension
92 * also processes db updates and clears the cache if the extension asks for it
94 * @param string $extensionKey
95 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
98 public function install($extensionKey) {
99 $extension = $this->enrichExtensionWithDetails($extensionKey);
100 $this->processDatabaseUpdates($extension);
101 $this->ensureConfiguredDirectoriesExist($extension);
102 $this->importInitialFiles($extension['siteRelPath'], $extensionKey);
103 if (!$this->isLoaded($extensionKey)) {
104 $this->loadExtension($extensionKey);
106 $this->reloadCaches();
107 $this->processRuntimeDatabaseUpdates($extensionKey);
108 $this->saveDefaultConfiguration($extension['key']);
109 if ($extension['clearcacheonload']) {
110 $this->cacheManager
->flushCaches();
112 $this->cacheManager
->flushCachesInGroup('system');
117 * Helper function to uninstall an extension
119 * @param string $extensionKey
120 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
123 public function uninstall($extensionKey) {
124 $dependentExtensions = $this->dependencyUtility
->findInstalledExtensionsThatDependOnMe($extensionKey);
125 if (is_array($dependentExtensions) && count($dependentExtensions) > 0) {
126 throw new \TYPO3\CMS\Extensionmanager\Exception\
ExtensionManagerException(
127 \TYPO3\CMS\Extbase\Utility\LocalizationUtility
::translate(
128 'extensionList.uninstall.dependencyError',
130 array($extensionKey, implode(',', $dependentExtensions))
135 $this->unloadExtension($extensionKey);
140 * Wrapper function to check for loaded extensions
142 * @param string $extensionKey
143 * @return boolean TRUE if extension is loaded
145 public function isLoaded($extensionKey) {
146 return $this->packageManager
->isPackageActive($extensionKey);
150 * Wrapper function for loading extensions
152 * @param string $extensionKey
155 protected function loadExtension($extensionKey) {
156 $this->packageManager
->activatePackage($extensionKey);
160 * Wrapper function for unloading extensions
162 * @param string $extensionKey
165 protected function unloadExtension($extensionKey) {
166 $this->packageManager
->deactivatePackage($extensionKey);
167 $this->cacheManager
->flushCachesInGroup('system');
171 * Checks if an extension is available in the system
173 * @param $extensionKey
176 public function isAvailable($extensionKey) {
177 return $this->packageManager
->isPackageAvailable($extensionKey);
181 * Fetch additional information for an extension key
183 * @param string $extensionKey
186 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
188 public function enrichExtensionWithDetails($extensionKey) {
189 $availableExtensions = $this->listUtility
->getAvailableExtensions();
190 if (isset($availableExtensions[$extensionKey])) {
191 $extension = $availableExtensions[$extensionKey];
193 throw new \TYPO3\CMS\Extensionmanager\Exception\
ExtensionManagerException('Extension ' . $extensionKey . ' is not available', 1342864081);
195 $availableAndInstalledExtensions = $this->listUtility
->enrichExtensionsWithEmConfAndTerInformation(array($extensionKey => $extension));
197 if (!isset($availableAndInstalledExtensions[$extensionKey])) {
198 throw new \TYPO3\CMS\Extensionmanager\Exception\
ExtensionManagerException(
199 'Please check your uploaded extension "' . $extensionKey . '". The configuration file "ext_emconf.php" seems to be invalid.',
204 return $availableAndInstalledExtensions[$extensionKey];
208 * Creates directories as requested in ext_emconf.php
210 * @param array $extension
212 protected function ensureConfiguredDirectoriesExist(array $extension) {
213 $this->fileHandlingUtility
->ensureConfiguredDirectoriesExist($extension);
217 * Gets the content of the ext_tables.sql and ext_tables_static+adt.sql files
218 * Additionally adds the table definitions for the cache tables
220 * @param array $extension
222 public function processDatabaseUpdates(array $extension) {
223 $extTablesSqlFile = PATH_site
. $extension['siteRelPath'] . 'ext_tables.sql';
224 $extTablesSqlContent = '';
225 if (file_exists($extTablesSqlFile)) {
226 $extTablesSqlContent .= \TYPO3\CMS\Core\Utility\GeneralUtility
::getUrl($extTablesSqlFile);
228 if ($extTablesSqlContent !== '') {
229 $this->updateDbWithExtTablesSql($extTablesSqlContent);
232 $this->importStaticSqlFile($extension['siteRelPath']);
233 $this->importT3DFile($extension['siteRelPath']);
237 * Gets all database updates due to runtime configuration, like caching framework or
238 * category api for example
240 * @param string $extensionKey
242 protected function processRuntimeDatabaseUpdates($extensionKey) {
243 $sqlString = $this->emitTablesDefinitionIsBeingBuiltSignal($extensionKey);
244 if (!empty($sqlString)) {
245 $this->updateDbWithExtTablesSql(implode(LF
. LF
. LF
. LF
, $sqlString));
250 * Emits a signal to manipulate the tables definitions
252 * @param string $extensionKey
254 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
256 protected function emitTablesDefinitionIsBeingBuiltSignal($extensionKey) {
257 $signalReturn = $this->signalSlotDispatcher
->dispatch(__CLASS__
, 'tablesDefinitionIsBeingBuilt', array(array(), $extensionKey));
258 $sqlString = $signalReturn[0];
259 if (!is_array($sqlString)) {
260 throw new \TYPO3\CMS\Extensionmanager\Exception\
ExtensionManagerException(
262 'The signal %s of class %s returned a value of type %s, but array was expected.',
263 'tablesDefinitionIsBeingBuilt',
274 * Reload Cache files and Typo3LoadedExtensions
278 public function reloadCaches() {
279 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility
::loadExtLocalconf(FALSE);
280 \TYPO3\CMS\Core\Core\Bootstrap
::getInstance()->loadExtensionTables(FALSE);
284 * Save default configuration of an extension
286 * @param string $extensionKey
289 protected function saveDefaultConfiguration($extensionKey) {
290 /** @var $configUtility \TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility */
291 $configUtility = $this->objectManager
->get('TYPO3\\CMS\\Extensionmanager\\Utility\\ConfigurationUtility');
292 $configUtility->saveDefaultConfiguration($extensionKey);
296 * Update database / process db updates from ext_tables
298 * @param string $rawDefinitions The raw SQL statements from ext_tables.sql
301 public function updateDbWithExtTablesSql($rawDefinitions) {
302 $fieldDefinitionsFromFile = $this->installToolSqlParser
->getFieldDefinitions_fileContent($rawDefinitions);
303 if (count($fieldDefinitionsFromFile)) {
304 $fieldDefinitionsFromCurrentDatabase = $this->installToolSqlParser
->getFieldDefinitions_database();
305 $diff = $this->installToolSqlParser
->getDatabaseExtra($fieldDefinitionsFromFile, $fieldDefinitionsFromCurrentDatabase);
306 $updateStatements = $this->installToolSqlParser
->getUpdateSuggestions($diff);
307 $db = $this->getDatabaseConnection();
308 foreach ((array) $updateStatements['add'] as $string) {
309 $db->admin_query($string);
311 foreach ((array) $updateStatements['change'] as $string) {
312 $db->admin_query($string);
314 foreach ((array) $updateStatements['create_table'] as $string) {
315 $db->admin_query($string);
321 * Import static SQL data (normally used for ext_tables_static+adt.sql)
323 * @param string $rawDefinitions
326 public function importStaticSql($rawDefinitions) {
327 $statements = $this->installToolSqlParser
->getStatementarray($rawDefinitions, 1);
328 list($statementsPerTable, $insertCount) = $this->installToolSqlParser
->getCreateTables($statements, 1);
329 $db = $this->getDatabaseConnection();
330 // Traverse the tables
331 foreach ($statementsPerTable as $table => $query) {
332 $db->admin_query('DROP TABLE IF EXISTS ' . $table);
333 $db->admin_query($query);
334 if ($insertCount[$table]) {
335 $insertStatements = $this->installToolSqlParser
->getTableInsertStatements($statements, $table);
336 foreach ($insertStatements as $statement) {
337 $db->admin_query($statement);
344 * Remove an extension (delete the directory)
346 * @param string $extension
347 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
350 public function removeExtension($extension) {
351 $absolutePath = $this->fileHandlingUtility
->getAbsoluteExtensionPath($extension);
352 if ($this->fileHandlingUtility
->isValidExtensionPath($absolutePath)) {
353 if ($this->packageManager
->isPackageAvailable($extension)) {
354 // Package manager deletes the extension and removes the entry from PackageStates.php
355 $this->packageManager
->deletePackage($extension);
357 // The extension is not listed in PackageStates.php, we can safely remove it
358 $this->fileHandlingUtility
->removeDirectory($absolutePath);
361 throw new \TYPO3\CMS\Extensionmanager\Exception\
ExtensionManagerException('No valid extension path given.', 1342875724);
366 * Get the data dump for an extension
368 * @param string $extension
371 public function getExtensionSqlDataDump($extension) {
372 $extension = $this->enrichExtensionWithDetails($extension);
373 $filePrefix = PATH_site
. $extension['siteRelPath'];
374 $sqlData['extTables'] = $this->getSqlDataDumpForFile($filePrefix . 'ext_tables.sql');
375 $sqlData['staticSql'] = $this->getSqlDataDumpForFile($filePrefix . 'ext_tables_static+adt.sql');
380 * Gets the sql data dump for a specific sql file (for example ext_tables.sql)
382 * @param string $sqlFile
385 protected function getSqlDataDumpForFile($sqlFile) {
387 if (file_exists($sqlFile)) {
388 $sqlContent = \TYPO3\CMS\Core\Utility\GeneralUtility
::getUrl($sqlFile);
389 $fieldDefinitions = $this->installToolSqlParser
->getFieldDefinitions_fileContent($sqlContent);
390 $sqlData = $this->databaseUtility
->dumpStaticTables($fieldDefinitions);
396 * Checks if an update for an extension is available
399 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extensionData
402 public function isUpdateAvailable(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension
$extensionData) {
403 $isUpdateAvailable = FALSE;
404 // Only check for update for TER extensions
405 $version = $extensionData->getIntegerVersion();
406 /** @var $highestTerVersionExtension \TYPO3\CMS\Extensionmanager\Domain\Model\Extension */
407 $highestTerVersionExtension = $this->extensionRepository
->findHighestAvailableVersion($extensionData->getExtensionKey());
408 if ($highestTerVersionExtension instanceof \TYPO3\CMS\Extensionmanager\Domain\Model\Extension
) {
409 $highestVersion = $highestTerVersionExtension->getIntegerVersion();
410 if ($highestVersion > $version) {
411 $this->dependencyUtility
->checkDependencies($highestTerVersionExtension);
412 if (!$this->dependencyUtility
->hasDependencyErrors()) {
413 $isUpdateAvailable = TRUE;
417 return $isUpdateAvailable;
421 * Uses the export import extension to import a T3D or XML file to PID 0
422 * Execution state is saved in the this->registry, so it only happens once
424 * @param string $extensionSiteRelPath
427 protected function importT3DFile($extensionSiteRelPath) {
428 $registryKeysToCheck = array(
429 $extensionSiteRelPath . 'Initialisation/data.t3d',
430 $extensionSiteRelPath . 'Initialisation/dataImported',
432 foreach ($registryKeysToCheck as $registryKeyToCheck) {
433 if ($this->registry
->get('extensionDataImport', $registryKeyToCheck)) {
434 // Data was imported before => early return
438 $importFileToUse = NULL;
439 $possibleImportFiles = array(
440 $extensionSiteRelPath . 'Initialisation/data.t3d',
441 $extensionSiteRelPath . 'Initialisation/data.xml'
443 foreach ($possibleImportFiles as $possibleImportFile) {
444 if (!file_exists(PATH_site
. $possibleImportFile)) {
447 $importFileToUse = $possibleImportFile;
449 if ($importFileToUse !== NULL) {
450 $importExportUtility = $this->objectManager
->get('TYPO3\\CMS\\Impexp\\Utility\\ImportExportUtility');
452 $importResult = $importExportUtility->importT3DFile(PATH_site
. $importFileToUse, 0);
453 $this->registry
->set('extensionDataImport', $extensionSiteRelPath . 'Initialisation/dataImported', 1);
454 $this->emitAfterExtensionT3DImportSignal($importFileToUse, $importResult);
455 } catch (\ErrorException
$e) {
456 /** @var \TYPO3\CMS\Core\Log\Logger $logger */
457 $logger = $this->objectManager
->get('TYPO3\\CMS\\Core\\Log\\LogManager')->getLogger(__CLASS__
);
458 $logger->log(\TYPO3\CMS\Core\Log\LogLevel
::WARNING
, $e->getMessage());
464 * Emits a signal after an t3d file was imported
466 * @param string $importFileToUse
467 * @param array $importResult
469 protected function emitAfterExtensionT3DImportSignal($importFileToUse, array $importResult) {
470 $this->signalSlotDispatcher
->dispatch(__CLASS__
, 'afterExtensionT3DImport', array($importFileToUse, $importResult, $this));
474 * Imports a static tables SQL File (ext_tables_static+adt)
475 * Execution state is saved in the this->registry, so it only happens once
477 * @param string $extensionSiteRelPath
480 protected function importStaticSqlFile($extensionSiteRelPath) {
481 $extTablesStaticSqlRelFile = $extensionSiteRelPath . 'ext_tables_static+adt.sql';
482 if (!$this->registry
->get('extensionDataImport', $extTablesStaticSqlRelFile)) {
483 $extTablesStaticSqlFile = PATH_site
. $extTablesStaticSqlRelFile;
484 if (file_exists($extTablesStaticSqlFile)) {
485 $extTablesStaticSqlContent = \TYPO3\CMS\Core\Utility\GeneralUtility
::getUrl($extTablesStaticSqlFile);
486 $this->importStaticSql($extTablesStaticSqlContent);
488 $this->registry
->set('extensionDataImport', $extTablesStaticSqlRelFile, 1);
489 $this->emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile);
494 * Emits a signal after a static sql file was imported
496 * @param string $extTablesStaticSqlRelFile
498 protected function emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile) {
499 $this->signalSlotDispatcher
->dispatch(__CLASS__
, 'afterExtensionStaticSqlImport', array($extTablesStaticSqlRelFile, $this));
503 * Imports files from Initialisation/Files to fileadmin
504 * via lowlevel copy directory method
506 * @param string $extensionSiteRelPath relative path to extension dir
507 * @param string $extensionKey
509 protected function importInitialFiles($extensionSiteRelPath, $extensionKey) {
510 $importRelFolder = $extensionSiteRelPath . 'Initialisation/Files';
511 if (!$this->registry
->get('extensionDataImport', $importRelFolder)) {
512 $importFolder = PATH_site
. $importRelFolder;
513 if (file_exists($importFolder)) {
514 $destinationRelPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . $extensionKey;
515 $destinationAbsolutePath = PATH_site
. $destinationRelPath;
516 if (!file_exists($destinationAbsolutePath) &&
517 \TYPO3\CMS\Core\Utility\GeneralUtility
::isAllowedAbsPath($destinationAbsolutePath)
519 \TYPO3\CMS\Core\Utility\GeneralUtility
::mkdir($destinationAbsolutePath);
521 \TYPO3\CMS\Core\Utility\GeneralUtility
::copyDirectory($importRelFolder, $destinationRelPath);
522 $this->registry
->set('extensionDataImport', $importRelFolder, 1);
523 $this->emitAfterExtensionFileImportSignal($destinationAbsolutePath);
529 * Emits a signal after extension files were imported
531 * @param string $destinationAbsolutePath
533 protected function emitAfterExtensionFileImportSignal($destinationAbsolutePath) {
534 $this->signalSlotDispatcher
->dispatch(__CLASS__
, 'afterExtensionFileImport', array($destinationAbsolutePath, $this));
538 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
540 protected function getDatabaseConnection() {
541 return $GLOBALS['TYPO3_DB'];