TYPO3 CMS  TYPO3_8-7
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 
105  public function markExtensionForInstallation($extensionKey)
106  {
107  // We have to check for dependencies of the extension first, before marking it for installation
108  // because this extension might have dependencies, which need to be installed first
109  $this->installUtility->reloadAvailableExtensions();
110  $extension = $this->getExtension($extensionKey);
111  $this->dependencyUtility->checkDependencies($extension);
112  $this->downloadQueue->addExtensionToInstallQueue($extension);
113  }
114 
121  public function markExtensionForCopy($extensionKey, $sourceFolder)
122  {
123  $this->downloadQueue->addExtensionToCopyQueue($extensionKey, $sourceFolder);
124  }
125 
131  public function markExtensionForDownload(Extension $extension)
132  {
133  // We have to check for dependencies of the extension first, before marking it for download
134  // because this extension might have dependencies, which need to be downloaded and installed first
135  $this->dependencyUtility->checkDependencies($extension);
136  if (!$this->dependencyUtility->hasDependencyErrors()) {
137  $this->downloadQueue->addExtensionToQueue($extension);
138  }
139  }
140 
144  public function markExtensionForUpdate(Extension $extension)
145  {
146  // We have to check for dependencies of the extension first, before marking it for download
147  // because this extension might have dependencies, which need to be downloaded and installed first
148  $this->dependencyUtility->checkDependencies($extension);
149  $this->downloadQueue->addExtensionToQueue($extension, 'update');
150  }
151 
158  {
159  $this->skipDependencyCheck = $skipDependencyCheck;
160  }
161 
166  {
167  $this->automaticInstallationEnabled = (bool)$automaticInstallationEnabled;
168  }
169 
176  public function installExtension(Extension $extension)
177  {
178  $this->downloadExtension($extension);
179  if (!$this->checkDependencies($extension)) {
180  return false;
181  }
182 
183  $downloadedDependencies = [];
184  $updatedDependencies = [];
185  $installQueue = [];
186 
187  // First resolve all dependencies and the sub-dependencies until all queues are empty as new extensions might be
188  // added each time
189  // Extensions have to be installed in reverse order. Extensions which were added at last are dependencies of
190  // earlier ones and need to be available before
191  while (!$this->downloadQueue->isCopyQueueEmpty()
192  || !$this->downloadQueue->isQueueEmpty('download')
193  || !$this->downloadQueue->isQueueEmpty('update')
194  ) {
195  // First copy all available extension
196  // This might change other queues again
197  $copyQueue = $this->downloadQueue->resetExtensionCopyStorage();
198  if (!empty($copyQueue)) {
199  $this->copyDependencies($copyQueue);
200  }
201  $installQueue = array_merge($this->downloadQueue->resetExtensionInstallStorage(), $installQueue);
202  // Get download and update information
203  $queue = $this->downloadQueue->resetExtensionQueue();
204  if (!empty($queue['download'])) {
205  $downloadedDependencies = array_merge($downloadedDependencies, $this->downloadDependencies($queue['download']));
206  }
207  $installQueue = array_merge($this->downloadQueue->resetExtensionInstallStorage(), $installQueue);
208  if ($this->automaticInstallationEnabled) {
209  if (!empty($queue['update'])) {
210  $this->downloadDependencies($queue['update']);
211  $updatedDependencies = array_merge($updatedDependencies, $this->uninstallDependenciesToBeUpdated($queue['update']));
212  }
213  $installQueue = array_merge($this->downloadQueue->resetExtensionInstallStorage(), $installQueue);
214  }
215  }
216 
217  // If there were any dependency errors we have to abort here
218  if ($this->dependencyUtility->hasDependencyErrors()) {
219  return false;
220  }
221 
222  // Attach extension to install queue
223  $this->downloadQueue->addExtensionToInstallQueue($extension);
224  $installQueue += $this->downloadQueue->resetExtensionInstallStorage();
225  $installedDependencies = [];
226  if ($this->automaticInstallationEnabled) {
227  $installedDependencies = $this->installDependencies($installQueue);
228  }
229 
230  return array_merge($downloadedDependencies, $updatedDependencies, $installedDependencies);
231  }
232 
238  public function getDependencyErrors()
239  {
240  return $this->dependencyUtility->getDependencyErrors();
241  }
242 
248  public function getExtension($extensionKey)
249  {
250  return $this->extensionModelUtility->mapExtensionArrayToModel(
251  $this->installUtility->enrichExtensionWithDetails($extensionKey)
252  );
253  }
254 
261  public function isAvailable($extensionKey)
262  {
263  return $this->installUtility->isAvailable($extensionKey);
264  }
265 
273  public function reloadPackageInformation($extensionKey)
274  {
275  $this->installUtility->reloadPackageInformation($extensionKey);
276  }
277 
283  protected function downloadExtension(Extension $extension)
284  {
285  $this->downloadMainExtension($extension);
286  $this->setInExtensionRepository($extension->getExtensionKey());
287  }
288 
295  protected function checkDependencies(Extension $extension)
296  {
297  $this->dependencyUtility->setSkipDependencyCheck($this->skipDependencyCheck);
298  $this->dependencyUtility->checkDependencies($extension);
299 
300  return !$this->dependencyUtility->hasDependencyErrors();
301  }
302 
310  protected function setInExtensionRepository($extensionKey)
311  {
313  $path = $paths[$this->downloadUtility->getDownloadPath()];
314  $localExtensionStorage = $path . $extensionKey . '/Initialisation/Extensions/';
315  $this->dependencyUtility->setLocalExtensionStorage($localExtensionStorage);
316  }
317 
323  protected function copyDependencies(array $copyQueue)
324  {
325  $installPaths = Extension::returnAllowedInstallPaths();
326  foreach ($copyQueue as $extensionKey => $sourceFolder) {
327  $destination = $installPaths['Local'] . $extensionKey;
328  GeneralUtility::mkdir($destination);
329  GeneralUtility::copyDirectory($sourceFolder . $extensionKey, $destination);
330  $this->markExtensionForInstallation($extensionKey);
331  $this->downloadQueue->removeExtensionFromCopyQueue($extensionKey);
332  }
333  }
334 
342  protected function uninstallDependenciesToBeUpdated(array $updateQueue)
343  {
344  $resolvedDependencies = [];
345  foreach ($updateQueue as $extensionToUpdate) {
346  $this->installUtility->uninstall($extensionToUpdate->getExtensionKey());
347  $resolvedDependencies['updated'][$extensionToUpdate->getExtensionKey()] = $extensionToUpdate;
348  }
349  return $resolvedDependencies;
350  }
351 
358  protected function installDependencies(array $installQueue)
359  {
360  if (empty($installQueue)) {
361  return [];
362  }
363  $this->emitWillInstallExtensionsSignal($installQueue);
364  $resolvedDependencies = [];
365  $this->installUtility->install(...array_keys($installQueue));
366  foreach ($installQueue as $extensionKey => $_) {
367  $this->emitHasInstalledExtensionSignal($extensionKey);
368  if (!is_array($resolvedDependencies['installed'])) {
369  $resolvedDependencies['installed'] = [];
370  }
371  $resolvedDependencies['installed'][$extensionKey] = $extensionKey;
372  }
373  return $resolvedDependencies;
374  }
375 
383  protected function downloadDependencies(array $downloadQueue)
384  {
385  $resolvedDependencies = [];
386  foreach ($downloadQueue as $extensionToDownload) {
387  $this->downloadUtility->download($extensionToDownload);
388  $this->downloadQueue->removeExtensionFromQueue($extensionToDownload);
389  $resolvedDependencies['downloaded'][$extensionToDownload->getExtensionKey()] = $extensionToDownload;
390  $this->markExtensionForInstallation($extensionToDownload->getExtensionKey());
391  }
392  return $resolvedDependencies;
393  }
394 
401  public function getAndResolveDependencies(Extension $extension)
402  {
403  $this->dependencyUtility->setSkipDependencyCheck($this->skipDependencyCheck);
404  $this->dependencyUtility->checkDependencies($extension);
405  $installQueue = $this->downloadQueue->getExtensionInstallStorage();
406  if (is_array($installQueue) && !empty($installQueue)) {
407  $installQueue = ['install' => $installQueue];
408  }
409  return array_merge($this->downloadQueue->getExtensionQueue(), $installQueue);
410  }
411 
419  public function downloadMainExtension(Extension $extension)
420  {
421  // The extension object has a uid if the extension is not present in the system
422  // or an update of a present extension is triggered.
423  if ($extension->getUid()) {
424  $this->downloadUtility->download($extension);
425  }
426  }
427 
431  protected function emitWillInstallExtensionsSignal(array $installQueue)
432  {
433  $this->getSignalSlotDispatcher()->dispatch(__CLASS__, 'willInstallExtensions', [$installQueue]);
434  }
435 
439  protected function emitHasInstalledExtensionSignal($extensionKey)
440  {
441  $this->getSignalSlotDispatcher()->dispatch(__CLASS__, 'hasInstalledExtensions', [$extensionKey]);
442  }
443 
449  protected function getSignalSlotDispatcher()
450  {
451  if (!isset($this->signalSlotDispatcher)) {
452  $this->signalSlotDispatcher = GeneralUtility::makeInstance(ObjectManager::class)
453  ->get(Dispatcher::class);
454  }
456  }
457 }
injectDownloadQueue(\TYPO3\CMS\Extensionmanager\Domain\Model\DownloadQueue $downloadQueue)
static copyDirectory($source, $destination)
static makeInstance($className,... $constructorArguments)
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