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