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