TYPO3  7.6
InstallUtility.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Utility;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
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.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
22 
27 {
32 
37 
41  protected $dependencyUtility;
42 
47 
51  protected $listUtility;
52 
56  protected $databaseUtility;
57 
62 
66  protected $packageManager;
67 
71  protected $cacheManager;
72 
77 
81  protected $registry;
82 
86  public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
87  {
88  $this->objectManager = $objectManager;
89  }
90 
94  public function injectInstallToolSqlParser(\TYPO3\CMS\Install\Service\SqlSchemaMigrationService $installToolSqlParser)
95  {
96  $this->installToolSqlParser = $installToolSqlParser;
97  }
98 
102  public function injectDependencyUtility(\TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility)
103  {
104  $this->dependencyUtility = $dependencyUtility;
105  }
106 
110  public function injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility)
111  {
112  $this->fileHandlingUtility = $fileHandlingUtility;
113  }
114 
118  public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
119  {
120  $this->listUtility = $listUtility;
121  }
122 
126  public function injectDatabaseUtility(\TYPO3\CMS\Extensionmanager\Utility\DatabaseUtility $databaseUtility)
127  {
128  $this->databaseUtility = $databaseUtility;
129  }
130 
134  public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
135  {
136  $this->extensionRepository = $extensionRepository;
137  }
138 
142  public function injectPackageManager(\TYPO3\CMS\Core\Package\PackageManager $packageManager)
143  {
144  $this->packageManager = $packageManager;
145  }
146 
150  public function injectCacheManager(\TYPO3\CMS\Core\Cache\CacheManager $cacheManager)
151  {
152  $this->cacheManager = $cacheManager;
153  }
154 
158  public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
159  {
160  $this->signalSlotDispatcher = $signalSlotDispatcher;
161  }
162 
166  public function injectRegistry(\TYPO3\CMS\Core\Registry $registry)
167  {
168  $this->registry = $registry;
169  }
170 
179  public function install($extensionKey)
180  {
181  $extension = $this->enrichExtensionWithDetails($extensionKey);
182  $this->ensureConfiguredDirectoriesExist($extension);
183  $this->loadExtension($extensionKey);
184  if (!empty($extension['clearcacheonload']) || !empty($extension['clearCacheOnLoad'])) {
185  $this->cacheManager->flushCaches();
186  } else {
187  $this->cacheManager->flushCachesInGroup('system');
188  }
189  $this->reloadCaches();
190  $this->processExtensionSetup($extensionKey);
191 
192  $this->emitAfterExtensionInstallSignal($extensionKey);
193  }
194 
198  public function processExtensionSetup($extensionKey)
199  {
200  $extension = $this->getExtensionArray($extensionKey);
201  $this->importInitialFiles($extension['siteRelPath'], $extensionKey);
202  $this->processDatabaseUpdates($extension);
203  $this->processRuntimeDatabaseUpdates($extensionKey);
204  $this->saveDefaultConfiguration($extensionKey);
205  }
206 
214  public function uninstall($extensionKey)
215  {
216  $dependentExtensions = $this->dependencyUtility->findInstalledExtensionsThatDependOnMe($extensionKey);
217  if (is_array($dependentExtensions) && !empty($dependentExtensions)) {
218  throw new ExtensionManagerException(
219  \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate(
220  'extensionList.uninstall.dependencyError',
221  'extensionmanager',
222  array($extensionKey, implode(',', $dependentExtensions))
223  ),
224  1342554622
225  );
226  } else {
227  $this->unloadExtension($extensionKey);
228  }
229  }
230 
237  public function isLoaded($extensionKey)
238  {
239  return $this->packageManager->isPackageActive($extensionKey);
240  }
241 
245  public function reloadAvailableExtensions() {
246  $this->listUtility->reloadAvailableExtensions();
247  }
248 
255  protected function loadExtension($extensionKey)
256  {
257  $this->packageManager->activatePackage($extensionKey);
258  }
259 
266  protected function unloadExtension($extensionKey)
267  {
268  $this->packageManager->deactivatePackage($extensionKey);
269  $this->emitAfterExtensionUninstallSignal($extensionKey);
270  $this->cacheManager->flushCachesInGroup('system');
271  }
272 
278  protected function emitAfterExtensionInstallSignal($extensionKey)
279  {
280  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionInstall', array($extensionKey, $this));
281  }
282 
288  protected function emitAfterExtensionUninstallSignal($extensionKey)
289  {
290  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionUninstall', array($extensionKey, $this));
291  }
292 
299  public function isAvailable($extensionKey)
300  {
301  return $this->packageManager->isPackageAvailable($extensionKey);
302  }
303 
313  public function reloadPackageInformation($extensionKey)
314  {
315  if ($this->packageManager->isPackageAvailable($extensionKey)) {
316  $this->reloadOpcache();
317  $this->packageManager->reloadPackageInformation($extensionKey);
318  }
319  }
320 
329  public function enrichExtensionWithDetails($extensionKey)
330  {
331  $extension = $this->getExtensionArray($extensionKey);
332  $availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfAndTerInformation(array($extensionKey => $extension));
333 
334  if (!isset($availableAndInstalledExtensions[$extensionKey])) {
335  throw new ExtensionManagerException(
336  'Please check your uploaded extension "' . $extensionKey . '". The configuration file "ext_emconf.php" seems to be invalid.',
337  1391432222
338  );
339  }
340 
341  return $availableAndInstalledExtensions[$extensionKey];
342  }
343 
349  protected function getExtensionArray($extensionKey)
350  {
351  $availableExtensions = $this->listUtility->getAvailableExtensions();
352  if (isset($availableExtensions[$extensionKey])) {
353  return $availableExtensions[$extensionKey];
354  } else {
355  throw new ExtensionManagerException('Extension ' . $extensionKey . ' is not available', 1342864081);
356  }
357  }
358 
364  protected function ensureConfiguredDirectoriesExist(array $extension)
365  {
366  $this->fileHandlingUtility->ensureConfiguredDirectoriesExist($extension);
367  }
368 
375  public function processDatabaseUpdates(array $extension)
376  {
377  $extTablesSqlFile = PATH_site . $extension['siteRelPath'] . 'ext_tables.sql';
378  $extTablesSqlContent = '';
379  if (file_exists($extTablesSqlFile)) {
380  $extTablesSqlContent .= GeneralUtility::getUrl($extTablesSqlFile);
381  }
382  if ($extTablesSqlContent !== '') {
383  $this->updateDbWithExtTablesSql($extTablesSqlContent);
384  }
385 
386  $this->importStaticSqlFile($extension['siteRelPath']);
387  $this->importT3DFile($extension['siteRelPath']);
388  }
389 
396  protected function processRuntimeDatabaseUpdates($extensionKey)
397  {
398  $sqlString = $this->emitTablesDefinitionIsBeingBuiltSignal($extensionKey);
399  if (!empty($sqlString)) {
400  $this->updateDbWithExtTablesSql(implode(LF . LF . LF . LF, $sqlString));
401  }
402  }
403 
411  protected function emitTablesDefinitionIsBeingBuiltSignal($extensionKey)
412  {
413  $signalReturn = $this->signalSlotDispatcher->dispatch(__CLASS__, 'tablesDefinitionIsBeingBuilt', array(array(), $extensionKey));
414  // This is important to support old associated returns
415  $signalReturn = array_values($signalReturn);
416  $sqlString = $signalReturn[0];
417  if (!is_array($sqlString)) {
418  throw new ExtensionManagerException(
419  sprintf(
420  'The signal %s of class %s returned a value of type %s, but array was expected.',
421  'tablesDefinitionIsBeingBuilt',
422  __CLASS__,
423  gettype($sqlString)
424  ),
425  1382360258
426  );
427  }
428  return $sqlString;
429  }
430 
436  public function reloadCaches()
437  {
438  $this->reloadOpcache();
439  \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::loadExtLocalconf(false);
440  \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->loadExtensionTables(false);
441  }
442 
446  protected function reloadOpcache()
447  {
448  GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
449  }
450 
457  protected function saveDefaultConfiguration($extensionKey)
458  {
460  $configUtility = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility::class);
461  $configUtility->saveDefaultConfiguration($extensionKey);
462  }
463 
470  public function updateDbWithExtTablesSql($rawDefinitions)
471  {
472  $fieldDefinitionsFromFile = $this->installToolSqlParser->getFieldDefinitions_fileContent($rawDefinitions);
473  if (!empty($fieldDefinitionsFromFile)) {
474  $fieldDefinitionsFromCurrentDatabase = $this->installToolSqlParser->getFieldDefinitions_database();
475  $diff = $this->installToolSqlParser->getDatabaseExtra($fieldDefinitionsFromFile, $fieldDefinitionsFromCurrentDatabase);
476  $updateStatements = $this->installToolSqlParser->getUpdateSuggestions($diff);
477  $db = $this->getDatabaseConnection();
478  foreach ((array)$updateStatements['add'] as $string) {
479  $db->admin_query($string);
480  }
481  foreach ((array)$updateStatements['change'] as $string) {
482  $db->admin_query($string);
483  }
484  foreach ((array)$updateStatements['create_table'] as $string) {
485  $db->admin_query($string);
486  }
487  }
488  }
489 
496  public function importStaticSql($rawDefinitions)
497  {
498  $statements = $this->installToolSqlParser->getStatementarray($rawDefinitions, 1);
499  list($statementsPerTable, $insertCount) = $this->installToolSqlParser->getCreateTables($statements, 1);
500  $db = $this->getDatabaseConnection();
501  // Traverse the tables
502  foreach ($statementsPerTable as $table => $query) {
503  $db->admin_query('DROP TABLE IF EXISTS ' . $table);
504  $db->admin_query($query);
505  if ($insertCount[$table]) {
506  $insertStatements = $this->installToolSqlParser->getTableInsertStatements($statements, $table);
507  foreach ($insertStatements as $statement) {
508  $db->admin_query($statement);
509  }
510  }
511  }
512  }
513 
521  public function removeExtension($extension)
522  {
523  $absolutePath = $this->fileHandlingUtility->getAbsoluteExtensionPath($extension);
524  if ($this->fileHandlingUtility->isValidExtensionPath($absolutePath)) {
525  if ($this->packageManager->isPackageAvailable($extension)) {
526  // Package manager deletes the extension and removes the entry from PackageStates.php
527  $this->packageManager->deletePackage($extension);
528  } else {
529  // The extension is not listed in PackageStates.php, we can safely remove it
530  $this->fileHandlingUtility->removeDirectory($absolutePath);
531  }
532  } else {
533  throw new ExtensionManagerException('No valid extension path given.', 1342875724);
534  }
535  }
536 
543  public function getExtensionSqlDataDump($extension)
544  {
545  $extension = $this->enrichExtensionWithDetails($extension);
546  $filePrefix = PATH_site . $extension['siteRelPath'];
547  $sqlData['extTables'] = $this->getSqlDataDumpForFile($filePrefix . 'ext_tables.sql');
548  $sqlData['staticSql'] = $this->getSqlDataDumpForFile($filePrefix . 'ext_tables_static+adt.sql');
549  return $sqlData;
550  }
551 
558  protected function getSqlDataDumpForFile($sqlFile)
559  {
560  $sqlData = '';
561  if (file_exists($sqlFile)) {
562  $sqlContent = GeneralUtility::getUrl($sqlFile);
563  $fieldDefinitions = $this->installToolSqlParser->getFieldDefinitions_fileContent($sqlContent);
564  $sqlData = $this->databaseUtility->dumpStaticTables($fieldDefinitions);
565  }
566  return $sqlData;
567  }
568 
569 
577  public function isUpdateAvailable(Extension $extensionData)
578  {
579  return (bool)$this->getUpdateableVersion($extensionData);
580  }
581 
589  public function getUpdateableVersion(Extension $extensionData)
590  {
591  // Only check for update for TER extensions
592  $version = $extensionData->getIntegerVersion();
593 
595  $extensionUpdates = $this->extensionRepository->findByVersionRangeAndExtensionKeyOrderedByVersion(
596  $extensionData->getExtensionKey(),
597  $version,
598  0,
599  false
600  );
601  if ($extensionUpdates->count() > 0) {
602  foreach ($extensionUpdates as $extensionUpdate) {
603  try {
604  $this->dependencyUtility->checkDependencies($extensionUpdate);
605  if (!$this->dependencyUtility->hasDependencyErrors()) {
606  return $extensionUpdate;
607  }
608  } catch (ExtensionManagerException $e) {
609  }
610  }
611  }
612  return false;
613  }
614 
622  protected function importT3DFile($extensionSiteRelPath)
623  {
624  $registryKeysToCheck = array(
625  $extensionSiteRelPath . 'Initialisation/data.t3d',
626  $extensionSiteRelPath . 'Initialisation/dataImported',
627  );
628  foreach ($registryKeysToCheck as $registryKeyToCheck) {
629  if ($this->registry->get('extensionDataImport', $registryKeyToCheck)) {
630  // Data was imported before => early return
631  return;
632  }
633  }
634  $importFileToUse = null;
635  $possibleImportFiles = array(
636  $extensionSiteRelPath . 'Initialisation/data.t3d',
637  $extensionSiteRelPath . 'Initialisation/data.xml'
638  );
639  foreach ($possibleImportFiles as $possibleImportFile) {
640  if (!file_exists(PATH_site . $possibleImportFile)) {
641  continue;
642  }
643  $importFileToUse = $possibleImportFile;
644  }
645  if ($importFileToUse !== null) {
647  $importExportUtility = $this->objectManager->get(ImportExportUtility::class);
648  try {
649  $importResult = $importExportUtility->importT3DFile(PATH_site . $importFileToUse, 0);
650  $this->registry->set('extensionDataImport', $extensionSiteRelPath . 'Initialisation/dataImported', 1);
651  $this->emitAfterExtensionT3DImportSignal($importFileToUse, $importResult);
652  } catch (\ErrorException $e) {
654  $logger = $this->objectManager->get(\TYPO3\CMS\Core\Log\LogManager::class)->getLogger(__CLASS__);
655  $logger->log(\TYPO3\CMS\Core\Log\LogLevel::WARNING, $e->getMessage());
656  }
657  }
658  }
659 
666  protected function emitAfterExtensionT3DImportSignal($importFileToUse, $importResult)
667  {
668  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionT3DImport', array($importFileToUse, $importResult, $this));
669  }
670 
678  protected function importStaticSqlFile($extensionSiteRelPath)
679  {
680  $extTablesStaticSqlRelFile = $extensionSiteRelPath . 'ext_tables_static+adt.sql';
681  if (!$this->registry->get('extensionDataImport', $extTablesStaticSqlRelFile)) {
682  $extTablesStaticSqlFile = PATH_site . $extTablesStaticSqlRelFile;
683  if (file_exists($extTablesStaticSqlFile)) {
684  $extTablesStaticSqlContent = GeneralUtility::getUrl($extTablesStaticSqlFile);
685  $this->importStaticSql($extTablesStaticSqlContent);
686  }
687  $this->registry->set('extensionDataImport', $extTablesStaticSqlRelFile, 1);
688  $this->emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile);
689  }
690  }
691 
697  protected function emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile)
698  {
699  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionStaticSqlImport', array($extTablesStaticSqlRelFile, $this));
700  }
701 
709  protected function importInitialFiles($extensionSiteRelPath, $extensionKey)
710  {
711  $importRelFolder = $extensionSiteRelPath . 'Initialisation/Files';
712  if (!$this->registry->get('extensionDataImport', $importRelFolder)) {
713  $importFolder = PATH_site . $importRelFolder;
714  if (file_exists($importFolder)) {
715  $destinationRelPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . $extensionKey;
716  $destinationAbsolutePath = PATH_site . $destinationRelPath;
717  if (!file_exists($destinationAbsolutePath) &&
718  GeneralUtility::isAllowedAbsPath($destinationAbsolutePath)
719  ) {
720  GeneralUtility::mkdir($destinationAbsolutePath);
721  }
722  GeneralUtility::copyDirectory($importRelFolder, $destinationRelPath);
723  $this->registry->set('extensionDataImport', $importRelFolder, 1);
724  $this->emitAfterExtensionFileImportSignal($destinationAbsolutePath);
725  }
726  }
727  }
728 
734  protected function emitAfterExtensionFileImportSignal($destinationAbsolutePath)
735  {
736  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionFileImport', array($destinationAbsolutePath, $this));
737  }
738 
742  protected function getDatabaseConnection()
743  {
744  return $GLOBALS['TYPO3_DB'];
745  }
746 }