TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
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 
24 
29 {
34 
39 
43  protected $dependencyUtility;
44 
49 
53  protected $listUtility;
54 
58  protected $databaseUtility;
59 
64 
68  protected $packageManager;
69 
73  protected $cacheManager;
74 
79 
83  protected $registry;
84 
88  public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
89  {
90  $this->objectManager = $objectManager;
91  }
92 
96  public function injectInstallToolSqlParser(\TYPO3\CMS\Install\Service\SqlSchemaMigrationService $installToolSqlParser)
97  {
98  $this->installToolSqlParser = $installToolSqlParser;
99  }
100 
104  public function injectDependencyUtility(\TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility)
105  {
106  $this->dependencyUtility = $dependencyUtility;
107  }
108 
112  public function injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility)
113  {
114  $this->fileHandlingUtility = $fileHandlingUtility;
115  }
116 
120  public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
121  {
122  $this->listUtility = $listUtility;
123  }
124 
128  public function injectDatabaseUtility(\TYPO3\CMS\Extensionmanager\Utility\DatabaseUtility $databaseUtility)
129  {
130  $this->databaseUtility = $databaseUtility;
131  }
132 
136  public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
137  {
138  $this->extensionRepository = $extensionRepository;
139  }
140 
144  public function injectPackageManager(\TYPO3\CMS\Core\Package\PackageManager $packageManager)
145  {
146  $this->packageManager = $packageManager;
147  }
148 
152  public function injectCacheManager(\TYPO3\CMS\Core\Cache\CacheManager $cacheManager)
153  {
154  $this->cacheManager = $cacheManager;
155  }
156 
160  public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
161  {
162  $this->signalSlotDispatcher = $signalSlotDispatcher;
163  }
164 
168  public function injectRegistry(\TYPO3\CMS\Core\Registry $registry)
169  {
170  $this->registry = $registry;
171  }
172 
181  public function install($extensionKey)
182  {
183  $extension = $this->enrichExtensionWithDetails($extensionKey, false);
184  $this->loadExtension($extensionKey);
185  if (!empty($extension['clearcacheonload']) || !empty($extension['clearCacheOnLoad'])) {
186  $this->cacheManager->flushCaches();
187  } else {
188  $this->cacheManager->flushCachesInGroup('system');
189  }
190  $this->reloadCaches();
191  $this->processExtensionSetup($extensionKey);
192 
193  $this->emitAfterExtensionInstallSignal($extensionKey);
194  }
195 
199  public function processExtensionSetup($extensionKey)
200  {
201  $extension = $this->enrichExtensionWithDetails($extensionKey, false);
202  $this->ensureConfiguredDirectoriesExist($extension);
203  $this->importInitialFiles($extension['siteRelPath'], $extensionKey);
204  $this->processDatabaseUpdates($extension);
205  $this->processRuntimeDatabaseUpdates($extensionKey);
206  $this->saveDefaultConfiguration($extensionKey);
207  }
208 
216  public function uninstall($extensionKey)
217  {
218  $dependentExtensions = $this->dependencyUtility->findInstalledExtensionsThatDependOnMe($extensionKey);
219  if (is_array($dependentExtensions) && !empty($dependentExtensions)) {
220  throw new ExtensionManagerException(
221  \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate(
222  'extensionList.uninstall.dependencyError',
223  'extensionmanager',
224  [$extensionKey, implode(',', $dependentExtensions)]
225  ),
226  1342554622
227  );
228  } else {
229  $this->unloadExtension($extensionKey);
230  }
231  }
232 
239  public function isLoaded($extensionKey)
240  {
241  return $this->packageManager->isPackageActive($extensionKey);
242  }
243 
247  public function reloadAvailableExtensions()
248  {
249  $this->listUtility->reloadAvailableExtensions();
250  }
251 
258  protected function loadExtension($extensionKey)
259  {
260  $this->packageManager->activatePackage($extensionKey);
261  }
262 
269  protected function unloadExtension($extensionKey)
270  {
271  $this->packageManager->deactivatePackage($extensionKey);
272  $this->emitAfterExtensionUninstallSignal($extensionKey);
273  $this->cacheManager->flushCachesInGroup('system');
274  }
275 
281  protected function emitAfterExtensionInstallSignal($extensionKey)
282  {
283  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionInstall', [$extensionKey, $this]);
284  }
285 
291  protected function emitAfterExtensionUninstallSignal($extensionKey)
292  {
293  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionUninstall', [$extensionKey, $this]);
294  }
295 
302  public function isAvailable($extensionKey)
303  {
304  return $this->packageManager->isPackageAvailable($extensionKey);
305  }
306 
316  public function reloadPackageInformation($extensionKey)
317  {
318  if ($this->packageManager->isPackageAvailable($extensionKey)) {
319  $this->reloadOpcache();
320  $this->packageManager->reloadPackageInformation($extensionKey);
321  }
322  }
323 
333  public function enrichExtensionWithDetails($extensionKey, $loadTerInformation = true)
334  {
335  $extension = $this->getExtensionArray($extensionKey);
336  if (!$loadTerInformation) {
337  $availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfInformation([$extensionKey => $extension]);
338  } else {
339  $availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfAndTerInformation([$extensionKey => $extension]);
340  }
341 
342  if (!isset($availableAndInstalledExtensions[$extensionKey])) {
343  throw new ExtensionManagerException(
344  'Please check your uploaded extension "' . $extensionKey . '". The configuration file "ext_emconf.php" seems to be invalid.',
345  1391432222
346  );
347  }
348 
349  return $availableAndInstalledExtensions[$extensionKey];
350  }
351 
357  protected function getExtensionArray($extensionKey)
358  {
359  $availableExtensions = $this->listUtility->getAvailableExtensions();
360  if (isset($availableExtensions[$extensionKey])) {
361  return $availableExtensions[$extensionKey];
362  } else {
363  throw new ExtensionManagerException('Extension ' . $extensionKey . ' is not available', 1342864081);
364  }
365  }
366 
372  protected function ensureConfiguredDirectoriesExist(array $extension)
373  {
374  $this->fileHandlingUtility->ensureConfiguredDirectoriesExist($extension);
375  }
376 
383  public function processDatabaseUpdates(array $extension)
384  {
385  $extTablesSqlFile = PATH_site . $extension['siteRelPath'] . 'ext_tables.sql';
386  $extTablesSqlContent = '';
387  if (file_exists($extTablesSqlFile)) {
388  $extTablesSqlContent .= file_get_contents($extTablesSqlFile);
389  }
390  if ($extTablesSqlContent !== '') {
391  try {
392  $this->updateDbWithExtTablesSql($extTablesSqlContent);
393  } catch (\TYPO3\CMS\Core\Database\Schema\Exception\StatementException $e) {
394  throw new ExtensionManagerException(
395  $e->getMessage(),
396  1476340371
397  );
398  }
399  }
400 
401  $this->importStaticSqlFile($extension['siteRelPath']);
402  $this->importT3DFile($extension['siteRelPath']);
403  }
404 
411  protected function processRuntimeDatabaseUpdates($extensionKey)
412  {
413  $sqlString = $this->emitTablesDefinitionIsBeingBuiltSignal($extensionKey);
414  if (!empty($sqlString)) {
415  $this->updateDbWithExtTablesSql(implode(LF . LF . LF . LF, $sqlString));
416  }
417  }
418 
426  protected function emitTablesDefinitionIsBeingBuiltSignal($extensionKey)
427  {
428  $signalReturn = $this->signalSlotDispatcher->dispatch(__CLASS__, 'tablesDefinitionIsBeingBuilt', [[], $extensionKey]);
429  // This is important to support old associated returns
430  $signalReturn = array_values($signalReturn);
431  $sqlString = $signalReturn[0];
432  if (!is_array($sqlString)) {
433  throw new ExtensionManagerException(
434  sprintf(
435  'The signal %s of class %s returned a value of type %s, but array was expected.',
436  'tablesDefinitionIsBeingBuilt',
437  __CLASS__,
438  gettype($sqlString)
439  ),
440  1382360258
441  );
442  }
443  return $sqlString;
444  }
445 
451  public function reloadCaches()
452  {
453  $this->reloadOpcache();
454  \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::loadExtLocalconf(false);
455  \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->loadExtensionTables(false);
456  }
457 
461  protected function reloadOpcache()
462  {
463  GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
464  }
465 
472  protected function saveDefaultConfiguration($extensionKey)
473  {
475  $configUtility = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility::class);
476  $configUtility->saveDefaultConfiguration($extensionKey);
477  }
478 
485  public function updateDbWithExtTablesSql($rawDefinitions)
486  {
487  $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
488  $statements = $sqlReader->getCreateTableStatementArray($rawDefinitions);
489  if (count($statements) !== 0) {
490  $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
491  $schemaMigrationService->install($statements);
492  }
493  }
494 
501  public function importStaticSql($rawDefinitions)
502  {
503  $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
504  $statements = $sqlReader->getStatementArray($rawDefinitions);
505 
506  $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
507  $schemaMigrationService->importStaticData($statements, true);
508  }
509 
517  public function removeExtension($extension)
518  {
519  $absolutePath = $this->fileHandlingUtility->getAbsoluteExtensionPath($extension);
520  if ($this->fileHandlingUtility->isValidExtensionPath($absolutePath)) {
521  if ($this->packageManager->isPackageAvailable($extension)) {
522  // Package manager deletes the extension and removes the entry from PackageStates.php
523  $this->packageManager->deletePackage($extension);
524  } else {
525  // The extension is not listed in PackageStates.php, we can safely remove it
526  $this->fileHandlingUtility->removeDirectory($absolutePath);
527  }
528  } else {
529  throw new ExtensionManagerException('No valid extension path given.', 1342875724);
530  }
531  }
532 
539  public function getExtensionSqlDataDump($extension)
540  {
541  $extension = $this->enrichExtensionWithDetails($extension);
542  $filePrefix = PATH_site . $extension['siteRelPath'];
543  $sqlData['extTables'] = $this->getSqlDataDumpForFile($filePrefix . 'ext_tables.sql');
544  $sqlData['staticSql'] = $this->getSqlDataDumpForFile($filePrefix . 'ext_tables_static+adt.sql');
545  return $sqlData;
546  }
547 
554  protected function getSqlDataDumpForFile($sqlFile)
555  {
556  $sqlData = '';
557  if (file_exists($sqlFile)) {
558  $sqlContent = file_get_contents($sqlFile);
559  $fieldDefinitions = $this->installToolSqlParser->getFieldDefinitions_fileContent($sqlContent);
560  $sqlData = $this->databaseUtility->dumpStaticTables($fieldDefinitions);
561  }
562  return $sqlData;
563  }
564 
572  public function isUpdateAvailable(Extension $extensionData)
573  {
574  return (bool)$this->getUpdateableVersion($extensionData);
575  }
576 
584  public function getUpdateableVersion(Extension $extensionData)
585  {
586  // Only check for update for TER extensions
587  $version = $extensionData->getIntegerVersion();
588 
590  $extensionUpdates = $this->extensionRepository->findByVersionRangeAndExtensionKeyOrderedByVersion(
591  $extensionData->getExtensionKey(),
592  $version,
593  0,
594  false
595  );
596  if ($extensionUpdates->count() > 0) {
597  foreach ($extensionUpdates as $extensionUpdate) {
598  try {
599  $this->dependencyUtility->checkDependencies($extensionUpdate);
600  if (!$this->dependencyUtility->hasDependencyErrors()) {
601  return $extensionUpdate;
602  }
603  } catch (ExtensionManagerException $e) {
604  }
605  }
606  }
607  return false;
608  }
609 
617  protected function importT3DFile($extensionSiteRelPath)
618  {
619  $registryKeysToCheck = [
620  $extensionSiteRelPath . 'Initialisation/data.t3d',
621  $extensionSiteRelPath . 'Initialisation/dataImported',
622  ];
623  foreach ($registryKeysToCheck as $registryKeyToCheck) {
624  if ($this->registry->get('extensionDataImport', $registryKeyToCheck)) {
625  // Data was imported before => early return
626  return;
627  }
628  }
629  $importFileToUse = null;
630  $possibleImportFiles = [
631  $extensionSiteRelPath . 'Initialisation/data.t3d',
632  $extensionSiteRelPath . 'Initialisation/data.xml'
633  ];
634  foreach ($possibleImportFiles as $possibleImportFile) {
635  if (!file_exists(PATH_site . $possibleImportFile)) {
636  continue;
637  }
638  $importFileToUse = $possibleImportFile;
639  }
640  if ($importFileToUse !== null) {
642  $importExportUtility = $this->objectManager->get(ImportExportUtility::class);
643  try {
644  $importResult = $importExportUtility->importT3DFile(PATH_site . $importFileToUse, 0);
645  $this->registry->set('extensionDataImport', $extensionSiteRelPath . 'Initialisation/dataImported', 1);
646  $this->emitAfterExtensionT3DImportSignal($importFileToUse, $importResult);
647  } catch (\ErrorException $e) {
649  $logger = $this->objectManager->get(\TYPO3\CMS\Core\Log\LogManager::class)->getLogger(__CLASS__);
650  $logger->log(\TYPO3\CMS\Core\Log\LogLevel::WARNING, $e->getMessage());
651  }
652  }
653  }
654 
661  protected function emitAfterExtensionT3DImportSignal($importFileToUse, $importResult)
662  {
663  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionT3DImport', [$importFileToUse, $importResult, $this]);
664  }
665 
673  protected function importStaticSqlFile($extensionSiteRelPath)
674  {
675  $extTablesStaticSqlRelFile = $extensionSiteRelPath . 'ext_tables_static+adt.sql';
676  if (!$this->registry->get('extensionDataImport', $extTablesStaticSqlRelFile)) {
677  $extTablesStaticSqlFile = PATH_site . $extTablesStaticSqlRelFile;
678  if (file_exists($extTablesStaticSqlFile)) {
679  $extTablesStaticSqlContent = file_get_contents($extTablesStaticSqlFile);
680  $this->importStaticSql($extTablesStaticSqlContent);
681  }
682  $this->registry->set('extensionDataImport', $extTablesStaticSqlRelFile, 1);
683  $this->emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile);
684  }
685  }
686 
692  protected function emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile)
693  {
694  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionStaticSqlImport', [$extTablesStaticSqlRelFile, $this]);
695  }
696 
704  protected function importInitialFiles($extensionSiteRelPath, $extensionKey)
705  {
706  $importRelFolder = $extensionSiteRelPath . 'Initialisation/Files';
707  if (!$this->registry->get('extensionDataImport', $importRelFolder)) {
708  $importFolder = PATH_site . $importRelFolder;
709  if (file_exists($importFolder)) {
710  $destinationRelPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . $extensionKey;
711  $destinationAbsolutePath = PATH_site . $destinationRelPath;
712  if (!file_exists($destinationAbsolutePath) &&
713  GeneralUtility::isAllowedAbsPath($destinationAbsolutePath)
714  ) {
715  GeneralUtility::mkdir($destinationAbsolutePath);
716  }
717  GeneralUtility::copyDirectory($importRelFolder, $destinationRelPath);
718  $this->registry->set('extensionDataImport', $importRelFolder, 1);
719  $this->emitAfterExtensionFileImportSignal($destinationAbsolutePath);
720  }
721  }
722  }
723 
729  protected function emitAfterExtensionFileImportSignal($destinationAbsolutePath)
730  {
731  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionFileImport', [$destinationAbsolutePath, $this]);
732  }
733 }
static copyDirectory($source, $destination)
injectDatabaseUtility(\TYPO3\CMS\Extensionmanager\Utility\DatabaseUtility $databaseUtility)
injectPackageManager(\TYPO3\CMS\Core\Package\PackageManager $packageManager)
injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
emitAfterExtensionT3DImportSignal($importFileToUse, $importResult)
emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile)
injectDependencyUtility(\TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility)
emitAfterExtensionFileImportSignal($destinationAbsolutePath)
injectCacheManager(\TYPO3\CMS\Core\Cache\CacheManager $cacheManager)
injectRegistry(\TYPO3\CMS\Core\Registry $registry)
injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
enrichExtensionWithDetails($extensionKey, $loadTerInformation=true)
injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
importInitialFiles($extensionSiteRelPath, $extensionKey)
static makeInstance($className,...$constructorArguments)
injectInstallToolSqlParser(\TYPO3\CMS\Install\Service\SqlSchemaMigrationService $installToolSqlParser)
injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility)