TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:09 +02:00
commit af8cc155b5
6818 changed files with 642608 additions and 0 deletions
+450
View File
@@ -0,0 +1,450 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* Abstract file representation in the file abstraction layer.
*/
abstract class AbstractFile implements FileInterface
{
/**
* Various file properties
*
* Note that all properties, which only the persisted (indexed) files have are stored in this
* overall properties array only. The only properties which really exist as object properties of
* the file object are the storage, the identifier, the fileName and the indexing status.
*
* @var array<non-empty-string, mixed>
*/
protected array $properties = [];
/**
* The storage this file is located in
*/
protected ?ResourceStorage $storage = null;
/**
* The file name of this file
*/
protected string $name = '';
/**
* If set to true, this file is regarded as being deleted.
*/
protected bool $deleted = false;
/******************
* VARIOUS FILE PROPERTY GETTERS
******************/
/**
* Returns true if the given property key exists for this file.
*
* @param non-empty-string $key
*/
public function hasProperty(string $key): bool
{
return array_key_exists($key, $this->properties);
}
/**
* Returns a property value
*
* @param non-empty-string $key
*/
public function getProperty(string $key): mixed
{
if ($this->hasProperty($key)) {
return $this->properties[$key];
}
return null;
}
/**
* Returns the properties of this object.
*
* @return array<non-empty-string, mixed>
*/
public function getProperties(): array
{
return $this->properties;
}
/**
* @return non-empty-string
*/
public function getHashedIdentifier(): string
{
return $this->properties['identifier_hash'];
}
public function getName(): string
{
// Do not check if file has been deleted because we might need the
// name for undeleting it.
return $this->name;
}
/**
* Returns the basename (the name without extension) of this file.
*/
public function getNameWithoutExtension(): string
{
return PathUtility::pathinfo($this->getName(), PATHINFO_FILENAME);
}
/**
* @throws \RuntimeException
* @return int<0, max>
*/
public function getSize(): int
{
if ($this->deleted) {
throw new \RuntimeException('File has been deleted.', 1329821480);
}
if (empty($this->properties['size'])) {
$fileInfo = $this->getStorage()->getFileInfoByIdentifier($this->getIdentifier(), ['size']);
$size = array_pop($fileInfo);
} else {
$size = $this->properties['size'];
}
return MathUtility::canBeInterpretedAsInteger($size) ? (int)$size : 0;
}
/**
* Returns the uid of this file
*/
public function getUid(): int
{
return (int)$this->getProperty('uid');
}
/**
* Returns the Sha1 of this file
*
* @throws \RuntimeException
* @return non-empty-string
*/
public function getSha1(): string
{
if ($this->deleted) {
throw new \RuntimeException('File has been deleted.', 1329821481);
}
return $this->getStorage()->hashFile($this, 'sha1');
}
/**
* Returns the creation time of the file as Unix timestamp
*
* @throws \RuntimeException
*/
public function getCreationTime(): int
{
if ($this->deleted) {
throw new \RuntimeException('File has been deleted.', 1329821487);
}
return (int)$this->getProperty('creation_date');
}
/**
* Returns the date (as UNIX timestamp) the file was last modified.
*
* @throws \RuntimeException
*/
public function getModificationTime(): int
{
if ($this->deleted) {
throw new \RuntimeException('File has been deleted.', 1329821488);
}
return (int)$this->getProperty('modification_date');
}
/**
* Get the extension of this file in a lower-case variant
*/
public function getExtension(): string
{
$pathinfo = PathUtility::pathinfo($this->getName());
return strtolower($pathinfo['extension'] ?? '');
}
/**
* Get the MIME type of this file
*
* @return non-empty-string mime type
*/
public function getMimeType(): string
{
if ($this->properties['mime_type'] ?? false) {
return $this->properties['mime_type'];
}
$fileInfo = $this->getStorage()->getFileInfoByIdentifier($this->getIdentifier(), ['mimetype']);
return array_pop($fileInfo);
}
/**
* Returns the fileType of this file
* basically there are only five main "file types"
* "audio"
* "image"
* "software"
* "text"
* "video"
* "other"
* see FileType enum
*/
public function getType(): int
{
return $this->getFileType()->value;
}
public function isType(FileType $fileType): bool
{
return $this->getFileType() === $fileType;
}
/**
* Returns the fileType of this file
* basically there are only five main "file types"
* "audio"
* "image"
* "software"
* "text"
* "video"
* "other"
* see FileType enum
*/
public function getFileType(): FileType
{
// this basically extracts the mimetype and guess the filetype based
// on the first part of the mimetype works for 99% of all cases, and
// we don't need to make an SQL statement like EXT:media does currently
if (!($this->properties['type'] ?? false)) {
$this->properties['type'] = FileType::tryFromMimeType($this->getMimeType())->value;
}
return $this->properties['type'] instanceof FileType ? $this->properties['type'] : FileType::from((int)$this->properties['type']);
}
/**
* Useful to find out if this file can be previewed or resized as image.
* @return bool true if File has an image-extension according to $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
*/
public function isImage(): bool
{
return GeneralUtility::inList(strtolower($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] ?? ''), $this->getExtension()) && $this->getSize() > 0;
}
/**
* Useful to find out if this file has a file extension based on any of the registered media extensions
* @return bool true if File is a media-extension according to $GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext']
*/
public function isMediaFile(): bool
{
return GeneralUtility::inList(strtolower($GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext'] ?? ''), $this->getExtension()) && $this->getSize() > 0;
}
/**
* Useful to find out if this file can be edited.
*
* @return bool true if File is a text-based file extension according to $GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext']
*/
public function isTextFile(): bool
{
return GeneralUtility::inList(strtolower($GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'] ?? ''), $this->getExtension());
}
/******************
* CONTENTS RELATED
******************/
/**
* Get the contents of this file
*
* @throws \RuntimeException
*/
public function getContents(): string
{
if ($this->deleted) {
throw new \RuntimeException('File has been deleted.', 1329821479);
}
return $this->getStorage()->getFileContents($this);
}
/**
* Replace the current file contents with the given string
*
* @throws \RuntimeException
* @return $this
*/
public function setContents(string $contents): self
{
if ($this->deleted) {
throw new \RuntimeException('File has been deleted.', 1329821478);
}
$this->getStorage()->setFileContents($this, $contents);
return $this;
}
/****************************************
* STORAGE AND MANAGEMENT RELATED METHODS
****************************************/
/**
* @throws \RuntimeException
*/
public function getStorage(): ResourceStorage
{
if ($this->storage === null) {
throw new \RuntimeException('You\'re using fileObjects without a storage.', 1381570091);
}
return $this->storage;
}
/**
* Checks if this file exists. This should normally always return TRUE;
* it might only return FALSE when this object has been created from an
* index record without checking for.
*
* @return bool TRUE if this file physically exists
*/
public function exists(): bool
{
if ($this->deleted) {
return false;
}
return $this->storage->hasFile($this->getIdentifier());
}
/**
* Sets the storage this file is located in. This is only meant for
* \TYPO3\CMS\Core\Resource-internal usage; don't use it to move files.
*
* @internal Should only be used by other parts of the File API (e.g. drivers after moving a file)
*
* @return $this
*/
public function setStorage(ResourceStorage $storage): self
{
$this->storage = $storage;
$this->properties['storage'] = $storage->getUid();
return $this;
}
/**
* Returns a combined identifier of this file, i.e. the storage UID and the
* folder identifier separated by a colon ":".
*
* @return string Combined storage and file identifier, e.g. StorageUID:path/and/fileName.png
*/
public function getCombinedIdentifier(): string
{
if (!empty($this->properties['storage']) && MathUtility::canBeInterpretedAsInteger($this->properties['storage'])) {
$combinedIdentifier = $this->properties['storage'] . ':' . $this->getIdentifier();
} else {
$combinedIdentifier = $this->getStorage()->getUid() . ':' . $this->getIdentifier();
}
return $combinedIdentifier;
}
/**
* Deletes this file from its storage. This also means that this object becomes useless.
*/
public function delete(): bool
{
// The storage will mark this file as deleted
$wasDeleted = $this->getStorage()->deleteFile($this);
// Unset all properties when deleting the file, as they will be stale anyway
// This needs to happen AFTER the storage deleted the file, because the storage
// emits a signal, which passes the file object to the slots, which may need
// all file properties of the deleted file.
$this->properties = [];
return $wasDeleted;
}
/**
* Marks this file as deleted. This should only be used inside the
* File Abstraction Layer, as it is a low-level API method.
*/
public function setDeleted(): void
{
$this->deleted = true;
}
/**
* Returns TRUE if this file has been deleted
*/
public function isDeleted(): bool
{
return $this->deleted;
}
/*****************
* SPECIAL METHODS
*****************/
/**
* Returns a publicly accessible URL for this file
*
* WARNING: Access to the file may be restricted by further means, e.g. some
* web-based authentication. You have to take care of this yourself.
*
* @return string|null NULL if file is deleted, the generated URL otherwise
*/
public function getPublicUrl(): ?string
{
if ($this->deleted) {
return null;
}
return $this->getStorage()->getPublicUrl($this);
}
/**
* Returns a path to a local version of this file to process it locally (e.g. with some system tool).
* If the file is normally located on a remote storages, this creates a local copy.
* If the file is already on the local system, this only makes a new copy if $writable is set to TRUE.
*
* @param bool $writable Set this to FALSE if you only want to do read operations on the file.
*
* @throws \RuntimeException
* @return non-empty-string
*/
public function getForLocalProcessing(bool $writable = true): string
{
if ($this->deleted) {
throw new \RuntimeException('File has been deleted.', 1329821486);
}
return $this->getStorage()->getFileForLocalProcessing($this, $writable);
}
/***********************
* INDEX RELATED METHODS
***********************/
/**
* Updates properties of this object.
* This method is used to reconstitute settings from the
* database into this object after being instantiated.
*/
abstract public function updateProperties(array $properties);
public function getParentFolder(): Folder
{
return $this->getStorage()->getFolder($this->getStorage()->getFolderIdentifierFromFileIdentifier($this->getIdentifier()));
}
}
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Cache;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Resource\Event\AfterFileContentsSetEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFileDeletedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFileMovedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFileRenamedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFileReplacedEvent;
final readonly class FlushCacheTagForFile
{
public function __construct(
private CacheManager $cacheManager,
#[Autowire(expression: 'service("features").isFeatureEnabled("frontend.cache.autoTagging")')]
private bool $autoTagging
) {}
#[AsEventListener(event: AfterFileContentsSetEvent::class)]
#[AsEventListener(event: AfterFileDeletedEvent::class)]
#[AsEventListener(event: AfterFileMovedEvent::class)]
#[AsEventListener(event: AfterFileRenamedEvent::class)]
#[AsEventListener(event: AfterFileReplacedEvent::class)]
public function __invoke(
AfterFileContentsSetEvent|AfterFileDeletedEvent|AfterFileMovedEvent|AfterFileRenamedEvent|AfterFileReplacedEvent $event
): void {
if (!$this->autoTagging) {
return;
}
$this->cacheManager->flushCachesByTag(sprintf('sys_file_%s', $event->getFile()->getProperty('uid')));
}
}
@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Cache;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Resource\Event\AfterFolderRenamedEvent;
use TYPO3\CMS\Core\Resource\Event\BeforeFolderMovedEvent;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\Folder;
final readonly class FlushCacheTagForFolder
{
public function __construct(
private CacheManager $cacheManager,
#[Autowire(expression: 'service("features").isFeatureEnabled("frontend.cache.autoTagging")')]
private bool $autoTagging
) {}
#[AsEventListener(event: AfterFolderRenamedEvent::class)]
#[AsEventListener(event: BeforeFolderMovedEvent::class)]
public function __invoke(AfterFolderRenamedEvent|BeforeFolderMovedEvent $event): void
{
if (!$this->autoTagging) {
return;
}
$files = $event->getFolder()->getFiles(0, 0, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, true);
$this->cacheManager->flushCachesByTags(
array_map(
static fn(File $file) => sprintf('sys_file_%s', $file->getProperty('uid')),
$files
)
);
}
}
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Cache;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Resource\Event\AfterFileMetaDataCreatedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFileMetaDataDeletedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFileMetaDataUpdatedEvent;
final readonly class FlushCacheTagForMetaData
{
public function __construct(
private CacheManager $cacheManager,
#[Autowire(expression: 'service("features").isFeatureEnabled("frontend.cache.autoTagging")')]
private bool $autoTagging
) {}
#[AsEventListener(event: AfterFileMetaDataCreatedEvent::class)]
#[AsEventListener(event: AfterFileMetaDataDeletedEvent::class)]
#[AsEventListener(event: AfterFileMetaDataUpdatedEvent::class)]
public function __invoke(
AfterFileMetaDataCreatedEvent|AfterFileMetaDataDeletedEvent|AfterFileMetaDataUpdatedEvent $event
): void {
if (!$this->autoTagging) {
return;
}
$cacheTags = [sprintf('sys_file_%s', $event->getFileUid())];
if (method_exists($event, 'getMetaDataUid')) {
$cacheTags[] = sprintf('sys_file_metadata_%s', $event->getMetaDataUid());
}
$this->cacheManager->flushCachesByTags($cacheTags);
}
}
+72
View File
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource;
use TYPO3\CMS\Core\Type\BitSet;
class Capabilities extends BitSet
{
/**
* Capability for being browsable by (backend) users
*/
public const CAPABILITY_BROWSABLE = 1;
/**
* Capability for publicly accessible storages (= accessible from the web)
*/
public const CAPABILITY_PUBLIC = 2;
/**
* Capability for writable storages. This only signifies writability in
* general - this might also be further limited by configuration.
*/
public const CAPABILITY_WRITABLE = 4;
/**
* Whether identifiers contain hierarchy information (folder structure).
*/
public const CAPABILITY_HIERARCHICAL_IDENTIFIERS = 8;
/**
* @param self::CAPABILITY_* $capability
* @return $this
*/
public function removeCapability(int $capability): self
{
$this->unset($capability);
return $this;
}
/**
* @param self::CAPABILITY_* ...$capabilities
* @return $this
*/
public function addCapabilities(int ...$capabilities): self
{
foreach ($capabilities as $capability) {
$this->set($capability);
}
return $this;
}
/**
* @param self::CAPABILITY_* $capability
*/
public function hasCapability(int $capability): bool
{
return $this->get($capability);
}
}
@@ -0,0 +1,232 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Collection;
use TYPO3\CMS\Core\Collection\AbstractRecordCollection;
use TYPO3\CMS\Core\Collection\CollectionInterface;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\FileInterface;
/**
* Abstract collection.
* @extends AbstractRecordCollection<FileInterface>
*/
abstract class AbstractFileCollection extends AbstractRecordCollection
{
/**
* The table name collections are stored to
*
* @var string
*/
protected static $storageTableName = 'sys_file_collection';
/**
* The type of file collection
* (see \TYPO3\CMS\Core\Collection\RecordCollectionRepository::TYPE constants)
*
* @var string
*/
protected static $type;
/**
* The name of the field items are handled with
* (usually either criteria, items or folder)
*
* @var string
*/
protected static $itemsCriteriaField;
/**
* Field contents of $itemsCriteriaField. Defines which the items or search criteria for the items
* depending on the type (see self::$type above) of this file collection.
*
* @var mixed
*/
protected $itemsCriteria;
/**
* Name of the table records of this collection are stored in
*
* @var string
*/
protected $itemTableName = 'sys_file';
/**
* Sets the description.
*
* @param string $description
*/
public function setDescription($description)
{
$this->description = $description;
}
/**
* Return the key of the current element
*
* @return string
*/
public function key(): mixed
{
/** @var File $currentRecord */
$currentRecord = $this->storage->current();
return $currentRecord->getIdentifier();
}
/**
* Generates comma-separated list of entry uids for usage in DataHandler
*
* @param bool $includeTableName
* @return string
*/
protected function getItemUidList($includeTableName = false)
{
$list = [];
/** @var File $entry */
foreach ($this->storage as $entry) {
$list[] = $this->getItemTableName() . '_' . $entry->getUid();
}
return implode(',', $list);
}
/**
* Returns an array of the persistable properties and contents
* which are processable by DataHandler.
*
* @return array
*/
protected function getPersistableDataArray()
{
return [
'title' => $this->getTitle(),
'type' => static::$type,
'description' => $this->getDescription(),
static::$itemsCriteriaField => $this->getItemsCriteria(),
];
}
/**
* Similar to method in \TYPO3\CMS\Core\Collection\AbstractRecordCollection,
* but without 'table_name' => $this->getItemTableName()
*
* @return array
*/
public function toArray()
{
$itemArray = [];
/** @var File $item */
foreach ($this->storage as $item) {
$itemArray[] = $item->toArray();
}
return [
'uid' => $this->getIdentifier(),
'title' => $this->getTitle(),
'description' => $this->getDescription(),
'items' => $itemArray,
];
}
/**
* Gets the current available items.
*
* @return array
*/
public function getItems()
{
$itemArray = [];
/** @var FileInterface $item */
foreach ($this->storage as $item) {
$itemArray[] = $item;
}
return $itemArray;
}
/**
* Similar to method in \TYPO3\CMS\Core\Collection\AbstractRecordCollection,
* but without $this->itemTableName= $array['table_name'],
* but with $this->storageItemsFieldContent = $array[self::$storageItemsField];
*/
public function fromArray(array $array)
{
$this->uid = $array['uid'];
$this->title = $array['title'];
$this->description = $array['description'];
$this->itemsCriteria = $array[static::$itemsCriteriaField];
}
/**
* Gets ths items criteria.
*
* @return mixed
*/
public function getItemsCriteria()
{
return $this->itemsCriteria;
}
/**
* Sets the items criteria.
*
* @param mixed $itemsCriteria
*/
public function setItemsCriteria($itemsCriteria)
{
$this->itemsCriteria = $itemsCriteria;
}
/**
* Adds a file to this collection.
*/
public function add(FileInterface $data)
{
$this->storage->push($data);
}
/**
* Adds all files of another collection to the current one.
*/
public function addAll(CollectionInterface $other)
{
/** @var File $value */
foreach ($other as $value) {
$this->add($value);
}
}
/**
* Removes a file from this collection.
*/
public function remove(File $file)
{
$offset = 0;
/** @var File $value */
foreach ($this->storage as $value) {
if ($value === $file) {
break;
}
$offset++;
}
$this->storage->offsetUnset($offset);
}
/**
* Removes all elements of the current collection.
*/
public function removeAll()
{
$this->storage = new \SplDoublyLinkedList();
}
}
@@ -0,0 +1,92 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Collection;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* A collection containing a set files belonging to certain categories.
* This collection is persisted to the database with the accordant category identifiers.
*/
class CategoryBasedFileCollection extends AbstractFileCollection
{
/**
* @var string
*/
protected static $storageTableName = 'sys_file_collection';
/**
* @var string
*/
protected static $type = 'categories';
/**
* @var string
*/
protected static $itemsCriteriaField = 'category';
/**
* @var string
*/
protected $itemTableName = 'sys_category';
/**
* Populates the content-entries of the collection
*/
public function loadContents()
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_category');
$queryBuilder->getRestrictions()->removeAll();
$statement = $queryBuilder->select('sys_file_metadata.file')
->from('sys_category')
->join(
'sys_category',
'sys_category_record_mm',
'sys_category_record_mm',
$queryBuilder->expr()->eq(
'sys_category_record_mm.uid_local',
$queryBuilder->quoteIdentifier('sys_category.uid')
)
)
->join(
'sys_category_record_mm',
'sys_file_metadata',
'sys_file_metadata',
$queryBuilder->expr()->eq(
'sys_category_record_mm.uid_foreign',
$queryBuilder->quoteIdentifier('sys_file_metadata.uid')
)
)
->where(
$queryBuilder->expr()->eq(
'sys_category.uid',
$queryBuilder->createNamedParameter($this->getItemsCriteria(), Connection::PARAM_INT)
),
$queryBuilder->expr()->eq(
'sys_category_record_mm.tablenames',
$queryBuilder->createNamedParameter('sys_file_metadata')
)
)
->executeQuery();
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
while ($record = $statement->fetchAssociative()) {
$this->add($resourceFactory->getFileObject((int)$record['file']));
}
}
}
@@ -0,0 +1,104 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Collection;
use TYPO3\CMS\Core\SingletonInterface;
/**
* Registry for FileCollection classes
*/
class FileCollectionRegistry implements SingletonInterface
{
/**
* Registered FileCollection types
*
* @var array
*/
protected $types = [];
/**
* Constructor
*/
public function __construct()
{
foreach ($GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['registeredCollections'] as $type => $class) {
$this->registerFileCollectionClass($class, $type);
}
}
/**
* Register a (new) FileCollection type
*
* @param string $className
* @param string $type FileCollection type max length 30 chars (db field restriction)
* @param bool $override existing FileCollection type
* @return bool TRUE if registration succeeded
* @throws \InvalidArgumentException
*/
public function registerFileCollectionClass($className, $type, $override = false)
{
if (strlen($type) > 30) {
throw new \InvalidArgumentException('FileCollection type can have a max string length of 30 bytes', 1391295611);
}
if (!class_exists($className)) {
throw new \InvalidArgumentException('Class ' . $className . ' does not exist.', 1391295613);
}
if (!in_array(AbstractFileCollection::class, class_parents($className) ?: [], true)) {
throw new \InvalidArgumentException('FileCollection ' . $className . ' needs to extend the AbstractFileCollection.', 1391295633);
}
if (isset($this->types[$type])) {
// Return immediately without changing configuration
if ($this->types[$type] === $className) {
return true;
}
if (!$override) {
throw new \InvalidArgumentException('FileCollections ' . $type . ' is already registered.', 1391295643);
}
}
$this->types[$type] = $className;
return true;
}
/**
* Returns a class name for a given type
*
* @param string $type
* @return string The class name
* @throws \InvalidArgumentException
*/
public function getFileCollectionClass($type)
{
if (!isset($this->types[$type])) {
throw new \InvalidArgumentException('Desired FileCollection type "' . $type . '" is not in the list of available FileCollections.', 1391295644);
}
return $this->types[$type];
}
/**
* Checks if the given FileCollection type exists
*
* @param string $type Type of the FileCollection
* @return bool TRUE if the FileCollection exists, FALSE otherwise
*/
public function fileCollectionTypeExists($type)
{
return isset($this->types[$type]);
}
}
@@ -0,0 +1,115 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Collection;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\StorageRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* A collection containing a set of files to be represented as a (virtual) folder.
* This collection is persisted to the database with the accordant folder reference.
*/
class FolderBasedFileCollection extends AbstractFileCollection
{
/**
* @var string
*/
protected static $storageTableName = 'sys_file_collection';
/**
* @var string
*/
protected static $type = 'folder';
/**
* @var string
*/
protected static $itemsCriteriaField = 'folder';
/**
* The folder
*/
protected ?Folder $folder = null;
protected bool $recursive = false;
/**
* Populates the content-entries of the storage
*
* Queries the underlying storage for entries of the collection
* and adds them to the collection data.
*
* If the content entries of the storage had not been loaded on creation
* ($fillItems = false) this function is to be used for loading the contents
* afterward.
*/
public function loadContents()
{
if ($this->folder instanceof Folder) {
$entries = $this->folder->getFiles(0, 0, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, $this->recursive);
foreach ($entries as $entry) {
$this->add($entry);
}
}
}
/**
* Gets the items criteria.
*
* @return string
*/
public function getItemsCriteria()
{
return $this->folder->getCombinedIdentifier();
}
/**
* Returns an array of the persistable properties and contents
* which are processable by DataHandler.
*
* @return array
*/
protected function getPersistableDataArray()
{
return [
'title' => $this->getTitle(),
'type' => self::$type,
'description' => $this->getDescription(),
'folder_identifier' => $this->folder->getCombinedIdentifier(),
];
}
/**
* Similar to method in \TYPO3\CMS\Core\Collection\AbstractRecordCollection,
* but without $this->itemTableName= $array['table_name'],
* but with $this->storageItemsFieldContent = $array[self::$storageItemsField];
*/
public function fromArray(array $array)
{
$this->uid = (int)$array['uid'];
$this->title = (string)$array['title'];
$this->description = (string)$array['description'];
$this->recursive = (bool)$array['recursive'];
if (str_contains($array['folder_identifier'] ?? '', ':')) {
$parts = GeneralUtility::trimExplode(':', $array['folder_identifier']);
$storageRepository = GeneralUtility::makeInstance(StorageRepository::class);
$storage = $storageRepository->findByUid((int)$parts[0]);
if ($storage) {
$this->folder = $storage->getFolder($parts[1]);
}
}
}
}
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Collection;
use TYPO3\CMS\Core\Resource\FileReference;
/**
* When first accessed, this class will initialize itself and find the file references
* for this record field.
*
* This class acts as a "Value holder", as it only fetches the related file references
* when needed.
*
* @internal not part of public API, as this needs to be streamlined and proven
*/
class LazyFileReferenceCollection implements \IteratorAggregate, \ArrayAccess, \Countable
{
/**
* @var FileReference[]|\Closure
*/
private array|\Closure $items;
public function __construct(
private readonly mixed $fieldValue,
\Closure $initialization
) {
$this->items = $initialization;
}
public function count(): int
{
$this->initialize();
return count($this->items);
}
private function initialize(): void
{
if ($this->items instanceof \Closure) {
$this->items = ($this->items)();
}
}
public function getIterator(): \Iterator
{
$this->initialize();
return new \ArrayIterator($this->items);
}
public function __toString(): string
{
return (string)$this->fieldValue;
}
public function offsetExists(mixed $offset): bool
{
$this->initialize();
return isset($this->items[$offset]);
}
public function offsetGet(mixed $offset): mixed
{
$this->initialize();
return $this->items[$offset] ?? null;
}
public function offsetSet(mixed $offset, mixed $value): void
{
if ($value instanceof FileReference === false) {
throw new \InvalidArgumentException(
'Modifying the file reference collection is only allowed by setting a value of type FileReference.',
1723188317
);
}
$this->items[$offset] = $value;
}
public function offsetUnset(mixed $offset): void
{
throw new \RuntimeException('Removing items from the file reference collection is not implemented.', 1723188318);
}
}
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Collection;
use TYPO3\CMS\Core\Resource\Folder;
/**
* When first accessed, this class will initialize itself and find the folders
* for this record field.
*
* This class acts as a "Value holder", as it only fetches the related folders
* when needed.
*
* @internal not part of public API, as this needs to be streamlined and proven
*/
class LazyFolderCollection implements \IteratorAggregate, \ArrayAccess, \Countable
{
/**
* @var Folder[]|\Closure
*/
private array|\Closure $items;
public function __construct(
private readonly mixed $fieldValue,
\Closure $initialization
) {
$this->items = $initialization;
}
public function count(): int
{
$this->initialize();
return count($this->items);
}
private function initialize(): void
{
if ($this->items instanceof \Closure) {
$this->items = ($this->items)();
}
}
public function getIterator(): \Iterator
{
$this->initialize();
return new \ArrayIterator($this->items);
}
public function __toString(): string
{
return (string)$this->fieldValue;
}
public function offsetExists(mixed $offset): bool
{
$this->initialize();
return isset($this->items[$offset]);
}
public function offsetGet(mixed $offset): mixed
{
$this->initialize();
return $this->items[$offset] ?? null;
}
public function offsetSet(mixed $offset, mixed $value): void
{
if ($value instanceof Folder === false) {
throw new \InvalidArgumentException(
'Modifying the folder collection is only allowed by setting a value of type Folder.',
1724136133
);
}
$this->items[$offset] = $value;
}
public function offsetUnset(mixed $offset): void
{
throw new \RuntimeException('Removing items from the folder collection is not implemented.', 1724136134);
}
}
@@ -0,0 +1,60 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Collection;
use TYPO3\CMS\Core\Resource\FileRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* A collection containing a static set of files. This collection is persisted
* to the database with references to all files it contains.
*/
class StaticFileCollection extends AbstractFileCollection
{
/**
* @var string
*/
protected static $type = 'static';
/**
* @var string
*/
protected static $itemsCriteriaField = 'files';
/**
* @var string
*/
protected $itemTableName = 'sys_file_reference';
/**
* Populates the content-entries of the storage
*
* Queries the underlying storage for entries of the collection
* and adds them to the collection data.
*
* If the content entries of the storage had not been loaded on creation
* ($fillItems = false) this function is to be used for loading the contents
* afterwards.
*/
public function loadContents()
{
$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
$fileReferences = $fileRepository->findByRelation('sys_file_collection', 'files', $this->getIdentifier());
foreach ($fileReferences as $file) {
$this->add($file);
}
}
}
@@ -0,0 +1,123 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource;
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\EventDispatcher\EventDispatcher;
use TYPO3\CMS\Core\Resource\Event\AfterDefaultUploadFolderWasResolvedEvent;
use TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException;
/**
* Finds the best matching upload folder for a specific backend user
* when uploading or selecting files, based on UserTSconfig or PageTSconfig
*/
#[Autoconfigure(public: true)]
readonly class DefaultUploadFolderResolver
{
public function __construct(
protected ResourceFactory $resourceFactory,
protected EventDispatcher $eventDispatcher,
) {}
public function resolve(BackendUserAuthentication $user, ?int $pid = null, ?string $table = null, ?string $field = null): Folder|bool
{
$uploadFolder = $this->getDefaultUploadFolderForUser($user);
$uploadFolder = $this->getDefaultUploadFolderForPage($pid) ?? $uploadFolder;
$uploadFolder = $this->eventDispatcher->dispatch(
new AfterDefaultUploadFolderWasResolvedEvent($uploadFolder, $pid, $table, $field)
)->getUploadFolder() ?? $uploadFolder;
$uploadFolder = $uploadFolder ?? $this->getDefaultUploadFolder($user);
return $uploadFolder instanceof Folder ? $uploadFolder : false;
}
public function getDefaultUploadFolderForUser(BackendUserAuthentication $backendUser): ?Folder
{
$uploadFolder = $backendUser->getTSConfig()['options.']['defaultUploadFolder'] ?? '';
return $this->resolveFolder($uploadFolder);
}
public function getDefaultUploadFolderForPage(?int $pid): ?Folder
{
$uploadFolder = BackendUtility::getPagesTSconfig($pid)['options.']['defaultUploadFolder'] ?? '';
return $this->resolveFolder($uploadFolder);
}
protected function resolveFolder(string $uploadPath): ?Folder
{
$uploadFolder = null;
if ($uploadPath) {
try {
$uploadFolder = $this->resourceFactory->getFolderObjectFromCombinedIdentifier($uploadPath);
} catch (FolderDoesNotExistException $e) {
}
}
return $uploadFolder;
}
/**
* Detects the first default folder of the first storage that the backend user has access to.
* If the default storage is not available, all other storages are then checked as well.
*
* @param BackendUserAuthentication $backendUser
* @return Folder|null
*/
protected function getDefaultUploadFolder(BackendUserAuthentication $backendUser): ?Folder
{
$uploadFolder = null;
foreach ($backendUser->getFileStorages() as $storage) {
if ($storage->isDefault() && $storage->isWritable()) {
try {
$uploadFolder = $storage->getDefaultFolder();
if ($uploadFolder->checkActionPermission('write')) {
break;
}
$uploadFolder = null;
} catch (Exception $folderAccessException) {
// If the folder is not accessible (no permissions / does not exist) we skip this one.
}
break;
}
}
if (!$uploadFolder instanceof Folder) {
foreach ($backendUser->getFileStorages() as $storage) {
if ($storage->isWritable()) {
try {
$uploadFolder = $storage->getDefaultFolder();
if ($uploadFolder->checkActionPermission('write')) {
break;
}
$uploadFolder = null;
} catch (Exception $folderAccessException) {
// If the folder is not accessible (no permissions / does not exist) try the next one.
}
}
}
}
return $uploadFolder;
}
}
+177
View File
@@ -0,0 +1,177 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Driver;
use TYPO3\CMS\Core\Resource\Capabilities;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* An abstract implementation of a storage driver.
*/
abstract class AbstractDriver implements DriverInterface
{
/*******************
* CAPABILITIES
*******************/
/**
* The capabilities of this driver. This value should be set in the constructor of derived classes.
*/
protected Capabilities $capabilities;
/**
* The storage uid the driver was instantiated for
*/
protected ?int $storageUid = null;
/**
* A list of all supported hash algorithms, written all lower case and
* without any dashes etc. (e.g. sha1 instead of SHA-1)
* Be sure to set this in inherited classes!
*
* @phpstan-var list<string>
*
* @todo: Remove this from this class. Properties of abstract classes MUST NOT be api. If all drivers
* need to implement this, consider creating a new method stub in the DriverInterface or consider
* creating a new SupportedHashAlgorithmsAwareInterface that demands implementations to provide said
* information. Inside this abstract class, this property is useless, however.
*/
protected array $supportedHashAlgorithms = [];
/**
* The configuration of this driver
*/
protected array $configuration = [];
/**
* Creates this object.
*/
public function __construct(array $configuration = [])
{
$this->configuration = $configuration;
}
/**
* Checks a fileName for validity. This could be overridden in concrete
* drivers if they have different file naming rules.
*/
protected function isValidFilename(string $fileName): bool
{
if (str_contains($fileName, '/')) {
return false;
}
if (!preg_match('/^[\\pL\\d[:blank:]._-]*$/u', $fileName)) {
return false;
}
return true;
}
/**
* Sets the storage uid the driver belongs to
*/
public function setStorageUid(int $storageUid): void
{
$this->storageUid = $storageUid;
}
/**
* Returns the capabilities of this driver.
*/
public function getCapabilities(): Capabilities
{
return $this->capabilities;
}
/**
* Returns TRUE if this driver has the given capability.
*
* @phpstan-param Capabilities::CAPABILITY_* $capability
*/
public function hasCapability(int $capability): bool
{
return $this->getCapabilities()->hasCapability($capability);
}
/*******************
* FILE FUNCTIONS
*******************/
/**
* Returns a temporary path for a given file, including the file extension.
*
* @phpstan-param non-empty-string $fileIdentifier
* @phpstan-return non-empty-string
*/
protected function getTemporaryPathForFile(string $fileIdentifier): string
{
return GeneralUtility::tempnam('fal-tempfile-', '.' . PathUtility::pathinfo($fileIdentifier, PATHINFO_EXTENSION));
}
/**
* Hashes a file identifier, taking the case sensitivity of the file system
* into account. This helps mitigating problems with case-insensitive
* databases.
*
* @phpstan-param non-empty-string $identifier
* @phpstan-return non-empty-string
*/
public function hashIdentifier(string $identifier): string
{
$identifier = $this->canonicalizeAndCheckFileIdentifier($identifier);
return sha1($identifier);
}
/**
* Returns TRUE if this driver uses case-sensitive identifiers. NOTE: This
* is a configurable setting, but the setting does not change the way the
* underlying file system treats the identifiers; the setting should
* therefore always reflect the file system and not try to change its
* behaviour
*/
public function isCaseSensitiveFileSystem(): bool
{
if (isset($this->configuration['caseSensitive'])) {
return (bool)$this->configuration['caseSensitive'];
}
return true;
}
/**
* Makes sure the path given as parameter is valid
*
* @phpstan-param non-empty-string $filePath The file path (most times filePath)
* @phpstan-return non-empty-string
*/
abstract protected function canonicalizeAndCheckFilePath(string $filePath): string;
/**
* Makes sure the identifier given as parameter is valid
*
* @phpstan-param non-empty-string $fileIdentifier The file Identifier
* @phpstan-return non-empty-string
*/
abstract protected function canonicalizeAndCheckFileIdentifier(string $fileIdentifier): string;
/**
* Makes sure the identifier given as parameter is valid
*
* @phpstan-param non-empty-string $folderIdentifier The folder identifier
* @phpstan-return non-empty-string
*/
abstract protected function canonicalizeAndCheckFolderIdentifier(string $folderIdentifier): string;
}
@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Driver;
use TYPO3\CMS\Core\Resource\Exception\InvalidPathException;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* Contains a few classes that might be useful for hierarchical drivers.
*/
abstract class AbstractHierarchicalFilesystemDriver extends AbstractDriver
{
/**
* Wrapper for \TYPO3\CMS\Core\Utility\GeneralUtility::validPathStr()
*
* @return bool TRUE if no '/', '..' or '\' is in the $theFile
*/
protected function isPathValid(string $theFile): bool
{
return GeneralUtility::validPathStr($theFile);
}
/**
* Makes sure the given path is valid.
*
* @phpstan-param non-empty-string $filePath The file path (including the file name!)
* @phpstan-return non-empty-string
*/
protected function canonicalizeAndCheckFilePath(string $filePath): string
{
$filePath = PathUtility::getCanonicalPath($filePath);
// $filePath must be valid
if (!$this->isPathValid($filePath)) {
throw new InvalidPathException('File ' . $filePath . ' is not valid (".." and "//" is not allowed in path).', 1320286857);
}
return $filePath;
}
/**
* Makes sure the Path given as parameter is valid.
*
* @param string $fileIdentifier The file path (including the file name!)
*/
protected function canonicalizeAndCheckFileIdentifier(string $fileIdentifier): string
{
if ($fileIdentifier !== '') {
$fileIdentifier = $this->canonicalizeAndCheckFilePath($fileIdentifier);
$fileIdentifier = '/' . ltrim($fileIdentifier, '/');
if (!$this->isCaseSensitiveFileSystem()) {
$fileIdentifier = mb_strtolower($fileIdentifier, 'utf-8');
}
}
return $fileIdentifier;
}
/**
* Makes sure the Path given as parameter is valid.
*
* @phpstan-param non-empty-string $folderIdentifier The file path (including the file name!)
* @phpstan-return non-empty-string
*/
protected function canonicalizeAndCheckFolderIdentifier(string $folderIdentifier): string
{
if ($folderIdentifier === '/') {
return '/';
}
return rtrim($this->canonicalizeAndCheckFileIdentifier($folderIdentifier), '/') . '/';
}
/**
* Returns the identifier of the folder the file resides in.
*
* @phpstan-param non-empty-string $fileIdentifier
* @phpstan-return non-empty-string
*/
public function getParentFolderIdentifierOfIdentifier(string $fileIdentifier): string
{
$fileIdentifier = $this->canonicalizeAndCheckFileIdentifier($fileIdentifier);
return rtrim(GeneralUtility::fixWindowsFilePath(PathUtility::dirname($fileIdentifier)), '/') . '/';
}
}
+459
View File
@@ -0,0 +1,459 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Driver;
use TYPO3\CMS\Core\Resource\Capabilities;
/**
* An interface Drivers have to implement to fulfil the needs
* of the FAL API.
*/
interface DriverInterface
{
/**
* Processes the configuration for this driver.
*/
public function processConfiguration(): void;
/**
* Sets the storage uid the driver belongs to
*/
public function setStorageUid(int $storageUid): void;
/**
* Initializes this object. This is called by the storage after the driver
* has been attached.
*/
public function initialize(): void;
/**
* Returns the capabilities of this driver.
*/
public function getCapabilities(): Capabilities;
/**
* Merges the capabilities merged by the user at the storage
* configuration into the actual capabilities of the driver
* and returns the result.
*/
public function mergeConfigurationCapabilities(Capabilities $capabilities): Capabilities;
/**
* Returns TRUE if this driver has the given capability.
*
* @param Capabilities::CAPABILITY_* $capability
*/
public function hasCapability(int $capability): bool;
/**
* Returns TRUE if this driver uses case-sensitive identifiers. NOTE: This
* is a configurable setting, but the setting does not change the way the
* underlying file system treats the identifiers; the setting should
* therefore always reflect the file system and not try to change its
* behaviour
*/
public function isCaseSensitiveFileSystem(): bool;
/**
* Cleans a fileName from not allowed characters
*
* @param non-empty-string $fileName
* @return non-empty-string the sanitized filename
*/
public function sanitizeFileName(string $fileName): string;
/**
* Hashes a file identifier, taking the case sensitivity of the file system
* into account. This helps mitigating problems with case-insensitive
* databases.
*
* @param non-empty-string $identifier
* @return non-empty-string
*/
public function hashIdentifier(string $identifier): string;
/**
* Returns the identifier of the root level folder of the storage.
*
* @return non-empty-string
*/
public function getRootLevelFolder(): string;
/**
* Returns the identifier of the default folder new files should be put into.
*
* @return non-empty-string
*/
public function getDefaultFolder(): string;
/**
* Returns the identifier of the folder the file resides in
*
* @param non-empty-string $fileIdentifier
* @return non-empty-string
*/
public function getParentFolderIdentifierOfIdentifier(string $fileIdentifier): string;
/**
* Returns the public URL to a file.
* Either fully qualified URL or relative to public web path (rawurlencoded).
*
* @param non-empty-string $identifier
* @return non-empty-string|null NULL if file is missing or deleted, the generated url otherwise
*/
public function getPublicUrl(string $identifier): ?string;
/**
* Creates a folder, within a parent folder.
* If no parent folder is given, a root level folder will be created
*
* @param non-empty-string $newFolderName
* @return non-empty-string the Identifier of the new folder
*/
public function createFolder(string $newFolderName, string $parentFolderIdentifier = '', bool $recursive = false): string;
/**
* Renames a folder in this storage.
*
* @param non-empty-string $folderIdentifier
* @param non-empty-string $newName
* @return array<string, string> A map of old to new file identifiers of all affected resources
*/
public function renameFolder(string $folderIdentifier, string $newName): array;
/**
* Removes a folder in filesystem.
*
* @param non-empty-string $folderIdentifier
*/
public function deleteFolder(string $folderIdentifier, bool $deleteRecursively = false): bool;
/**
* Checks if a file exists.
*
* @param non-empty-string $fileIdentifier
*/
public function fileExists(string $fileIdentifier): bool;
/**
* Checks if a folder exists.
*
* @param non-empty-string $folderIdentifier
*/
public function folderExists(string $folderIdentifier): bool;
/**
* Checks if a folder contains files and (if supported) other folders.
*
* @param non-empty-string $folderIdentifier
* @return bool TRUE if there are no files and folders within $folder
*/
public function isFolderEmpty(string $folderIdentifier): bool;
/**
* Adds a file from the local server hard disk to a given path in TYPO3s
* virtual file system. This assumes that the local file exists, so no
* further check is done here! After a successful operation the original
* file must not exist anymore.
*
* @param non-empty-string $localFilePath within public web path
* @param non-empty-string $targetFolderIdentifier
* @param string $newFileName optional, if not given original name is used
* @param bool $removeOriginal if set the original file will be removed
* after successful operation
* @return non-empty-string the identifier of the new file
*/
public function addFile(string $localFilePath, string $targetFolderIdentifier, string $newFileName = '', bool $removeOriginal = true): string;
/**
* Creates a new (empty) file and returns the identifier.
*
* @param non-empty-string $fileName
* @param non-empty-string $parentFolderIdentifier
* @return non-empty-string
*/
public function createFile(string $fileName, string $parentFolderIdentifier): string;
/**
* Copies a file *within* the current storage.
* Note that this is only about an inner storage copy action,
* where a file is just copied to another folder in the same storage.
*
* @param non-empty-string $fileIdentifier
* @param non-empty-string $targetFolderIdentifier
* @param non-empty-string $fileName
* @return non-empty-string the Identifier of the new file
*/
public function copyFileWithinStorage(string $fileIdentifier, string $targetFolderIdentifier, string $fileName): string;
/**
* Renames a file in this storage.
*
* @param non-empty-string $fileIdentifier
* @param non-empty-string $newName The target path (including the file name!)
* @return non-empty-string The identifier of the file after renaming
*/
public function renameFile(string $fileIdentifier, string $newName): string;
/**
* Replaces a file with file in local file system.
*
* @param non-empty-string $fileIdentifier
* @param non-empty-string $localFilePath
*/
public function replaceFile(string $fileIdentifier, string $localFilePath): bool;
/**
* Removes a file from the filesystem. This does not check if the file is
* still used or if it is a bad idea to delete it for some other reason
* this has to be taken care of in the upper layers (e.g. the Storage)!
*
* @param non-empty-string $fileIdentifier
*/
public function deleteFile(string $fileIdentifier): bool;
/**
* Creates a hash for a file.
*
* @param non-empty-string $fileIdentifier
* @param non-empty-string $hashAlgorithm The hash algorithm to use
*/
public function hash(string $fileIdentifier, string $hashAlgorithm): string;
/**
* Moves a file *within* the current storage.
* Note that this is only about an inner-storage move action,
* where a file is just moved to another folder in the same storage.
*
* @param non-empty-string $fileIdentifier
* @param non-empty-string $targetFolderIdentifier
* @param non-empty-string $newFileName
* @return non-empty-string
*/
public function moveFileWithinStorage(string $fileIdentifier, string $targetFolderIdentifier, string $newFileName): string;
/**
* Folder equivalent to moveFileWithinStorage().
*
* @param non-empty-string $sourceFolderIdentifier
* @param non-empty-string $targetFolderIdentifier
* @param non-empty-string $newFolderName
* @return array<non-empty-string, non-empty-string> All files which are affected, map of old => new file identifiers
*/
public function moveFolderWithinStorage(string $sourceFolderIdentifier, string $targetFolderIdentifier, string $newFolderName): array;
/**
* Folder equivalent to copyFileWithinStorage().
*
* @param non-empty-string $sourceFolderIdentifier
* @param non-empty-string $targetFolderIdentifier
* @param non-empty-string $newFolderName
*/
public function copyFolderWithinStorage(string $sourceFolderIdentifier, string $targetFolderIdentifier, string $newFolderName): bool;
/**
* Returns the contents of a file. Beware that this requires to load the
* complete file into memory and also may require fetching the file from an
* external location. So this might be an expensive operation (both in terms
* of processing resources and money) for large files.
*
* @param non-empty-string $fileIdentifier
*/
public function getFileContents(string $fileIdentifier): string;
/**
* Sets the contents of a file to the specified value.
*
* @param non-empty-string $fileIdentifier
* @return int<0, max> The number of bytes written to the file
*/
public function setFileContents(string $fileIdentifier, string $contents): int;
/**
* Checks if a file inside a folder exists
*
* @param non-empty-string $fileName
* @param non-empty-string $folderIdentifier
*/
public function fileExistsInFolder(string $fileName, string $folderIdentifier): bool;
/**
* Checks if a folder inside a folder exists.
*
* @param non-empty-string $folderName
* @param non-empty-string $folderIdentifier
*/
public function folderExistsInFolder(string $folderName, string $folderIdentifier): bool;
/**
* Returns a path to a local copy of a file for processing it. When changing the
* file, you have to take care of replacing the current version yourself!
*
* @param non-empty-string $fileIdentifier
* @param bool $writable Set this to FALSE if you only need the file for read
* operations. This might speed up things, e.g. by using
* a cached local version. Never modify the file if you
* have set this flag!
* @return non-empty-string The path to the file on the local disk
*/
public function getFileForLocalProcessing(string $fileIdentifier, bool $writable = true): string;
/**
* Returns the permissions of a file/folder as an array
* (keys r, w) of boolean flags
*
* @param non-empty-string $identifier
* @return array{r: bool, w: bool}
*/
public function getPermissions(string $identifier): array;
/**
* Directly output the contents of the file to the output
* buffer. Should not take care of header files or flushing
* buffer before. Will be taken care of by the Storage.
*
* @param non-empty-string $identifier
*/
public function dumpFileContents(string $identifier): void;
/**
* Checks if a given identifier is within a container, e.g. if
* a file or folder is within another folder.
* This can e.g. be used to check for web-mounts.
*
* Hint: this also needs to return TRUE if the given identifier
* matches the container identifier to allow access to the root
* folder of a file mount.
*
* @param non-empty-string $folderIdentifier
* @param non-empty-string $identifier identifier to be checked against $folderIdentifier
* @return bool TRUE if $content is within or matches $folderIdentifier
*/
public function isWithin(string $folderIdentifier, string $identifier): bool;
/**
* Returns information about a file.
*
* @param non-empty-string $fileIdentifier
* @param list<string> $propertiesToExtract Array of properties which are be extracted
* If empty all will be extracted
* @return array<string, mixed>
*/
public function getFileInfoByIdentifier(string $fileIdentifier, array $propertiesToExtract = []): array;
/**
* Returns information about a folder.
*
* @param non-empty-string $folderIdentifier
* @return array{
* identifier: non-empty-string,
* name: string,
* mtime: int,
* ctime: int,
* storage: int,
* }
*/
public function getFolderInfoByIdentifier(string $folderIdentifier): array;
/**
* Returns the identifier of a file inside the folder
*
* @param non-empty-string $fileName
* @param non-empty-string $folderIdentifier
* @return non-empty-string file identifier
*/
public function getFileInFolder(string $fileName, string $folderIdentifier): string;
/**
* Returns a list of files inside the specified path
*
* @param non-empty-string $folderIdentifier
* @param int<0, max> $start
* @param int<0, max> $numberOfItems
* @param list<callable> $filenameFilterCallbacks callbacks for filtering the items
* @param string $sort Property name used to sort the items.
* Among them may be: '' (empty, no sorting), name,
* fileext, size, tstamp and rw.
* If a driver does not support the given property, it
* should fall back to "name".
* @param bool $sortRev TRUE to indicate reverse sorting (last to first)
* @return list<string> of FileIdentifiers
*/
public function getFilesInFolder(
string $folderIdentifier,
int $start = 0,
int $numberOfItems = 0,
bool $recursive = false,
array $filenameFilterCallbacks = [],
string $sort = '',
bool $sortRev = false
): array;
/**
* Returns the identifier of a folder inside the folder
*
* @param non-empty-string $folderName The name of the target folder
* @param non-empty-string $folderIdentifier
* @return non-empty-string folder identifier
*/
public function getFolderInFolder(string $folderName, string $folderIdentifier): string;
/**
* Returns a list of folders inside the specified path
*
* @param non-empty-string $folderIdentifier
* @param int<0, max> $start
* @param int<0, max> $numberOfItems
* @param list<callable> $folderNameFilterCallbacks callbacks for filtering the items
* @param string $sort Property name used to sort the items.
* Among them may be: '' (empty, no sorting), name,
* fileext, size, tstamp and rw.
* If a driver does not support the given property, it
* should fall back to "name".
* @param bool $sortRev TRUE to indicate reverse sorting (last to first)
* @return array<string|int, string> folder identifiers (where key and value are identical, but int-like identifiers
* will get converted to int array keys)
*/
public function getFoldersInFolder(
string $folderIdentifier,
int $start = 0,
int $numberOfItems = 0,
bool $recursive = false,
array $folderNameFilterCallbacks = [],
string $sort = '',
bool $sortRev = false
): array;
/**
* Returns the number of files inside the specified path
*
* @param non-empty-string $folderIdentifier
* @param list<callable> $filenameFilterCallbacks callbacks for filtering the items
* @return int<0, max> Number of files in folder
*/
public function countFilesInFolder(string $folderIdentifier, bool $recursive = false, array $filenameFilterCallbacks = []): int;
/**
* Returns the number of folders inside the specified path
*
* @param non-empty-string $folderIdentifier
* @param list<callable> $folderNameFilterCallbacks callbacks for filtering the items
* @return int<0, max> Number of folders in folder
*/
public function countFoldersInFolder(string $folderIdentifier, bool $recursive = false, array $folderNameFilterCallbacks = []): int;
}
+140
View File
@@ -0,0 +1,140 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Driver;
use TYPO3\CMS\Core\SingletonInterface;
/**
* Registry for driver classes.
*/
class DriverRegistry implements SingletonInterface
{
/**
* @var array
*/
protected $drivers = [];
/**
* @var array
*/
protected $driverConfigurations = [];
/**
* Creates this object by detecting all available drivers registered in $TYPO3_CONF_VARS.
*/
public function __construct()
{
$driverConfigurations = $GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['registeredDrivers'];
foreach ($driverConfigurations as $shortName => $driverConfig) {
$shortName = $shortName ?: $driverConfig['shortName'] ?? '';
$this->registerDriverClass($driverConfig['class'] ?? '', $shortName, $driverConfig['label'] ?? '', $driverConfig['flexFormDS'] ?? '');
}
}
/**
* Registers a driver class with an optional short name.
*
* @param string $className
* @param string|null $shortName
* @param string $label
* @param string $flexFormDataStructurePathAndFilename
* @return bool TRUE if registering succeeded
* @throws \InvalidArgumentException
*/
public function registerDriverClass($className, $shortName = null, $label = null, $flexFormDataStructurePathAndFilename = null)
{
// todo: Default of $shortName must be empty string, not null.
$shortName = (string)$shortName;
// check if the class is available for TYPO3 before registering the driver
if (!class_exists($className)) {
throw new \InvalidArgumentException('Class ' . $className . ' does not exist.', 1314979197);
}
if (!in_array(DriverInterface::class, class_implements($className) ?: [], true)) {
throw new \InvalidArgumentException('Driver ' . $className . ' needs to implement the DriverInterface.', 1387619575);
}
if ($shortName === '') {
$shortName = $className;
}
if (array_key_exists($shortName, $this->drivers)) {
// Return immediately without changing configuration
if ($this->drivers[$shortName] === $className) {
return true;
}
throw new \InvalidArgumentException('Driver ' . $shortName . ' is already registered.', 1314979451);
}
$this->drivers[$shortName] = $className;
$this->driverConfigurations[$shortName] = [
'class' => $className,
'shortName' => $shortName,
'label' => $label,
'flexFormDS' => $flexFormDataStructurePathAndFilename,
];
return true;
}
/**
* Adds the TCA information so the registered drivers can be selected when creating a sys_file_storage
* in the TYPO3 Backend.
*/
public function addDriversToTCA(): void
{
$driverFieldConfig = &$GLOBALS['TCA']['sys_file_storage']['columns']['driver']['config'];
$types = &$GLOBALS['TCA']['sys_file_storage']['types'];
foreach ($this->driverConfigurations as $driver) {
$label = $driver['label'] ?: $driver['class'];
$driverId = $driver['shortName'];
$driverFieldConfig['items'][$driverId] = ['label' => $label, 'value' => $driverId];
$types[$driverId] = $types['0'];
if ($driver['flexFormDS']) {
$types[$driverId]['columnsOverrides']['configuration']['config']['ds'] = $driver['flexFormDS'];
}
}
}
/**
* Returns a class name for a given class name or short name.
*
* @param string $shortName
* @return string The class name
* @throws \InvalidArgumentException
*/
public function getDriverClass($shortName)
{
if (in_array($shortName, $this->drivers) && class_exists($shortName)) {
return $shortName;
}
if (!array_key_exists($shortName, $this->drivers)) {
throw new \InvalidArgumentException(
'Desired storage "' . $shortName . '" is not in the list of available storages.',
1314085990
);
}
return $this->drivers[$shortName];
}
/**
* Checks if the given driver exists
*
* @param string $shortName Name of the driver
* @return bool TRUE if the driver exists, FALSE otherwise
*/
public function driverExists($shortName)
{
return array_key_exists($shortName, $this->drivers);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Driver;
use Psr\Http\Message\ResponseInterface;
/**
* An interface FAL drivers have to implement to fulfil the needs
* of streaming files using PSR-7 Response objects.
*
* @internal
*/
interface StreamableDriverInterface
{
/**
* Streams a file using a PSR-7 Response object.
*/
public function streamFile(string $identifier, array $properties): ResponseInterface;
}
@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Enum;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Enumeration for DuplicationBehavior
*/
enum DuplicationBehavior: string
{
/**
* If a file is uploaded and another file with
* the same name already exists, the new file
* is renamed.
*/
case RENAME = 'rename';
/**
* If a file is uploaded and another file with
* the same name already exists, the old file
* gets overwritten by the new file.
*/
case REPLACE = 'replace';
/**
* If a file is uploaded and another file with
* the same name already exists, the process is
* aborted.
*/
case CANCEL = 'cancel';
/**
* Return the default duplication behaviour action, set in TSconfig
*/
public static function getDefaultDuplicationBehaviour(?BackendUserAuthentication $backendUserAuthentication = null): DuplicationBehavior
{
if ($backendUserAuthentication === null) {
return self::CANCEL;
}
$defaultAction = $backendUserAuthentication->getTSConfig()['options.']['file_list.']['uploader.']['defaultAction'] ?? '';
if ($defaultAction === '') {
return self::CANCEL;
}
$duplicationBehavior = self::tryFrom($defaultAction);
if ($duplicationBehavior !== null) {
return $duplicationBehavior;
}
GeneralUtility::makeInstance(LogManager::class)
->getLogger(__CLASS__)
->warning('TSConfig: options.file_list.uploader.defaultAction contains an invalid value ("{value}"), fallback to default value: "{default}"', [
'value' => $defaultAction,
'default' => self::CANCEL->value,
]);
return self::CANCEL;
}
}
@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FolderInterface;
/**
* Event that is fired after the default upload folder for a user was checked
*/
final class AfterDefaultUploadFolderWasResolvedEvent
{
public function __construct(
private ?FolderInterface $uploadFolder,
private readonly ?int $pid,
private readonly ?string $table,
private readonly ?string $fieldName
) {}
public function getUploadFolder(): ?FolderInterface
{
return $this->uploadFolder;
}
public function setUploadFolder(FolderInterface $uploadFolder): void
{
$this->uploadFolder = $uploadFolder;
}
public function getPid(): ?int
{
return $this->pid;
}
public function getTable(): ?string
{
return $this->table;
}
public function getFieldName(): ?string
{
return $this->fieldName;
}
}
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired after a file was added to the Resource Storage / Driver.
*
* Use case: Using listeners for this event allows to e.g. post-check permissions or
* specific analysis of files like additional metadata analysis after adding them to TYPO3.
*/
final readonly class AfterFileAddedEvent
{
public function __construct(private FileInterface $file, private Folder $folder) {}
public function getFile(): FileInterface
{
return $this->file;
}
public function getFolder(): Folder
{
return $this->folder;
}
}
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
/**
* This event is fired once an index was just added to the database (= indexed).
*
* Examples: Allows to additionally populate custom fields of the sys_file/sys_file_metadata database records.
*/
final readonly class AfterFileAddedToIndexEvent
{
public function __construct(private int $fileUid, private array $record) {}
public function getFileUid(): int
{
return $this->fileUid;
}
public function getRecord(): array
{
return $this->record;
}
}
@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
/**
* Event that is triggered after a file command has been processed. Can be used
* to perform additional tasks for specific commands. For example, trigger a
* custom indexer after a file has been uploaded.
*/
final readonly class AfterFileCommandProcessedEvent
{
public function __construct(
private array $command,
private mixed $result,
private string $conflictMode
) {}
/**
* A single command, e.g.
*
* ```
* 'upload' => [
* 'target' => '1:/some/folder/'
* 'data' => '1'
* ]
* ```
*
* @return array<string, array<string, mixed>>
*/
public function getCommand(): array
{
return $this->command;
}
/**
* @return mixed The result - Depending on the performed action,
* this could e.g. be a File or just a boolean.
*/
public function getResult(): mixed
{
return $this->result;
}
/**
* @return string The current conflict mode
* @see DuplicationBehavior
*/
public function getConflictMode(): string
{
return $this->conflictMode;
}
}
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FileInterface;
/**
* This event is fired after the contents of a file got set / replaced.
*
* Examples: Listeners can analyze content for AI purposes within Extensions.
*/
final readonly class AfterFileContentsSetEvent
{
public function __construct(private FileInterface $file, private string $content) {}
public function getFile(): FileInterface
{
return $this->file;
}
public function getContent(): string
{
return $this->content;
}
}
@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired after a file was copied within a Resource Storage / Driver.
* The folder represents the "target folder".
*
* Example: Listeners can sign up for listing duplicates using this event.
*/
final readonly class AfterFileCopiedEvent
{
public function __construct(
private FileInterface $file,
private Folder $folder,
private string $newFileIdentifier,
private ?FileInterface $newFile
) {}
public function getFile(): FileInterface
{
return $this->file;
}
public function getFolder(): Folder
{
return $this->folder;
}
public function getNewFileIdentifier(): string
{
return $this->newFileIdentifier;
}
public function getNewFile(): ?FileInterface
{
return $this->newFile;
}
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired before a file was created within a Resource Storage / Driver.
* The folder represents the "target folder".
*
* Example: This allows to modify a file or check for an appropriate signature after a file was created in TYPO3.
*/
final readonly class AfterFileCreatedEvent
{
public function __construct(private string $fileName, private Folder $folder) {}
public function getFileName(): string
{
return $this->fileName;
}
public function getFolder(): Folder
{
return $this->folder;
}
}
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FileInterface;
/**
* This event is fired after a file was deleted.
*
* Example: If an extension provides additional functionality (e.g. variants), this event allows listener to also clean
* up their custom handling. This can also be used for versioning of files.
*/
final readonly class AfterFileDeletedEvent
{
public function __construct(private FileInterface $file) {}
public function getFile(): FileInterface
{
return $this->file;
}
}
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
/**
* This event is fired once a file was just marked as missing in the database (sys_file).
*
* Example: If a file is marked as missing, listeners can try to recover a file. This can happen on specific setups
* where editors also work via FTP.
*/
final readonly class AfterFileMarkedAsMissingEvent
{
public function __construct(private int $fileUid) {}
public function getFileUid(): int
{
return $this->fileUid;
}
}
@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
/**
* This event is fired once metadata of a file was added to the database, so it can be
* enriched with more information.
*/
final class AfterFileMetaDataCreatedEvent
{
public function __construct(
private readonly int $fileUid,
private readonly int $metaDataUid,
private array $record
) {}
public function getFileUid(): int
{
return $this->fileUid;
}
public function getMetaDataUid(): int
{
return $this->metaDataUid;
}
public function getRecord(): array
{
return $this->record;
}
public function setRecord(array $record): void
{
$this->record = $record;
}
}
@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
/**
* This event is fired once all metadata of a file was removed, in order to manage custom metadata that was
* added previously
*/
final readonly class AfterFileMetaDataDeletedEvent
{
public function __construct(private int $fileUid) {}
public function getFileUid(): int
{
return $this->fileUid;
}
}
@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
/**
* This event is fired once metadata of a file was updated, in order to update custom metadata fields accordingly
*/
final readonly class AfterFileMetaDataUpdatedEvent
{
public function __construct(
private int $fileUid,
private int $metaDataUid,
private array $record
) {}
public function getFileUid(): int
{
return $this->fileUid;
}
public function getMetaDataUid(): int
{
return $this->metaDataUid;
}
public function getRecord(): array
{
return $this->record;
}
}
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\FolderInterface;
/**
* This event is fired after a file was moved within a Resource Storage / Driver.
* The folder represents the "target folder".
*
* Examples: Use this to update custom third party handlers that rely on specific paths.
*/
final readonly class AfterFileMovedEvent
{
public function __construct(
private FileInterface $file,
private Folder $folder,
private FolderInterface $originalFolder
) {}
public function getFile(): FileInterface
{
return $this->file;
}
public function getFolder(): Folder
{
return $this->folder;
}
public function getOriginalFolder(): FolderInterface
{
return $this->originalFolder;
}
}
@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Driver\DriverInterface;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Resource\ProcessedFile;
/**
* This event is fired after a file object has been processed.
*
* This allows to further customize a file object's processed file.
*/
final class AfterFileProcessingEvent
{
public function __construct(
private readonly DriverInterface $driver,
private ProcessedFile $processedFile,
private readonly FileInterface $file,
private readonly string $taskType,
private readonly array $configuration
) {}
public function getProcessedFile(): ProcessedFile
{
return $this->processedFile;
}
public function setProcessedFile(ProcessedFile $processedFile): void
{
$this->processedFile = $processedFile;
}
public function getDriver(): DriverInterface
{
return $this->driver;
}
public function getFile(): FileInterface
{
return $this->file;
}
public function getTaskType(): string
{
return $this->taskType;
}
public function getConfiguration(): array
{
return $this->configuration;
}
}
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
/**
* This event is fired once a file was just removed in the database (sys_file).
*
* Example can be to further handle files and manage them separately outside of TYPO3's index.
*/
final readonly class AfterFileRemovedFromIndexEvent
{
public function __construct(private int $fileUid) {}
public function getFileUid(): int
{
return $this->fileUid;
}
}
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FileInterface;
/**
* This event is fired after a file was renamed in order to further process a file or filename
* or update custom references to a file.
*/
final readonly class AfterFileRenamedEvent
{
public function __construct(private FileInterface $file, private ?string $targetFileName) {}
public function getFile(): FileInterface
{
return $this->file;
}
public function getTargetFileName(): ?string
{
return $this->targetFileName;
}
}
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FileInterface;
/**
* This event is fired after a file was replaced.
*
* Example: Further process a file or create variants, or index the contents of a file for AI analysis etc.
*/
final readonly class AfterFileReplacedEvent
{
public function __construct(private FileInterface $file, private string $localFilePath) {}
public function getFile(): FileInterface
{
return $this->file;
}
public function getLocalFilePath(): string
{
return $this->localFilePath;
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\File;
/**
* This event is fired once an index was just updated inside the database (= indexed).
* Custom listeners can update further index values when a file was updated.
*/
final readonly class AfterFileUpdatedInIndexEvent
{
public function __construct(
private File $file,
private array $properties,
private array $updatedFields
) {}
public function getFile(): File
{
return $this->file;
}
public function getRelevantProperties(): array
{
return $this->properties;
}
public function getUpdatedFields(): array
{
return $this->updatedFields;
}
}
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired after a folder was added to the Resource Storage / Driver.
*
* This allows to customize permissions or set up editor permissions automatically via listeners.
*/
final readonly class AfterFolderAddedEvent
{
public function __construct(private Folder $folder) {}
public function getFolder(): Folder
{
return $this->folder;
}
}
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\FolderInterface;
/**
* This event is fired after a folder was copied to the Resource Storage / Driver.
*
* Example: Custom listeners can analyze contents of a file or add custom permissions to a folder automatically.
*/
final readonly class AfterFolderCopiedEvent
{
public function __construct(
private Folder $folder,
private Folder $targetParentFolder,
private ?FolderInterface $targetFolder
) {}
public function getFolder(): Folder
{
return $this->folder;
}
public function getTargetParentFolder(): Folder
{
return $this->targetParentFolder;
}
public function getTargetFolder(): ?FolderInterface
{
return $this->targetFolder;
}
}
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired after a folder was deleted. Custom listeners can then further clean up permissions or
* third-party processed files with this event.
*/
final readonly class AfterFolderDeletedEvent
{
public function __construct(private Folder $folder, private bool $wasDeleted) {}
public function getFolder(): Folder
{
return $this->folder;
}
public function isDeleted(): bool
{
return $this->wasDeleted;
}
}
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\FolderInterface;
/**
* This event is fired after a folder was moved within the Resource Storage / Driver.
*
* Custom references can be updated via listeners of this event.
*/
final readonly class AfterFolderMovedEvent
{
public function __construct(
private Folder $folder,
private Folder $targetParentFolder,
private ?FolderInterface $targetFolder
) {}
public function getFolder(): Folder
{
return $this->folder;
}
public function getTargetParentFolder(): Folder
{
return $this->targetParentFolder;
}
public function getTargetFolder(): ?FolderInterface
{
return $this->targetFolder;
}
}
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired after a folder was renamed.
*
* Examples: Add custom processing of folders or adjust permissions.
*/
final readonly class AfterFolderRenamedEvent
{
public function __construct(
private Folder $folder,
private Folder $sourceFolder
) {}
public function getFolder(): Folder
{
return $this->folder;
}
public function getSourceFolder(): Folder
{
return $this->sourceFolder;
}
}
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\ResourceStorage;
/**
* This event is fired after a resource object was built/created.
*
* Custom handlers can be initialized at this moment for any kind of source as well.
*/
final class AfterResourceStorageInitializationEvent
{
public function __construct(private ResourceStorage $storage) {}
public function getStorage(): ResourceStorage
{
return $this->storage;
}
public function setStorage(ResourceStorage $storage): void
{
$this->storage = $storage;
}
}
@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Driver\DriverInterface;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\ResourceStorage;
/**
* This event is fired before a file is about to be added to the Resource Storage / Driver.
*
* This allows to do custom checks to a file or restrict access to a file before the file is added.
*/
final class BeforeFileAddedEvent
{
public function __construct(
private string $fileName,
private readonly string $sourceFilePath,
private readonly Folder $targetFolder,
private readonly ResourceStorage $storage,
private readonly DriverInterface $driver
) {}
public function getFileName(): string
{
return $this->fileName;
}
public function setFileName(string $fileName): void
{
$this->fileName = $fileName;
}
public function getSourceFilePath(): string
{
return $this->sourceFilePath;
}
public function getTargetFolder(): Folder
{
return $this->targetFolder;
}
public function getStorage(): ResourceStorage
{
return $this->storage;
}
public function getDriver(): DriverInterface
{
return $this->driver;
}
}
@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FileInterface;
/**
* This event is fired before the contents of a file gets set / replaced.
*
* This allows to further analyze or modify the content of a file before it is written by the driver.
*/
final class BeforeFileContentsSetEvent
{
public function __construct(private readonly FileInterface $file, private string $content) {}
public function getFile(): FileInterface
{
return $this->file;
}
public function getContent(): string
{
return $this->content;
}
public function setContent(string $content): void
{
$this->content = $content;
}
}
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired before a file is about to be copied within a Resource Storage / Driver.
* The folder represents the "target folder".
*
* This allows to further analyze or modify the file or metadata before it is written by the driver.
*/
final readonly class BeforeFileCopiedEvent
{
public function __construct(private FileInterface $file, private Folder $folder) {}
public function getFile(): FileInterface
{
return $this->file;
}
public function getFolder(): Folder
{
return $this->folder;
}
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired before a file is about to be created within a Resource Storage / Driver.
* The folder represents the "target folder".
*
* This allows to further analyze or modify the file or filename before it is written by the driver.
*/
final readonly class BeforeFileCreatedEvent
{
public function __construct(private string $fileName, private Folder $folder) {}
public function getFileName(): string
{
return $this->fileName;
}
public function getFolder(): Folder
{
return $this->folder;
}
}
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FileInterface;
/**
* This event is fired before a file is about to be deleted.
*
* Event listeners can clean up third-party references with this event.
*/
final readonly class BeforeFileDeletedEvent
{
public function __construct(private FileInterface $file) {}
public function getFile(): FileInterface
{
return $this->file;
}
}
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired before a file is about to be moved within a Resource Storage / Driver.
* The folder represents the "target folder".
*/
final readonly class BeforeFileMovedEvent
{
public function __construct(
private FileInterface $file,
private Folder $folder,
private string $targetFileName
) {}
public function getFile(): FileInterface
{
return $this->file;
}
public function getFolder(): Folder
{
return $this->folder;
}
public function getTargetFileName(): string
{
return $this->targetFileName;
}
}
@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Driver\DriverInterface;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Resource\ProcessedFile;
/**
* This event is fired before a file object is processed.
*
* Allows to add further information or enrich the file before the processing is kicking in.
*/
final class BeforeFileProcessingEvent
{
public function __construct(
private readonly DriverInterface $driver,
private ProcessedFile $processedFile,
private readonly FileInterface $file,
private readonly string $taskType,
private readonly array $configuration
) {}
public function getProcessedFile(): ProcessedFile
{
return $this->processedFile;
}
public function setProcessedFile(ProcessedFile $processedFile): void
{
$this->processedFile = $processedFile;
}
public function getDriver(): DriverInterface
{
return $this->driver;
}
public function getFile(): FileInterface
{
return $this->file;
}
public function getTaskType(): string
{
return $this->taskType;
}
public function getConfiguration(): array
{
return $this->configuration;
}
}
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FileInterface;
/**
* This event is fired before a file is about to be renamed. Custom listeners can further rename the file
* according to specific guidelines based on the project.
*/
final readonly class BeforeFileRenamedEvent
{
public function __construct(private FileInterface $file, private ?string $targetFileName) {}
public function getFile(): FileInterface
{
return $this->file;
}
public function getTargetFileName(): ?string
{
return $this->targetFileName;
}
}
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\FileInterface;
/**
* This event is fired before a file is about to be replaced.
* Custom listeners can check for file integrity or analyze the content of the file before it gets added.
*/
final readonly class BeforeFileReplacedEvent
{
public function __construct(private FileInterface $file, private string $localFilePath) {}
public function getFile(): FileInterface
{
return $this->file;
}
public function getLocalFilePath(): string
{
return $this->localFilePath;
}
}
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired before a folder is about to be added to the Resource Storage / Driver.
* This allows to further specify folder names according to regulations for a specific project.
*/
final readonly class BeforeFolderAddedEvent
{
public function __construct(private Folder $parentFolder, private string $folderName) {}
public function getParentFolder(): Folder
{
return $this->parentFolder;
}
public function getFolderName(): string
{
return $this->folderName;
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired before a folder is about to be copied to the Resource Storage / Driver.
* Listeners could add deferred processing / queuing of large folders.
*/
final readonly class BeforeFolderCopiedEvent
{
public function __construct(
private Folder $folder,
private Folder $targetParentFolder,
private string $targetFolderName
) {}
public function getFolder(): Folder
{
return $this->folder;
}
public function getTargetParentFolder(): Folder
{
return $this->targetParentFolder;
}
public function getTargetFolderName(): string
{
return $this->targetFolderName;
}
}
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired before a folder is about to be deleted.
*
* Listeners can use this event to clean up further external references to a folder / files in this folder.
*/
final readonly class BeforeFolderDeletedEvent
{
public function __construct(private Folder $folder) {}
public function getFolder(): Folder
{
return $this->folder;
}
}
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired before a folder is about to be moved to the Resource Storage / Driver.
* Listeners can be used to modify a folder name before it is actually moved or to ensure consistency
* or specific rules when moving folders.
*/
final readonly class BeforeFolderMovedEvent
{
public function __construct(
private Folder $folder,
private Folder $targetParentFolder,
private string $targetFolderName
) {}
public function getFolder(): Folder
{
return $this->folder;
}
public function getTargetParentFolder(): Folder
{
return $this->targetParentFolder;
}
public function getTargetFolderName(): string
{
return $this->targetFolderName;
}
}
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Folder;
/**
* This event is fired before a folder is about to be renamed.
* Listeners can be used to modify a folder name before it is actually moved or to ensure consistency
* or specific rules when renaming folders.
*/
final readonly class BeforeFolderRenamedEvent
{
public function __construct(private Folder $folder, private string $targetName) {}
public function getFolder(): Folder
{
return $this->folder;
}
public function getTargetName(): string
{
return $this->targetName;
}
}
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
/**
* This event is fired before a resource object is actually built/created.
*
* Example: A database record can be enriched to add dynamic values to each resource (file/folder) before
* creation of a storage
*/
final class BeforeResourceStorageInitializationEvent
{
public function __construct(private $storageUid, private $record, private ?string $fileIdentifier) {}
public function getStorageUid(): int
{
return $this->storageUid;
}
public function setStorageUid(int $storageUid): void
{
$this->storageUid = $storageUid;
}
public function getRecord(): array
{
return $this->record;
}
public function setRecord(array $record): void
{
$this->record = $record;
}
public function getFileIdentifier(): ?string
{
return $this->fileIdentifier;
}
public function setFileIdentifier(?string $fileIdentifier): void
{
$this->fileIdentifier = $fileIdentifier;
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
/**
* Event that is called after a record has been loaded from database
* Allows other places to do extension of metadata at runtime or
* for example translation and workspace overlay.
*/
final class EnrichFileMetaDataEvent
{
public function __construct(private readonly int $fileUid, private readonly int $metaDataUid, private array $record) {}
public function getFileUid(): int
{
return $this->fileUid;
}
public function getMetaDataUid(): int
{
return $this->metaDataUid;
}
public function getRecord(): array
{
return $this->record;
}
public function setRecord(array $record): void
{
$this->record = $record;
}
}
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Driver\DriverInterface;
use TYPO3\CMS\Core\Resource\ResourceInterface;
use TYPO3\CMS\Core\Resource\ResourceStorage;
/**
* This event is fired before TYPO3 FAL's native URL generation for a Resource is instantiated.
*
* This allows for listeners to create custom links to certain files (e.g. restrictions) for creating
* authorized deeplinks.
*/
final class GeneratePublicUrlForResourceEvent
{
private ?string $publicUrl = null;
public function __construct(
private readonly ResourceInterface $resource,
private readonly ResourceStorage $storage,
private readonly DriverInterface $driver
) {}
public function getResource(): ResourceInterface
{
return $this->resource;
}
public function getStorage(): ResourceStorage
{
return $this->storage;
}
public function getDriver(): DriverInterface
{
return $this->driver;
}
public function getPublicUrl(): ?string
{
return $this->publicUrl;
}
public function setPublicUrl(?string $publicUrl): void
{
$this->publicUrl = $publicUrl;
}
}
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use Psr\EventDispatcher\StoppableEventInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Resource\ResourceInterface;
/**
* Event that is triggered when a file should be dumped to the browser, allowing to perform custom
* security/access checks when accessing a file through a direct link, and returning an alternative
* Response.
*
* It is also possible to replace the file during this event, but not setting a response.
*
* As soon as a custom Response is added, the propagation is stopped.
*/
final class ModifyFileDumpEvent implements StoppableEventInterface
{
private ?ResponseInterface $response = null;
public function __construct(private ResourceInterface $file, private ServerRequestInterface $request) {}
public function getFile(): ResourceInterface
{
return $this->file;
}
public function setFile(ResourceInterface $file): void
{
$this->file = $file;
}
public function getRequest(): ServerRequestInterface
{
return $this->request;
}
public function setResponse(ResponseInterface $response): void
{
$this->response = $response;
}
public function getResponse(): ?ResponseInterface
{
return $this->response;
}
public function isPropagationStopped(): bool
{
return $this->response !== null;
}
}
@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Event;
use TYPO3\CMS\Core\Resource\Driver\DriverInterface;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\ResourceStorage;
/**
* This event is fired after a file name has been sanitized and before a file is added to FAL. Listeners can use this
* event to modify the file name, and name the file according to naming conventions of a specific project.
*/
final class SanitizeFileNameEvent
{
public function __construct(
private string $fileName,
private readonly string $originalFileName,
private readonly Folder $targetFolder,
private readonly ResourceStorage $storage,
private readonly DriverInterface $driver
) {}
public function getFileName(): string
{
return $this->fileName;
}
public function getOriginalFileName(): string
{
return $this->originalFileName;
}
public function setFileName(string $fileName): void
{
$this->fileName = $fileName;
}
public function getTargetFolder(): Folder
{
return $this->targetFolder;
}
public function getStorage(): ResourceStorage
{
return $this->storage;
}
public function getDriver(): DriverInterface
{
return $this->driver;
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource;
/**
* An exception when something is wrong with the file handling
*/
class Exception extends \TYPO3\CMS\Core\Exception {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the file handling
*/
abstract class AbstractFileOperationException extends Exception {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the file handling
*/
class ExistingTargetFileNameException extends Exception {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the file handling
*/
class ExistingTargetFolderException extends Exception {}
@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\ProcessedFile;
/**
* Exception indicating that a file is already processed
*
* @internal
*/
class FileAlreadyProcessedException extends Exception
{
/**
* @var ProcessedFile
*/
private $processedFile;
public function __construct(ProcessedFile $processedFile, int $code = 0)
{
$this->processedFile = $processedFile;
parent::__construct(sprintf('File "%s" has already been processed', $processedFile->getIdentifier()), $code);
}
public function getProcessedFile(): ProcessedFile
{
return $this->processedFile;
}
}
@@ -0,0 +1,21 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
/**
* An exception to throw if a file does not exist
*/
class FileDoesNotExistException extends ResourceDoesNotExistException {}
@@ -0,0 +1,21 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the file handling
*/
class FileOperationErrorException extends AbstractFileOperationException {}
@@ -0,0 +1,21 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
/**
* An exception to throw if a folder does not exist
*/
class FolderDoesNotExistException extends ResourceDoesNotExistException {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the file handling
*/
class IllegalFileExtensionException extends Exception {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the file handling
*/
class InsufficientFileAccessPermissionsException extends Exception {}
@@ -0,0 +1,21 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the file handling
*/
class InsufficientFileReadPermissionsException extends InsufficientFileAccessPermissionsException {}
@@ -0,0 +1,21 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the file handling
*/
class InsufficientFileWritePermissionsException extends InsufficientFileAccessPermissionsException {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the file handling
*/
class InsufficientFolderAccessPermissionsException extends Exception {}
@@ -0,0 +1,21 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the file handling
*/
class InsufficientFolderReadPermissionsException extends InsufficientFolderAccessPermissionsException {}
@@ -0,0 +1,21 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the file handling
*/
class InsufficientFolderWritePermissionsException extends InsufficientFolderAccessPermissionsException {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the file handling
*/
class InsufficientUserPermissionsException extends Exception {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the configuration
*/
class InvalidConfigurationException extends Exception {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the File
*/
class InvalidFileException extends Exception {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the File name
*/
class InvalidFileNameException extends Exception {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the Folder
*/
class InvalidFolderException extends Exception {}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the Hash
* Is thrown for example when the driver returns an unexpected (non-string) hash value
*/
class InvalidHashException extends Exception {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the path
*/
class InvalidPathException extends Exception {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the file handling
*/
class InvalidTargetFolderException extends Exception {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* Thrown if an invalid uid is handled.
*/
class InvalidUidException extends Exception {}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with the Mount Point
*/
class NotInMountPointException extends Exception {}
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\File;
/**
* Exception indicating that an online media asset is already present in the target folder
*/
class OnlineMediaAlreadyExistsException extends Exception
{
public function __construct(
private readonly File $onlineMedia,
int $code = 0
) {
parent::__construct(
sprintf('Online media asset "%s" does already exist in the target folder.', $onlineMedia->getName()),
$code
);
}
public function getOnlineMedia(): File
{
return $this->onlineMedia;
}
}
@@ -0,0 +1,23 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception to throw if a resource (file/folder) does not exist
*/
class ResourceDoesNotExistException extends Exception {}
@@ -0,0 +1,25 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when something is wrong with fetching the permissions for a file or a folder.
*
* Extending \RuntimeException for backwards compatibility.
*/
class ResourcePermissionsUnavailableException extends Exception {}
@@ -0,0 +1,21 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when the upload goes wrong
*/
class UploadException extends AbstractFileOperationException {}
@@ -0,0 +1,21 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Exception;
/**
* An exception when the size of the uploaded file has exceeded
*/
class UploadSizeException extends AbstractFileOperationException {}
+405
View File
@@ -0,0 +1,405 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource;
use Psr\Http\Message\UriInterface;
use TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior;
use TYPO3\CMS\Core\SystemResource\Identifier\FalResourceIdentifier;
use TYPO3\CMS\Core\SystemResource\Publishing\SystemResourceUriGeneratorInterface;
use TYPO3\CMS\Core\SystemResource\Type\PublicResourceInterface;
use TYPO3\CMS\Core\SystemResource\Type\SystemResourceInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* File representation in the file abstraction layer.
*/
class File extends AbstractFile implements PublicResourceInterface, SystemResourceInterface
{
/**
* Contains the names of all properties that have been update since the
* instantiation of this object
*/
protected array $updatedProperties = [];
private ?MetaDataAspect $metaDataAspect = null;
protected string $identifier;
/**
* Constructor for a file object. Should normally not be used directly, use
* the corresponding factory methods instead.
*/
public function __construct(array $fileData, ResourceStorage $storage, array $metaData = [])
{
$this->identifier = $fileData['identifier'] ?? '';
$this->name = $fileData['name'] ?? '';
$this->properties = $fileData;
$this->storage = $storage;
if ($metaData !== []) {
$this->getMetaData()->add($metaData);
}
}
public function getIdentifier(): string
{
return $this->identifier;
}
/*******************************
* VARIOUS FILE PROPERTY GETTERS
*******************************/
/**
* Returns a property value
*
* @param non-empty-string $key
*/
public function getProperty(string $key): mixed
{
if (parent::hasProperty($key)) {
return parent::getProperty($key);
}
return $this->getMetaData()[$key];
}
/**
* Renames this file.
*
* @param non-empty-string $newName The new file name
*/
public function rename(string $newName, DuplicationBehavior $conflictMode = DuplicationBehavior::RENAME): FileInterface
{
if ($this->deleted) {
throw new \RuntimeException('File has been deleted.', 1329821482);
}
return $this->getStorage()->renameFile($this, $newName, $conflictMode);
}
/**
* Copies this file into a target folder
* @param Folder $targetFolder Folder to copy file into.
* @param string|null $targetFileName an optional destination fileName
*
* @return self The new (copied) file.
*/
public function copyTo(Folder $targetFolder, ?string $targetFileName = null, DuplicationBehavior $conflictMode = DuplicationBehavior::RENAME): FileInterface
{
if ($this->deleted) {
throw new \RuntimeException('File has been deleted.', 1329821483);
}
return $targetFolder->getStorage()->copyFile($this, $targetFolder, $targetFileName, $conflictMode);
}
/**
* Moves the file into the target folder
*
* @param Folder $targetFolder Folder to move file into.
* @param string|null $targetFileName an optional destination fileName
* @param DuplicationBehavior $conflictMode
*
* @return FileInterface This file object, with updated properties.
* @throws \RuntimeException
*/
public function moveTo(Folder $targetFolder, ?string $targetFileName = null, DuplicationBehavior $conflictMode = DuplicationBehavior::RENAME): FileInterface
{
if ($this->deleted) {
throw new \RuntimeException('File has been deleted.', 1329821484);
}
return $targetFolder->getStorage()->moveFile($this, $targetFolder, $targetFileName, $conflictMode);
}
/**
* Checks if the file has a (metadata) property which
* can be retrieved by "getProperty"
*/
public function hasProperty(string $key): bool
{
if (!parent::hasProperty($key)) {
return isset($this->getMetaData()[$key]);
}
return true;
}
/**
* Returns the properties of this object.
*/
public function getProperties(): array
{
return array_merge(
parent::getProperties(),
array_diff_key($this->getMetaData()->get(), parent::getProperties()),
[
'metadata_uid' => $this->getMetaData()->get()['uid'] ?? 0,
]
);
}
/******************
* CONTENTS RELATED
******************/
/**
* Get the contents of this file
*/
public function getContents(): string
{
return $this->getStorage()->getFileContents($this);
}
/**
* Gets SHA1 hash.
*
* @return non-empty-string
*/
public function getSha1(): string
{
if (empty($this->properties['sha1'])) {
$this->properties['sha1'] = parent::getSha1();
}
return $this->properties['sha1'];
}
/**
* Replace the current file contents with the given string
*
* @return $this
*/
public function setContents(string $contents): self
{
$this->getStorage()->setFileContents($this, $contents);
return $this;
}
/***********************
* INDEX RELATED METHODS
***********************/
/**
* Returns TRUE if this file is indexed
*/
public function isIndexed(): bool
{
return true;
}
/**
* Updates the properties of this file, e.g. after re-indexing or moving it.
* By default, only properties that exist as a key in the $properties array
* are overwritten. If you want to explicitly unset a property, set the
* corresponding key to NULL in the array.
*
* NOTE: This method should not be called from outside the File Abstraction Layer (FAL)!
*
* @internal
*/
public function updateProperties(array $properties): void
{
// Setting identifier and name to update values; we have to do this
// here because we might need a new identifier when loading
// (and thus possibly indexing) a file.
if (isset($properties['identifier'])) {
$this->identifier = $properties['identifier'];
}
if (isset($properties['name'])) {
$this->name = $properties['name'];
}
if (isset($properties['uid']) && $this->properties['uid'] != 0) {
unset($properties['uid']);
}
foreach ($properties as $key => $value) {
if (!isset($this->properties[$key]) || $this->properties[$key] !== $value) {
if (!in_array($key, $this->updatedProperties)) {
$this->updatedProperties[] = $key;
}
$this->properties[$key] = $value;
}
}
// If the mime_type property should be updated and it was changed also update the type.
if (array_key_exists('mime_type', $properties) && in_array('mime_type', $this->updatedProperties)) {
$this->updatedProperties[] = 'type';
unset($this->properties['type']);
$this->getType();
}
if (array_key_exists('storage', $properties) && in_array('storage', $this->updatedProperties)) {
$this->storage = GeneralUtility::makeInstance(StorageRepository::class)->findByUid((int)$properties['storage']);
}
}
/**
* Returns the names of all properties that have been updated in this record
*/
public function getUpdatedProperties(): array
{
return $this->updatedProperties;
}
/****************************************
* STORAGE AND MANAGEMENT RELATED METHODS
****************************************/
/**
* Check if a file operation (= action) is allowed for this file
*
* @param string $action can be read, write, delete
*/
public function checkActionPermission(string $action): bool
{
return $this->getStorage()->checkFileActionPermission($action, $this);
}
/*****************
* SPECIAL METHODS
*****************/
/**
* Creates a MD5 hash checksum based on the combined identifier of the file,
* the files' mimetype and the systems' encryption key.
* used to generate a thumbnail, and this hash is checked if valid
*
* @return string the MD5 hash
*/
public function calculateChecksum(): string
{
return md5(
$this->getCombinedIdentifier() . '|'
. $this->getMimeType() . '|'
. $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']
);
}
/**
* Returns a modified version of the file.
*
* @param string $taskType The task type of this processing
* @param array $configuration the processing configuration, see manual for that
*/
public function process(string $taskType, array $configuration): ProcessedFile
{
return $this->getStorage()->processFile($this, $taskType, $configuration);
}
/**
* Returns an array representation of the file.
* (This is used by the generic listing module vidi when displaying file records.)
*
* @return array<non-empty-string, mixed> Array of main data of the file. Don't rely on all data to be present here, it's just a selection of the most relevant information.
*/
public function toArray(): array
{
$array = [
'id' => $this->getCombinedIdentifier(),
'name' => $this->getName(),
'extension' => $this->getExtension(),
'type' => $this->getType(),
'mimetype' => $this->getMimeType(),
'size' => $this->getSize(),
'url' => $this->getPublicUrl(),
'indexed' => true,
'uid' => $this->getUid(),
'permissions' => [
'read' => $this->checkActionPermission('read'),
'write' => $this->checkActionPermission('write'),
'delete' => $this->checkActionPermission('delete'),
],
'checksum' => $this->calculateChecksum(),
];
foreach ($this->properties as $key => $value) {
$array[$key] = $value;
}
$stat = $this->getStorage()->getFileInfo($this);
foreach ($stat as $key => $value) {
$array[$key] = $value;
}
return $array;
}
public function isMissing(): bool
{
return (bool)$this->getProperty('missing');
}
public function setMissing(bool $missing): void
{
$this->updateProperties(['missing' => $missing ? 1 : 0]);
}
/**
* Returns a publicly accessible URL for this file
* When file is marked as missing or deleted no url is returned
*
* WARNING: Access to the file may be restricted by further means, e.g. some
* web-based authentication. You have to take care of this yourself.
*/
public function getPublicUrl(): ?string
{
if ($this->isMissing() || $this->deleted) {
return null;
}
return $this->getStorage()->getPublicUrl($this);
}
/**
* @internal Only for use in Repositories and indexer
*/
public function _getPropertyRaw(string $key): mixed
{
return parent::getProperty($key);
}
/**
* Loads the metadata of a file in an encapsulated aspect
*/
public function getMetaData(): MetaDataAspect
{
if ($this->metaDataAspect === null) {
$this->metaDataAspect = GeneralUtility::makeInstance(MetaDataAspect::class, $this);
}
return $this->metaDataAspect;
}
/***********************************
* System Resources implementation *
***********************************/
public function getHash(): string
{
return $this->getSha1();
}
public function getPublicUri(SystemResourceUriGeneratorInterface $uriGenerator): UriInterface
{
return $uriGenerator->generateForFile($this);
}
public function isPublished(): bool
{
return $this->getStorage()->isPublic();
}
public function getResourceIdentifier(): string
{
return (string)(new FalResourceIdentifier(
(string)$this->getStorage()->getUid(),
$this->getIdentifier(),
sprintf('File: uid: %d, identifier: %s', $this->getUid(), $this->getIdentifier()),
));
}
public function __toString(): string
{
return $this->getResourceIdentifier();
}
}
@@ -0,0 +1,151 @@
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Collection\AbstractRecordCollection;
use TYPO3\CMS\Core\Collection\CollectionInterface;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
use TYPO3\CMS\Core\Http\ApplicationType;
use TYPO3\CMS\Core\Resource\Collection\FileCollectionRegistry;
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Repository for accessing file collections stored in the database
*/
readonly class FileCollectionRepository
{
public function __construct(
private ConnectionPool $connectionPool,
private FileCollectionRegistry $fileCollectionRegistry
) {}
/**
* Finds a record collection by uid.
*
* @throws Exception\ResourceDoesNotExistException
*/
public function findByUid(int $uid): ?CollectionInterface
{
$object = null;
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_file_collection');
if ($this->isFrontendRequest()) {
$queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
} else {
$queryBuilder->getRestrictions()->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
}
$data = $queryBuilder->select('*')
->from('sys_file_collection')
->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, Connection::PARAM_INT)))
->executeQuery()
->fetchAssociative();
if (is_array($data)) {
$object = $this->createDomainObject($data);
}
if ($object === null) {
throw new ResourceDoesNotExistException('Could not find row with uid "' . $uid . '" in table "sys_file_collection"', 1314354066);
}
return $object;
}
/**
* Finds record collection by type.
*
* @return CollectionInterface[]|null
*/
public function findByType(string $type): ?array
{
$expressionBuilder = $this->connectionPool->getQueryBuilderForTable('sys_file_collection')->expr();
return $this->queryMultipleRecords([
$expressionBuilder->eq('type', $expressionBuilder->literal($type)),
]);
}
/**
* Finds all record collections.
*
* @return CollectionInterface[]|null
*/
public function findAll(): ?array
{
return $this->queryMultipleRecords();
}
/**
* Queries for multiple records for the given conditions.
*
* @param array $conditions Conditions concatenated with AND for query
* @return CollectionInterface[]|null
*/
protected function queryMultipleRecords(array $conditions = []): ?array
{
$result = null;
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_file_collection');
$queryBuilder->getRestrictions()->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$queryBuilder->select('*')->from('sys_file_collection');
if (!empty($conditions)) {
$queryBuilder->where(...$conditions);
}
$data = $queryBuilder->executeQuery()->fetchAllAssociative();
if (!empty($data)) {
$result = $this->createMultipleDomainObjects($data);
}
return $result;
}
/**
* Creates multiple record collection domain objects.
*
* @param array $data Array of multiple database records to be reconstituted
* @return CollectionInterface[]
*/
protected function createMultipleDomainObjects(array $data): array
{
$collections = [];
foreach ($data as $collection) {
$collections[] = $this->createDomainObject($collection);
}
return $collections;
}
protected function isFrontendRequest(): bool
{
if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
&& ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()
) {
return true;
}
return false;
}
/**
* Creates a record collection domain object.
*
* @param array $record Database record to be reconstituted
*/
protected function createDomainObject(array $record): CollectionInterface
{
/** @var AbstractRecordCollection $className */
$className = $this->fileCollectionRegistry->getFileCollectionClass($record['type']);
return $className::create($record);
}
}
+153
View File
@@ -0,0 +1,153 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource;
/**
* Interface for a file object. This can be any kind of file object,
* e.g. a processed file (which is not a FAL file), or a file reference object,
* which is a decorator around a "File" object, but of course without any additional
* file on the file system.
*/
interface FileInterface extends ResourceInterface
{
/*******************************
* VARIOUS FILE PROPERTY GETTERS
*******************************/
/**
* Returns true if the given key exists for this file.
*
* @param non-empty-string $key
*/
public function hasProperty(string $key): bool;
/**
* Get the value of the $key property.
*
* @param non-empty-string $key
*/
public function getProperty(string $key): mixed;
/**
* MUST return the size of the file as unsigned int i.e. 0-max.
*
* In case of errors, e.g. when the file is deleted or not readable,
* this method MAY either throw an Exception or return 0.
*
* @return int<0, max>
*/
public function getSize(): int;
/**
* Returns the Sha1 of this file
*
* @return non-empty-string
*/
public function getSha1(): string;
/**
* Returns the basename (the name without extension) of this file.
*/
public function getNameWithoutExtension(): string;
/**
* Get the file extension
*/
public function getExtension(): string;
/**
* Get the MIME type of this file
*
* @return non-empty-string mime type
*/
public function getMimeType(): string;
/**
* Returns the modification time of the file as Unix timestamp
*/
public function getModificationTime(): int;
/**
* Returns the creation time of the file as Unix timestamp
*/
public function getCreationTime(): int;
/******************
* CONTENTS RELATED
******************/
/**
* Get the contents of this file
*/
public function getContents(): string;
/**
* Replace the current file contents with the given string.
*
* @todo: Consider to remove this function from the interface, as its
* implementation in FileInUse could cause unforseen side-effects by setting
* contents on the original file instead of just on the Usage of the file.
* @todo: At the same time, it could be considered whether to make the whole
* interface a read-only FileInterface, so that all file management and
* modification functions are removed...
* @return $this
*/
public function setContents(string $contents): self;
/****************************************
* STORAGE AND MANAGEMENT RELATED METHODS
****************************************/
/**
* Deletes this file from its storage. This also means that this object becomes useless.
*/
public function delete(): bool;
/*****************
* SPECIAL METHODS
*****************/
/**
* Returns a publicly accessible URL for this file
*
* WARNING: Access to the file may be restricted by further means, e.g.
* some web-based authentication. You have to take care of this yourself.
*
* @return non-empty-string|null NULL if file is missing or deleted, the generated url otherwise
*/
public function getPublicUrl(): ?string;
/**
* Returns TRUE if this file is indexed
*/
public function isIndexed(): bool;
/**
* Returns a path to a local version of this file to process it locally (e.g. with some system tool).
* If the file is normally located on a remote storages, this creates a local copy.
* If the file is already on the local system, this only makes a new copy if $writable is set to TRUE.
*
* @param bool $writable Set this to FALSE if you only want to do read operations on the file.
* @return non-empty-string
*/
public function getForLocalProcessing(bool $writable = true): string;
/**
* Returns an array representation of the file.
* (This is used by the generic listing module vidi when displaying file records.)
*
* @return array<string, mixed> Array of main data of the file. Don't rely on all data to be present here, it's just a selection of the most relevant information.
*/
public function toArray(): array;
}
+508
View File
@@ -0,0 +1,508 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\ReferenceIndex;
use TYPO3\CMS\Core\Resource\Enum\DuplicationBehavior;
use TYPO3\CMS\Core\Schema\Capability\TcaSchemaCapability;
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Representation of a specific usage of a file with possibilities to override certain
* properties of the original file just for this usage of the file.
*
* It acts as a decorator over the original file in the way that most method calls are
* directly passed along to the original file object.
*
* All file related methods are directly passed along; only meta-data functionality is adopted
* in this decorator class to prioritize possible overrides for the metadata for this specific usage
* of the file.
*/
class FileReference implements FileInterface
{
/**
* Various properties of the FileReference. Note that these information can be different
* to the ones found in the originalFile.
*/
protected array $propertiesOfFileReference;
/**
* Reference to the original File object underlying this FileReference.
*/
protected File $originalFile;
/**
* Properties merged with the parent object (File) if
* the value is not defined (NULL). Thus, FileReference properties act
* as overlays for the defined File properties.
*/
protected array $mergedProperties = [];
/**
* Constructor for a file in use object. Should normally not be used
* directly, use the corresponding factory methods instead.
*
* @throws \InvalidArgumentException
*/
public function __construct(array $fileReferenceData, ?ResourceFactory $factory = null)
{
$this->propertiesOfFileReference = $fileReferenceData;
if (!$fileReferenceData['uid_local']) {
throw new \InvalidArgumentException('Incorrect reference to original file given for FileReference.', 1300098528);
}
$this->originalFile = $this->getFileObject((int)$fileReferenceData['uid_local'], $factory);
}
private function getFileObject(int $uidLocal, ?ResourceFactory $factory = null): File
{
if ($factory === null) {
$factory = GeneralUtility::makeInstance(ResourceFactory::class);
}
return $factory->getFileObject($uidLocal);
}
/*******************************
* VARIOUS FILE PROPERTY GETTERS
*******************************/
/**
* Returns true if the given key exists for this file.
*
* @param non-empty-string $key The property to be looked up
*/
public function hasProperty(string $key): bool
{
return array_key_exists($key, $this->getProperties());
}
/**
* Gets a property, falling back to values of the parent.
*
* @param non-empty-string $key The property to be looked up
* @throws \InvalidArgumentException
*/
public function getProperty(string $key): mixed
{
if (!$this->hasProperty($key)) {
throw new \InvalidArgumentException('Property "' . $key . '" was not found in file reference or original file.', 1314226805);
}
$properties = $this->getProperties();
return $properties[$key];
}
/**
* Gets a property of the file reference.
*
* @param string $key The property to be looked up
* @throws \InvalidArgumentException
*/
public function getReferenceProperty(string $key): mixed
{
if (!array_key_exists($key, $this->propertiesOfFileReference)) {
throw new \InvalidArgumentException('Property "' . $key . '" of file reference was not found.', 1360684914);
}
return $this->propertiesOfFileReference[$key];
}
/**
* Gets all properties, falling back to values of the parent.
*/
public function getProperties(): array
{
if (empty($this->mergedProperties)) {
$this->mergedProperties = $this->propertiesOfFileReference;
ArrayUtility::mergeRecursiveWithOverrule(
$this->mergedProperties,
$this->originalFile->getProperties(),
true,
true,
false
);
array_walk($this->mergedProperties, $this->restoreNonNullValuesCallback(...));
}
return $this->mergedProperties;
}
/**
* Callback to handle the NULL value feature
*
* @param mixed $value
* @param mixed $key
*/
protected function restoreNonNullValuesCallback(&$value, $key)
{
if (array_key_exists($key, $this->propertiesOfFileReference) && $this->propertiesOfFileReference[$key] !== null) {
$value = $this->propertiesOfFileReference[$key];
}
}
/**
* Gets all properties of the file reference.
*/
public function getReferenceProperties(): array
{
return $this->propertiesOfFileReference;
}
public function getName(): string
{
return $this->originalFile->getName();
}
/**
* Returns the title text to this image
*
* @todo Possibly move this to the image domain object instead
*/
public function getTitle(): string
{
return (string)$this->getProperty('title');
}
/**
* Returns the alternative text to this image
*
* @todo Possibly move this to the image domain object instead
*/
public function getAlternative(): string
{
return (string)$this->getProperty('alternative');
}
/**
* Returns the description text to this file
*
* @todo Possibly move this to the image domain object instead
*/
public function getDescription(): string
{
return (string)$this->getProperty('description');
}
/**
* Returns the link that should be active when clicking on this image
*
* @todo Move this to the image domain object instead
*/
public function getLink(): string
{
return $this->propertiesOfFileReference['link'];
}
/**
* Returns the uid of this File In Use
*/
public function getUid(): int
{
return (int)$this->propertiesOfFileReference['uid'];
}
/**
* @return int<0, max>
*/
public function getSize(): int
{
return $this->originalFile->getSize();
}
/**
* Returns the Sha1 of this file
*
* @return non-empty-string
*/
public function getSha1(): string
{
return $this->originalFile->getSha1();
}
/**
* Get the file extension of this file
*
* @return string The file extension
*/
public function getExtension(): string
{
return $this->originalFile->getExtension();
}
/**
* Returns the basename (the name without extension) of this file.
*/
public function getNameWithoutExtension(): string
{
return $this->originalFile->getNameWithoutExtension();
}
/**
* Get the MIME type of this file
*
* @return non-empty-string mime type
*/
public function getMimeType(): string
{
return $this->originalFile->getMimeType();
}
/**
* Returns the modification time of the file as Unix timestamp
*/
public function getModificationTime(): int
{
return $this->originalFile->getModificationTime();
}
/**
* Returns the creation time of the file as Unix timestamp
*/
public function getCreationTime(): int
{
return $this->originalFile->getCreationTime();
}
/**
* Returns the fileType of this file
*/
public function getType(): int
{
return $this->originalFile->getType();
}
public function isType(FileType $fileType): bool
{
return $this->getFileType() === $fileType;
}
public function getFileType(): FileType
{
return $this->originalFile->getFileType();
}
/**
* Check if file is marked as missing by indexer
*/
public function isMissing(): bool
{
return (bool)$this->originalFile->getProperty('missing');
}
/******************
* CONTENTS RELATED
******************/
/**
* Get the contents of this file
*/
public function getContents(): string
{
return $this->originalFile->getContents();
}
/**
* Replace the current file contents with the given string
*
* @param string $contents The contents to write to the file.
*
* @return $this
*/
public function setContents(string $contents): self
{
$this->originalFile->setContents($contents);
return $this;
}
/****************************************
* STORAGE AND MANAGEMENT RELATED METHODS
****************************************/
/**
* Get the storage the original file is located in
*/
public function getStorage(): ResourceStorage
{
return $this->originalFile->getStorage();
}
/**
* Returns the identifier of the underlying original file
*
* @return non-empty-string
*/
public function getIdentifier(): string
{
return $this->originalFile->getIdentifier();
}
/**
* Returns a combined identifier of the underlying original file
*
* @return string Combined storage and file identifier, e.g. StorageUID:path/and/fileName.png
*/
public function getCombinedIdentifier(): string
{
return $this->originalFile->getCombinedIdentifier();
}
/**
* Deletes only this particular FileReference from the persistence layer (table: sys_file_reference)
* and leaves the original file untouched.
*/
public function delete(): bool
{
$schema = GeneralUtility::makeInstance(TcaSchemaFactory::class)->get('sys_file_reference');
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
if ($schema->hasCapability(TcaSchemaCapability::SoftDelete)) {
$softDeleteFieldName = $schema->getCapability(TcaSchemaCapability::SoftDelete)->getFieldName();
$affectedRows = $connectionPool->getConnectionForTable('sys_file_reference')
->update(
'sys_file_reference',
[
$softDeleteFieldName => 1,
],
[
'uid' => $this->getUid(),
]
);
} else {
$affectedRows = $connectionPool->getConnectionForTable('sys_file_reference')
->delete(
'sys_file_reference',
[
'uid' => $this->getUid(),
]
);
}
if ($affectedRows === 1) {
$table = $this->propertiesOfFileReference['tablenames'];
$uidForeign = $this->propertiesOfFileReference['uid_foreign'];
$referenceIndex = GeneralUtility::makeInstance(ReferenceIndex::class);
$referenceIndex->updateRefIndexTable($table, $uidForeign);
$referenceIndex->updateRefIndexTable('sys_file_reference', $this->getUid());
}
return $affectedRows === 1;
}
/**
* Renames the fileName in this particular usage.
*
* @param non-empty-string $newName The new file name
* @param DuplicationBehavior $conflictMode
*/
public function rename(string $newName, DuplicationBehavior $conflictMode = DuplicationBehavior::RENAME): FileInterface
{
// @todo Implement this function. This should only rename the
// FileReference (sys_file_reference) record, not the file itself.
throw new \BadMethodCallException('Function not implemented FileReference::rename().', 1333754473);
//return $this->fileRepository->renameUsageRecord($this, $newName);
}
/*****************
* SPECIAL METHODS
*****************/
/**
* Returns a publicly accessible URL for this file
*
* WARNING: Access to the file may be restricted by further means, e.g.
* some web-based authentication. You have to take care of this yourself.
*
* @return non-empty-string|null NULL if file is missing or deleted, the generated url otherwise
*/
public function getPublicUrl(): ?string
{
return $this->originalFile->getPublicUrl();
}
/**
* Returns TRUE if this file is indexed.
* This is always true for FileReference objects, as they rely on a
* sys_file_reference record to be present, which in turn can only exist if
* the original file is indexed.
*/
public function isIndexed(): bool
{
return true;
}
/**
* Returns a path to a local version of this file to process it locally (e.g. with some system tool).
* If the file is normally located on a remote storages, this creates a local copy.
* If the file is already on the local system, this only makes a new copy if $writable is set to TRUE.
*
* @param bool $writable Set this to FALSE if you only want to do read operations on the file.
* @return non-empty-string
*/
public function getForLocalProcessing(bool $writable = true): string
{
return $this->originalFile->getForLocalProcessing($writable);
}
/**
* Returns an array representation of the file.
* (This is used by the generic listing module vidi when displaying file records.)
*
* @return array<non-empty-string, mixed> Array of main data of the file. Don't rely on all data to be present here, it's just a selection of the most relevant information.
*/
public function toArray(): array
{
return array_merge($this->originalFile->toArray(), $this->propertiesOfFileReference);
}
/**
* Gets the original file being referenced.
*/
public function getOriginalFile(): File
{
return $this->originalFile;
}
/**
* @return non-empty-string
*/
public function getHashedIdentifier(): string
{
return $this->getStorage()->hashFileIdentifier($this->getIdentifier());
}
public function getParentFolder(): FolderInterface
{
return $this->originalFile->getParentFolder();
}
/**
* Avoids exporting original file object which contains
* singleton dependencies that must not be serialized.
*
* @return string[]
*/
public function __sleep(): array
{
$keys = get_object_vars($this);
unset($keys['originalFile'], $keys['mergedProperties']);
return array_keys($keys);
}
public function __wakeup(): void
{
$factory = GeneralUtility::makeInstance(ResourceFactory::class);
$this->originalFile = $this->getFileObject(
(int)$this->propertiesOfFileReference['uid_local'],
$factory
);
}
}
+186
View File
@@ -0,0 +1,186 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource;
use Psr\Http\Message\ServerRequestInterface;
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
use TYPO3\CMS\Core\Database\RelationHandler;
use TYPO3\CMS\Core\Http\ApplicationType;
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Repository for accessing file objects.
* It also serves as the public API for the indexing part of files in general.
*
* It is however recommended to use the ResourceFactory instead of this class,
* as it is more flexible.
*/
#[Autoconfigure(public: true)]
readonly class FileRepository
{
public function __construct(
protected ResourceFactory $factory,
protected TcaSchemaFactory $tcaSchemaFactory,
) {}
/**
* Finds a File matching the given uid, regardless of the storage.
*/
public function findByUid(int $uid): File
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file');
if ($this->isFrontend()) {
$queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
}
$row = $queryBuilder
->select('*')
->from('sys_file')
->where(
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, Connection::PARAM_INT))
)
->executeQuery()
->fetchAssociative();
if (!is_array($row)) {
throw new \RuntimeException('Could not find row with UID "' . $uid . '" in table "sys_file"', 1314354065);
}
return $this->createDomainObject($row);
}
/**
* Creates an object managed by this repository.
*/
protected function createDomainObject(array $databaseRow): File
{
return $this->factory->getFileObject((int)$databaseRow['uid'], $databaseRow);
}
/**
* Find FileReference objects by relation to other records
*
* @param string $tableName Table name of the related record
* @param string $fieldName Field name of the related record
* @param int $uid The UID of the related record (needs to be the localized uid, as translated IRRE elements relate to them)
* @param int|null $workspaceId
* @return FileReference[] An array of file references, empty if no objects found
*/
public function findByRelation(string $tableName, string $fieldName, int $uid, ?int $workspaceId = null): array
{
$itemList = [];
$referenceUids = [];
if ($this->isFrontend()) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('sys_file_reference');
$queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
$res = $queryBuilder
->select('uid')
->from('sys_file_reference')
->where(
$queryBuilder->expr()->eq(
'uid_foreign',
$queryBuilder->createNamedParameter($uid, Connection::PARAM_INT)
),
$queryBuilder->expr()->eq(
'tablenames',
$queryBuilder->createNamedParameter($tableName)
),
$queryBuilder->expr()->eq(
'fieldname',
$queryBuilder->createNamedParameter($fieldName)
)
)
->orderBy('sorting_foreign')
->executeQuery();
while ($row = $res->fetchAssociative()) {
$referenceUids[] = $row['uid'];
}
} else {
$schema = $this->tcaSchemaFactory->get($tableName);
$workspaceId ??= GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('workspace', 'id', 0);
$relationHandler = GeneralUtility::makeInstance(RelationHandler::class);
$relationHandler->setWorkspaceId($workspaceId);
$relationHandler->initializeForField(
$tableName,
$schema->getField($fieldName),
$uid
);
if (!empty($relationHandler->tableArray['sys_file_reference'])) {
$relationHandler->processDeletePlaceholder();
$referenceUids = $relationHandler->tableArray['sys_file_reference'];
}
}
if (!empty($referenceUids)) {
foreach ($referenceUids as $referenceUid) {
try {
// Just passing the reference uid, the factory is doing workspace
// overlays automatically depending on the current environment
$itemList[] = $this->factory->getFileReferenceObject($referenceUid);
} catch (ResourceDoesNotExistException) {
// No handling, just omit the invalid reference uid
}
}
$itemList = $this->reapplySorting($itemList);
}
return $itemList;
}
/**
* As sorting might have changed due to workspace overlays, PHP does the sorting again.
*
* @param FileReference[] $itemList
* @return FileReference[]
*/
protected function reapplySorting(array $itemList): array
{
uasort(
$itemList,
static function (FileReference $a, FileReference $b) {
$sortA = (int)$a->getReferenceProperty('sorting_foreign');
$sortB = (int)$b->getReferenceProperty('sorting_foreign');
if ($sortA === $sortB) {
return 0;
}
return ($sortA < $sortB) ? -1 : 1;
}
);
return $itemList;
}
/**
* This function can be mocked in unit tests to be able to test frontend behaviour.
*/
protected function isFrontend(): bool
{
if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
&& ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()
) {
return true;
}
return false;
}
}
+69
View File
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource;
enum FileType: int
{
/**
* any other file
*/
case UNKNOWN = 0;
/**
* Any kind of text
* @see http://www.iana.org/assignments/media-types/text
*/
case TEXT = 1;
/**
* Any kind of image
* @see http://www.iana.org/assignments/media-types/image
*/
case IMAGE = 2;
/**
* Any kind of audio file
* @see http://www.iana.org/assignments/media-types/audio
*/
case AUDIO = 3;
/**
* Any kind of video
* @see http://www.iana.org/assignments/media-types/video
*/
case VIDEO = 4;
/**
* Any kind of application
* @see http://www.iana.org/assignments/media-types/application
*/
case APPLICATION = 5;
public static function tryFromMimeType(string $mimeType): self
{
[$fileType] = explode('/', $mimeType);
return match (strtolower($fileType)) {
'text' => FileType::TEXT,
'image' => FileType::IMAGE,
'audio' => FileType::AUDIO,
'video' => FileType::VIDEO,
'application', 'software' => FileType::APPLICATION,
default => FileType::UNKNOWN,
};
}
}
@@ -0,0 +1,197 @@
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Resource\Filter;
use TYPO3\CMS\Core\Resource\Driver\DriverInterface;
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Utility methods for filtering filenames
*/
class FileExtensionFilter
{
/**
* Allowed file extensions. If NULL, all extensions are allowed.
*
* @var string[]|null
*/
protected ?array $allowedFileExtensions = null;
/**
* Disallowed file extensions. If NULL, no extension is disallowed (i.e. all are allowed).
*
* @var string[]|null
*/
protected ?array $disallowedFileExtensions = null;
public function filter(array $references, string $allowedFileExtensions, string $disallowedFileExtensions): array
{
if ($allowedFileExtensions !== '') {
$this->setAllowedFileExtensions($allowedFileExtensions);
}
if ($disallowedFileExtensions !== '') {
$this->setDisallowedFileExtensions($disallowedFileExtensions);
}
$cleanReferences = [];
foreach ($references as $reference) {
if (empty($reference)) {
continue;
}
$parts = GeneralUtility::revExplode('_', (string)$reference, 2);
$fileReferenceUid = (int)$parts[count($parts) - 1];
try {
$fileReference = GeneralUtility::makeInstance(ResourceFactory::class)->getFileReferenceObject($fileReferenceUid);
$file = $fileReference->getOriginalFile();
if ($this->isAllowed($file->getExtension())) {
$cleanReferences[] = $reference;
}
} catch (ResourceDoesNotExistException $e) {
// do nothing
}
}
return $cleanReferences;
}
/**
* Entry method for use as filelist filter.
*
* We use -1 as the "don't include“ return value, for historic reasons,
* as call_user_func() used to return FALSE if calling the method failed.
*
* @param string $itemName
* @param string $itemIdentifier
* @param string $parentIdentifier
* @param array $additionalInformation Additional information about the inspected item
* @param DriverInterface $driver
* @return bool|int -1 if the file should not be included in a listing
*/
public function filterFileList($itemName, $itemIdentifier, $parentIdentifier, array $additionalInformation, DriverInterface $driver)
{
$returnCode = true;
// Early return in case no file filters are set at all
if ($this->allowedFileExtensions === null && $this->disallowedFileExtensions === null) {
return $returnCode;
}
// Check that this is a file and not a folder
if ($driver->fileExists($itemIdentifier)) {
try {
$fileInfo = $driver->getFileInfoByIdentifier($itemIdentifier, ['extension']);
} catch (\InvalidArgumentException $e) {
$fileInfo = [];
}
if (!$this->isAllowed((string)($fileInfo['extension'] ?? ''))) {
$returnCode = -1;
}
}
return $returnCode;
}
/**
* Checks whether a file is allowed according to the criteria defined in the class variables ($this->allowedFileExtensions etc.)
*
* @internal this is used internally for TYPO3 core only
*/
public function isAllowed(string $fileExtension): bool
{
$fileExtension = strtolower($fileExtension);
$result = true;
// Check allowed file extensions
if (!empty($this->allowedFileExtensions) && !in_array($fileExtension, $this->allowedFileExtensions, true)) {
$result = false;
}
// Check disallowed file extensions
if (!empty($this->disallowedFileExtensions) && in_array($fileExtension, $this->disallowedFileExtensions, true)) {
$result = false;
}
return $result;
}
/**
* Set allowed file extensions
*
* @param mixed $allowedFileExtensions Comma-separated list or array, of allowed file extensions
*/
public function setAllowedFileExtensions(mixed $allowedFileExtensions): void
{
$this->allowedFileExtensions = $this->convertToLowercaseArray($allowedFileExtensions);
}
public function getAllowedFileExtensions(): ?array
{
return $this->allowedFileExtensions;
}
/**
* Set disallowed file extensions
*
* @param mixed $disallowedFileExtensions Comma-separated list or array, of allowed file extensions
*/
public function setDisallowedFileExtensions(mixed $disallowedFileExtensions): void
{
$this->disallowedFileExtensions = $this->convertToLowercaseArray($disallowedFileExtensions);
}
public function getDisallowedFileExtensions(): ?array
{
return $this->disallowedFileExtensions;
}
/**
* Compared the current allowed and disallowed lists and returns
* a filtered list either as allow or as disallow list. The "mode"
* is indicated by the array key, which is either "allowedFileExtensions"
* or "disallowedFileExtensions".
*/
public function getFilteredFileExtensions(): array
{
if ($this->disallowedFileExtensions === null) {
return ['allowedFileExtensions' => $this->allowedFileExtensions ?? ['*']];
}
if ($this->allowedFileExtensions === null) {
return ['disallowedFileExtensions' => $this->disallowedFileExtensions];
}
return ['allowedFileExtensions' => array_filter($this->allowedFileExtensions, function (string $fileExtension): bool {
return !in_array($fileExtension, $this->disallowedFileExtensions, true);
})];
}
/**
* Converts mixed (string or array) input arguments into an array, NULL if empty.
*
* All array values will be converted to lower case.
*/
protected function convertToLowercaseArray(mixed $inputArgument): ?array
{
$returnValue = null;
if (is_array($inputArgument)) {
$returnValue = $inputArgument;
} elseif ((string)$inputArgument !== '') {
$returnValue = GeneralUtility::trimExplode(',', $inputArgument);
}
if (is_array($returnValue)) {
$returnValue = array_map(strtolower(...), $returnValue);
}
return $returnValue;
}
}

Some files were not shown because too many files have changed in this diff Show More