[TASK] Enable EM offline mode when using composer
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Classes / Controller / UploadExtensionFileController.php
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Controller;
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
17 use TYPO3\CMS\Core\Core\Bootstrap;
18 use TYPO3\CMS\Core\Messaging\FlashMessage;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20 use TYPO3\CMS\Extensionmanager\Exception\DependencyConfigurationNotFoundException;
21 use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
22
23 /**
24 * Controller for handling upload of a local extension file
25 * Handles .t3x or .zip files
26 */
27 class UploadExtensionFileController extends AbstractController
28 {
29 /**
30 * @var \TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility
31 */
32 protected $configurationUtility;
33
34 /**
35 * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
36 */
37 protected $extensionRepository;
38
39 /**
40 * @var \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
41 */
42 protected $fileHandlingUtility;
43
44 /**
45 * @var \TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility
46 */
47 protected $terUtility;
48
49 /**
50 * @var \TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService
51 */
52 protected $managementService;
53
54 /**
55 * @var string
56 */
57 protected $extensionBackupPath = '';
58
59 /**
60 * @var bool
61 */
62 protected $removeFromOriginalPath = false;
63
64 /**
65 * @param \TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility $configurationUtility
66 */
67 public function injectConfigurationUtility(\TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility $configurationUtility)
68 {
69 $this->configurationUtility = $configurationUtility;
70 }
71
72 /**
73 * @param \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository
74 */
75 public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
76 {
77 $this->extensionRepository = $extensionRepository;
78 }
79
80 /**
81 * @param \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility
82 */
83 public function injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility)
84 {
85 $this->fileHandlingUtility = $fileHandlingUtility;
86 }
87
88 /**
89 * @param \TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility $terUtility
90 */
91 public function injectTerUtility(\TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility $terUtility)
92 {
93 $this->terUtility = $terUtility;
94 }
95
96 /**
97 * @param \TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService
98 */
99 public function injectManagementService(\TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService)
100 {
101 $this->managementService = $managementService;
102 }
103
104 /**
105 * Remove backup folder before destruction
106 */
107 public function __destruct()
108 {
109 $this->removeBackupFolder();
110 }
111
112 /**
113 * Render upload extension form
114 *
115 * @return void
116 */
117 public function formAction()
118 {
119 if (Bootstrap::usesComposerClassLoading()) {
120 throw new ExtensionManagerException(
121 'Composer mode is active. You are not allowed to upload any extension file.',
122 1444725828821
123 );
124 }
125 }
126
127 /**
128 * Extract an uploaded file and install the matching extension
129 *
130 * @param bool $overwrite Overwrite existing extension if TRUE
131 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
132 * @return void
133 */
134 public function extractAction($overwrite = false)
135 {
136 if (Bootstrap::usesComposerClassLoading()) {
137 throw new ExtensionManagerException(
138 'Composer mode is active. You are not allowed to upload any extension file.',
139 1444725853814
140 );
141 }
142 $file = $_FILES['tx_extensionmanager_tools_extensionmanagerextensionmanager'];
143 $fileName = pathinfo($file['name']['extensionFile'], PATHINFO_BASENAME);
144 try {
145 // If the file name isn't valid an error will be thrown
146 $this->checkFileName($fileName);
147 if (!empty($file['tmp_name']['extensionFile'])) {
148 $tempFile = GeneralUtility::upload_to_tempfile($file['tmp_name']['extensionFile']);
149 } else {
150 throw new ExtensionManagerException(
151 'Creating temporary file failed. Check your upload_max_filesize and post_max_size limits.',
152 1342864339
153 );
154 }
155 $extensionData = $this->extractExtensionFromFile($tempFile, $fileName, $overwrite);
156 $emConfiguration = $this->configurationUtility->getCurrentConfiguration('extensionmanager');
157 if (!$emConfiguration['automaticInstallation']['value']) {
158 $this->addFlashMessage(
159 $this->translate('extensionList.uploadFlashMessage.message', array($extensionData['extKey'])),
160 $this->translate('extensionList.uploadFlashMessage.title'),
161 FlashMessage::OK
162 );
163 } else {
164 if ($this->activateExtension($extensionData['extKey'])) {
165 $this->addFlashMessage(
166 $this->translate('extensionList.installedFlashMessage.message', array($extensionData['extKey'])),
167 '',
168 FlashMessage::OK
169 );
170 } else {
171 $this->redirect('unresolvedDependencies', 'List', null, array('extensionKey' => $extensionData['extKey']));
172 }
173 }
174 } catch (\TYPO3\CMS\Extbase\Mvc\Exception\StopActionException $exception) {
175 throw $exception;
176 } catch (DependencyConfigurationNotFoundException $exception) {
177 $this->addFlashMessage($exception->getMessage(), '', FlashMessage::ERROR);
178 } catch (\Exception $exception) {
179 $this->removeExtensionAndRestoreFromBackup($fileName);
180 $this->addFlashMessage($exception->getMessage(), '', FlashMessage::ERROR);
181 }
182 $this->redirect('index', 'List', null, array(self::TRIGGER_RefreshModuleMenu => true));
183 }
184
185 /**
186 * Validate the filename of an uploaded file
187 *
188 * @param string $fileName
189 * @return void
190 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
191 */
192 public function checkFileName($fileName)
193 {
194 $extension = pathinfo($fileName, PATHINFO_EXTENSION);
195 if (empty($fileName)) {
196 throw new ExtensionManagerException('No file given.', 1342858852);
197 }
198 if ($extension !== 't3x' && $extension !== 'zip') {
199 throw new ExtensionManagerException('Wrong file format "' . $extension . '" given. Allowed formats are t3x and zip.', 1342858853);
200 }
201 }
202
203 /**
204 * Extract a given t3x or zip file
205 *
206 * @param string $uploadPath Path to existing extension file
207 * @param string $fileName Filename of the uploaded file
208 * @param bool $overwrite If true, extension will be replaced
209 * @return array Extension data
210 * @throws ExtensionManagerException
211 * @throws DependencyConfigurationNotFoundException
212 */
213 public function extractExtensionFromFile($uploadPath, $fileName, $overwrite)
214 {
215 $fileExtension = pathinfo($fileName, PATHINFO_EXTENSION);
216 if ($fileExtension === 't3x') {
217 $extensionData = $this->getExtensionFromT3xFile($uploadPath, $overwrite);
218 } else {
219 $extensionData = $this->getExtensionFromZipFile($uploadPath, $fileName, $overwrite);
220 }
221
222 return $extensionData;
223 }
224
225 /**
226 * @param string $extensionKey
227 * @return bool
228 */
229 public function activateExtension($extensionKey)
230 {
231 $this->managementService->reloadPackageInformation($extensionKey);
232 $extension = $this->managementService->getExtension($extensionKey);
233 return is_array($this->managementService->installExtension($extension));
234 }
235
236 /**
237 * Extracts a given t3x file and installs the extension
238 *
239 * @param string $file Path to uploaded file
240 * @param bool $overwrite Overwrite existing extension if TRUE
241 * @throws ExtensionManagerException
242 * @throws DependencyConfigurationNotFoundException
243 * @return array
244 */
245 protected function getExtensionFromT3xFile($file, $overwrite = false)
246 {
247 $fileContent = GeneralUtility::getUrl($file);
248 if (!$fileContent) {
249 throw new ExtensionManagerException('File had no or wrong content.', 1342859339);
250 }
251 $extensionData = $this->terUtility->decodeExchangeData($fileContent);
252 if (empty($extensionData['extKey'])) {
253 throw new ExtensionManagerException('Decoding the file went wrong. No extension key found', 1342864309);
254 }
255 $isExtensionAvailable = $this->managementService->isAvailable($extensionData['extKey']);
256 if (!$overwrite && $isExtensionAvailable) {
257 throw new ExtensionManagerException($this->translate('extensionList.overwritingDisabled'), 1342864310);
258 }
259 if ($isExtensionAvailable) {
260 $this->copyExtensionFolderToTempFolder($extensionData['extKey']);
261 }
262 $this->removeFromOriginalPath = true;
263 $extension = $this->extensionRepository->findOneByExtensionKeyAndVersion($extensionData['extKey'], $extensionData['EM_CONF']['version']);
264 $this->fileHandlingUtility->unpackExtensionFromExtensionDataArray($extensionData, $extension);
265
266 if (empty($extension)
267 && empty($extensionData['EM_CONF']['constraints'])
268 && !isset($extensionData['FILES']['ext_emconf.php'])
269 && !isset($extensionData['FILES']['/ext_emconf.php'])
270 ) {
271 throw new DependencyConfigurationNotFoundException('Extension cannot be installed automatically because no dependencies could be found! Please check dependencies manually (on typo3.org) before installing the extension.', 1439587168);
272 }
273
274 return $extensionData;
275 }
276
277 /**
278 * Extracts a given zip file and installs the extension
279 * As there is no information about the extension key in the zip
280 * we have to use the file name to get that information
281 * filename format is expected to be extensionkey_version.zip
282 *
283 * @param string $file Path to uploaded file
284 * @param string $fileName Filename (basename) of uploaded file
285 * @param bool $overwrite Overwrite existing extension if TRUE
286 * @return array
287 * @throws ExtensionManagerException
288 */
289 protected function getExtensionFromZipFile($file, $fileName, $overwrite = false)
290 {
291 // Remove version and extension from filename to determine the extension key
292 $extensionKey = $this->getExtensionKeyFromFileName($fileName);
293 $isExtensionAvailable = $this->managementService->isAvailable($extensionKey);
294 if (!$overwrite && $isExtensionAvailable) {
295 throw new ExtensionManagerException('Extension is already available and overwriting is disabled.', 1342864311);
296 }
297 if ($isExtensionAvailable) {
298 $this->copyExtensionFolderToTempFolder($extensionKey);
299 }
300 $this->removeFromOriginalPath = true;
301 $this->fileHandlingUtility->unzipExtensionFromFile($file, $extensionKey);
302
303 return array('extKey' => $extensionKey);
304 }
305
306 /**
307 * Removes version and file extension from filename to determine extension key
308 *
309 * @param string $fileName
310 * @return string
311 */
312 protected function getExtensionKeyFromFileName($fileName)
313 {
314 return preg_replace('/_(\\d+)(\\.|\\-)(\\d+)(\\.|\\-)(\\d+).*/i', '', strtolower(substr($fileName, 0, -4)));
315 }
316
317 /**
318 * Copies current extension folder to typo3temp directory as backup
319 *
320 * @param string $extensionKey
321 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
322 * @return void
323 */
324 protected function copyExtensionFolderToTempFolder($extensionKey)
325 {
326 $this->extensionBackupPath = PATH_site . 'typo3temp/' . $extensionKey . substr(sha1($extensionKey . microtime()), 0, 7) . '/';
327 GeneralUtility::mkdir($this->extensionBackupPath);
328 GeneralUtility::copyDirectory(
329 $this->fileHandlingUtility->getExtensionDir($extensionKey),
330 $this->extensionBackupPath
331 );
332 }
333
334 /**
335 * Removes the extension directory and restores the extension from the backup directory
336 *
337 * @param string $fileName
338 * @see UploadExtensionFileController::extractAction
339 * @return void
340 */
341 protected function removeExtensionAndRestoreFromBackup($fileName)
342 {
343 $extDirPath = $this->fileHandlingUtility->getExtensionDir($this->getExtensionKeyFromFileName($fileName));
344 if ($this->removeFromOriginalPath && is_dir($extDirPath)) {
345 GeneralUtility::rmdir($extDirPath, true);
346 }
347 if (!empty($this->extensionBackupPath)) {
348 GeneralUtility::mkdir($extDirPath);
349 GeneralUtility::copyDirectory($this->extensionBackupPath, $extDirPath);
350 }
351 }
352
353 /**
354 * Removes the backup folder in typo3temp
355 * @return void
356 */
357 protected function removeBackupFolder()
358 {
359 if (!empty($this->extensionBackupPath)) {
360 GeneralUtility::rmdir($this->extensionBackupPath, true);
361 $this->extensionBackupPath = '';
362 }
363 }
364 }