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
@@ -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);
}
}
}