[BUGFIX] Uninstall extension with dependency throws Exception
[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 textfile 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 * Extension Manager Install Utility
31 *
32 * @author Susanne Moog <susanne.moog@typo3.org>
33 */
34 class InstallUtility implements \TYPO3\CMS\Core\SingletonInterface {
35
36 /**
37 * @var \TYPO3\CMS\Extbase\Object\ObjectManager
38 */
39 public $objectManager;
40
41 /**
42 * @var \TYPO3\CMS\Install\Service\SqlSchemaMigrationService
43 */
44 public $installToolSqlParser;
45
46 /**
47 * @var \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility
48 */
49 protected $dependencyUtility;
50
51 /**
52 * @var \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
53 */
54 protected $fileHandlingUtility;
55
56 /**
57 * @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
58 */
59 protected $listUtility;
60
61 /**
62 * @var \TYPO3\CMS\Extensionmanager\Utility\DatabaseUtility
63 */
64 protected $databaseUtility;
65
66 /**
67 * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
68 */
69 public $extensionRepository;
70
71 /**
72 * @param \TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility
73 * @return void
74 */
75 public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility) {
76 $this->listUtility = $listUtility;
77 }
78
79 /**
80 * @param \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $filehandlingUtility
81 * @return void
82 */
83 public function injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility) {
84 $this->fileHandlingUtility = $fileHandlingUtility;
85 }
86
87 /**
88 * @param \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility
89 * @return void
90 */
91 public function injectDependencyUtility(\TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility) {
92 $this->dependencyUtility = $dependencyUtility;
93 }
94
95 /**
96 * @param \TYPO3\CMS\Extensionmanager\Utility\DatabaseUtility $databaseUtility
97 * @return void
98 */
99 public function injectDatabaseUtility(\TYPO3\CMS\Extensionmanager\Utility\DatabaseUtility $databaseUtility) {
100 $this->databaseUtility = $databaseUtility;
101 }
102
103 /**
104 * Inject emConfUtility
105 *
106 * @param \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository
107 * @return void
108 */
109 public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository) {
110 $this->extensionRepository = $extensionRepository;
111 }
112
113 /**
114 * __construct
115 */
116 public function __construct() {
117 $this->objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
118 /** @var $installToolSqlParser \TYPO3\CMS\Install\Service\SqlSchemaMigrationService */
119 $this->installToolSqlParser = $this->objectManager->get('TYPO3\\CMS\\Install\\Service\\SqlSchemaMigrationService');
120 $this->dependencyUtility = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Utility\\DependencyUtility');
121 }
122
123 /**
124 * Helper function to install an extension
125 * also processes db updates and clears the cache if the extension asks for it
126 *
127 * @param string $extensionKey
128 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
129 * @return void
130 */
131 public function install($extensionKey) {
132 $extension = $this->enrichExtensionWithDetails($extensionKey);
133 $this->processDatabaseUpdates($extension);
134 $this->ensureConfiguredDirectoriesExist($extension);
135 if ($extension['clearcacheonload']) {
136 $GLOBALS['typo3CacheManager']->flushCaches();
137 }
138 if (!$this->isLoaded($extensionKey)) {
139 $this->loadExtension($extensionKey);
140 }
141 $this->reloadCaches();
142 $this->processCachingFrameworkUpdates();
143 $this->saveDefaultConfiguration($extension['key']);
144 }
145
146 /**
147 * Helper function to uninstall an extension
148 *
149 * @param string $extensionKey
150 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
151 * @return void
152 */
153 public function uninstall($extensionKey) {
154 $dependentExtensions = $this->dependencyUtility->findInstalledExtensionsThatDependOnMe($extensionKey);
155 if (is_array($dependentExtensions) && count($dependentExtensions) > 0) {
156 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(
157 \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate(
158 'extensionList.uninstall.dependencyError',
159 'extensionmanager',
160 array($extensionKey, implode(',', $dependentExtensions))
161 ),
162 1342554622
163 );
164 } else {
165 $this->unloadExtension($extensionKey);
166 }
167 }
168
169 /**
170 * Wrapper function to check for loaded extensions
171 *
172 * @param string $extensionKey
173 * @return boolean TRUE if extension is loaded
174 */
175 public function isLoaded($extensionKey) {
176 return \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded($extensionKey);
177 }
178
179 /**
180 * Wrapper function for loading extensions
181 *
182 * @param string $extensionKey
183 * @return void
184 */
185 protected function loadExtension($extensionKey) {
186 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::loadExtension($extensionKey);
187 }
188
189 /**
190 * Wrapper function for unloading extensions
191 *
192 * @param string $extensionKey
193 * @return void
194 */
195 protected function unloadExtension($extensionKey) {
196 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::unloadExtension($extensionKey);
197 }
198
199 /**
200 * Checks if an extension is available in the system
201 *
202 * @param $extensionKey
203 * @return boolean
204 */
205 public function isAvailable($extensionKey) {
206 $availableExtensions = $this->listUtility->getAvailableExtensions();
207 return array_key_exists($extensionKey, $availableExtensions);
208 }
209
210 /**
211 * Fetch additional information for an extension key
212 *
213 * @param string $extensionKey
214 * @access private
215 * @return array
216 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
217 */
218 public function enrichExtensionWithDetails($extensionKey) {
219 $availableExtensions = $this->listUtility->getAvailableExtensions();
220 if (isset($availableExtensions[$extensionKey])) {
221 $extension = $availableExtensions[$extensionKey];
222 } else {
223 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Extension ' . $extensionKey . ' is not available', 1342864081);
224 }
225 $availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfAndTerInformation(array($extensionKey => $extension));
226 return $availableAndInstalledExtensions[$extensionKey];
227 }
228
229 /**
230 * Creates directories as requested in ext_emconf.php
231 *
232 * @param array $extension
233 */
234 protected function ensureConfiguredDirectoriesExist(array $extension) {
235 $this->fileHandlingUtility->ensureConfiguredDirectoriesExist($extension);
236 }
237
238 /**
239 * Gets the content of the ext_tables.sql and ext_tables_static+adt.sql files
240 * Additionally adds the table definitions for the cache tables
241 *
242 * @param array $extension
243 */
244 public function processDatabaseUpdates(array $extension) {
245 $extTablesSqlFile = PATH_site . $extension['siteRelPath'] . '/ext_tables.sql';
246 $extTablesSqlContent = '';
247 if (file_exists($extTablesSqlFile)) {
248 $extTablesSqlContent .= \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl($extTablesSqlFile);
249 }
250 if ($extTablesSqlContent !== '') {
251 $this->updateDbWithExtTablesSql($extTablesSqlContent);
252 }
253 $extTablesStaticSqlFile = PATH_site . $extension['siteRelPath'] . '/ext_tables_static+adt.sql';
254 if (file_exists($extTablesStaticSqlFile)) {
255 $extTablesStaticSqlContent = \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl($extTablesStaticSqlFile);
256 $this->importStaticSql($extTablesStaticSqlContent);
257 }
258 }
259
260 /**
261 * Gets all registered caches and creates required caching framework tables.
262 *
263 * @return void
264 */
265 protected function processCachingFrameworkUpdates() {
266 $extTablesSqlContent = '';
267
268 // @TODO: This should probably moved to TYPO3\CMS\Core\Cache\Cache->getDatabaseTableDefinitions ?!
269 $GLOBALS['typo3CacheManager']->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
270 $extTablesSqlContent .= \TYPO3\CMS\Core\Cache\Cache::getDatabaseTableDefinitions();
271
272 if ($extTablesSqlContent !== '') {
273 $this->updateDbWithExtTablesSql($extTablesSqlContent);
274 }
275 }
276
277 /**
278 * Reload Cache files and Typo3LoadedExtensions
279 *
280 * @return void
281 */
282 public function reloadCaches() {
283 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::removeCacheFiles();
284 // Set new extlist / extlistArray for extension load changes at runtime
285 /** @var $configurationManager \TYPO3\CMS\Core\Configuration\ConfigurationManager */
286 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
287 $localConfiguration = $configurationManager->getLocalConfiguration();
288 $GLOBALS['TYPO3_CONF_VARS']['EXT']['extListArray'] = $localConfiguration['EXT']['extListArray'];
289 $GLOBALS['TYPO3_CONF_VARS']['EXT']['extList'] = implode(',', $GLOBALS['TYPO3_CONF_VARS']['EXT']['extListArray']);
290 \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->reloadTypo3LoadedExtAndClassLoaderAndExtLocalconf();
291 }
292
293 /**
294 * Save default configuration of an extension
295 *
296 * @param string $extensionKey
297 * @return void
298 */
299 protected function saveDefaultConfiguration($extensionKey) {
300 /** @var $configUtility \TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility */
301 $configUtility = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Utility\\ConfigurationUtility');
302 $configUtility->saveDefaultConfiguration($extensionKey);
303 }
304
305 /**
306 * Update database / process db updates from ext_tables
307 *
308 * @param string $rawDefinitions The raw SQL statements from ext_tables.sql
309 * @return void
310 */
311 public function updateDbWithExtTablesSql($rawDefinitions) {
312 $fieldDefinitionsFromFile = $this->installToolSqlParser->getFieldDefinitions_fileContent($rawDefinitions);
313 if (count($fieldDefinitionsFromFile)) {
314 $fieldDefinitionsFromCurrentDatabase = $this->installToolSqlParser->getFieldDefinitions_database();
315 $diff = $this->installToolSqlParser->getDatabaseExtra($fieldDefinitionsFromFile, $fieldDefinitionsFromCurrentDatabase);
316 $updateStatements = $this->installToolSqlParser->getUpdateSuggestions($diff);
317 foreach ((array) $updateStatements['add'] as $string) {
318 $GLOBALS['TYPO3_DB']->admin_query($string);
319 }
320 foreach ((array) $updateStatements['change'] as $string) {
321 $GLOBALS['TYPO3_DB']->admin_query($string);
322 }
323 foreach ((array) $updateStatements['create_table'] as $string) {
324 $GLOBALS['TYPO3_DB']->admin_query($string);
325 }
326 }
327 }
328
329 /**
330 * Import static SQL data (normally used for ext_tables_static+adt.sql)
331 *
332 * @param string $rawDefinitions
333 * @return void
334 */
335 public function importStaticSql($rawDefinitions) {
336 $statements = $this->installToolSqlParser->getStatementarray($rawDefinitions, 1);
337 list($statementsPerTable, $insertCount) = $this->installToolSqlParser->getCreateTables($statements, 1);
338 // Traverse the tables
339 foreach ($statementsPerTable as $table => $query) {
340 $GLOBALS['TYPO3_DB']->admin_query('DROP TABLE IF EXISTS ' . $table);
341 $GLOBALS['TYPO3_DB']->admin_query($query);
342 if ($insertCount[$table]) {
343 $insertStatements = $this->installToolSqlParser->getTableInsertStatements($statements, $table);
344 foreach ($insertStatements as $statement) {
345 $GLOBALS['TYPO3_DB']->admin_query($statement);
346 }
347 }
348 }
349 }
350
351 /**
352 * Remove an extension (delete the directory)
353 *
354 * @param string $extension
355 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
356 * @return void
357 */
358 public function removeExtension($extension) {
359 $absolutePath = $this->fileHandlingUtility->getAbsoluteExtensionPath($extension);
360 if ($this->fileHandlingUtility->isValidExtensionPath($absolutePath)) {
361 $this->fileHandlingUtility->removeDirectory($absolutePath);
362 } else {
363 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('No valid extension path given.', 1342875724);
364 }
365 }
366
367 /**
368 * Get the data dump for an extension
369 *
370 * @param string $extension
371 * @return array
372 */
373 public function getExtensionSqlDataDump($extension) {
374 $extension = $this->enrichExtensionWithDetails($extension);
375 $filePrefix = PATH_site . $extension['siteRelPath'];
376 $sqlData['extTables'] = $this->getSqlDataDumpForFile($filePrefix . '/ext_tables.sql');
377 $sqlData['staticSql'] = $this->getSqlDataDumpForFile($filePrefix . '/ext_tables_static+adt.sql');
378 return $sqlData;
379 }
380
381 /**
382 * Gets the sql data dump for a specific sql file (for example ext_tables.sql)
383 *
384 * @param string $sqlFile
385 * @return string
386 */
387 protected function getSqlDataDumpForFile($sqlFile) {
388 $sqlData = '';
389 if (file_exists($sqlFile)) {
390 $sqlContent = \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl($sqlFile);
391 $fieldDefinitions = $this->installToolSqlParser->getFieldDefinitions_fileContent($sqlContent);
392 $sqlData = $this->databaseUtility->dumpStaticTables($fieldDefinitions);
393 }
394 return $sqlData;
395 }
396
397 /**
398 * Checks if an update for an extension is available
399 *
400 * @internal
401 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extensionData
402 * @return boolean
403 */
404 public function isUpdateAvailable(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extensionData) {
405 // Only check for update for TER extensions
406 $version = $extensionData->getIntegerVersion();
407 /** @var $highestTerVersionExtension \TYPO3\CMS\Extensionmanager\Domain\Model\Extension */
408 $highestTerVersionExtension = $this->extensionRepository->findHighestAvailableVersion($extensionData->getExtensionKey());
409 if ($highestTerVersionExtension instanceof \TYPO3\CMS\Extensionmanager\Domain\Model\Extension) {
410 $highestVersion = $highestTerVersionExtension->getIntegerVersion();
411 if ($highestVersion > $version) {
412 return TRUE;
413 }
414 }
415 return FALSE;
416 }
417
418 }
419
420
421 ?>