[TASK] Detect and import xml file as well as t3d on ext initialization
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Classes / Utility / InstallUtility.php
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Utility;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2012-2013 Susanne Moog <susanne.moog@typo3.org>
8 * All rights reserved
9 *
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.
15 *
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.
20 *
21 *
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.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 /**
31 * Extension Manager Install Utility
32 *
33 * @author Susanne Moog <susanne.moog@typo3.org>
34 */
35 class InstallUtility implements \TYPO3\CMS\Core\SingletonInterface {
36
37 /**
38 * @var \TYPO3\CMS\Extbase\Object\ObjectManager
39 * @inject
40 */
41 public $objectManager;
42
43 /**
44 * @var \TYPO3\CMS\Install\Service\SqlSchemaMigrationService
45 * @inject
46 */
47 public $installToolSqlParser;
48
49 /**
50 * @var \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility
51 * @inject
52 */
53 protected $dependencyUtility;
54
55 /**
56 * @var \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
57 * @inject
58 */
59 protected $fileHandlingUtility;
60
61 /**
62 * @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
63 * @inject
64 */
65 protected $listUtility;
66
67 /**
68 * @var \TYPO3\CMS\Extensionmanager\Utility\DatabaseUtility
69 * @inject
70 */
71 protected $databaseUtility;
72
73 /**
74 * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
75 * @inject
76 */
77 public $extensionRepository;
78
79 /**
80 * @var \TYPO3\CMS\Core\Package\PackageManager
81 * @inject
82 */
83 protected $packageManager;
84
85 /**
86 * @var \TYPO3\CMS\Core\Cache\CacheManager
87 * @inject
88 */
89 protected $cacheManager;
90
91 /**
92 * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
93 * @inject
94 */
95 protected $signalSlotDispatcher;
96
97 /**
98 * @var \TYPO3\CMS\Core\Registry
99 * @inject
100 */
101 protected $registry;
102
103 /**
104 * Helper function to install an extension
105 * also processes db updates and clears the cache if the extension asks for it
106 *
107 * @param string $extensionKey
108 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
109 * @return void
110 */
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);
118 }
119 $this->reloadCaches();
120 $this->processRuntimeDatabaseUpdates($extensionKey);
121 $this->saveDefaultConfiguration($extension['key']);
122 if ($extension['clearcacheonload']) {
123 $this->cacheManager->flushCaches();
124 } else {
125 $this->cacheManager->flushCachesInGroup('system');
126 }
127 }
128
129 /**
130 * Helper function to uninstall an extension
131 *
132 * @param string $extensionKey
133 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
134 * @return void
135 */
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',
142 'extensionmanager',
143 array($extensionKey, implode(',', $dependentExtensions))
144 ),
145 1342554622
146 );
147 } else {
148 $this->unloadExtension($extensionKey);
149 }
150 }
151
152 /**
153 * Wrapper function to check for loaded extensions
154 *
155 * @param string $extensionKey
156 * @return boolean TRUE if extension is loaded
157 */
158 public function isLoaded($extensionKey) {
159 return $this->packageManager->isPackageActive($extensionKey);
160 }
161
162 /**
163 * Wrapper function for loading extensions
164 *
165 * @param string $extensionKey
166 * @return void
167 */
168 protected function loadExtension($extensionKey) {
169 $this->packageManager->activatePackage($extensionKey);
170 }
171
172 /**
173 * Wrapper function for unloading extensions
174 *
175 * @param string $extensionKey
176 * @return void
177 */
178 protected function unloadExtension($extensionKey) {
179 $this->packageManager->deactivatePackage($extensionKey);
180 $this->cacheManager->flushCachesInGroup('system');
181 }
182
183 /**
184 * Checks if an extension is available in the system
185 *
186 * @param $extensionKey
187 * @return boolean
188 */
189 public function isAvailable($extensionKey) {
190 return $this->packageManager->isPackageAvailable($extensionKey);
191 }
192
193 /**
194 * Fetch additional information for an extension key
195 *
196 * @param string $extensionKey
197 * @access private
198 * @return array
199 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
200 */
201 public function enrichExtensionWithDetails($extensionKey) {
202 $availableExtensions = $this->listUtility->getAvailableExtensions();
203 if (isset($availableExtensions[$extensionKey])) {
204 $extension = $availableExtensions[$extensionKey];
205 } else {
206 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Extension ' . $extensionKey . ' is not available', 1342864081);
207 }
208 $availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfAndTerInformation(array($extensionKey => $extension));
209
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.',
213 1391432222
214 );
215 }
216
217 return $availableAndInstalledExtensions[$extensionKey];
218 }
219
220 /**
221 * Creates directories as requested in ext_emconf.php
222 *
223 * @param array $extension
224 */
225 protected function ensureConfiguredDirectoriesExist(array $extension) {
226 $this->fileHandlingUtility->ensureConfiguredDirectoriesExist($extension);
227 }
228
229 /**
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
232 *
233 * @param array $extension
234 */
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);
240 }
241 if ($extTablesSqlContent !== '') {
242 $this->updateDbWithExtTablesSql($extTablesSqlContent);
243 }
244
245 $this->importStaticSqlFile($extension['siteRelPath']);
246 $this->importT3DFile($extension['siteRelPath']);
247 }
248
249 /**
250 * Gets all database updates due to runtime configuration, like caching framework or
251 * category api for example
252 *
253 * @param string $extensionKey
254 */
255 protected function processRuntimeDatabaseUpdates($extensionKey) {
256 $sqlString = $this->emitTablesDefinitionIsBeingBuiltSignal($extensionKey);
257 if (!empty($sqlString)) {
258 $this->updateDbWithExtTablesSql(implode(LF . LF . LF . LF, $sqlString));
259 }
260 }
261
262 /**
263 * Emits a signal to manipulate the tables definitions
264 *
265 * @param string $extensionKey
266 * @return mixed
267 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
268 */
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(
274 sprintf(
275 'The signal %s of class %s returned a value of type %s, but array was expected.',
276 'tablesDefinitionIsBeingBuilt',
277 __CLASS__,
278 gettype($sqlString)
279 ),
280 1382360258
281 );
282 }
283 return $sqlString;
284 }
285
286 /**
287 * Reload Cache files and Typo3LoadedExtensions
288 *
289 * @return void
290 */
291 public function reloadCaches() {
292 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::loadExtLocalconf(FALSE);
293 \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->loadExtensionTables(FALSE);
294 }
295
296 /**
297 * Save default configuration of an extension
298 *
299 * @param string $extensionKey
300 * @return void
301 */
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);
306 }
307
308 /**
309 * Update database / process db updates from ext_tables
310 *
311 * @param string $rawDefinitions The raw SQL statements from ext_tables.sql
312 * @return void
313 */
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);
323 }
324 foreach ((array) $updateStatements['change'] as $string) {
325 $db->admin_query($string);
326 }
327 foreach ((array) $updateStatements['create_table'] as $string) {
328 $db->admin_query($string);
329 }
330 }
331 }
332
333 /**
334 * Import static SQL data (normally used for ext_tables_static+adt.sql)
335 *
336 * @param string $rawDefinitions
337 * @return void
338 */
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);
351 }
352 }
353 }
354 }
355
356 /**
357 * Remove an extension (delete the directory)
358 *
359 * @param string $extension
360 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
361 * @return void
362 */
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);
369 } else {
370 // The extension is not listed in PackageStates.php, we can safely remove it
371 $this->fileHandlingUtility->removeDirectory($absolutePath);
372 }
373 } else {
374 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('No valid extension path given.', 1342875724);
375 }
376 }
377
378 /**
379 * Get the data dump for an extension
380 *
381 * @param string $extension
382 * @return array
383 */
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');
389 return $sqlData;
390 }
391
392 /**
393 * Gets the sql data dump for a specific sql file (for example ext_tables.sql)
394 *
395 * @param string $sqlFile
396 * @return string
397 */
398 protected function getSqlDataDumpForFile($sqlFile) {
399 $sqlData = '';
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);
404 }
405 return $sqlData;
406 }
407
408 /**
409 * Checks if an update for an extension is available
410 *
411 * @internal
412 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extensionData
413 * @return boolean
414 */
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;
427 }
428 }
429 }
430 return $isUpdateAvailable;
431 }
432
433 /**
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
436 *
437 * @param string $extensionSiteRelPath
438 * @return void
439 */
440 protected function importT3DFile($extensionSiteRelPath) {
441 $registryKeysToCheck = array(
442 $extensionSiteRelPath . 'Initialisation/data.t3d',
443 $extensionSiteRelPath . 'Initialisation/dataImported',
444 );
445 foreach ($registryKeysToCheck as $registryKeyToCheck) {
446 if ($this->registry->get('extensionDataImport', $registryKeyToCheck)) {
447 // Data was imported before => early return
448 return;
449 }
450 }
451 $importFileToUse = NULL;
452 $possibleImportFiles = array(
453 $extensionSiteRelPath . 'Initialisation/data.t3d',
454 $extensionSiteRelPath . 'Initialisation/data.xml'
455 );
456 foreach ($possibleImportFiles as $possibleImportFile) {
457 if (!file_exists(PATH_site . $possibleImportFile)) {
458 continue;
459 }
460 $importFileToUse = $possibleImportFile;
461 }
462 if ($importFileToUse !== NULL) {
463 $importExportUtility = $this->objectManager->get('TYPO3\\CMS\\Impexp\\Utility\\ImportExportUtility');
464 try {
465 $importResult = $importExportUtility->importT3DFile(PATH_site . $importFileToUse, 0);
466 $this->registry->set('extensionDataImport', $extensionSiteRelPath . 'Initialisation/dataImported', 1);
467 $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionT3DImport', array($importFileToUse, $importResult, $this));
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());
472 }
473 }
474 }
475
476 /**
477 * Imports a static tables SQL File (ext_tables_static+adt)
478 * Execution state is saved in the this->registry, so it only happens once
479 *
480 * @param string $extensionSiteRelPath
481 * @return void
482 */
483 protected function importStaticSqlFile($extensionSiteRelPath) {
484 $extTablesStaticSqlRelFile = $extensionSiteRelPath . 'ext_tables_static+adt.sql';
485 if (!$this->registry->get('extensionDataImport', $extTablesStaticSqlRelFile)) {
486 $extTablesStaticSqlFile = PATH_site . $extTablesStaticSqlRelFile;
487 if (file_exists($extTablesStaticSqlFile)) {
488 $extTablesStaticSqlContent = \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl($extTablesStaticSqlFile);
489 $this->importStaticSql($extTablesStaticSqlContent);
490 }
491 $this->registry->set('extensionDataImport', $extTablesStaticSqlRelFile, 1);
492 $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionStaticSqlImport', array($extTablesStaticSqlRelFile, $this));
493 }
494 }
495
496 /**
497 * Imports files from Initialisation/Files to fileadmin
498 * via lowlevel copy directory method
499 *
500 * @param string $extensionSiteRelPath relative path to extension dir
501 * @param string $extensionKey
502 */
503 protected function importInitialFiles($extensionSiteRelPath, $extensionKey) {
504 $importRelFolder = $extensionSiteRelPath . 'Initialisation/Files';
505 if (!$this->registry->get('extensionDataImport', $importRelFolder)) {
506 $importFolder = PATH_site . $importRelFolder;
507 if (file_exists($importFolder)) {
508 $destinationRelPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . $extensionKey;
509 $destinationAbsolutePath = PATH_site . $destinationRelPath;
510 if (!file_exists($destinationAbsolutePath) &&
511 \TYPO3\CMS\Core\Utility\GeneralUtility::isAllowedAbsPath($destinationAbsolutePath)
512 ) {
513 \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir($destinationAbsolutePath);
514 }
515 \TYPO3\CMS\Core\Utility\GeneralUtility::copyDirectory($importRelFolder, $destinationRelPath);
516 $this->registry->set('extensionDataImport', $importRelFolder, 1);
517 $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionFileImport', array($destinationAbsolutePath, $this));
518 }
519 }
520 }
521
522
523 /**
524 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
525 */
526 protected function getDatabaseConnection() {
527 return $GLOBALS['TYPO3_DB'];
528 }
529 }