7fff9d9e7a68f5b62992c031be5ab72848ce7975
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Classes / Service / ExtensionManagementService.php
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Service;
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 use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
30
31 /**
32 * Service class for managing multiple step processes (dependencies for example)
33 *
34 * @author Susanne Moog <susanne.moog@typo3.org>
35 */
36 class ExtensionManagementService implements \TYPO3\CMS\Core\SingletonInterface {
37
38 /**
39 * @var \TYPO3\CMS\Extensionmanager\Domain\Model\DownloadQueue
40 * @inject
41 */
42 protected $downloadQueue;
43
44 /**
45 * @var \TYPO3\CMS\Extensionmanager\Utility\DependencyUtility
46 * @inject
47 */
48 protected $dependencyUtility;
49
50 /**
51 * @var \TYPO3\CMS\Extensionmanager\Utility\InstallUtility
52 * @inject
53 */
54 protected $installUtility;
55
56 /**
57 * @var \TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility
58 * @inject
59 */
60 protected $extensionModelUtility;
61
62 /**
63 * @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
64 * @inject
65 */
66 protected $listUtility;
67
68 /**
69 * @var \TYPO3\CMS\Extensionmanager\Utility\DownloadUtility
70 * @inject
71 */
72 protected $downloadUtility;
73
74 /**
75 * @var bool
76 */
77 protected $skipSystemDependencyCheck = FALSE;
78
79 /**
80 * @param string $extensionKey
81 * @return void
82 */
83 public function markExtensionForInstallation($extensionKey) {
84 // We have to check for dependencies of the extension first, before marking it for installation
85 // because this extension might have dependencies, which need to be installed first
86 $this->dependencyUtility->checkDependencies(
87 $this->extensionModelUtility->mapExtensionArrayToModel(
88 $this->installUtility->enrichExtensionWithDetails($extensionKey)
89 )
90 );
91 $this->downloadQueue->addExtensionToInstallQueue($extensionKey);
92 }
93
94 /**
95 * Mark an extension for copy
96 *
97 * @param string $extensionKey
98 * @param string $sourceFolder
99 * @return void
100 */
101 public function markExtensionForCopy($extensionKey, $sourceFolder) {
102 $this->downloadQueue->addExtensionToCopyQueue($extensionKey, $sourceFolder);
103 }
104
105 /**
106 * Mark an extension for download
107 *
108 * @param Extension $extension
109 * @return void
110 */
111 public function markExtensionForDownload(Extension $extension) {
112 // We have to check for dependencies of the extension first, before marking it for download
113 // because this extension might have dependencies, which need to be downloaded and installed first
114 $this->dependencyUtility->checkDependencies($extension);
115 $this->downloadQueue->addExtensionToQueue($extension);
116 }
117
118 /**
119 * @param Extension $extension
120 * @return void
121 */
122 public function markExtensionForUpdate(Extension $extension) {
123 // We have to check for dependencies of the extension first, before marking it for download
124 // because this extension might have dependencies, which need to be downloaded and installed first
125 $this->dependencyUtility->checkDependencies($extension);
126 $this->downloadQueue->addExtensionToQueue($extension, 'update');
127 }
128
129 /**
130 * Enables or disables the dependency check for system environment (PHP, TYPO3) before extension installation
131 *
132 * @param bool $skipSystemDependencyCheck
133 */
134 public function setSkipSystemDependencyCheck($skipSystemDependencyCheck) {
135 $this->skipSystemDependencyCheck = $skipSystemDependencyCheck;
136 }
137
138 /**
139 * Install the extension
140 *
141 * @param Extension $extension
142 * @return bool|array Returns FALSE if dependencies cannot be resolved, otherwise array with installation information
143 */
144 public function installExtension(Extension $extension) {
145 $this->downloadExtension($extension);
146
147 if (!$this->checkDependencies($extension)) {
148 return FALSE;
149 }
150
151 $updatedDependencies = array();
152 $installedDependencies = array();
153 $queue = $this->downloadQueue->getExtensionQueue();
154 $copyQueue = $this->downloadQueue->getExtensionCopyStorage();
155
156 if (count($copyQueue) > 0) {
157 $this->copyDependencies($copyQueue);
158 }
159 $downloadedDependencies = array();
160 if (array_key_exists('download', $queue)) {
161 $downloadedDependencies = $this->downloadDependencies($queue['download']);
162 }
163 if (array_key_exists('update', $queue)) {
164 $this->downloadDependencies($queue['update']);
165 $updatedDependencies = $this->uninstallDependenciesToBeUpdated($queue['update']);
166 }
167 // add extension at the end of the download queue
168 $this->downloadQueue->addExtensionToInstallQueue($extension->getExtensionKey());
169 $installQueue = $this->downloadQueue->getExtensionInstallStorage();
170 if (count($installQueue) > 0) {
171 $installedDependencies = $this->installDependencies($installQueue);
172 }
173 return array_merge($downloadedDependencies, $updatedDependencies, $installedDependencies);
174 }
175
176 /**
177 * Returns the unresolved dependency errors
178 *
179 * @return array
180 */
181 public function getDependencyErrors() {
182 return $this->dependencyUtility->getDependencyErrors();
183 }
184
185 /**
186 * Download an extension
187 *
188 * @param Extension $extension
189 */
190 protected function downloadExtension(Extension $extension) {
191 $this->downloadMainExtension($extension);
192 $this->setInExtensionRepository($extension->getExtensionKey());
193 }
194
195 /**
196 * Check dependencies for an extension and its required extensions
197 *
198 * @param Extension $extension
199 * @return bool Returns TRUE if all dependencies can be resolved, otherwise FALSE
200 */
201 protected function checkDependencies(Extension $extension) {
202 $this->dependencyUtility->setSkipSystemDependencyCheck($this->skipSystemDependencyCheck);
203 $this->dependencyUtility->checkDependencies($extension);
204
205 return !$this->dependencyUtility->hasDependencyErrors();
206 }
207
208 /**
209 * Sets the path to the repository in an extension
210 * (Initialisation/Extensions) depending on the extension
211 * that is currently installed
212 *
213 * @param string $extensionKey
214 */
215 protected function setInExtensionRepository($extensionKey) {
216 $paths = Extension::returnInstallPaths();
217 $path = $paths[$this->downloadUtility->getDownloadPath()];
218 $localExtensionStorage = $path . $extensionKey . '/Initialisation/Extensions/';
219 $this->dependencyUtility->setLocalExtensionStorage($localExtensionStorage);
220 }
221
222 /**
223 * Copies locally provided extensions to typo3conf/ext
224 *
225 * @param array $copyQueue
226 * @return void
227 */
228 protected function copyDependencies(array $copyQueue) {
229 $installPaths = Extension::returnAllowedInstallPaths();
230 foreach ($copyQueue as $extensionKey => $sourceFolder) {
231 $destination = $installPaths['Local'] . $extensionKey;
232 \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir($destination);
233 \TYPO3\CMS\Core\Utility\GeneralUtility::copyDirectory($sourceFolder . $extensionKey, $destination);
234 $this->markExtensionForInstallation($extensionKey);
235 $this->downloadQueue->removeExtensionFromCopyQueue($extensionKey);
236 }
237 }
238
239 /**
240 * Uninstall extensions that will be updated
241 * This is not strictly necessary but cleaner all in all
242 *
243 * @param Extension[] $updateQueue
244 * @return array
245 */
246 protected function uninstallDependenciesToBeUpdated(array $updateQueue) {
247 $resolvedDependencies = array();
248 foreach ($updateQueue as $extensionToUpdate) {
249 $this->installUtility->uninstall($extensionToUpdate->getExtensionKey());
250 $resolvedDependencies['updated'][$extensionToUpdate->getExtensionKey()] = $extensionToUpdate;
251 }
252 return $resolvedDependencies;
253 }
254
255 /**
256 * Install dependent extensions
257 *
258 * @param array $installQueue
259 * @return array
260 */
261 protected function installDependencies(array $installQueue) {
262 if (!empty($installQueue)) {
263 $this->emitWillInstallExtensionsSignal($installQueue);
264 }
265 $resolvedDependencies = array();
266 foreach ($installQueue as $extensionKey => $extensionDetails) {
267 $this->installUtility->install($extensionDetails);
268 $this->emitHasInstalledExtensionSignal($extensionDetails);
269 if (!is_array($resolvedDependencies['installed'])) {
270 $resolvedDependencies['installed'] = array();
271 }
272 $resolvedDependencies['installed'][$extensionKey] = $extensionDetails;
273 }
274 return $resolvedDependencies;
275 }
276
277 /**
278 * Download dependencies
279 * expects an array of extension objects to download
280 *
281 * @param Extension[] $downloadQueue
282 * @return array
283 */
284 protected function downloadDependencies(array $downloadQueue) {
285 $resolvedDependencies = array();
286 foreach ($downloadQueue as $extensionToDownload) {
287 $this->downloadUtility->download($extensionToDownload);
288 $this->downloadQueue->removeExtensionFromQueue($extensionToDownload);
289 $resolvedDependencies['downloaded'][$extensionToDownload->getExtensionKey()] = $extensionToDownload;
290 $this->markExtensionForInstallation($extensionToDownload->getExtensionKey());
291 }
292 return $resolvedDependencies;
293 }
294
295 /**
296 * Get and resolve dependencies
297 *
298 * @param Extension $extension
299 * @return array
300 */
301 public function getAndResolveDependencies(Extension $extension) {
302 $this->dependencyUtility->setSkipSystemDependencyCheck($this->skipSystemDependencyCheck);
303 $this->dependencyUtility->checkDependencies($extension);
304 $installQueue = $this->downloadQueue->getExtensionInstallStorage();
305 if (is_array($installQueue) && count($installQueue) > 0) {
306 $installQueue = array('install' => $installQueue);
307 }
308 return array_merge($this->downloadQueue->getExtensionQueue(), $installQueue);
309 }
310
311 /**
312 * Downloads the extension the user wants to install
313 * This is separated from downloading the dependencies
314 * as an extension is able to provide it's own dependencies
315 *
316 * @param Extension $extension
317 * @return void
318 */
319 public function downloadMainExtension(Extension $extension) {
320 // The extension object has a uid if the extension is not present in the system
321 // or an update of a present extension is triggered.
322 if ($extension->getUid()) {
323 $this->downloadUtility->download($extension);
324 }
325 }
326
327 /**
328 * @param array $installQueue
329 */
330 protected function emitWillInstallExtensionsSignal(array $installQueue) {
331 $this->getSignalSlotDispatcher()->dispatch(__CLASS__, 'willInstallExtensions', array($installQueue));
332 }
333
334 /**
335 * @param string $extensionKey
336 */
337 protected function emitHasInstalledExtensionSignal($extensionKey) {
338 $this->getSignalSlotDispatcher()->dispatch(__CLASS__, 'hasInstalledExtensions', array($extensionKey));
339 }
340
341 /**
342 * Get the SignalSlot dispatcher
343 *
344 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
345 */
346 protected function getSignalSlotDispatcher() {
347 if (!isset($this->signalSlotDispatcher)) {
348 $this->signalSlotDispatcher = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager')
349 ->get('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
350 }
351 return $this->signalSlotDispatcher;
352 }
353
354 }