1<?php
2namespace 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
17use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
18use TYPO3\CMS\Core\Core\Environment;
19use TYPO3\CMS\Core\Database\Schema\SchemaMigrator;
20use TYPO3\CMS\Core\Database\Schema\SqlReader;
21use TYPO3\CMS\Core\Service\OpcodeCacheService;
22use TYPO3\CMS\Core\Utility\GeneralUtility;
23use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
24use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
25use TYPO3\CMS\Impexp\Utility\ImportExportUtility;
26
27/**
28 * Extension Manager Install Utility
29 * @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
30 */
31class InstallUtility implements \TYPO3\CMS\Core\SingletonInterface
32{
33    /**
34     * @var \TYPO3\CMS\Extbase\Object\ObjectManager
35     */
36    public $objectManager;
37
38    /**
39     * @var \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility
40     */
41    protected $dependencyUtility;
42
43    /**
44     * @var \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
45     */
46    protected $fileHandlingUtility;
47
48    /**
49     * @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
50     */
51    protected $listUtility;
52
53    /**
54     * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
55     */
56    public $extensionRepository;
57
58    /**
59     * @var \TYPO3\CMS\Core\Package\PackageManager
60     */
61    protected $packageManager;
62
63    /**
64     * @var \TYPO3\CMS\Core\Cache\CacheManager
65     */
66    protected $cacheManager;
67
68    /**
69     * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
70     */
71    protected $signalSlotDispatcher;
72
73    /**
74     * @var \TYPO3\CMS\Core\Registry
75     */
76    protected $registry;
77
78    /**
79     * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager
80     */
81    public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
82    {
83        $this->objectManager = $objectManager;
84    }
85
86    /**
87     * @param \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility
88     */
89    public function injectDependencyUtility(\TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility)
90    {
91        $this->dependencyUtility = $dependencyUtility;
92    }
93
94    /**
95     * @param \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility
96     */
97    public function injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility)
98    {
99        $this->fileHandlingUtility = $fileHandlingUtility;
100    }
101
102    /**
103     * @param \TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility
104     */
105    public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
106    {
107        $this->listUtility = $listUtility;
108    }
109
110    /**
111     * @param \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository
112     */
113    public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
114    {
115        $this->extensionRepository = $extensionRepository;
116    }
117
118    /**
119     * @param \TYPO3\CMS\Core\Package\PackageManager $packageManager
120     */
121    public function injectPackageManager(\TYPO3\CMS\Core\Package\PackageManager $packageManager)
122    {
123        $this->packageManager = $packageManager;
124    }
125
126    /**
127     * @param \TYPO3\CMS\Core\Cache\CacheManager $cacheManager
128     */
129    public function injectCacheManager(\TYPO3\CMS\Core\Cache\CacheManager $cacheManager)
130    {
131        $this->cacheManager = $cacheManager;
132    }
133
134    /**
135     * @param \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher
136     */
137    public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
138    {
139        $this->signalSlotDispatcher = $signalSlotDispatcher;
140    }
141
142    /**
143     * @param \TYPO3\CMS\Core\Registry $registry
144     */
145    public function injectRegistry(\TYPO3\CMS\Core\Registry $registry)
146    {
147        $this->registry = $registry;
148    }
149
150    /**
151     * Helper function to install an extension
152     * also processes db updates and clears the cache if the extension asks for it
153     *
154     * @param array $extensionKeys
155     * @throws ExtensionManagerException
156     */
157    public function install(...$extensionKeys)
158    {
159        $flushCaches = false;
160        foreach ($extensionKeys as $extensionKey) {
161            $this->loadExtension($extensionKey);
162            $extension = $this->enrichExtensionWithDetails($extensionKey, false);
163            $this->saveDefaultConfiguration($extensionKey);
164            if (!empty($extension['clearcacheonload']) || !empty($extension['clearCacheOnLoad'])) {
165                $flushCaches = true;
166            }
167        }
168
169        if ($flushCaches) {
170            $this->cacheManager->flushCaches();
171        } else {
172            $this->cacheManager->flushCachesInGroup('system');
173        }
174        $this->reloadCaches();
175        $this->updateDatabase($extensionKeys);
176
177        foreach ($extensionKeys as $extensionKey) {
178            $this->processExtensionSetup($extensionKey);
179            $this->emitAfterExtensionInstallSignal($extensionKey);
180        }
181    }
182
183    /**
184     * @param string $extensionKey
185     */
186    public function processExtensionSetup($extensionKey)
187    {
188        $extension = $this->enrichExtensionWithDetails($extensionKey, false);
189        $this->ensureConfiguredDirectoriesExist($extension);
190        $this->importInitialFiles($extension['siteRelPath'] ?? '', $extensionKey);
191        $this->importStaticSqlFile($extension['siteRelPath']);
192        $this->importT3DFile($extension['siteRelPath']);
193    }
194
195    /**
196     * Helper function to uninstall an extension
197     *
198     * @param string $extensionKey
199     * @throws ExtensionManagerException
200     */
201    public function uninstall($extensionKey)
202    {
203        $dependentExtensions = $this->dependencyUtility->findInstalledExtensionsThatDependOnMe($extensionKey);
204        if (is_array($dependentExtensions) && !empty($dependentExtensions)) {
205            throw new ExtensionManagerException(
206                \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate(
207                    'extensionList.uninstall.dependencyError',
208                    'extensionmanager',
209                    [$extensionKey, implode(',', $dependentExtensions)]
210                ),
211                1342554622
212            );
213        }
214        $this->unloadExtension($extensionKey);
215    }
216
217    /**
218     * Wrapper function to check for loaded extensions
219     *
220     * @param string $extensionKey
221     * @return bool TRUE if extension is loaded
222     */
223    public function isLoaded($extensionKey)
224    {
225        return $this->packageManager->isPackageActive($extensionKey);
226    }
227
228    /**
229     * Reset and reload the available extensions
230     */
231    public function reloadAvailableExtensions()
232    {
233        $this->listUtility->reloadAvailableExtensions();
234    }
235
236    /**
237     * Wrapper function for loading extensions
238     *
239     * @param string $extensionKey
240     */
241    protected function loadExtension($extensionKey)
242    {
243        $this->packageManager->activatePackage($extensionKey);
244    }
245
246    /**
247     * Wrapper function for unloading extensions
248     *
249     * @param string $extensionKey
250     */
251    protected function unloadExtension($extensionKey)
252    {
253        $this->packageManager->deactivatePackage($extensionKey);
254        $this->emitAfterExtensionUninstallSignal($extensionKey);
255        $this->cacheManager->flushCachesInGroup('system');
256    }
257
258    /**
259     * Emits a signal after an extension has been installed
260     *
261     * @param string $extensionKey
262     */
263    protected function emitAfterExtensionInstallSignal($extensionKey)
264    {
265        $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionInstall', [$extensionKey, $this]);
266    }
267
268    /**
269     * Emits a signal after an extension has been uninstalled
270     *
271     * @param string $extensionKey
272     */
273    protected function emitAfterExtensionUninstallSignal($extensionKey)
274    {
275        $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionUninstall', [$extensionKey, $this]);
276    }
277
278    /**
279     * Checks if an extension is available in the system
280     *
281     * @param string $extensionKey
282     * @return bool
283     */
284    public function isAvailable($extensionKey)
285    {
286        return $this->packageManager->isPackageAvailable($extensionKey);
287    }
288
289    /**
290     * Reloads the package information, if the package is already registered
291     *
292     * @param string $extensionKey
293     * @throws \TYPO3\CMS\Core\Package\Exception\InvalidPackageStateException if the package isn't available
294     * @throws \TYPO3\CMS\Core\Package\Exception\InvalidPackageKeyException if an invalid package key was passed
295     * @throws \TYPO3\CMS\Core\Package\Exception\InvalidPackagePathException if an invalid package path was passed
296     * @throws \TYPO3\CMS\Core\Package\Exception\InvalidPackageManifestException if no extension configuration file could be found
297     */
298    public function reloadPackageInformation($extensionKey)
299    {
300        if ($this->packageManager->isPackageAvailable($extensionKey)) {
301            $this->reloadOpcache();
302            $this->packageManager->reloadPackageInformation($extensionKey);
303        }
304    }
305
306    /**
307     * Fetch additional information for an extension key
308     *
309     * @param string $extensionKey
310     * @param bool $loadTerInformation
311     * @internal
312     * @return array
313     * @throws ExtensionManagerException
314     */
315    public function enrichExtensionWithDetails($extensionKey, $loadTerInformation = true)
316    {
317        $extension = $this->getExtensionArray($extensionKey);
318        if (!$loadTerInformation) {
319            $availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfInformation([$extensionKey => $extension]);
320        } else {
321            $availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfAndTerInformation([$extensionKey => $extension]);
322        }
323
324        if (!isset($availableAndInstalledExtensions[$extensionKey])) {
325            throw new ExtensionManagerException(
326                'Please check your uploaded extension "' . $extensionKey . '". The configuration file "ext_emconf.php" seems to be invalid.',
327                1391432222
328            );
329        }
330
331        return $availableAndInstalledExtensions[$extensionKey];
332    }
333
334    /**
335     * @param string $extensionKey
336     * @return array
337     * @throws ExtensionManagerException
338     */
339    protected function getExtensionArray($extensionKey)
340    {
341        $availableExtensions = $this->listUtility->getAvailableExtensions();
342        if (isset($availableExtensions[$extensionKey])) {
343            return $availableExtensions[$extensionKey];
344        }
345        throw new ExtensionManagerException('Extension ' . $extensionKey . ' is not available', 1342864081);
346    }
347
348    /**
349     * Creates directories as requested in ext_emconf.php
350     *
351     * @param array $extension
352     */
353    protected function ensureConfiguredDirectoriesExist(array $extension)
354    {
355        $this->fileHandlingUtility->ensureConfiguredDirectoriesExist($extension);
356    }
357
358    /**
359     * Gets the content of the ext_tables.sql and ext_tables_static+adt.sql files
360     * Additionally adds the table definitions for the cache tables
361     *
362     * @param array $extension
363     * @deprecated since TYPO3 v9, will be removed with TYPO3v10
364     */
365    public function processDatabaseUpdates(array $extension)
366    {
367        trigger_error('This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
368        $extTablesSqlFile = Environment::getPublicPath() . '/' . $extension['siteRelPath'] . 'ext_tables.sql';
369        $extTablesSqlContent = '';
370        if (file_exists($extTablesSqlFile)) {
371            $extTablesSqlContent .= file_get_contents($extTablesSqlFile);
372        }
373        if ($extTablesSqlContent !== '') {
374            try {
375                $this->updateDbWithExtTablesSql($extTablesSqlContent);
376            } catch (\TYPO3\CMS\Core\Database\Schema\Exception\StatementException $e) {
377                throw new ExtensionManagerException(
378                    $e->getMessage(),
379                    1476340371
380                );
381            }
382        }
383
384        $this->importStaticSqlFile($extension['siteRelPath']);
385        $this->importT3DFile($extension['siteRelPath']);
386    }
387
388    /**
389     * Emits a signal to manipulate the tables definitions
390     *
391     * @param string $extensionKey
392     * @throws ExtensionManagerException
393     * @return mixed
394     * @deprecated since TYPO3 v9, will be removed with TYPO3v10
395     * @see \TYPO3\CMS\Core\Database\Schema\SqlReader::emitTablesDefinitionIsBeingBuiltSignal
396     */
397    protected function emitTablesDefinitionIsBeingBuiltSignal($extensionKey)
398    {
399        $signalReturn = $this->signalSlotDispatcher->dispatch(__CLASS__, 'tablesDefinitionIsBeingBuilt', [[], $extensionKey]);
400        // This is important to support old associated returns
401        $signalReturn = array_values($signalReturn);
402        $sqlString = $signalReturn[0];
403        if (!is_array($sqlString)) {
404            throw new ExtensionManagerException(
405                sprintf(
406                    'The signal %s of class %s returned a value of type %s, but array was expected.',
407                    'tablesDefinitionIsBeingBuilt',
408                    __CLASS__,
409                    gettype($sqlString)
410                ),
411                1382360258
412            );
413        }
414        if (!empty($sqlString)) {
415            trigger_error(
416                sprintf(
417                    'The signal %s of class %s is deprecated and will be removed in TYPO3 v10.0.',
418                    'tablesDefinitionIsBeingBuilt',
419                    __CLASS__
420                ),
421                E_USER_DEPRECATED
422            );
423        }
424        return $sqlString;
425    }
426
427    /**
428     * Reload Cache files and Typo3LoadedExtensions
429     */
430    public function reloadCaches()
431    {
432        $this->reloadOpcache();
433        \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::loadExtLocalconf(false);
434        \TYPO3\CMS\Core\Core\Bootstrap::loadBaseTca(false);
435        \TYPO3\CMS\Core\Core\Bootstrap::loadExtTables(false);
436    }
437
438    /**
439     * Reloads PHP opcache
440     */
441    protected function reloadOpcache()
442    {
443        GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
444    }
445
446    /**
447     * Executes all safe database statements.
448     * Tables and fields are created and altered. Nothing gets deleted or renamed here.
449     *
450     * @param array $extensionKeys
451     */
452    protected function updateDatabase(array $extensionKeys)
453    {
454        $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
455        $schemaMigrator = GeneralUtility::makeInstance(SchemaMigrator::class);
456        $sqlStatements = [];
457        $sqlStatements[] = $sqlReader->getTablesDefinitionString();
458        foreach ($extensionKeys as $extensionKey) {
459            $sqlStatements += $this->emitTablesDefinitionIsBeingBuiltSignal($extensionKey);
460        }
461        $sqlStatements = $sqlReader->getCreateTableStatementArray(implode(LF . LF, array_filter($sqlStatements)));
462        $updateStatements = $schemaMigrator->getUpdateSuggestions($sqlStatements);
463
464        $updateStatements = array_merge_recursive(...array_values($updateStatements));
465        $selectedStatements = [];
466        foreach (['add', 'change', 'create_table', 'change_table'] as $action) {
467            if (empty($updateStatements[$action])) {
468                continue;
469            }
470            $selectedStatements = array_merge(
471                $selectedStatements,
472                array_combine(array_keys($updateStatements[$action]), array_fill(0, count($updateStatements[$action]), true))
473            );
474        }
475
476        $schemaMigrator->migrate($sqlStatements, $selectedStatements);
477    }
478
479    /**
480     * Save default configuration of an extension
481     *
482     * @param string $extensionKey
483     */
484    protected function saveDefaultConfiguration($extensionKey)
485    {
486        $extensionConfiguration = $this->objectManager->get(ExtensionConfiguration::class);
487        $extensionConfiguration->synchronizeExtConfTemplateWithLocalConfiguration($extensionKey);
488    }
489
490    /**
491     * Update database / process db updates from ext_tables
492     *
493     * @param string $rawDefinitions The raw SQL statements from ext_tables.sql
494     * @deprecated since TYPO3 v9, will be removed with TYPO3v10
495     */
496    public function updateDbWithExtTablesSql($rawDefinitions)
497    {
498        trigger_error('This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
499        $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
500        $statements = $sqlReader->getCreateTableStatementArray($rawDefinitions);
501        if (count($statements) !== 0) {
502            $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
503            $schemaMigrationService->install($statements);
504        }
505    }
506
507    /**
508     * Import static SQL data (normally used for ext_tables_static+adt.sql)
509     *
510     * @param string $rawDefinitions
511     */
512    public function importStaticSql($rawDefinitions)
513    {
514        $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
515        $statements = $sqlReader->getStatementArray($rawDefinitions);
516
517        $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
518        $schemaMigrationService->importStaticData($statements, true);
519    }
520
521    /**
522     * Remove an extension (delete the directory)
523     *
524     * @param string $extension
525     * @throws ExtensionManagerException
526     */
527    public function removeExtension($extension)
528    {
529        $absolutePath = $this->fileHandlingUtility->getAbsoluteExtensionPath($extension);
530        if ($this->fileHandlingUtility->isValidExtensionPath($absolutePath)) {
531            if ($this->packageManager->isPackageAvailable($extension)) {
532                // Package manager deletes the extension and removes the entry from PackageStates.php
533                $this->packageManager->deletePackage($extension);
534            } else {
535                // The extension is not listed in PackageStates.php, we can safely remove it
536                $this->fileHandlingUtility->removeDirectory($absolutePath);
537            }
538        } else {
539            throw new ExtensionManagerException('No valid extension path given.', 1342875724);
540        }
541    }
542
543    /**
544     * Checks if an update for an extension is available which also resolves dependencies.
545     *
546     * @internal
547     * @param Extension $extensionData
548     * @return bool
549     */
550    public function isUpdateAvailable(Extension $extensionData)
551    {
552        return (bool)$this->getUpdateableVersion($extensionData);
553    }
554
555    /**
556     * Returns the updateable version for an extension which also resolves dependencies.
557     *
558     * @internal
559     * @param Extension $extensionData
560     * @return bool|Extension FALSE if no update available otherwise latest possible update
561     */
562    public function getUpdateableVersion(Extension $extensionData)
563    {
564        // Only check for update for TER extensions
565        $version = $extensionData->getIntegerVersion();
566
567        /** @var $extensionUpdates[] \TYPO3\CMS\Extensionmanager\Domain\Model\Extension */
568        $extensionUpdates = $this->extensionRepository->findByVersionRangeAndExtensionKeyOrderedByVersion(
569            $extensionData->getExtensionKey(),
570            $version,
571            0,
572            false
573        );
574        if ($extensionUpdates->count() > 0) {
575            foreach ($extensionUpdates as $extensionUpdate) {
576                try {
577                    $this->dependencyUtility->checkDependencies($extensionUpdate);
578                    if (!$this->dependencyUtility->hasDependencyErrors()) {
579                        return $extensionUpdate;
580                    }
581                } catch (ExtensionManagerException $e) {
582                }
583            }
584        }
585        return false;
586    }
587
588    /**
589     * Uses the export import extension to import a T3D or XML file to PID 0
590     * Execution state is saved in the this->registry, so it only happens once
591     *
592     * @param string $extensionSiteRelPath
593     */
594    protected function importT3DFile($extensionSiteRelPath)
595    {
596        $registryKeysToCheck = [
597            $extensionSiteRelPath . 'Initialisation/data.t3d',
598            $extensionSiteRelPath . 'Initialisation/dataImported',
599        ];
600        foreach ($registryKeysToCheck as $registryKeyToCheck) {
601            if ($this->registry->get('extensionDataImport', $registryKeyToCheck)) {
602                // Data was imported before => early return
603                return;
604            }
605        }
606        $importFileToUse = null;
607        $possibleImportFiles = [
608            $extensionSiteRelPath . 'Initialisation/data.t3d',
609            $extensionSiteRelPath . 'Initialisation/data.xml'
610        ];
611        foreach ($possibleImportFiles as $possibleImportFile) {
612            if (!file_exists(Environment::getPublicPath() . '/' . $possibleImportFile)) {
613                continue;
614            }
615            $importFileToUse = $possibleImportFile;
616        }
617        if ($importFileToUse !== null) {
618            /** @var ImportExportUtility $importExportUtility */
619            $importExportUtility = $this->objectManager->get(ImportExportUtility::class);
620            try {
621                $importResult = $importExportUtility->importT3DFile(Environment::getPublicPath() . '/' . $importFileToUse, 0);
622                $this->registry->set('extensionDataImport', $extensionSiteRelPath . 'Initialisation/dataImported', 1);
623                $this->emitAfterExtensionT3DImportSignal($importFileToUse, $importResult);
624            } catch (\ErrorException $e) {
625                $logger = $this->objectManager->get(\TYPO3\CMS\Core\Log\LogManager::class)->getLogger(__CLASS__);
626                $logger->log(\TYPO3\CMS\Core\Log\LogLevel::WARNING, $e->getMessage());
627            }
628        }
629    }
630
631    /**
632     * Emits a signal after an t3d file was imported
633     *
634     * @param string $importFileToUse
635     * @param int $importResult
636     */
637    protected function emitAfterExtensionT3DImportSignal($importFileToUse, $importResult)
638    {
639        $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionT3DImport', [$importFileToUse, $importResult, $this]);
640    }
641
642    /**
643     * Imports a static tables SQL File (ext_tables_static+adt)
644     * Execution state is saved in the this->registry, so it only happens once
645     *
646     * @param string $extensionSiteRelPath
647     */
648    protected function importStaticSqlFile($extensionSiteRelPath)
649    {
650        $extTablesStaticSqlRelFile = $extensionSiteRelPath . 'ext_tables_static+adt.sql';
651        if (!$this->registry->get('extensionDataImport', $extTablesStaticSqlRelFile)) {
652            $extTablesStaticSqlFile = Environment::getPublicPath() . '/' . $extTablesStaticSqlRelFile;
653            $shortFileHash = '';
654            if (file_exists($extTablesStaticSqlFile)) {
655                $extTablesStaticSqlContent = file_get_contents($extTablesStaticSqlFile);
656                $shortFileHash = md5($extTablesStaticSqlContent);
657                $this->importStaticSql($extTablesStaticSqlContent);
658            }
659            $this->registry->set('extensionDataImport', $extTablesStaticSqlRelFile, $shortFileHash);
660            $this->emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile);
661        }
662    }
663
664    /**
665     * Emits a signal after a static sql file was imported
666     *
667     * @param string $extTablesStaticSqlRelFile
668     */
669    protected function emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile)
670    {
671        $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionStaticSqlImport', [$extTablesStaticSqlRelFile, $this]);
672    }
673
674    /**
675     * Imports files from Initialisation/Files to fileadmin
676     * via lowlevel copy directory method
677     *
678     * @param string $extensionSiteRelPath relative path to extension dir
679     * @param string $extensionKey
680     */
681    protected function importInitialFiles($extensionSiteRelPath, $extensionKey)
682    {
683        $importRelFolder = $extensionSiteRelPath . 'Initialisation/Files';
684        if (!$this->registry->get('extensionDataImport', $importRelFolder)) {
685            $importFolder = Environment::getPublicPath() . '/' . $importRelFolder;
686            if (file_exists($importFolder)) {
687                $destinationRelPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . $extensionKey;
688                $destinationAbsolutePath = Environment::getPublicPath() . '/' . $destinationRelPath;
689                if (!file_exists($destinationAbsolutePath) &&
690                    GeneralUtility::isAllowedAbsPath($destinationAbsolutePath)
691                ) {
692                    GeneralUtility::mkdir($destinationAbsolutePath);
693                }
694                GeneralUtility::copyDirectory($importRelFolder, $destinationRelPath);
695                $this->registry->set('extensionDataImport', $importRelFolder, 1);
696                $this->emitAfterExtensionFileImportSignal($destinationAbsolutePath);
697            }
698        }
699    }
700
701    /**
702     * Emits a signal after extension files were imported
703     *
704     * @param string $destinationAbsolutePath
705     */
706    protected function emitAfterExtensionFileImportSignal($destinationAbsolutePath)
707    {
708        $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionFileImport', [$destinationAbsolutePath, $this]);
709    }
710}
711