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