/** * Initialize new row with default values from various sources * * @param array $result * @return array * @todo: Should not implode valid values with | again, container & elements should work * @todo: with the array as it was done for select items * @throws \UnexpectedValueException */ public function addData(array $result) { foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) { if (empty($fieldConfig['config']['type']) || $fieldConfig['config']['type'] !== 'group' || empty($fieldConfig['config']['internal_type'])) { continue; } $databaseRowFieldContent = ''; if (!empty($result['databaseRow'][$fieldName])) { $databaseRowFieldContent = (string) $result['databaseRow'][$fieldName]; } $internalType = $fieldConfig['config']['internal_type']; if ($internalType === 'file_reference' || $internalType === 'file') { $files = array(); // Simple list of files $fileList = GeneralUtility::trimExplode(',', $databaseRowFieldContent, true); foreach ($fileList as $file) { if ($file) { $files[] = rawurlencode($file) . '|' . rawurlencode(PathUtility::basename($file)); } } $result['databaseRow'][$fieldName] = implode(',', $files); } elseif ($internalType === 'db') { /** @var $relationHandler RelationHandler */ $relationHandler = GeneralUtility::makeInstance(RelationHandler::class); $relationHandler->start($databaseRowFieldContent, $fieldConfig['config']['allowed'], $fieldConfig['config']['MM'], $result['databaseRow']['uid'], $result['tableName'], $fieldConfig['config']); $relationHandler->getFromDB(); $result['databaseRow'][$fieldName] = $relationHandler->readyForInterface(); } else { // @todo: "folder" is a valid internal_type, too. throw new \UnexpectedValueException('TCA internal_type of field "' . $fieldName . '" in table ' . $result['tableName'] . ' must be set to either "db", "file" or "file_reference"', 1438780511); } } return $result; }
/** * Get all the complex data for the loader. * This return value will be cached and stored in the database * There is no file monitoring for this cache * * @param Loader $autoLoader * @param int $type * * @return array */ public function prepareLoader(Loader $autoLoader, $type) { $languageOverride = []; if ($type === LoaderInterface::EXT_TABLES) { return $languageOverride; } $languageOverridePath = ExtensionManagementUtility::extPath($autoLoader->getExtensionKey()) . 'Resources/Private/Language/Overrides/'; if (!is_dir($languageOverridePath)) { return $languageOverride; } $files = GeneralUtility::getAllFilesAndFoldersInPath([], $languageOverridePath, 'xlf,php,xml', false, 99); foreach ($files as $file) { $file = str_replace($languageOverridePath, '', $file); $parts = GeneralUtility::trimExplode('/', $file, true); $extension = GeneralUtility::camelCaseToLowerCaseUnderscored($parts[0]); unset($parts[0]); $parts = array_values($parts); // language $language = 'default'; $fileParts = GeneralUtility::trimExplode('.', PathUtility::basename($file), true); if (strlen($fileParts[0]) === 2) { $language = $fileParts[0]; unset($fileParts[0]); $parts[sizeof($parts) - 1] = implode('.', $fileParts); } $languageOverride[] = ['language' => $language, 'original' => 'EXT:' . $extension . '/' . implode('/', $parts), 'override' => 'EXT:' . $autoLoader->getExtensionKey() . '/Resources/Private/Language/Overrides/' . $file]; } return $languageOverride; }
/** * Return a public path pointing to a resource. * * @param string $resource * @return string */ public static function getRelativePath($resource) { // If file is not found, resolve the path if (!is_file(PATH_site . $resource)) { $resource = substr(self::resolvePath($resource), strlen(PATH_site)); } return PathUtility::getRelativePathTo(PathUtility::dirname(PATH_site . $resource)) . PathUtility::basename($resource); }
/** * The actual text extraction. * * @param FileInterface $file * @return string */ public function extractText(FileInterface $file) { $localTempFile = $file->getForLocalProcessing(false); // extract text $content = file_get_contents($localTempFile); // In case of remote storage, the temporary copy of the // original file in typo3temp must be removed // Simply compare the filenames, because the filename is so unique that // it is nearly impossible to have a file with this name in a storage if (PathUtility::basename($localTempFile) !== $file->getName()) { unlink($localTempFile); } return $content; }
/** * Handles the existing files of a Fine Uploader form. * The values are stored in the GET/POST var at the index "fieldValue". * * @return string */ public function getExistingFiles() { $files = array(); $fieldValue = GeneralUtility::_GP('fieldValue'); if ($fieldValue != '') { $imagePath = GeneralUtility::getFileAbsFileName($fieldValue); $imageName = PathUtility::basename($imagePath); $imageDirectoryPath = PathUtility::dirname($imagePath); $imageDirectoryPath = PathUtility::getRelativePath(PATH_site, $imageDirectoryPath); $imageUrl = GeneralUtility::locationHeaderUrl('/' . $imageDirectoryPath . $imageName); if (file_exists($imagePath)) { $files[] = array('name' => $imageName, 'uuid' => $imageUrl, 'thumbnailUrl' => $imageUrl); } } return json_encode($files); }
/** * Handles the existing files of a Fine Uploader form. * The values are stored in the GET/POST var at the index "fieldValue". * * @return string */ public function getExistingFiles() { $files = []; $fieldValue = GeneralUtility::_GP('fieldValue'); if ($fieldValue != '') { // $imagePath = GeneralUtility::getFileAbsFileName($fieldValue); $imagePath = str_replace('new:', '', $fieldValue); $imageName = PathUtility::basename($imagePath); $imageDirectoryPath = PathUtility::dirname($imagePath); $imageDirectoryPath = PathUtility::getRelativePath(PATH_site, $imageDirectoryPath); $imageUrl = GeneralUtility::locationHeaderUrl('/' . $imageDirectoryPath . $imageName); if (file_exists($imagePath)) { $files[] = ['name' => $imageName, 'uuid' => $imageUrl, 'thumbnailUrl' => $imageUrl]; } } return $files; }
/** * @param ResourceStorage $storage * @param AbstractDriver $driver * @param ResourceInterface $resource * @param boolean $relativeToCurrentScript * @param array $urlData */ public function getPublicUrl(ResourceStorage $storage, AbstractDriver $driver, ResourceInterface $resource, $relativeToCurrentScript, array $urlData) { if (!$driver instanceof LocalDriver) { // We cannot handle other files than local files yet return; } $publicUrl = $this->resourcePublisher->getResourceWebUri($resource); if ($publicUrl !== FALSE) { // If requested, make the path relative to the current script in order to make it possible // to use the relative file if ($relativeToCurrentScript) { $publicUrl = PathUtility::getRelativePathTo(PathUtility::dirname(PATH_site . $publicUrl)) . PathUtility::basename($publicUrl); } // $urlData['publicUrl'] is passed by reference, so we can change that here and the value will be taken into account $urlData['publicUrl'] = $publicUrl; } }
/** * Processing of the data value in case the field type is "group" * * @param string $data The field value * @param array $fieldConfig TCA field config * @param array $TSconfig TCEform TSconfig for the record * @param string $table Table name * @param array $row The row * @param string $field Field name * @return string The processed input field value ($data) * @access private * @see renderRecord() */ public function renderRecord_groupProc($data, $fieldConfig, $TSconfig, $table, $row, $field) { switch ($fieldConfig['config']['internal_type']) { case 'file_reference': case 'file': // Init array used to accumulate the files: $dataAcc = array(); // Now, load the files into the $dataAcc array, whether stored by MM or as a list of filenames: if ($fieldConfig['config']['MM']) { $loadDB = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class); $loadDB->start('', 'files', $fieldConfig['config']['MM'], $row['uid']); // Setting dummy startup foreach ($loadDB->itemArray as $value) { if ($value['id']) { $dataAcc[] = rawurlencode($value['id']) . '|' . rawurlencode(PathUtility::basename($value['id'])); } } } else { $fileList = GeneralUtility::trimExplode(',', $data, TRUE); foreach ($fileList as $value) { if ($value) { $dataAcc[] = rawurlencode($value) . '|' . rawurlencode(PathUtility::basename($value)); } } } // Implode the accumulation array to a comma separated string: $data = implode(',', $dataAcc); break; case 'db': $loadDB = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class); /** @var $loadDB \TYPO3\CMS\Core\Database\RelationHandler */ $loadDB->start($data, $fieldConfig['config']['allowed'], $fieldConfig['config']['MM'], $row['uid'], $table, $fieldConfig['config']); $loadDB->getFromDB(); $data = $loadDB->readyForInterface(); break; } return $data; }
/** * @param string $identifier * @param string $type * @return array */ protected function getShortInfo($identifier, $type) { return ['identifier' => $identifier, 'name' => PathUtility::basename($identifier), 'type' => $type]; }
/** * Adds a files content to the export memory * * @param array $fI File information with three keys: "filename" = filename without path, "ID_absFile" = absolute filepath to the file (including the filename), "ID" = md5 hash of "ID_absFile". "relFileName" is optional for files attached to records, but mandatory for soft referenced files (since the relFileName determines where such a file should be stored!) * @param string $recordRef If the file is related to a record, this is the id on the form [table]:[id]. Information purposes only. * @param string $fieldname If the file is related to a record, this is the field name it was related to. Information purposes only. * @return void */ public function export_addFile($fI, $recordRef = '', $fieldname = '') { if (!@is_file($fI['ID_absFile'])) { $this->error($fI['ID_absFile'] . ' was not a file! Skipping.'); return; } if (filesize($fI['ID_absFile']) >= $this->maxFileSize) { $this->error($fI['ID_absFile'] . ' was larger (' . GeneralUtility::formatSize(filesize($fI['ID_absFile'])) . ') than the maxFileSize (' . GeneralUtility::formatSize($this->maxFileSize) . ')! Skipping.'); return; } $fileInfo = stat($fI['ID_absFile']); $fileRec = array(); $fileRec['filesize'] = $fileInfo['size']; $fileRec['filename'] = PathUtility::basename($fI['ID_absFile']); $fileRec['filemtime'] = $fileInfo['mtime']; //for internal type file_reference $fileRec['relFileRef'] = PathUtility::stripPathSitePrefix($fI['ID_absFile']); if ($recordRef) { $fileRec['record_ref'] = $recordRef . '/' . $fieldname; } if ($fI['relFileName']) { $fileRec['relFileName'] = $fI['relFileName']; } // Setting this data in the header $this->dat['header']['files'][$fI['ID']] = $fileRec; // ... and for the recordlisting, why not let us know WHICH relations there was... if ($recordRef && $recordRef !== '_SOFTREF_') { $refParts = explode(':', $recordRef, 2); if (!is_array($this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'])) { $this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'] = array(); } $this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'][] = $fI['ID']; } $fileMd5 = md5_file($fI['ID_absFile']); if (!$this->saveFilesOutsideExportFile) { // ... and finally add the heavy stuff: $fileRec['content'] = GeneralUtility::getUrl($fI['ID_absFile']); } else { GeneralUtility::upload_copy_move($fI['ID_absFile'], $this->getTemporaryFilesPathForExport() . $fileMd5); } $fileRec['content_md5'] = $fileMd5; $this->dat['files'][$fI['ID']] = $fileRec; // For soft references, do further processing: if ($recordRef === '_SOFTREF_') { // RTE files? if ($RTEoriginal = $this->getRTEoriginalFilename(PathUtility::basename($fI['ID_absFile']))) { $RTEoriginal_absPath = PathUtility::dirname($fI['ID_absFile']) . '/' . $RTEoriginal; if (@is_file($RTEoriginal_absPath)) { $RTEoriginal_ID = md5($RTEoriginal_absPath); $fileInfo = stat($RTEoriginal_absPath); $fileRec = array(); $fileRec['filesize'] = $fileInfo['size']; $fileRec['filename'] = PathUtility::basename($RTEoriginal_absPath); $fileRec['filemtime'] = $fileInfo['mtime']; $fileRec['record_ref'] = '_RTE_COPY_ID:' . $fI['ID']; $this->dat['header']['files'][$fI['ID']]['RTE_ORIG_ID'] = $RTEoriginal_ID; // Setting this data in the header $this->dat['header']['files'][$RTEoriginal_ID] = $fileRec; $fileMd5 = md5_file($RTEoriginal_absPath); if (!$this->saveFilesOutsideExportFile) { // ... and finally add the heavy stuff: $fileRec['content'] = GeneralUtility::getUrl($RTEoriginal_absPath); } else { GeneralUtility::upload_copy_move($RTEoriginal_absPath, $this->getTemporaryFilesPathForExport() . $fileMd5); } $fileRec['content_md5'] = $fileMd5; $this->dat['files'][$RTEoriginal_ID] = $fileRec; } else { $this->error('RTE original file "' . PathUtility::stripPathSitePrefix($RTEoriginal_absPath) . '" was not found!'); } } // Files with external media? // This is only done with files grabbed by a softreference parser since it is deemed improbable that hard-referenced files should undergo this treatment. $html_fI = pathinfo(PathUtility::basename($fI['ID_absFile'])); if ($this->includeExtFileResources && GeneralUtility::inList($this->extFileResourceExtensions, strtolower($html_fI['extension']))) { $uniquePrefix = '###' . md5($GLOBALS['EXEC_TIME']) . '###'; if (strtolower($html_fI['extension']) === 'css') { $prefixedMedias = explode($uniquePrefix, preg_replace('/(url[[:space:]]*\\([[:space:]]*["\']?)([^"\')]*)(["\']?[[:space:]]*\\))/i', '\\1' . $uniquePrefix . '\\2' . $uniquePrefix . '\\3', $fileRec['content'])); } else { // html, htm: $htmlParser = GeneralUtility::makeInstance(HtmlParser::class); $prefixedMedias = explode($uniquePrefix, $htmlParser->prefixResourcePath($uniquePrefix, $fileRec['content'], array(), $uniquePrefix)); } $htmlResourceCaptured = false; foreach ($prefixedMedias as $k => $v) { if ($k % 2) { $EXTres_absPath = GeneralUtility::resolveBackPath(PathUtility::dirname($fI['ID_absFile']) . '/' . $v); $EXTres_absPath = GeneralUtility::getFileAbsFileName($EXTres_absPath); if ($EXTres_absPath && GeneralUtility::isFirstPartOfStr($EXTres_absPath, PATH_site . $this->fileadminFolderName . '/') && @is_file($EXTres_absPath)) { $htmlResourceCaptured = true; $EXTres_ID = md5($EXTres_absPath); $this->dat['header']['files'][$fI['ID']]['EXT_RES_ID'][] = $EXTres_ID; $prefixedMedias[$k] = '{EXT_RES_ID:' . $EXTres_ID . '}'; // Add file to memory if it is not set already: if (!isset($this->dat['header']['files'][$EXTres_ID])) { $fileInfo = stat($EXTres_absPath); $fileRec = array(); $fileRec['filesize'] = $fileInfo['size']; $fileRec['filename'] = PathUtility::basename($EXTres_absPath); $fileRec['filemtime'] = $fileInfo['mtime']; $fileRec['record_ref'] = '_EXT_PARENT_:' . $fI['ID']; // Media relative to the HTML file. $fileRec['parentRelFileName'] = $v; // Setting this data in the header $this->dat['header']['files'][$EXTres_ID] = $fileRec; // ... and finally add the heavy stuff: $fileRec['content'] = GeneralUtility::getUrl($EXTres_absPath); $fileRec['content_md5'] = md5($fileRec['content']); $this->dat['files'][$EXTres_ID] = $fileRec; } } } } if ($htmlResourceCaptured) { $this->dat['files'][$fI['ID']]['tokenizedContent'] = implode('', $prefixedMedias); } } } }
/** * Returns the role of an item (currently only folders; can later be extended for files as well) * * @param string $folderIdentifier * @return string */ public function getRole($folderIdentifier) { $name = PathUtility::basename($folderIdentifier); $role = $this->mappingFolderNameToRole[$name]; if (empty($role)) { $role = FolderInterface::ROLE_DEFAULT; } return $role; }
/** * Extracts a specific FileInformation from the FileSystems. * * @param string $fileIdentifier * @param string $containerPath * @param string $property * * @return bool|int|string * @throws \InvalidArgumentException */ public function getSpecificFileInformation($fileIdentifier, $containerPath, $property) { $baseName = basename($fileIdentifier); $parts = explode('/', $fileIdentifier); $identifier = $this->canonicalizeAndCheckFileIdentifier($containerPath . PathUtility::basename($fileIdentifier)); $file = $this->filesystem->getMetadata($fileIdentifier); switch ($property) { case 'size': return $file['size']; case 'atime': return $file['timestamp']; case 'mtime': return $file['timestamp']; case 'ctime': return $file['timestamp']; case 'name': return $baseName; case 'mimetype': return 'application/octet-stream'; case 'identifier': return $identifier; case 'storage': return $this->storageUid; case 'identifier_hash': return $this->hashIdentifier($identifier); case 'folder_hash': if (1 < count($parts)) { return $this->hashIdentifier($this->getParentFolderIdentifierOfIdentifier($identifier)); } elseif (1 === count($parts)) { return sha1('/'); } else { return ''; } default: throw new \InvalidArgumentException(sprintf('The information "%s" is not available.', $property)); } }
/** * Moves a file from the local filesystem to this storage. * * @param string $localFilePath The file on the server's hard disk to add. * @param Folder $targetFolder The target path, without the fileName * @param string $targetFileName The fileName. If not set, the local file name is used. * @param string $conflictMode possible value are 'cancel', 'replace', 'changeName' * * @throws \InvalidArgumentException * @throws Exception\ExistingTargetFileNameException * @return FileInterface */ public function addFile($localFilePath, Folder $targetFolder, $targetFileName = '', $conflictMode = 'changeName') { $localFilePath = PathUtility::getCanonicalPath($localFilePath); if (!file_exists($localFilePath)) { throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1319552745); } $targetFolder = $targetFolder ?: $this->getDefaultFolder(); $targetFileName = $this->driver->sanitizeFileName($targetFileName ?: PathUtility::basename($localFilePath)); $this->assureFileAddPermissions('', $targetFolder, $targetFileName); // We do not care whether the file exists yet because $targetFileName may be changed by an // external slot and only then we should check how to proceed according to $conflictMode $targetFileName = $this->emitPreFileAddSignal($targetFileName, $targetFolder, $localFilePath); if ($conflictMode === 'cancel' && $this->driver->fileExistsInFolder($targetFileName, $targetFolder->getIdentifier())) { throw new Exception\ExistingTargetFileNameException('File "' . $targetFileName . '" already exists in folder ' . $targetFolder->getIdentifier(), 1322121068); } elseif ($conflictMode === 'changeName') { $targetFileName = $this->getUniqueName($targetFolder, $targetFileName); } $fileIdentifier = $this->driver->addFile($localFilePath, $targetFolder->getIdentifier(), $targetFileName); $file = ResourceFactory::getInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $fileIdentifier); $this->emitPostFileAddSignal($file, $targetFolder); return $file; }
/** * Returns information about a file. * * @param string $folderIdentifier * @return array */ public function getFolderInfoByIdentifier($folderIdentifier) { $folderIdentifier = $this->canonicalizeAndCheckFolderIdentifier($folderIdentifier); $details = $this->adapter->getDetails($this->rootPath . $folderIdentifier); return ['identifier' => $folderIdentifier, 'name' => PathUtility::basename($folderIdentifier), 'mtime' => $details['mtime'], 'ctime' => $details['ctime'], 'storage' => $this->storageUid]; }
/** * A file is about to be added as a *replacement* of an existing * one. * * @param File $file * @param string $uploadedFileName * @return void */ public function postFileReplace(File $file, $uploadedFileName) { $folder = $file->getParentFolder(); $storageConfiguration = $folder->getStorage()->getConfiguration(); $storageRecord = $folder->getStorage()->getStorageRecord(); if ($storageRecord['driver'] !== 'Local') { // Unfortunately unsupported yet return; } $targetDirectory = $storageConfiguration['pathType'] === 'relative' ? PATH_site : ''; $targetDirectory .= rtrim(rtrim($storageConfiguration['basePath'], '/') . $folder->getIdentifier(), '/'); $targetFileName = $targetDirectory . '/' . $file->getName(); $this->processFile($targetFileName, PathUtility::basename($targetFileName), $targetDirectory, $file); $this->populateMetadata($file, $folder); }
/** * Create file in directory and return the new (unique) filename * * @param string $origDirPrefix Directory prefix, relative, with trailing slash * @param string $fileName Filename (without path) * @param string $fileID File ID from import memory * @param string $table Table for which the processing occurs * @param string $uid UID of record from table * @return string|NULL New relative filename, if any */ public function processSoftReferences_saveFile_createRelFile($origDirPrefix, $fileName, $fileID, $table, $uid) { // If the fileID map contains an entry for this fileID then just return the relative filename of that entry; // we don't want to write another unique filename for this one! if (isset($this->fileIDMap[$fileID])) { return PathUtility::stripPathSitePrefix($this->fileIDMap[$fileID]); } if ($this->legacyImport) { // set dirPrefix to fileadmin because the right target folder is set and checked for permissions later $dirPrefix = $this->fileadminFolderName . '/'; } else { // Verify FileMount access to dir-prefix. Returns the best alternative relative path if any $dirPrefix = $this->verifyFolderAccess($origDirPrefix); } if ($dirPrefix && (!$this->update || $origDirPrefix === $dirPrefix) && $this->checkOrCreateDir($dirPrefix)) { $fileHeaderInfo = $this->dat['header']['files'][$fileID]; $updMode = $this->update && $this->import_mapId[$table][$uid] === $uid && $this->import_mode[$table . ':' . $uid] !== 'as_new'; // Create new name for file: // Must have same ID in map array (just for security, is not really needed) and NOT be set "as_new". // Write main file: if ($this->legacyImport) { $fileWritten = $this->writeSysFileResourceForLegacyImport($fileName, $fileID); if ($fileWritten) { $newName = 'file:' . $fileName; return $newName; // no support for HTML/CSS file resources attached ATM - see below } } else { if ($updMode) { $newName = PATH_site . $dirPrefix . $fileName; } else { // Create unique filename: $fileProcObj = $this->getFileProcObj(); $newName = $fileProcObj->getUniqueName($fileName, PATH_site . $dirPrefix); } if ($this->writeFileVerify($newName, $fileID)) { // If the resource was an HTML/CSS file with resources attached, we will write those as well! if (is_array($fileHeaderInfo['EXT_RES_ID'])) { $tokenizedContent = $this->dat['files'][$fileID]['tokenizedContent']; $tokenSubstituted = false; $fileProcObj = $this->getFileProcObj(); if ($updMode) { foreach ($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) { if ($this->dat['files'][$res_fileID]['filename']) { // Resolve original filename: $relResourceFileName = $this->dat['files'][$res_fileID]['parentRelFileName']; $absResourceFileName = GeneralUtility::resolveBackPath(PATH_site . $origDirPrefix . $relResourceFileName); $absResourceFileName = GeneralUtility::getFileAbsFileName($absResourceFileName); if ($absResourceFileName && GeneralUtility::isFirstPartOfStr($absResourceFileName, PATH_site . $this->fileadminFolderName . '/')) { $destDir = PathUtility::stripPathSitePrefix(PathUtility::dirname($absResourceFileName) . '/'); if ($this->verifyFolderAccess($destDir, true) && $this->checkOrCreateDir($destDir)) { $this->writeFileVerify($absResourceFileName, $res_fileID); } else { $this->error('ERROR: Could not create file in directory "' . $destDir . '"'); } } else { $this->error('ERROR: Could not resolve path for "' . $relResourceFileName . '"'); } $tokenizedContent = str_replace('{EXT_RES_ID:' . $res_fileID . '}', $relResourceFileName, $tokenizedContent); $tokenSubstituted = true; } } } else { // Create the resouces directory name (filename without extension, suffixed "_FILES") $resourceDir = PathUtility::dirname($newName) . '/' . preg_replace('/\\.[^.]*$/', '', PathUtility::basename($newName)) . '_FILES'; if (GeneralUtility::mkdir($resourceDir)) { foreach ($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) { if ($this->dat['files'][$res_fileID]['filename']) { $absResourceFileName = $fileProcObj->getUniqueName($this->dat['files'][$res_fileID]['filename'], $resourceDir); $relResourceFileName = substr($absResourceFileName, strlen(PathUtility::dirname($resourceDir)) + 1); $this->writeFileVerify($absResourceFileName, $res_fileID); $tokenizedContent = str_replace('{EXT_RES_ID:' . $res_fileID . '}', $relResourceFileName, $tokenizedContent); $tokenSubstituted = true; } } } } // If substitutions has been made, write the content to the file again: if ($tokenSubstituted) { GeneralUtility::writeFile($newName, $tokenizedContent); } } return PathUtility::stripPathSitePrefix($newName); } } } return null; }
/** * Returns the public URL to a file. * * @param \TYPO3\CMS\Core\Resource\ResourceInterface $resource * @param bool $relativeToCurrentScript Determines whether the URL returned should be relative to the current script, in case it is relative at all (only for the LocalDriver) * @return string */ public function getPublicUrl(\TYPO3\CMS\Core\Resource\ResourceInterface $resource, $relativeToCurrentScript = false) { if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($resource->getIdentifier(), '/_processed_/')) { $publicUrl = '../typo3temp/yag' . $resource->getIdentifier(); // TODO: ....!!!! } else { $item = $resource->getProperty('yagItem'); if (!$item instanceof \Tx_Yag_Domain_Model_Item) { $pathInfo = new PathInfo($resource->getIdentifier()); $item = $this->getItem($pathInfo); } $publicUrl = $this->yagFileSystemDiv->getFileRelFileName($item->getSourceuri()); } if ($relativeToCurrentScript) { $publicUrl = PathUtility::getRelativePathTo(PathUtility::dirname(PATH_site . $publicUrl)) . PathUtility::basename($publicUrl); } return $publicUrl; }
/** * Moves a file from the local filesystem to this storage. * * @param string $localFilePath The file on the server's hard disk to add * @param Folder $targetFolder The target folder where the file should be added * @param string $targetFileName The name of the file to be add, If not set, the local file name is used * @param string $conflictMode a value of the DuplicationBehavior enumeration * * @throws \InvalidArgumentException * @throws Exception\ExistingTargetFileNameException * @return FileInterface */ public function addFile($localFilePath, Folder $targetFolder, $targetFileName = '', $conflictMode = DuplicationBehavior::RENAME) { $localFilePath = PathUtility::getCanonicalPath($localFilePath); // File is not available locally NOR is it an uploaded file if (!is_uploaded_file($localFilePath) && !file_exists($localFilePath)) { throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1319552745); } $conflictMode = DuplicationBehavior::cast($conflictMode); $targetFolder = $targetFolder ?: $this->getDefaultFolder(); $targetFileName = $this->sanitizeFileName($targetFileName ?: PathUtility::basename($localFilePath), $targetFolder); $targetFileName = $this->emitPreFileAddSignal($targetFileName, $targetFolder, $localFilePath); $this->assureFileAddPermissions($targetFolder, $targetFileName); if ($conflictMode->equals(DuplicationBehavior::CANCEL) && $this->driver->fileExistsInFolder($targetFileName, $targetFolder->getIdentifier())) { throw new Exception\ExistingTargetFileNameException('File "' . $targetFileName . '" already exists in folder ' . $targetFolder->getIdentifier(), 1322121068); } elseif ($conflictMode->equals(DuplicationBehavior::RENAME)) { $targetFileName = $this->getUniqueName($targetFolder, $targetFileName); } $fileIdentifier = $this->driver->addFile($localFilePath, $targetFolder->getIdentifier(), $targetFileName); $file = ResourceFactory::getInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $fileIdentifier); if ($this->autoExtractMetadataEnabled()) { $indexer = GeneralUtility::makeInstance(Indexer::class, $this); $indexer->extractMetaData($file); } $this->emitPostFileAddSignal($file, $targetFolder); return $file; }
/** * Performs the database update. * * @param array $dbQueries queries done in this update * @param mixed $customMessages custom messages * @return boolean TRUE on success, FALSE on error */ public function performUpdate(array &$dbQueries, &$customMessages) { $this->init(); if (!PATH_site) { throw new \Exception('PATH_site was undefined.'); } $fileadminDirectory = rtrim($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/'); $targetDirectory = '/_migrated/RTE/'; $fullTargetDirectory = PATH_site . $fileadminDirectory . $targetDirectory; // Create the directory, if necessary if (!is_dir($fullTargetDirectory)) { \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($fullTargetDirectory); } $oldRecords = $this->findMagicImagesInOldLocation(); foreach ($oldRecords as $refRecord) { // Is usually uploads/RTE_magicC_123423324.png.png $sourceFileName = $refRecord['ref_string']; // Absolute path/filename $fullSourceFileName = PATH_site . $refRecord['ref_string']; $targetFileName = $targetDirectory . \TYPO3\CMS\Core\Utility\PathUtility::basename($refRecord['ref_string']); // Full directory $fullTargetFileName = $fullTargetDirectory . \TYPO3\CMS\Core\Utility\PathUtility::basename($refRecord['ref_string']); // maybe the file has been moved previously if (!file_exists($fullTargetFileName)) { // If the source file does not exist, we should just continue, but leave a message in the docs; // ideally, the user would be informed after the update as well. if (!file_exists(PATH_site . $sourceFileName)) { $this->logger->notice('File ' . $sourceFileName . ' does not exist. Reference was not migrated.', array()); $format = 'File \'%s\' does not exist. Referencing field: %s.%d.%s. The reference was not migrated.'; $message = sprintf($format, $sourceFileName, $refRecord['tablename'], $refRecord['recuid'], $refRecord['field']); $customMessages .= PHP_EOL . $message; continue; } rename($fullSourceFileName, $fullTargetFileName); } // Get the File object $file = $this->storage->getFile($targetFileName); if ($file instanceof \TYPO3\CMS\Core\Resource\File) { // And now update the referencing field $targetFieldName = $refRecord['field']; $targetRecord = $this->db->exec_SELECTgetSingleRow('uid, ' . $targetFieldName, $refRecord['tablename'], 'uid=' . (int) $refRecord['recuid']); if ($targetRecord) { // Replace the old filename with the new one, and add data-* attributes used by the RTE $searchString = 'src="' . $sourceFileName . '"'; $replacementString = 'src="' . $fileadminDirectory . $targetFileName . '"'; $replacementString .= ' data-htmlarea-file-uid="' . $file->getUid() . '"'; $replacementString .= ' data-htmlarea-file-table="sys_file"'; $targetRecord[$targetFieldName] = str_replace($searchString, $replacementString, $targetRecord[$targetFieldName]); // Update the record $this->db->exec_UPDATEquery($refRecord['tablename'], 'uid=' . (int) $refRecord['recuid'], array($targetFieldName => $targetRecord[$targetFieldName])); $queries[] = str_replace(LF, ' ', $this->db->debug_lastBuiltQuery); // Finally, update the sys_refindex table as well $this->db->exec_UPDATEquery('sys_refindex', 'hash=' . $this->db->fullQuoteStr($refRecord['hash'], 'sys_refindex'), array('ref_table' => 'sys_file', 'softref_key' => 'rtehtmlarea_images', 'ref_uid' => $file->getUid(), 'ref_string' => $fileadminDirectory . $targetFileName)); $queries[] = str_replace(LF, ' ', $this->db->debug_lastBuiltQuery); } } } return TRUE; }
/** * Processes upload of a file. * * @param string $fileName Full path to the file to be processed * @param string $targetFileName Expected target file name, if not converted (only file name, no path) * @param string $targetDirectory * @param \TYPO3\CMS\Core\Resource\File $file * @param \TYPO3\CMS\Core\Authentication\BackendUserAuthentication $backendUser * @param callback $callbackNotification Callback to send notification * @return string File name that was finally written */ public function processFile($fileName, $targetFileName = '', $targetDirectory = '', \TYPO3\CMS\Core\Resource\File $file = null, \TYPO3\CMS\Core\Authentication\BackendUserAuthentication $backendUser = null, $callbackNotification = null) { $this->lastMetadata = null; if (!(empty($targetFileName) && empty($targetDirectory))) { $targetDirectory = rtrim($targetDirectory, '/') . '/'; $ruleset = $this->getRuleset($fileName, $targetDirectory . $targetFileName, $backendUser); } else { $ruleset = $this->getRuleset($fileName, $fileName, $backendUser); } if (count($ruleset) === 0) { // File does not match any rule set return $fileName; } $processedFileName = $this->getProcessedFileName($fileName, $backendUser, $ruleset); if ($processedFileName === null) { // No processing to do return $fileName; } // Make file name relative, store as $targetFileName if (empty($targetFileName)) { $targetFileName = PathUtility::stripPathSitePrefix($fileName); } // Extract the extension if (($dotPosition = strrpos($fileName, '.')) === false) { // File has no extension return $fileName; } $fileExtension = strtolower(substr($fileName, $dotPosition + 1)); if ($fileExtension === 'png' && !$ruleset['resize_png_with_alpha']) { if (ImageUtility::isTransparentPng($fileName)) { $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:image_autoresize/Resources/Private/Language/locallang.xml:message.imageTransparent'), $targetFileName); $this->notify($callbackNotification, $message, \TYPO3\CMS\Core\Messaging\FlashMessage::WARNING); return $fileName; } } if (!is_writable($fileName)) { $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:image_autoresize/Resources/Private/Language/locallang.xml:message.imageNotWritable'), $targetFileName); $this->notify($callbackNotification, $message, \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR); return $fileName; } $targetDestFileName = $fileName; if (isset($ruleset['conversion_mapping'][$fileExtension])) { // File format will be converted $destExtension = $ruleset['conversion_mapping'][$fileExtension]; $destDirectory = PathUtility::dirname($fileName); $destFileName = PathUtility::basename(substr($fileName, 0, strlen($fileName) - strlen($fileExtension)) . $destExtension); if (empty($targetDirectory)) { // Ensures $destFileName does not yet exist, otherwise make it unique! /* @var $fileFunc \TYPO3\CMS\Core\Utility\File\BasicFileUtility */ $fileFunc = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Utility\File\BasicFileUtility::class); $destFileName = $fileFunc->getUniqueName($destFileName, $destDirectory); $targetDestFileName = $destFileName; } else { $destFileName = $destDirectory . '/' . $destFileName; $targetDestFileName = $targetDirectory . PathUtility::basename(substr($targetFileName, 0, strlen($targetFileName) - strlen($fileExtension)) . $destExtension); } } else { // File format stays the same $destExtension = $fileExtension; $destFileName = $fileName; } // Image is bigger than allowed, will now resize it to (hopefully) make it lighter /** @var $gifCreator \TYPO3\CMS\Frontend\Imaging\GifBuilder */ $gifCreator = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\Imaging\GifBuilder::class); $gifCreator->init(); $gifCreator->absPrefix = PATH_site; $imParams = $ruleset['keep_metadata'] === '1' ? '###SkipStripProfile###' : ''; $metadata = ImageUtility::getMetadata($fileName, true); $this->lastMetadata = $metadata; $isRotated = false; if ($ruleset['auto_orient'] === '1') { $orientation = ImageUtility::getOrientation($fileName); $isRotated = ImageUtility::isRotated($orientation); $transformation = ImageUtility::getTransformation($orientation); if ($transformation !== '') { $imParams .= ' ' . $transformation; } } if ($isRotated) { // Invert max_width and max_height as the picture // will be automatically rotated $options = array('maxW' => $ruleset['max_height'], 'maxH' => $ruleset['max_width']); } else { $options = array('maxW' => $ruleset['max_width'], 'maxH' => $ruleset['max_height']); } $originalFileSize = filesize($fileName); $tempFileInfo = null; $tempFileInfo = $gifCreator->imageMagickConvert($fileName, $destExtension, '', '', $imParams, '', $options, true); if (filesize($tempFileInfo[3]) >= $originalFileSize - 10240 && $destExtension === $fileExtension) { // Conversion leads to same or bigger file (rounded to 10KB to accomodate tiny variations in compression) => skip! $tempFileInfo = null; } if ($tempFileInfo) { // Signal to post-process the image $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterImageResize', array('operation' => $fileName === $destFileName ? 'RESIZE' : 'RESIZE_CONVERT', 'source' => $fileName, 'destination' => $tempFileInfo[3], 'newWidth' => &$tempFileInfo[0], 'newHeight' => &$tempFileInfo[1])); $newFileSize = filesize($tempFileInfo[3]); $this->reportAdditionalStorageClaimed($originalFileSize - $newFileSize); // Replace original file @unlink($fileName); @rename($tempFileInfo[3], $destFileName); if ($fileName === $destFileName) { $message = sprintf($this->localize('LLL:EXT:image_autoresize/Resources/Private/Language/locallang.xml:message.imageResized'), $targetFileName, $tempFileInfo[0], $tempFileInfo[1]); } else { $message = sprintf($this->localize('LLL:EXT:image_autoresize/Resources/Private/Language/locallang.xml:message.imageResizedAndRenamed'), $targetFileName, $tempFileInfo[0], $tempFileInfo[1], PathUtility::basename($targetDestFileName)); } // Indexation in TYPO3 6.2 is using another signal, after the file // has been actually uploaded $this->lastMetadata['COMPUTED']['Width'] = $tempFileInfo[0]; $this->lastMetadata['COMPUTED']['Height'] = $tempFileInfo[1]; if ($isRotated && $ruleset['keep_metadata'] === '1' && $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_version_5'] === 'gm') { ImageUtility::resetOrientation($destFileName); } $this->notify($callbackNotification, $message, \TYPO3\CMS\Core\Messaging\FlashMessage::INFO); } else { // Destination file was not written $destFileName = $fileName; } return $destFileName; }
/** * Creates a virtual File object to be used transparently by external * metadata extraction services as if it would come from standard FAL. * * @param string $fileName * @param array $metadata * @return \TYPO3\CMS\Core\Resource\File */ protected static function getVirtualFileObject($fileName, array $metadata) { /** @var \TYPO3\CMS\Core\Resource\ResourceFactory $resourceFactory */ $resourceFactory = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\ResourceFactory::class); $recordData = ['uid' => 0, 'pid' => 0, 'name' => 'Temporary Upload Storage', 'description' => 'Internal storage, mounting the temporary PHP upload directory.', 'driver' => 'Local', 'processingfolder' => '', 'configuration' => '', 'is_online' => true, 'is_browsable' => false, 'is_public' => false, 'is_writable' => false, 'is_default' => false]; $storageConfiguration = ['basePath' => PathUtility::dirname($fileName), 'pathType' => 'absolute']; $virtualStorage = $resourceFactory->createStorageObject($recordData, $storageConfiguration); $name = PathUtility::basename($fileName); $extension = strtolower(substr($name, strrpos($name, '.') + 1)); /** @var \TYPO3\CMS\Core\Resource\File $virtualFileObject */ $virtualFileObject = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\File::class, ['identifier' => '/' . $name, 'name' => $name, 'extension' => $extension], $virtualStorage, $metadata); return $virtualFileObject; }
/** * Removes a temporary file. * * When working with a file, the actual file might be on a remote storage. * To work with it it gets copied to local storage, those temporary local * copies need to be removed when they're not needed anymore. * * @param string $localTempFilePath Path to the local file copy * @param \TYPO3\CMS\Core\Resource\FileInterface $sourceFile Original file */ protected function cleanupTempFile($localTempFilePath, FileInterface $sourceFile) { if (PathUtility::basename($localTempFilePath) !== $sourceFile->getName()) { unlink($localTempFilePath); } }
/** * Returns information about a file. * * @param string $folderIdentifier * @return array */ public function getFolderInfoByIdentifier($folderIdentifier) { $folderIdentifier = $this->canonicalizeAndCheckFolderIdentifier($folderIdentifier); return array('identifier' => $folderIdentifier, 'name' => PathUtility::basename($folderIdentifier), 'storage' => $this->storageUid); }
/** * Adds a file from the local server disk. If the file already exists and * overwriting is disabled, * * @param string $localFilePath * @param string $fileName * @param string $conflictMode a value of the \TYPO3\CMS\Core\Resource\DuplicationBehavior enumeration * @return File The file object */ public function addFile($localFilePath, $fileName = null, $conflictMode = DuplicationBehavior::CANCEL) { $fileName = $fileName ? $fileName : PathUtility::basename($localFilePath); return $this->storage->addFile($localFilePath, $this, $fileName, $conflictMode); }
/** * Creates the index entry for a given file. * * @param string $fileName * @param integer $width * @param integer $height * @return void */ protected static function createIndex($fileName, $width, $height) { $relativePath = substr(PathUtility::dirname($fileName), strlen(PATH_site)); $resourceFactory = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance(); $targetFolder = $resourceFactory->retrieveFileOrFolderObject($relativePath); $targetFilename = PathUtility::basename($fileName); $storageConfiguration = $targetFolder->getStorage()->getConfiguration(); if (!isset($storageConfiguration['basePath'])) { // Probably a file found in uploads/ or similar return; } $basePath = rtrim($storageConfiguration['basePath'], '/') . '/'; $basePath = GeneralUtility::getFileAbsFileName($basePath); $identifier = substr($fileName, strlen($basePath) - 1); // TODO: possibly create file with nearly no info and populate them with // a call to $file->getStorage()->getFileInfo($file) instead of using $driver /** @var \TYPO3\CMS\Core\Resource\Driver\AbstractDriver $driver */ $driver = static::accessProtectedProperty($targetFolder->getStorage(), 'driver'); $fileInfo = $driver->getFileInfoByIdentifier($identifier); $file = $resourceFactory->createFileObject($fileInfo); /** @var \TYPO3\CMS\Core\Resource\FileRepository $fileRepository */ $fileRepository = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\FileRepository::class); $fileRepository->addToIndex($file); }
/** * Adds a file from the local server disk. If the file already exists and * overwriting is disabled, * * @param string $localFilePath * @param string $fileName * @param string $conflictMode possible value are 'cancel', 'replace', 'changeName' * @return File The file object */ public function addFile($localFilePath, $fileName = NULL, $conflictMode = 'cancel') { $fileName = $fileName ? $fileName : PathUtility::basename($localFilePath); return $this->storage->addFile($localFilePath, $this, $fileName, $conflictMode); }
/** * Bulk function, can be used for anything to get a file or folder * * 1. It's a UID * 2. It's a combined identifier * 3. It's just a path/filename (coming from the oldstyle/backwards compatibility) * * Files, previously laid on fileadmin/ or something, will be "mapped" to the storage the file is * in now. Files like typo3temp/ or typo3conf/ will be moved to the first writable storage * in its processing folder * * $input could be * - "2:myfolder/myfile.jpg" (combined identifier) * - "23" (file UID) * - "uploads/myfile.png" (backwards-compatibility, storage "0") * - "file:23" * * @param string $input * @return File|Folder */ public function retrieveFileOrFolderObject($input) { // Remove PATH_site because absolute paths under Windows systems contain ':' // This is done in all considered sub functions anyway $input = str_replace(PATH_site, '', $input); if (GeneralUtility::isFirstPartOfStr($input, 'file:')) { $input = substr($input, 5); return $this->retrieveFileOrFolderObject($input); } elseif (MathUtility::canBeInterpretedAsInteger($input)) { return $this->getFileObject($input); } elseif (strpos($input, ':') > 0) { list($prefix) = explode(':', $input); if (MathUtility::canBeInterpretedAsInteger($prefix)) { // path or folder in a valid storageUID return $this->getObjectFromCombinedIdentifier($input); } elseif ($prefix == 'EXT') { $input = GeneralUtility::getFileAbsFileName($input); if (empty($input)) { return null; } $input = PathUtility::getRelativePath(PATH_site, PathUtility::dirname($input)) . PathUtility::basename($input); return $this->getFileObjectFromCombinedIdentifier($input); } else { return null; } } else { // this is a backwards-compatible way to access "0-storage" files or folders // eliminate double slashes, /./ and /../ $input = PathUtility::getCanonicalPath(ltrim($input, '/')); if (@is_file(PATH_site . $input)) { // only the local file return $this->getFileObjectFromCombinedIdentifier($input); } else { // only the local path return $this->getFolderObjectFromCombinedIdentifier($input); } } }