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