first commit

This commit is contained in:
salam committed 2026-10-19 08:18:39 +02:00
commit 1218dd1517
12716 files changed
+989233

No files matched your search

@@ -0,0 +1,366 @@
<?php
namespace TYPO3\CMS\Frontend\Resource;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use TYPO3\CMS\Core\LinkHandling\LinkService;
use TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
use TYPO3\CMS\Core\Resource\FileCollectionRepository;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Resource\FileRepository;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Object to collect files from various sources during runtime
* Sources can be file references, file collections or folders
*
* Use in FILES Content Object or for a Fluid Data Processor
*
* Is not persisted, use only in FE.
* @internal this is a internal TYPO3 implementation and solely used for EXT:frontend and not part of TYPO3's Core API.
*/
class FileCollector implements \Countable, LoggerAwareInterface
{
use LoggerAwareTrait;
/**
* The files
*
* @var array
*/
protected $files = [];
/**
* The file repository
*
* @var \TYPO3\CMS\Core\Resource\FileRepository
*/
protected $fileRepository;
/**
* The file collection repository
*
* @var \TYPO3\CMS\Core\Resource\FileCollectionRepository
*/
protected $fileCollectionRepository;
/**
* The resource factory
*
* @var \TYPO3\CMS\Core\Resource\ResourceFactory
*/
protected $resourceFactory;
/**
* Add files
*
* @param array $fileUids
*/
public function addFiles(array $fileUids = [])
{
if (!empty($fileUids)) {
foreach ($fileUids as $fileUid) {
try {
$this->addFileObject($this->getResourceFactory()->getFileObject($fileUid));
} catch (Exception $e) {
$this->logger->warning(
'The file with uid "' . $fileUid
. '" could not be found and won\'t be included in frontend output',
['exception' => $e]
);
}
}
}
}
/**
* Add files to the collection from a relation
*
* @param string $relationTable The table of the relation (e.g. tt_content or pages)
* @param string $relationField The field which holds the files (e.g. media or images)
* @param array $referenceRecord the record which is referencing the files
*/
public function addFilesFromRelation($relationTable, $relationField, array $referenceRecord)
{
if (is_object($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE']->sys_page)) {
$fileReferences = $this->getFileReferences($relationTable, $relationField, $referenceRecord);
} else {
$fileReferences = $this->getFileRepository()->findByRelation($relationTable, $relationField, $referenceRecord['uid']);
}
if (!empty($fileReferences)) {
$this->addFileObjects($fileReferences);
}
}
/**
* Add files from UIDs of a reference
*
* @param array $fileReferenceUids
*/
public function addFileReferences(array $fileReferenceUids = [])
{
foreach ($fileReferenceUids as $fileReferenceUid) {
$fileObject = $this->getFileRepository()->findFileReferenceByUid($fileReferenceUid);
$this->addFileObject($fileObject);
}
}
/**
* Add files to the collection from multiple file collections
*
* @param array $fileCollectionUids The file collections uids
*/
public function addFilesFromFileCollections(array $fileCollectionUids = [])
{
foreach ($fileCollectionUids as $fileCollectionUid) {
$this->addFilesFromFileCollection($fileCollectionUid);
}
}
/**
* Add files to the collection from one single file collection
*
* @param int $fileCollectionUid The file collections uid
*/
public function addFilesFromFileCollection($fileCollectionUid = null)
{
if (!empty($fileCollectionUid)) {
try {
$fileCollection = $this->getFileCollectionRepository()->findByUid($fileCollectionUid);
if ($fileCollection instanceof \TYPO3\CMS\Core\Resource\Collection\AbstractFileCollection) {
$fileCollection->loadContents();
$files = $fileCollection->getItems();
$this->addFileObjects($files);
}
} catch (Exception $e) {
$this->logger->warning(
'The file-collection with uid "' . $fileCollectionUid
. '" could not be found or contents could not be loaded and won\'t be included in frontend output.',
['exception' => $e]
);
}
}
}
/**
* Add files to the collection from multiple folders
*
* @param array $folderIdentifiers The folder identifiers
* @param bool $recursive Add files recursive from given folders
*/
public function addFilesFromFolders(array $folderIdentifiers = [], $recursive = false)
{
foreach ($folderIdentifiers as $folderIdentifier) {
$this->addFilesFromFolder($folderIdentifier, $recursive);
}
}
/**
* Add files to the collection from one single folder
*
* @param string $folderIdentifier The folder identifier
* @param bool $recursive Add files recursive from given folders
*/
public function addFilesFromFolder($folderIdentifier, $recursive = false)
{
if ($folderIdentifier) {
try {
if (strpos($folderIdentifier, 't3://folder') === 0) {
// a t3://folder link to a folder in FAL
$linkService = GeneralUtility::makeInstance(LinkService::class);
$data = $linkService->resolveByStringRepresentation($folderIdentifier);
$folder = $data['folder'];
} else {
$folder = $this->getResourceFactory()->getFolderObjectFromCombinedIdentifier($folderIdentifier);
}
if ($folder instanceof Folder) {
$files = $folder->getFiles(0, 0, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, $recursive);
$this->addFileObjects(array_values($files));
}
} catch (Exception $e) {
$this->logger->warning(
'The folder with identifier "' . $folderIdentifier
. '" could not be found and won\'t be included in frontend output',
['exception' => $e]
);
}
}
}
/**
* Sort the file objects based on a property
*
* @param string $sortingProperty The sorting property
* @param string $sortingOrder can be ascending or descending or "random"
*/
public function sort($sortingProperty = '', $sortingOrder = 'ascending')
{
if ($sortingProperty !== '' && count($this->files) > 1) {
@usort(
$this->files,
function (
FileInterface $a,
FileInterface $b
) use ($sortingProperty) {
if ($a->hasProperty($sortingProperty) && $b->hasProperty($sortingProperty)) {
return strnatcasecmp($a->getProperty($sortingProperty), $b->getProperty($sortingProperty));
}
return 0;
}
);
switch (strtolower($sortingOrder)) {
case 'descending':
case 'desc':
$this->files = array_reverse($this->files);
break;
case 'random':
case 'rand':
shuffle($this->files);
break;
}
}
}
/**
* Add a file object to the collection
*
* @param FileInterface $file The file object
*/
public function addFileObject(FileInterface $file)
{
$this->files[] = $file;
}
/**
* Add multiple file objects to the collection
*
* @param FileInterface[] $files The file objects
*/
public function addFileObjects($files)
{
$this->files = array_merge($this->files, $files);
}
/**
* Final getter method to fetch the accumulated data
*
* @return array
*/
public function getFiles()
{
return $this->files;
}
/**
* @return int
*/
public function count()
{
return count($this->files);
}
/**
* Gets file references for a given record field.
*
* @param string $tableName Name of the table
* @param string $fieldName Name of the field
* @param array $element The parent element referencing to files
* @return array
*/
protected function getFileReferences($tableName, $fieldName, array $element): array
{
/** @var FileRepository $fileRepository */
$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
$currentId = !empty($element['uid']) ? $element['uid'] : 0;
// Fetch the references of the default element
try {
$references = $fileRepository->findByRelation($tableName, $fieldName, $currentId);
} catch (FileDoesNotExistException $e) {
/**
* We just catch the exception here
* Reasoning: There is nothing an editor or even admin could do
*/
return [];
} catch (\InvalidArgumentException $e) {
/**
* The storage does not exist anymore
* Log the exception message for admins as they maybe can restore the storage
*/
$logMessage = $e->getMessage() . ' (table: "' . $tableName . '", fieldName: "' . $fieldName . '", currentId: ' . $currentId . ')';
$this->logger->error($logMessage, ['exception' => $e]);
return [];
}
$localizedId = null;
if (isset($element['_LOCALIZED_UID'])) {
$localizedId = $element['_LOCALIZED_UID'];
} elseif (isset($element['_PAGES_OVERLAY_UID'])) {
$localizedId = $element['_PAGES_OVERLAY_UID'];
}
$isTableLocalizable = (
!empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
&& !empty($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
);
if ($isTableLocalizable && $localizedId !== null) {
$localizedReferences = $fileRepository->findByRelation($tableName, $fieldName, $localizedId);
$references = $localizedReferences;
}
return $references;
}
/**
* @return ResourceFactory
*/
protected function getResourceFactory()
{
if ($this->resourceFactory === null) {
$this->resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
}
return $this->resourceFactory;
}
/**
* @return FileCollectionRepository
*/
protected function getFileCollectionRepository()
{
if ($this->fileCollectionRepository === null) {
$this->fileCollectionRepository = GeneralUtility::makeInstance(FileCollectionRepository::class);
}
return $this->fileCollectionRepository;
}
/**
* @return FileRepository
*/
protected function getFileRepository()
{
if ($this->fileRepository === null) {
$this->fileRepository = GeneralUtility::makeInstance(FileRepository::class);
}
return $this->fileRepository;
}
}
@@ -0,0 +1,107 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Frontend\Resource;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
use TYPO3\CMS\Core\Resource\Exception\InvalidFileException;
use TYPO3\CMS\Core\Resource\Exception\InvalidFileNameException;
use TYPO3\CMS\Core\Resource\Exception\InvalidPathException;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* Checks if a given file path is allowed to be used in TYPO3 Frontend.
*
* Currently allowed is:
* - a file (which must exist) from any of the allowedPaths option, without any ".." inside the path name
* - an external URL
*
* The sanitize method either returns a full URL (in case it's a valid http/https resource)
* or a path relative to the public folder of the TYPO3 Frontend.
*/
class FilePathSanitizer
{
/**
* These are the only paths that are allowed for resources in TYPO3 Frontend.
* Additional paths can be added via $GLOBALS['TYPO3_CONF_VARS']['FE']['addAllowedPaths'], where all paths should
* be suffixed with a slash "/".
*
* @var array
*/
protected $allowedPaths = [];
/**
* Sets the paths from where TypoScript resources are allowed to be used:
*/
public function __construct()
{
$this->allowedPaths = [
$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'],
'uploads/',
'typo3temp/',
PathUtility::stripPathSitePrefix(Environment::getBackendPath()) . '/ext/',
PathUtility::stripPathSitePrefix(Environment::getFrameworkBasePath()) . '/',
PathUtility::stripPathSitePrefix(Environment::getExtensionsPath()) . '/',
];
if (!empty($GLOBALS['TYPO3_CONF_VARS']['FE']['addAllowedPaths'])) {
$paths = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['addAllowedPaths'], true);
foreach ($paths as $path) {
if (is_string($path)) {
$this->allowedPaths[] = $path;
}
}
}
}
/**
* Returns the reference used for the frontend inclusion, checks against allowed paths for inclusion.
*
* @param string $originalFileName
* @return string Resulting filename, is either a full absolute URL or a relative path.
*/
public function sanitize(string $originalFileName): string
{
$file = trim($originalFileName);
if (empty($file)) {
throw new InvalidFileNameException('Empty file name given', 1530169746);
}
if (strpos($file, '../') !== false) {
throw new InvalidPathException('File path "' . $file . '" contains illegal string "../"', 1530169814);
}
// if this is an URL, it can be returned directly
$urlScheme = parse_url($file, PHP_URL_SCHEME);
if ($urlScheme === 'https' || $urlScheme === 'http' || is_file(Environment::getPublicPath() . '/' . $file)) {
return $file;
}
// this call also resolves EXT:myext/ files
$file = GeneralUtility::getFileAbsFileName($file);
if (!$file || is_dir($file)) {
throw new FileDoesNotExistException('File "' . $originalFileName . '" was not found', 1530169845);
}
$file = PathUtility::stripPathSitePrefix($file);
// Check if the found file is in the allowed paths
foreach ($this->allowedPaths as $allowedPath) {
if (strpos((string)$file, (string)$allowedPath, 0) === 0) {
return $file;
}
}
throw new InvalidFileException('"' . $file . '" was not located in the allowed paths', 1530169955);
}
}