2 namespace TYPO3\CMS\Extensionmanager\Utility
;
4 /***************************************************************
7 * (c) 2012-2013 Susanne Moog <susanne.moog@typo3.org>
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the text file GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
31 * Extension Manager Install Utility
33 * @author Susanne Moog <susanne.moog@typo3.org>
35 class InstallUtility
implements \TYPO3\CMS\Core\SingletonInterface
{
38 * @var \TYPO3\CMS\Extbase\Object\ObjectManager
41 public $objectManager;
44 * @var \TYPO3\CMS\Install\Service\SqlSchemaMigrationService
47 public $installToolSqlParser;
50 * @var \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility
53 protected $dependencyUtility;
56 * @var \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
59 protected $fileHandlingUtility;
62 * @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
65 protected $listUtility;
68 * @var \TYPO3\CMS\Extensionmanager\Utility\DatabaseUtility
71 protected $databaseUtility;
74 * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
77 public $extensionRepository;
80 * @var \TYPO3\CMS\Core\Package\PackageManager
83 protected $packageManager;
86 * @var \TYPO3\CMS\Core\Cache\CacheManager
89 protected $cacheManager;
92 * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
95 protected $signalSlotDispatcher;
98 * @var \TYPO3\CMS\Core\Registry
104 * Helper function to install an extension
105 * also processes db updates and clears the cache if the extension asks for it
107 * @param string $extensionKey
108 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
111 public function install($extensionKey) {
112 $extension = $this->enrichExtensionWithDetails($extensionKey);
113 $this->processDatabaseUpdates($extension);
114 $this->ensureConfiguredDirectoriesExist($extension);
115 $this->importInitialFiles($extension['siteRelPath'], $extensionKey);
116 if (!$this->isLoaded($extensionKey)) {
117 $this->loadExtension($extensionKey);
119 $this->reloadCaches();
120 $this->processRuntimeDatabaseUpdates($extensionKey);
121 $this->saveDefaultConfiguration($extension['key']);
122 if ($extension['clearcacheonload']) {
123 $this->cacheManager
->flushCaches();
125 $this->cacheManager
->flushCachesInGroup('system');
130 * Helper function to uninstall an extension
132 * @param string $extensionKey
133 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
136 public function uninstall($extensionKey) {
137 $dependentExtensions = $this->dependencyUtility
->findInstalledExtensionsThatDependOnMe($extensionKey);
138 if (is_array($dependentExtensions) && count($dependentExtensions) > 0) {
139 throw new \TYPO3\CMS\Extensionmanager\Exception\
ExtensionManagerException(
140 \TYPO3\CMS\Extbase\Utility\LocalizationUtility
::translate(
141 'extensionList.uninstall.dependencyError',
143 array($extensionKey, implode(',', $dependentExtensions))
148 $this->unloadExtension($extensionKey);
153 * Wrapper function to check for loaded extensions
155 * @param string $extensionKey
156 * @return boolean TRUE if extension is loaded
158 public function isLoaded($extensionKey) {
159 return $this->packageManager
->isPackageActive($extensionKey);
163 * Wrapper function for loading extensions
165 * @param string $extensionKey
168 protected function loadExtension($extensionKey) {
169 $this->packageManager
->activatePackage($extensionKey);
173 * Wrapper function for unloading extensions
175 * @param string $extensionKey
178 protected function unloadExtension($extensionKey) {
179 $this->packageManager
->deactivatePackage($extensionKey);
180 $this->cacheManager
->flushCachesInGroup('system');
184 * Checks if an extension is available in the system
186 * @param $extensionKey
189 public function isAvailable($extensionKey) {
190 return $this->packageManager
->isPackageAvailable($extensionKey);
194 * Fetch additional information for an extension key
196 * @param string $extensionKey
199 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
201 public function enrichExtensionWithDetails($extensionKey) {
202 $availableExtensions = $this->listUtility
->getAvailableExtensions();
203 if (isset($availableExtensions[$extensionKey])) {
204 $extension = $availableExtensions[$extensionKey];
206 throw new \TYPO3\CMS\Extensionmanager\Exception\
ExtensionManagerException('Extension ' . $extensionKey . ' is not available', 1342864081);
208 $availableAndInstalledExtensions = $this->listUtility
->enrichExtensionsWithEmConfAndTerInformation(array($extensionKey => $extension));
210 if (!isset($availableAndInstalledExtensions[$extensionKey])) {
211 throw new \TYPO3\CMS\Extensionmanager\Exception\
ExtensionManagerException(
212 'Please check your uploaded extension "' . $extensionKey . '". The configuration file "ext_emconf.php" seems to be invalid.',
217 return $availableAndInstalledExtensions[$extensionKey];
221 * Creates directories as requested in ext_emconf.php
223 * @param array $extension
225 protected function ensureConfiguredDirectoriesExist(array $extension) {
226 $this->fileHandlingUtility
->ensureConfiguredDirectoriesExist($extension);
230 * Gets the content of the ext_tables.sql and ext_tables_static+adt.sql files
231 * Additionally adds the table definitions for the cache tables
233 * @param array $extension
235 public function processDatabaseUpdates(array $extension) {
236 $extTablesSqlFile = PATH_site
. $extension['siteRelPath'] . 'ext_tables.sql';
237 $extTablesSqlContent = '';
238 if (file_exists($extTablesSqlFile)) {
239 $extTablesSqlContent .= \TYPO3\CMS\Core\Utility\GeneralUtility
::getUrl($extTablesSqlFile);
241 if ($extTablesSqlContent !== '') {
242 $this->updateDbWithExtTablesSql($extTablesSqlContent);
245 $this->importStaticSqlFile($extension['siteRelPath']);
246 $this->importT3DFile($extension['siteRelPath']);
250 * Gets all database updates due to runtime configuration, like caching framework or
251 * category api for example
253 * @param string $extensionKey
255 protected function processRuntimeDatabaseUpdates($extensionKey) {
256 $sqlString = $this->emitTablesDefinitionIsBeingBuiltSignal($extensionKey);
257 if (!empty($sqlString)) {
258 $this->updateDbWithExtTablesSql(implode(LF
. LF
. LF
. LF
, $sqlString));
263 * Emits a signal to manipulate the tables definitions
265 * @param string $extensionKey
267 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
269 protected function emitTablesDefinitionIsBeingBuiltSignal($extensionKey) {
270 $signalReturn = $this->signalSlotDispatcher
->dispatch(__CLASS__
, 'tablesDefinitionIsBeingBuilt', array('sqlString' => array(), 'extensionKey' => $extensionKey));
271 $sqlString = $signalReturn['sqlString'];
272 if (!is_array($sqlString)) {
273 throw new \TYPO3\CMS\Extensionmanager\Exception\
ExtensionManagerException(
275 'The signal %s of class %s returned a value of type %s, but array was expected.',
276 'tablesDefinitionIsBeingBuilt',
287 * Reload Cache files and Typo3LoadedExtensions
291 public function reloadCaches() {
292 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility
::loadExtLocalconf(FALSE);
293 \TYPO3\CMS\Core\Core\Bootstrap
::getInstance()->loadExtensionTables(FALSE);
297 * Save default configuration of an extension
299 * @param string $extensionKey
302 protected function saveDefaultConfiguration($extensionKey) {
303 /** @var $configUtility \TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility */
304 $configUtility = $this->objectManager
->get('TYPO3\\CMS\\Extensionmanager\\Utility\\ConfigurationUtility');
305 $configUtility->saveDefaultConfiguration($extensionKey);
309 * Update database / process db updates from ext_tables
311 * @param string $rawDefinitions The raw SQL statements from ext_tables.sql
314 public function updateDbWithExtTablesSql($rawDefinitions) {
315 $fieldDefinitionsFromFile = $this->installToolSqlParser
->getFieldDefinitions_fileContent($rawDefinitions);
316 if (count($fieldDefinitionsFromFile)) {
317 $fieldDefinitionsFromCurrentDatabase = $this->installToolSqlParser
->getFieldDefinitions_database();
318 $diff = $this->installToolSqlParser
->getDatabaseExtra($fieldDefinitionsFromFile, $fieldDefinitionsFromCurrentDatabase);
319 $updateStatements = $this->installToolSqlParser
->getUpdateSuggestions($diff);
320 $db = $this->getDatabaseConnection();
321 foreach ((array) $updateStatements['add'] as $string) {
322 $db->admin_query($string);
324 foreach ((array) $updateStatements['change'] as $string) {
325 $db->admin_query($string);
327 foreach ((array) $updateStatements['create_table'] as $string) {
328 $db->admin_query($string);
334 * Import static SQL data (normally used for ext_tables_static+adt.sql)
336 * @param string $rawDefinitions
339 public function importStaticSql($rawDefinitions) {
340 $statements = $this->installToolSqlParser
->getStatementarray($rawDefinitions, 1);
341 list($statementsPerTable, $insertCount) = $this->installToolSqlParser
->getCreateTables($statements, 1);
342 $db = $this->getDatabaseConnection();
343 // Traverse the tables
344 foreach ($statementsPerTable as $table => $query) {
345 $db->admin_query('DROP TABLE IF EXISTS ' . $table);
346 $db->admin_query($query);
347 if ($insertCount[$table]) {
348 $insertStatements = $this->installToolSqlParser
->getTableInsertStatements($statements, $table);
349 foreach ($insertStatements as $statement) {
350 $db->admin_query($statement);
357 * Remove an extension (delete the directory)
359 * @param string $extension
360 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
363 public function removeExtension($extension) {
364 $absolutePath = $this->fileHandlingUtility
->getAbsoluteExtensionPath($extension);
365 if ($this->fileHandlingUtility
->isValidExtensionPath($absolutePath)) {
366 if ($this->packageManager
->isPackageAvailable($extension)) {
367 // Package manager deletes the extension and removes the entry from PackageStates.php
368 $this->packageManager
->deletePackage($extension);
370 // The extension is not listed in PackageStates.php, we can safely remove it
371 $this->fileHandlingUtility
->removeDirectory($absolutePath);
374 throw new \TYPO3\CMS\Extensionmanager\Exception\
ExtensionManagerException('No valid extension path given.', 1342875724);
379 * Get the data dump for an extension
381 * @param string $extension
384 public function getExtensionSqlDataDump($extension) {
385 $extension = $this->enrichExtensionWithDetails($extension);
386 $filePrefix = PATH_site
. $extension['siteRelPath'];
387 $sqlData['extTables'] = $this->getSqlDataDumpForFile($filePrefix . 'ext_tables.sql');
388 $sqlData['staticSql'] = $this->getSqlDataDumpForFile($filePrefix . 'ext_tables_static+adt.sql');
393 * Gets the sql data dump for a specific sql file (for example ext_tables.sql)
395 * @param string $sqlFile
398 protected function getSqlDataDumpForFile($sqlFile) {
400 if (file_exists($sqlFile)) {
401 $sqlContent = \TYPO3\CMS\Core\Utility\GeneralUtility
::getUrl($sqlFile);
402 $fieldDefinitions = $this->installToolSqlParser
->getFieldDefinitions_fileContent($sqlContent);
403 $sqlData = $this->databaseUtility
->dumpStaticTables($fieldDefinitions);
409 * Checks if an update for an extension is available
412 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extensionData
415 public function isUpdateAvailable(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension
$extensionData) {
416 $isUpdateAvailable = FALSE;
417 // Only check for update for TER extensions
418 $version = $extensionData->getIntegerVersion();
419 /** @var $highestTerVersionExtension \TYPO3\CMS\Extensionmanager\Domain\Model\Extension */
420 $highestTerVersionExtension = $this->extensionRepository
->findHighestAvailableVersion($extensionData->getExtensionKey());
421 if ($highestTerVersionExtension instanceof \TYPO3\CMS\Extensionmanager\Domain\Model\Extension
) {
422 $highestVersion = $highestTerVersionExtension->getIntegerVersion();
423 if ($highestVersion > $version) {
424 $this->dependencyUtility
->checkDependencies($highestTerVersionExtension);
425 if (!$this->dependencyUtility
->hasDependencyErrors()) {
426 $isUpdateAvailable = TRUE;
430 return $isUpdateAvailable;
434 * Uses the export import extension to import a T3D or XML file to PID 0
435 * Execution state is saved in the this->registry, so it only happens once
437 * @param string $extensionSiteRelPath
440 protected function importT3DFile($extensionSiteRelPath) {
441 $registryKeysToCheck = array(
442 $extensionSiteRelPath . 'Initialisation/data.t3d',
443 $extensionSiteRelPath . 'Initialisation/dataImported',
445 foreach ($registryKeysToCheck as $registryKeyToCheck) {
446 if ($this->registry
->get('extensionDataImport', $registryKeyToCheck)) {
447 // Data was imported before => early return
451 $importFileToUse = NULL;
452 $possibleImportFiles = array(
453 $extensionSiteRelPath . 'Initialisation/data.t3d',
454 $extensionSiteRelPath . 'Initialisation/data.xml'
456 foreach ($possibleImportFiles as $possibleImportFile) {
457 if (!file_exists(PATH_site
. $possibleImportFile)) {
460 $importFileToUse = $possibleImportFile;
462 if ($importFileToUse !== NULL) {
463 $importExportUtility = $this->objectManager
->get('TYPO3\\CMS\\Impexp\\Utility\\ImportExportUtility');
465 $importResult = $importExportUtility->importT3DFile(PATH_site
. $importFileToUse, 0);
466 $this->registry
->set('extensionDataImport', $extensionSiteRelPath . 'Initialisation/dataImported', 1);
467 $this->emitAfterExtensionT3DImportSignal($importFileToUse, $importResult);
468 } catch (\ErrorException
$e) {
469 /** @var \TYPO3\CMS\Core\Log\Logger $logger */
470 $logger = $this->objectManager
->get('TYPO3\\CMS\\Core\\Log\\LogManager')->getLogger(__CLASS__
);
471 $logger->log(\TYPO3\CMS\Core\Log\LogLevel
::WARNING
, $e->getMessage());
477 * Emits a signal after an t3d file was imported
479 * @param string $importFileToUse
480 * @param array $importResult
482 protected function emitAfterExtensionT3DImportSignal($importFileToUse, array $importResult) {
483 $this->signalSlotDispatcher
->dispatch(__CLASS__
, 'afterExtensionT3DImport', array($importFileToUse, $importResult, $this));
487 * Imports a static tables SQL File (ext_tables_static+adt)
488 * Execution state is saved in the this->registry, so it only happens once
490 * @param string $extensionSiteRelPath
493 protected function importStaticSqlFile($extensionSiteRelPath) {
494 $extTablesStaticSqlRelFile = $extensionSiteRelPath . 'ext_tables_static+adt.sql';
495 if (!$this->registry
->get('extensionDataImport', $extTablesStaticSqlRelFile)) {
496 $extTablesStaticSqlFile = PATH_site
. $extTablesStaticSqlRelFile;
497 if (file_exists($extTablesStaticSqlFile)) {
498 $extTablesStaticSqlContent = \TYPO3\CMS\Core\Utility\GeneralUtility
::getUrl($extTablesStaticSqlFile);
499 $this->importStaticSql($extTablesStaticSqlContent);
501 $this->registry
->set('extensionDataImport', $extTablesStaticSqlRelFile, 1);
502 $this->emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile);
507 * Emits a signal after a static sql file was imported
509 * @param string $extTablesStaticSqlRelFile
511 protected function emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile) {
512 $this->signalSlotDispatcher
->dispatch(__CLASS__
, 'afterExtensionStaticSqlImport', array($extTablesStaticSqlRelFile, $this));
516 * Imports files from Initialisation/Files to fileadmin
517 * via lowlevel copy directory method
519 * @param string $extensionSiteRelPath relative path to extension dir
520 * @param string $extensionKey
522 protected function importInitialFiles($extensionSiteRelPath, $extensionKey) {
523 $importRelFolder = $extensionSiteRelPath . 'Initialisation/Files';
524 if (!$this->registry
->get('extensionDataImport', $importRelFolder)) {
525 $importFolder = PATH_site
. $importRelFolder;
526 if (file_exists($importFolder)) {
527 $destinationRelPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . $extensionKey;
528 $destinationAbsolutePath = PATH_site
. $destinationRelPath;
529 if (!file_exists($destinationAbsolutePath) &&
530 \TYPO3\CMS\Core\Utility\GeneralUtility
::isAllowedAbsPath($destinationAbsolutePath)
532 \TYPO3\CMS\Core\Utility\GeneralUtility
::mkdir($destinationAbsolutePath);
534 \TYPO3\CMS\Core\Utility\GeneralUtility
::copyDirectory($importRelFolder, $destinationRelPath);
535 $this->registry
->set('extensionDataImport', $importRelFolder, 1);
536 $this->emitAfterExtensionFileImportSignal($destinationAbsolutePath);
542 * Emits a signal after extension files were imported
544 * @param string $destinationAbsolutePath
546 protected function emitAfterExtensionFileImportSignal($destinationAbsolutePath) {
547 $this->signalSlotDispatcher
->dispatch(__CLASS__
, 'afterExtensionFileImport', array($destinationAbsolutePath, $this));
551 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
553 protected function getDatabaseConnection() {
554 return $GLOBALS['TYPO3_DB'];