TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
FileHandlingUtility.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Utility;
3 
8 
9 /*
10  * This file is part of the TYPO3 CMS project.
11  *
12  * It is free software; you can redistribute it and/or modify it under
13  * the terms of the GNU General Public License, either version 2
14  * of the License, or any later version.
15  *
16  * For the full copyright and license information, please read the
17  * LICENSE.txt file that was distributed with this source code.
18  *
19  * The TYPO3 project - inspiring people to share!
20  */
21 
26 {
30  protected $emConfUtility;
31 
35  protected $installUtility;
36 
40  protected $languageService;
41 
45  public function injectEmConfUtility(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility)
46  {
47  $this->emConfUtility = $emConfUtility;
48  }
49 
53  public function injectInstallUtility(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility)
54  {
55  $this->installUtility = $installUtility;
56  }
57 
61  public function injectLanguageService(\TYPO3\CMS\Lang\LanguageService $languageService)
62  {
63  $this->languageService = $languageService;
64  }
65 
71  public function initializeObject()
72  {
73  $this->languageService->includeLLFile('EXT:extensionmanager/Resources/Private/Language/locallang.xlf');
74  }
75 
84  public function unpackExtensionFromExtensionDataArray(array $extensionData, Extension $extension = null, $pathType = 'Local')
85  {
86  $extensionDir = $this->makeAndClearExtensionDir($extensionData['extKey'], $pathType);
87  $files = $this->extractFilesArrayFromExtensionData($extensionData);
88  $directories = $this->extractDirectoriesFromExtensionData($files);
89  $files = array_diff_key($files, array_flip($directories));
90  $this->createDirectoriesForExtensionFiles($directories, $extensionDir);
91  $this->writeExtensionFiles($files, $extensionDir);
92  $this->writeEmConfToFile($extensionData, $extensionDir, $extension);
93  $this->reloadPackageInformation($extensionData['extKey']);
94  }
95 
102  protected function extractDirectoriesFromExtensionData(array $files)
103  {
104  $directories = [];
105  foreach ($files as $filePath => $file) {
106  preg_match('/(.*)\\//', $filePath, $matches);
107  if (!empty($matches[0])) {
108  $directories[] = $matches[0];
109  }
110  }
111  return array_unique($directories);
112  }
113 
120  protected function extractFilesArrayFromExtensionData(array $extensionData)
121  {
122  return $extensionData['FILES'];
123  }
124 
133  protected function createDirectoriesForExtensionFiles(array $directories, $rootPath)
134  {
135  foreach ($directories as $directory) {
136  $this->createNestedDirectory($rootPath . $directory);
137  }
138  }
139 
146  protected function createNestedDirectory($directory)
147  {
148  try {
149  GeneralUtility::mkdir_deep($directory);
150  } catch (\RuntimeException $exception) {
151  throw new ExtensionManagerException(
152  sprintf($this->languageService->getLL('fileHandling.couldNotCreateDirectory'), $this->getRelativePath($directory)),
153  1337280416
154  );
155  }
156  }
157 
165  protected function writeExtensionFiles(array $files, $rootPath)
166  {
167  foreach ($files as $file) {
168  GeneralUtility::writeFile($rootPath . $file['name'], $file['content']);
169  }
170  }
171 
181  protected function makeAndClearExtensionDir($extensionKey, $pathType = 'Local')
182  {
183  $extDirPath = $this->getExtensionDir($extensionKey, $pathType);
184  if (is_dir($extDirPath)) {
185  $this->removeDirectory($extDirPath);
186  }
187  $this->addDirectory($extDirPath);
188 
189  return $extDirPath;
190  }
191 
200  public function getExtensionDir($extensionKey, $pathType = 'Local')
201  {
203  $path = $paths[$pathType];
204  if (!$path || !is_dir($path) || !$extensionKey) {
205  throw new ExtensionManagerException(
206  sprintf($this->languageService->getLL('fileHandling.installPathWasNoDirectory'), $this->getRelativePath($path)),
207  1337280417
208  );
209  }
210 
211  return $path . $extensionKey . '/';
212  }
213 
221  protected function addDirectory($extDirPath)
222  {
223  GeneralUtility::mkdir($extDirPath);
224  if (!is_dir($extDirPath)) {
225  throw new ExtensionManagerException(
226  sprintf($this->languageService->getLL('fileHandling.couldNotCreateDirectory'), $this->getRelativePath($extDirPath)),
227  1337280418
228  );
229  }
230  }
231 
237  public function ensureConfiguredDirectoriesExist(array $extension)
238  {
239  foreach ($this->getAbsolutePathsToConfiguredDirectories($extension) as $directory) {
240  if (!$this->directoryExists($directory)) {
241  $this->createNestedDirectory($directory);
242  }
243  }
244  }
245 
252  protected function directoryExists($directory)
253  {
254  return is_dir($directory);
255  }
256 
263  protected function getAbsolutePathsToConfiguredDirectories(array $extension)
264  {
265  $requestedDirectories = [];
266  $requestUploadFolder = isset($extension['uploadfolder']) ? (bool)$extension['uploadfolder'] : false;
267  if ($requestUploadFolder) {
268  $requestedDirectories[] = $this->getAbsolutePath($this->getPathToUploadFolder($extension));
269  }
270 
271  $requestCreateDirectories = empty($extension['createDirs']) ? false : (string)$extension['createDirs'];
272  if ($requestCreateDirectories) {
273  foreach (GeneralUtility::trimExplode(',', $extension['createDirs']) as $directoryToCreate) {
274  $requestedDirectories[] = $this->getAbsolutePath($directoryToCreate);
275  }
276  }
277 
278  return $requestedDirectories;
279  }
280 
287  protected function getPathToUploadFolder($extension)
288  {
289  return 'uploads/tx_' . str_replace('_', '', $extension['key']) . '/';
290  }
291 
299  public function removeDirectory($extDirPath)
300  {
301  $extDirPath = GeneralUtility::fixWindowsFilePath($extDirPath);
302  $extensionPathWithoutTrailingSlash = rtrim($extDirPath, '/');
303  if (is_link($extensionPathWithoutTrailingSlash) && TYPO3_OS !== 'WIN') {
304  $result = unlink($extensionPathWithoutTrailingSlash);
305  } else {
306  $result = GeneralUtility::rmdir($extDirPath, true);
307  }
308  if ($result === false) {
309  throw new ExtensionManagerException(
310  sprintf($this->languageService->getLL('fileHandling.couldNotRemoveDirectory'), $this->getRelativePath($extDirPath)),
311  1337280415
312  );
313  }
314  }
315 
325  protected function writeEmConfToFile(array $extensionData, $rootPath, Extension $extension = null)
326  {
327  $emConfFileData = [];
328  if (file_exists($rootPath . 'ext_emconf.php')) {
329  $emConfFileData = $this->emConfUtility->includeEmConf(
330  [
331  'key' => $extensionData['extKey'],
332  'siteRelPath' => PathUtility::stripPathSitePrefix($rootPath)
333  ]
334  );
335  }
336  $extensionData['EM_CONF'] = array_replace_recursive($emConfFileData, $extensionData['EM_CONF']);
337  $emConfContent = $this->emConfUtility->constructEmConf($extensionData, $extension);
338  GeneralUtility::writeFile($rootPath . 'ext_emconf.php', $emConfContent);
339  }
340 
347  public function isValidExtensionPath($path)
348  {
349  $allowedPaths = Extension::returnAllowedInstallPaths();
350  foreach ($allowedPaths as $allowedPath) {
351  if (GeneralUtility::isFirstPartOfStr($path, $allowedPath)) {
352  return true;
353  }
354  }
355  return false;
356  }
357 
365  protected function getAbsolutePath($relativePath)
366  {
367  $absolutePath = GeneralUtility::getFileAbsFileName(GeneralUtility::resolveBackPath(PATH_site . $relativePath));
368  if (empty($absolutePath)) {
369  throw new ExtensionManagerException('Illegal relative path given', 1350742864);
370  }
371  return $absolutePath;
372  }
373 
380  protected function getRelativePath($absolutePath)
381  {
382  return PathUtility::stripPathSitePrefix($absolutePath);
383  }
384 
391  public function getAbsoluteExtensionPath($extension)
392  {
393  $extension = $this->installUtility->enrichExtensionWithDetails($extension);
394  $absolutePath = $this->getAbsolutePath($extension['siteRelPath']);
395  return $absolutePath;
396  }
397 
404  public function getExtensionVersion($extension)
405  {
406  $extensionData = $this->installUtility->enrichExtensionWithDetails($extension);
407  $version = $extensionData['version'];
408  return $version;
409  }
410 
417  public function createZipFileFromExtension($extension)
418  {
419  $extensionPath = $this->getAbsoluteExtensionPath($extension);
420 
421  // Add trailing slash to the extension path, getAllFilesAndFoldersInPath explicitly requires that.
422  $extensionPath = PathUtility::sanitizeTrailingSeparator($extensionPath);
423 
424  $version = $this->getExtensionVersion($extension);
425  if (empty($version)) {
426  $version = '0.0.0';
427  }
428 
429  if (!@is_dir(PATH_site . 'typo3temp/var/ExtensionManager/')) {
430  GeneralUtility::mkdir(PATH_site . 'typo3temp/var/ExtensionManager/');
431  }
432  $fileName = $this->getAbsolutePath('typo3temp/var/ExtensionManager/' . $extension . '_' . $version . '_' . date('YmdHi', $GLOBALS['EXEC_TIME']) . '.zip');
433 
434  $zip = new \ZipArchive();
435  $zip->open($fileName, \ZipArchive::CREATE);
436 
437  $excludePattern = $GLOBALS['TYPO3_CONF_VARS']['EXT']['excludeForPackaging'];
438 
439  // Get all the files of the extension, but exclude the ones specified in the excludePattern
441  [], // No files pre-added
442  $extensionPath, // Start from here
443  '', // Do not filter files by extension
444  true, // Include subdirectories
445  PHP_INT_MAX, // Recursion level
446  $excludePattern // Files and directories to exclude.
447  );
448 
449  // Make paths relative to extension root directory.
450  $files = GeneralUtility::removePrefixPathFromList($files, $extensionPath);
451 
452  // Remove the one empty path that is the extension dir itself.
453  $files = array_filter($files);
454 
455  foreach ($files as $file) {
456  $fullPath = $extensionPath . $file;
457  // Distinguish between files and directories, as creation of the archive
458  // fails on Windows when trying to add a directory with "addFile".
459  if (is_dir($fullPath)) {
460  $zip->addEmptyDir($file);
461  } else {
462  $zip->addFile($fullPath, $file);
463  }
464  }
465 
466  $zip->close();
467  return $fileName;
468  }
469 
479  public function unzipExtensionFromFile($file, $fileName, $pathType = 'Local')
480  {
481  $extensionDir = $this->makeAndClearExtensionDir($fileName, $pathType);
482  $zip = zip_open($file);
483  if (is_resource($zip)) {
484  while (($zipEntry = zip_read($zip)) !== false) {
485  if (strpos(zip_entry_name($zipEntry), '/') !== false) {
486  $last = strrpos(zip_entry_name($zipEntry), '/');
487  $dir = substr(zip_entry_name($zipEntry), 0, $last);
488  $file = substr(zip_entry_name($zipEntry), strrpos(zip_entry_name($zipEntry), '/') + 1);
489  if (!is_dir($extensionDir . $dir)) {
490  GeneralUtility::mkdir_deep($extensionDir . $dir);
491  }
492  if (trim($file) !== '') {
493  $return = GeneralUtility::writeFile($extensionDir . $dir . '/' . $file, zip_entry_read($zipEntry, zip_entry_filesize($zipEntry)));
494  if ($return === false) {
495  throw new ExtensionManagerException('Could not write file ' . $this->getRelativePath($file), 1344691048);
496  }
497  }
498  } else {
499  GeneralUtility::writeFile($extensionDir . zip_entry_name($zipEntry), zip_entry_read($zipEntry, zip_entry_filesize($zipEntry)));
500  }
501  }
502  } else {
503  throw new ExtensionManagerException('Unable to open zip file ' . $this->getRelativePath($file), 1344691049);
504  }
505  }
506 
514  public function sendZipFileToBrowserAndDelete($fileName, $downloadName = '')
515  {
516  if ($downloadName === '') {
517  $downloadName = basename($fileName, '.zip');
518  }
519  header('Content-Type: application/zip');
520  header('Content-Length: ' . filesize($fileName));
521  header('Content-Disposition: attachment; filename="' . $downloadName . '.zip"');
522  readfile($fileName);
523  unlink($fileName);
524  die;
525  }
526 
534  public function sendSqlDumpFileToBrowserAndDelete($fileName, $downloadName = '')
535  {
536  if ($downloadName === '') {
537  $downloadName = basename($fileName, '.sql');
538  } else {
539  $downloadName = basename($downloadName, '.sql');
540  }
541  header('Content-Type: text');
542  header('Content-Length: ' . filesize($fileName));
543  header('Content-Disposition: attachment; filename="' . $downloadName . '.sql"');
544  readfile($fileName);
545  unlink($fileName);
546  die;
547  }
548 
552  protected function reloadPackageInformation($extensionKey)
553  {
554  $this->installUtility->reloadPackageInformation($extensionKey);
555  }
556 }
unzipExtensionFromFile($file, $fileName, $pathType= 'Local')
injectInstallUtility(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility)
writeEmConfToFile(array $extensionData, $rootPath, Extension $extension=null)
static rmdir($path, $removeNonEmpty=false)
static isFirstPartOfStr($str, $partStr)
unpackExtensionFromExtensionDataArray(array $extensionData, Extension $extension=null, $pathType= 'Local')
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static mkdir_deep($directory, $deepDirectory= '')
static writeFile($file, $content, $changePermissions=false)
static removePrefixPathFromList(array $fileArr, $prefixToRemove)
injectLanguageService(\TYPO3\CMS\Lang\LanguageService $languageService)
makeAndClearExtensionDir($extensionKey, $pathType= 'Local')
injectEmConfUtility(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static getFileAbsFileName($filename, $_=null, $_2=null)
static getAllFilesAndFoldersInPath(array $fileArr, $path, $extList= '', $regDirs=false, $recursivityLevels=99, $excludePattern= '')
static sanitizeTrailingSeparator($path, $separator= '/')