[TASK] Migrate extension install upgrade wizards to new API
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Updates / AbstractDownloadExtensionUpdate.php
1 <?php
2
3 namespace TYPO3\CMS\Install\Updates;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Symfony\Component\Console\Output\OutputInterface;
19 use TYPO3\CMS\Core\Core\Environment;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Extbase\Object\ObjectManager;
22 use TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility;
23 use TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility;
24 use TYPO3\CMS\Extensionmanager\Utility\InstallUtility;
25 use TYPO3\CMS\Extensionmanager\Utility\ListUtility;
26
27 /**
28 * Download extension from TER
29 */
30 abstract class AbstractDownloadExtensionUpdate implements UpgradeWizardInterface, ConfirmableInterface, ChattyInterface
31 {
32 /**
33 * @var string
34 */
35 protected $repositoryUrl = 'https://typo3.org/fileadmin/ter/@filename';
36
37 /**
38 * @var OutputInterface
39 */
40 protected $output;
41
42 /**
43 * @var \TYPO3\CMS\Install\Updates\ExtensionModel
44 */
45 protected $extension;
46
47 public function setOutput(OutputInterface $output): void
48 {
49 $this->output = $output;
50 }
51
52 /**
53 * Execute the update
54 * Called when a wizard reports that an update is necessary
55 *
56 * @return bool
57 */
58 public function executeUpdate(): bool
59 {
60 return $this->installExtension($this->extension);
61 }
62
63 /**
64 * This method can be called to install an extension following all proper processes
65 * (e.g. installing in extList, respecting priority, etc.)
66 *
67 * @param \TYPO3\CMS\Install\Updates\ExtensionModel $extension
68 * @return bool whether the installation worked or not
69 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
70 */
71 protected function installExtension(ExtensionModel $extension): bool
72 {
73 $updateSuccessful = true;
74 /** @var ObjectManager $objectManager */
75 $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
76
77 /** @var ListUtility $extensionListUtility */
78 $extensionListUtility = $objectManager->get(ListUtility::class);
79 $availableExtensions = $extensionListUtility->getAvailableExtensions();
80
81 $extensionKey = $extension->getKey();
82 $isExtensionAvailable = !empty($availableExtensions[$extensionKey]);
83 $isComposerMode = Environment::isComposerMode();
84
85 if (!$isComposerMode && !$isExtensionAvailable) {
86 /** @var TerUtility $extensionTerUtility */
87 $extensionTerUtility = $objectManager->get(TerUtility::class);
88 $t3xContent = $this->fetchExtension($extensionKey, $extension->getVersionString());
89 if (empty($t3xContent)) {
90 $updateSuccessful = false;
91 $this->output->writeln('<error>The extension ' . $extensionKey . ' could not be downloaded.</error>');
92 }
93 $t3xExtracted = $extensionTerUtility->decodeExchangeData($t3xContent);
94 if (empty($t3xExtracted) || !is_array($t3xExtracted) || empty($t3xExtracted['extKey'])) {
95 $updateSuccessful = false;
96 $this->output->writeln('<error>The extension ' . $extensionKey . ' could not be extracted.</error>');
97 }
98
99 /** @var FileHandlingUtility $extensionFileHandlingUtility */
100 $extensionFileHandlingUtility = $objectManager->get(FileHandlingUtility::class);
101 $extensionFileHandlingUtility->unpackExtensionFromExtensionDataArray($t3xExtracted);
102
103 // The listUtility now needs to have the regenerated list of packages
104 $extensionListUtility->reloadAvailableExtensions();
105 }
106
107 if ($isComposerMode && !$isExtensionAvailable) {
108 $updateSuccessful = false;
109 $this->output->writeln('<warning>The extension ' .
110 $extensionKey .
111 ' can not be downloaded since ' .
112 'Composer is used for package management. Please require this ' .
113 'extension as package via Composer: ' .
114 '"composer require ' .
115 $extension->getComposerName() .
116 ':^' .
117 $extension->getVersionString() .
118 '"</warning>');
119 }
120
121 if ($updateSuccessful) {
122 /** @var InstallUtility $extensionInstallUtility */
123 $extensionInstallUtility = $objectManager->get(InstallUtility::class);
124 $extensionInstallUtility->install($extensionKey);
125 }
126
127 return $updateSuccessful;
128 }
129
130 /**
131 * Fetch extension from repository
132 *
133 * @param string $extensionKey The extension key to fetch
134 * @param string $version The version to fetch
135 * @throws \InvalidArgumentException
136 * @return string T3X file content
137 */
138 protected function fetchExtension($extensionKey, $version): string
139 {
140 if (empty($extensionKey) || empty($version)) {
141 throw new \InvalidArgumentException(
142 'No extension key for fetching an extension was given.',
143 1344687432
144 );
145 }
146
147 $filename = $extensionKey[0] . '/' . $extensionKey[1] . '/' . $extensionKey . '_' . $version . '.t3x';
148 $url = str_replace('@filename', $filename, $this->repositoryUrl);
149
150 return $this->fetchUrl($url);
151 }
152
153 /**
154 * Open an URL and return the response
155 * This wrapper method is required to try several download methods if
156 * the configuration is not valid or initially written by the installer.
157 *
158 * @param string $url The URL to file
159 * @throws \Exception
160 * @throws \InvalidArgumentException
161 * @return string File content
162 */
163 protected function fetchUrl($url): string
164 {
165 if (empty($url)) {
166 throw new \InvalidArgumentException(
167 'No URL for downloading an extension given.',
168 1344687436
169 );
170 }
171
172 $fileContent = GeneralUtility::getUrl($url);
173
174 // Can not fetch url, throw an exception
175 if ($fileContent === false) {
176 throw new \RuntimeException(
177 'Can not fetch URL "' . $url . '". Possible reasons are network problems or misconfiguration.',
178 1344685036
179 );
180 }
181
182 return $fileContent;
183 }
184 }