ed5e768c8fa0b21967c20a7f4bd819896a7b0adc
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Classes / Utility / FileHandlingUtility.php
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Utility;
3 use \TYPO3\CMS\Core\Utility\GeneralUtility;
4
5 /***************************************************************
6 * Copyright notice
7 *
8 * (c) 2012 Susanne Moog <susanne.moog@typo3.org>
9 * All rights reserved
10 *
11 * This script is part of the TYPO3 project. The TYPO3 project is
12 * free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * The GNU General Public License can be found at
18 * http://www.gnu.org/copyleft/gpl.html.
19 * A copy is found in the textfile GPL.txt and important notices to the license
20 * from the author is found in LICENSE.txt distributed with these scripts.
21 *
22 *
23 * This script is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * This copyright notice MUST APPEAR in all copies of the script!
29 ***************************************************************/
30 /**
31 * Utility for dealing with files and folders
32 *
33 * @author Susanne Moog <susanne.moog@typo3.org>
34 */
35 class FileHandlingUtility implements \TYPO3\CMS\Core\SingletonInterface {
36
37 /**
38 * @var \TYPO3\CMS\Extensionmanager\Utility\EmConfUtility
39 */
40 protected $emConfUtility;
41
42 /**
43 * Injector for Tx_Extensionmanager_Utility_EmConf
44 *
45 * @param \TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility
46 * @return void
47 */
48 public function injectEmConfUtility(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility) {
49 $this->emConfUtility = $emConfUtility;
50 }
51
52 /**
53 * @var \TYPO3\CMS\Extensionmanager\Utility\InstallUtility
54 */
55 protected $installUtility;
56
57 /**
58 * Injector for Tx_Extensionmanager_Utility_Install
59 *
60 * @param \TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility
61 * @return void
62 */
63 public function injectInstallUtility(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility) {
64 $this->installUtility = $installUtility;
65 }
66
67 /**
68 * Unpack an extension in t3x data format and write files
69 *
70 * @param array $extensionData
71 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension
72 * @param string $pathType
73 * @return void
74 */
75 public function unpackExtensionFromExtensionDataArray(array $extensionData, \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension = NULL, $pathType = 'Local') {
76 $extensionDir = $this->makeAndClearExtensionDir($extensionData['extKey'], $pathType);
77 $files = $this->extractFilesArrayFromExtensionData($extensionData);
78 $directories = $this->extractDirectoriesFromExtensionData($files);
79 $this->createDirectoriesForExtensionFiles($directories, $extensionDir);
80 $this->writeExtensionFiles($files, $extensionDir);
81 $this->writeEmConfToFile($extensionData, $extensionDir, $extension);
82 }
83
84 /**
85 * Extract needed directories from given extensionDataFilesArray
86 *
87 * @param array $files
88 * @return array
89 */
90 protected function extractDirectoriesFromExtensionData(array $files) {
91 $directories = array();
92 foreach ($files as $filePath => $file) {
93 preg_match('/(.*)\\//', $filePath, $matches);
94 $directories[] = $matches[0];
95 }
96 return $directories;
97 }
98
99 /**
100 * Returns the "FILES" part from the data array
101 *
102 * @param array $extensionData
103 * @return mixed
104 */
105 protected function extractFilesArrayFromExtensionData(array $extensionData) {
106 return $extensionData['FILES'];
107 }
108
109 /**
110 * Loops over an array of directories and creates them in the given root path
111 * It also creates nested directory structures
112 *
113 * @param array $directories
114 * @param string $rootPath
115 * @return void
116 */
117 protected function createDirectoriesForExtensionFiles(array $directories, $rootPath) {
118 foreach ($directories as $directory) {
119 $this->createNestedDirectory($rootPath . $directory);
120 }
121 }
122
123 /**
124 * Wrapper for utility method to create directory recusively
125 *
126 * @param string $directory Absolute path
127 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
128 */
129 protected function createNestedDirectory($directory) {
130 try {
131 GeneralUtility::mkdir_deep($directory);
132 } catch(\RuntimeException $exception) {
133 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf($GLOBALS['LANG']->getLL('clearMakeExtDir_could_not_create_dir'), $this->getRelativePath($directory)), 1337280416);
134 }
135
136 }
137
138 /**
139 * Loops over an array of files and writes them to the given rootPath
140 *
141 * @param array $files
142 * @param string $rootPath
143 * @return void
144 */
145 protected function writeExtensionFiles(array $files, $rootPath) {
146 foreach ($files as $file) {
147 GeneralUtility::writeFile($rootPath . $file['name'], $file['content']);
148 }
149 }
150
151 /**
152 * Removes the current extension of $type and creates the base folder for
153 * the new one (which is going to be imported)
154 *
155 * @param string $extensionkey
156 * @param string $pathType Extension installation scope (Local,Global,System)
157 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
158 * @return string
159 */
160 protected function makeAndClearExtensionDir($extensionkey, $pathType = 'Local') {
161 $paths = \TYPO3\CMS\Extensionmanager\Domain\Model\Extension::returnInstallPaths();
162 $path = $paths[$pathType];
163 if (!$path || !is_dir($path) || !$extensionkey) {
164 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('ERROR: The extension install path "%s" was no directory!', $this->getRelativePath($path)), 1337280417);
165 } else {
166 $extDirPath = $path . $extensionkey . '/';
167 if (is_dir($extDirPath)) {
168 $this->removeDirectory($extDirPath);
169 }
170 $this->addDirectory($extDirPath);
171 }
172 return $extDirPath;
173 }
174
175 /**
176 * Add specified directory
177 *
178 * @param string $extDirPath
179 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
180 * @return void
181 */
182 protected function addDirectory($extDirPath) {
183 GeneralUtility::mkdir($extDirPath);
184 if (!is_dir($extDirPath)) {
185 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf($GLOBALS['LANG']->getLL('clearMakeExtDir_could_not_create_dir'), $this->getRelativePath($extDirPath)), 1337280416);
186 }
187 }
188
189 /**
190 * Creates directories configured in ext_emconf.php if not already present
191 *
192 * @param array $extension
193 */
194 public function ensureConfiguredDirectoriesExist(array $extension) {
195 foreach ($this->getAbsolutePathsToConfiguredDirectories($extension) as $directory) {
196 if (!$this->directoryExists($directory)) {
197 $this->createNestedDirectory($directory);
198 }
199 }
200 }
201
202 /**
203 * Wrapper method for directory existance check
204 *
205 * @param string $directory
206 * @return boolean
207 */
208 protected function directoryExists($directory) {
209 return is_dir($directory);
210 }
211
212 /**
213 * Checks configuration and returns an array of absolute paths that should be created
214 *
215 * @param array $extension
216 * @return array
217 */
218 protected function getAbsolutePathsToConfiguredDirectories(array $extension) {
219 $requestedDirectories = array();
220 $requestUploadFolder = isset($extension['uploadfolder']) ? (boolean)$extension['uploadfolder'] : FALSE;
221 if ($requestUploadFolder) {
222 $requestedDirectories[] = $this->getAbsolutePath($this->getPathToUploadFolder($extension));
223 }
224
225 $requestCreateDirectories = empty($extension['createDirs']) ? FALSE : (string)$extension['createDirs'];
226 if ($requestCreateDirectories) {
227 foreach (GeneralUtility::trimExplode(',', $extension['createDirs']) as $directoryToCreate) {
228 $requestedDirectories[] = $this->getAbsolutePath($directoryToCreate);
229 }
230 }
231
232 return $requestedDirectories;
233 }
234
235 /**
236 * Upload folders always reside in “uploads/tx_[extKey-with-no-underscore]”
237 *
238 * @param array $extension
239 * @return string
240 */
241 protected function getPathToUploadFolder($extension) {
242 return 'uploads/tx_' . str_replace('_', '', $extension['key']) . '/';
243 }
244
245 /**
246 * Remove specified directory
247 *
248 * @param string $extDirPath
249 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
250 * @return void
251 */
252 public function removeDirectory($extDirPath) {
253 $res = GeneralUtility::rmdir($extDirPath, TRUE);
254 if ($res === FALSE) {
255 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf($GLOBALS['LANG']->getLL('clearMakeExtDir_could_not_remove_dir'), $this->getRelativePath($extDirPath)), 1337280415);
256 }
257 }
258
259 /**
260 * Constructs emConf and writes it to corresponding file
261 *
262 * @param array $extensionData
263 * @param string $rootPath
264 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension
265 * @return void
266 */
267 protected function writeEmConfToFile(array $extensionData, $rootPath, \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension = NULL) {
268 $emConfContent = $this->emConfUtility->constructEmConf($extensionData, $extension);
269 GeneralUtility::writeFile($rootPath . 'ext_emconf.php', $emConfContent);
270 }
271
272 /**
273 * Is the given path a valid path for extension installation
274 *
275 * @param string $path the absolute (!) path in question
276 * @return boolean
277 */
278 public function isValidExtensionPath($path) {
279 $allowedPaths = \TYPO3\CMS\Extensionmanager\Domain\Model\Extension::returnAllowedInstallPaths();
280 foreach ($allowedPaths as $allowedPath) {
281 if (GeneralUtility::isFirstPartOfStr($path, $allowedPath)) {
282 return TRUE;
283 }
284 }
285 return FALSE;
286 }
287
288 /**
289 * Returns absolute path
290 *
291 * @param string $relativePath
292 * @return string
293 */
294 protected function getAbsolutePath($relativePath) {
295 $absolutePath = GeneralUtility::getFileAbsFileName(GeneralUtility::resolveBackPath(PATH_site . $relativePath));
296 if (empty($absolutePath)) {
297 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Illegal relative path given', 1350742864);
298 }
299 return $absolutePath;
300 }
301
302 /**
303 * Returns relative path
304 *
305 * @param string $absolutePath
306 * @return string
307 */
308 protected function getRelativePath($absolutePath) {
309 return substr($absolutePath, strlen(PATH_site));
310 }
311
312 /**
313 * Get extension path for an available or installed extension
314 *
315 * @param string $extension
316 * @return string
317 */
318 public function getAbsoluteExtensionPath($extension) {
319 $extension = $this->installUtility->enrichExtensionWithDetails($extension);
320 $absolutePath = $this->getAbsolutePath($extension['siteRelPath']);
321 return $absolutePath;
322 }
323
324 /**
325 * Get version of an available or installed extension
326 *
327 * @param string $extension
328 * @return string
329 */
330 public function getExtensionVersion($extension) {
331 $extensionData = $this->installUtility->enrichExtensionWithDetails($extension);
332 $version = $extensionData['version'];
333 return $version;
334 }
335
336 /**
337 * Create a zip file from an extension
338 *
339 * @param array $extension
340 * @return string
341 */
342 public function createZipFileFromExtension($extension) {
343 $extensionPath = $this->getAbsoluteExtensionPath($extension);
344 $version = $this->getExtensionVersion($extension);
345 $fileName = $this->getAbsolutePath('typo3temp/' . $extension . '_' . $version . '.zip');
346 $zip = new \ZipArchive();
347 $zip->open($fileName, \ZipArchive::CREATE);
348 $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($extensionPath));
349 foreach ($iterator as $key => $value) {
350 $archiveName = str_replace($extensionPath, '', $key);
351 if (\TYPO3\CMS\Core\Utility\StringUtility::isLastPartOfString($key, '.')) {
352 continue;
353 } else {
354 $zip->addFile($key, $archiveName);
355 }
356 }
357 $zip->close();
358 return $fileName;
359 }
360
361 /**
362 * Unzip an extension.zip.
363 *
364 * @param string $file path to zip file
365 * @param string $fileName file name
366 * @param string $pathType path type (Local, Global, System)
367 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
368 * @return void
369 */
370 public function unzipExtensionFromFile($file, $fileName, $pathType = 'Local') {
371 $extensionDir = $this->makeAndClearExtensionDir($fileName, $pathType);
372 $zip = zip_open($file);
373 if (is_resource($zip)) {
374 while (($zipEntry = zip_read($zip)) !== FALSE) {
375 if (strpos(zip_entry_name($zipEntry), DIRECTORY_SEPARATOR) !== FALSE) {
376 $last = strrpos(zip_entry_name($zipEntry), DIRECTORY_SEPARATOR);
377 $dir = substr(zip_entry_name($zipEntry), 0, $last);
378 $file = substr(zip_entry_name($zipEntry), strrpos(zip_entry_name($zipEntry), DIRECTORY_SEPARATOR) + 1);
379 if (!is_dir($dir)) {
380 GeneralUtility::mkdir_deep($extensionDir . $dir);
381 }
382 if (strlen(trim($file)) > 0) {
383 $return = GeneralUtility::writeFile($extensionDir . $dir . '/' . $file, zip_entry_read($zipEntry, zip_entry_filesize($zipEntry)));
384 if ($return === FALSE) {
385 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Could not write file ' . $this->getRelativePath($file), 1344691048);
386 }
387 }
388 } else {
389 GeneralUtility::writeFile($extensionDir . zip_entry_name($zipEntry), zip_entry_read($zipEntry, zip_entry_filesize($zipEntry)));
390 }
391 }
392 } else {
393 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Unable to open zip file ' . $this->getRelativePath($file), 1344691049);
394 }
395 }
396
397 /**
398 * Sends a zip file to the browser and deletes it afterwards
399 *
400 * @param string $fileName
401 * @param string $downloadName
402 * @return void
403 */
404 public function sendZipFileToBrowserAndDelete($fileName, $downloadName = '') {
405 if ($downloadName === '') {
406 $downloadName = basename($fileName, '.zip');
407 }
408 header('Content-Type: application/zip');
409 header('Content-Length: ' . filesize($fileName));
410 header('Content-Disposition: attachment; filename="' . $downloadName . '.zip"');
411 readfile($fileName);
412 unlink($fileName);
413 die;
414 }
415
416 /**
417 * Sends the sql dump file to the browser and deletes it afterwards
418 *
419 * @param string $fileName
420 * @param string $downloadName
421 * @return void
422 */
423 public function sendSqlDumpFileToBrowserAndDelete($fileName, $downloadName = '') {
424 if ($downloadName === '') {
425 $downloadName = basename($fileName, '.sql');
426 } else {
427 $downloadName = basename($downloadName, '.sql');
428 }
429 header('Content-Type: text');
430 header('Content-Length: ' . filesize($fileName));
431 header('Content-Disposition: attachment; filename="' . $downloadName . '.sql"');
432 readfile($fileName);
433 unlink($fileName);
434 die;
435 }
436
437 }
438
439
440 ?>