TYPO3 CMS  TYPO3_7-6
ExtensionManagementService.php
Go to the documentation of this file.
1 <?php
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 
21 
26 {
30  protected $downloadQueue;
31 
35  protected $dependencyUtility;
36 
40  protected $installUtility;
41 
46 
50  protected $downloadUtility;
51 
56 
60  protected $skipDependencyCheck = false;
61 
65  public function injectDownloadQueue(\TYPO3\CMS\Extensionmanager\Domain\Model\DownloadQueue $downloadQueue)
66  {
67  $this->downloadQueue = $downloadQueue;
68  }
69 
73  public function injectDependencyUtility(\TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility)
74  {
75  $this->dependencyUtility = $dependencyUtility;
76  }
77 
81  public function injectInstallUtility(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility)
82  {
83  $this->installUtility = $installUtility;
84  }
85 
89  public function injectExtensionModelUtility(\TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility $extensionModelUtility)
90  {
91  $this->extensionModelUtility = $extensionModelUtility;
92  }
93 
97  public function injectDownloadUtility(\TYPO3\CMS\Extensionmanager\Utility\DownloadUtility $downloadUtility)
98  {
99  $this->downloadUtility = $downloadUtility;
100  }
101 
106  public function markExtensionForInstallation($extensionKey)
107  {
108  // We have to check for dependencies of the extension first, before marking it for installation
109  // because this extension might have dependencies, which need to be installed first
110  $this->installUtility->reloadAvailableExtensions();
111  $extension = $this->getExtension($extensionKey);
112  $this->dependencyUtility->checkDependencies($extension);
113  $this->downloadQueue->addExtensionToInstallQueue($extension);
114  }
115 
123  public function markExtensionForCopy($extensionKey, $sourceFolder)
124  {
125  $this->downloadQueue->addExtensionToCopyQueue($extensionKey, $sourceFolder);
126  }
127 
134  public function markExtensionForDownload(Extension $extension)
135  {
136  // We have to check for dependencies of the extension first, before marking it for download
137  // because this extension might have dependencies, which need to be downloaded and installed first
138  $this->dependencyUtility->checkDependencies($extension);
139  if (!$this->dependencyUtility->hasDependencyErrors()) {
140  $this->downloadQueue->addExtensionToQueue($extension);
141  }
142  }
143 
148  public function markExtensionForUpdate(Extension $extension)
149  {
150  // We have to check for dependencies of the extension first, before marking it for download
151  // because this extension might have dependencies, which need to be downloaded and installed first
152  $this->dependencyUtility->checkDependencies($extension);
153  $this->downloadQueue->addExtensionToQueue($extension, 'update');
154  }
155 
162  {
163  $this->skipDependencyCheck = $skipDependencyCheck;
164  }
165 
170  {
171  $this->automaticInstallationEnabled = (bool)$automaticInstallationEnabled;
172  }
173 
180  public function installExtension(Extension $extension)
181  {
182  $this->downloadExtension($extension);
183  if (!$this->checkDependencies($extension)) {
184  return false;
185  }
186 
187  $downloadedDependencies = [];
188  $updatedDependencies = [];
189  $installQueue = [];
190 
191  // First resolve all dependencies and the sub-dependencies until all queues are empty as new extensions might be
192  // added each time
193  // Extensions have to be installed in reverse order. Extensions which were added at last are dependencies of
194  // earlier ones and need to be available before
195  while (!$this->downloadQueue->isCopyQueueEmpty()
196  || !$this->downloadQueue->isQueueEmpty('download')
197  || !$this->downloadQueue->isQueueEmpty('update')
198  ) {
199  // First copy all available extension
200  // This might change other queues again
201  $copyQueue = $this->downloadQueue->resetExtensionCopyStorage();
202  if (!empty($copyQueue)) {
203  $this->copyDependencies($copyQueue);
204  }
205  $installQueue = array_merge($this->downloadQueue->resetExtensionInstallStorage(), $installQueue);
206  // Get download and update information
207  $queue = $this->downloadQueue->resetExtensionQueue();
208  if (!empty($queue['download'])) {
209  $downloadedDependencies = array_merge($downloadedDependencies, $this->downloadDependencies($queue['download']));
210  }
211  $installQueue = array_merge($this->downloadQueue->resetExtensionInstallStorage(), $installQueue);
212  if ($this->automaticInstallationEnabled) {
213  if (!empty($queue['update'])) {
214  $this->downloadDependencies($queue['update']);
215  $updatedDependencies = array_merge($updatedDependencies, $this->uninstallDependenciesToBeUpdated($queue['update']));
216  }
217  $installQueue = array_merge($this->downloadQueue->resetExtensionInstallStorage(), $installQueue);
218  }
219  }
220 
221  // If there were any dependency errors we have to abort here
222  if ($this->dependencyUtility->hasDependencyErrors()) {
223  return false;
224  }
225 
226  // Attach extension to install queue
227  $this->downloadQueue->addExtensionToInstallQueue($extension);
228  $installQueue += $this->downloadQueue->resetExtensionInstallStorage();
229  $installedDependencies = [];
230  if ($this->automaticInstallationEnabled) {
231  $installedDependencies = $this->installDependencies($installQueue);
232  }
233 
234  return array_merge($downloadedDependencies, $updatedDependencies, $installedDependencies);
235  }
236 
242  public function getDependencyErrors()
243  {
244  return $this->dependencyUtility->getDependencyErrors();
245  }
246 
252  public function getExtension($extensionKey)
253  {
254  return $this->extensionModelUtility->mapExtensionArrayToModel(
255  $this->installUtility->enrichExtensionWithDetails($extensionKey)
256  );
257  }
258 
265  public function isAvailable($extensionKey)
266  {
267  return $this->installUtility->isAvailable($extensionKey);
268  }
269 
277  public function reloadPackageInformation($extensionKey)
278  {
279  $this->installUtility->reloadPackageInformation($extensionKey);
280  }
281 
287  protected function downloadExtension(Extension $extension)
288  {
289  $this->downloadMainExtension($extension);
290  $this->setInExtensionRepository($extension->getExtensionKey());
291  }
292 
299  protected function checkDependencies(Extension $extension)
300  {
301  $this->dependencyUtility->setSkipDependencyCheck($this->skipDependencyCheck);
302  $this->dependencyUtility->checkDependencies($extension);
303 
304  return !$this->dependencyUtility->hasDependencyErrors();
305  }
306 
314  protected function setInExtensionRepository($extensionKey)
315  {
317  $path = $paths[$this->downloadUtility->getDownloadPath()];
318  $localExtensionStorage = $path . $extensionKey . '/Initialisation/Extensions/';
319  $this->dependencyUtility->setLocalExtensionStorage($localExtensionStorage);
320  }
321 
328  protected function copyDependencies(array $copyQueue)
329  {
330  $installPaths = Extension::returnAllowedInstallPaths();
331  foreach ($copyQueue as $extensionKey => $sourceFolder) {
332  $destination = $installPaths['Local'] . $extensionKey;
333  GeneralUtility::mkdir($destination);
334  GeneralUtility::copyDirectory($sourceFolder . $extensionKey, $destination);
335  $this->markExtensionForInstallation($extensionKey);
336  $this->downloadQueue->removeExtensionFromCopyQueue($extensionKey);
337  }
338  }
339 
347  protected function uninstallDependenciesToBeUpdated(array $updateQueue)
348  {
349  $resolvedDependencies = [];
350  foreach ($updateQueue as $extensionToUpdate) {
351  $this->installUtility->uninstall($extensionToUpdate->getExtensionKey());
352  $resolvedDependencies['updated'][$extensionToUpdate->getExtensionKey()] = $extensionToUpdate;
353  }
354  return $resolvedDependencies;
355  }
356 
363  protected function installDependencies(array $installQueue)
364  {
365  if (!empty($installQueue)) {
366  $this->emitWillInstallExtensionsSignal($installQueue);
367  }
368  $resolvedDependencies = [];
369  foreach ($installQueue as $extensionKey => $_) {
370  $this->installUtility->install($extensionKey);
371  $this->emitHasInstalledExtensionSignal($extensionKey);
372  if (!is_array($resolvedDependencies['installed'])) {
373  $resolvedDependencies['installed'] = [];
374  }
375  $resolvedDependencies['installed'][$extensionKey] = $extensionKey;
376  }
377  return $resolvedDependencies;
378  }
379 
387  protected function downloadDependencies(array $downloadQueue)
388  {
389  $resolvedDependencies = [];
390  foreach ($downloadQueue as $extensionToDownload) {
391  $this->downloadUtility->download($extensionToDownload);
392  $this->downloadQueue->removeExtensionFromQueue($extensionToDownload);
393  $resolvedDependencies['downloaded'][$extensionToDownload->getExtensionKey()] = $extensionToDownload;
394  $this->markExtensionForInstallation($extensionToDownload->getExtensionKey());
395  }
396  return $resolvedDependencies;
397  }
398 
405  public function getAndResolveDependencies(Extension $extension)
406  {
407  $this->dependencyUtility->setSkipDependencyCheck($this->skipDependencyCheck);
408  $this->dependencyUtility->checkDependencies($extension);
409  $installQueue = $this->downloadQueue->getExtensionInstallStorage();
410  if (is_array($installQueue) && !empty($installQueue)) {
411  $installQueue = ['install' => $installQueue];
412  }
413  return array_merge($this->downloadQueue->getExtensionQueue(), $installQueue);
414  }
415 
424  public function downloadMainExtension(Extension $extension)
425  {
426  // The extension object has a uid if the extension is not present in the system
427  // or an update of a present extension is triggered.
428  if ($extension->getUid()) {
429  $this->downloadUtility->download($extension);
430  }
431  }
432 
436  protected function emitWillInstallExtensionsSignal(array $installQueue)
437  {
438  $this->getSignalSlotDispatcher()->dispatch(__CLASS__, 'willInstallExtensions', [$installQueue]);
439  }
440 
444  protected function emitHasInstalledExtensionSignal($extensionKey)
445  {
446  $this->getSignalSlotDispatcher()->dispatch(__CLASS__, 'hasInstalledExtensions', [$extensionKey]);
447  }
448 
454  protected function getSignalSlotDispatcher()
455  {
456  if (!isset($this->signalSlotDispatcher)) {
457  $this->signalSlotDispatcher = GeneralUtility::makeInstance(ObjectManager::class)
458  ->get(Dispatcher::class);
459  }
461  }
462 }
injectDownloadQueue(\TYPO3\CMS\Extensionmanager\Domain\Model\DownloadQueue $downloadQueue)
static copyDirectory($source, $destination)
injectExtensionModelUtility(\TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility $extensionModelUtility)
injectDownloadUtility(\TYPO3\CMS\Extensionmanager\Utility\DownloadUtility $downloadUtility)
injectInstallUtility(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility)
injectDependencyUtility(\TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility)
$signalSlotDispatcher