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,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\Search\QueryRestrictions;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression;
use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionInterface;
/**
* Filters missing files from search result
*/
class ConsistencyRestriction implements QueryRestrictionInterface
{
/**
* @var QueryBuilder
*/
private $queryBuilder;
public function __construct(QueryBuilder $queryBuilder)
{
$this->queryBuilder = $queryBuilder;
}
public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression
{
$constraints = [];
foreach ($queriedTables as $tableAlias => $tableName) {
if ($tableName === 'sys_file') {
$constraints[] = $this->queryBuilder->expr()->eq($tableAlias . '.missing', $this->queryBuilder->createNamedParameter(0, Connection::PARAM_INT));
}
}
return $expressionBuilder->and(...$constraints);
}
}
@@ -0,0 +1,55 @@
<?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\Search\QueryRestrictions;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression;
use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Limits search result to files with given folder hashes
*/
class FolderHashesRestriction implements QueryRestrictionInterface
{
/**
* @var array
*/
private $folderHashes;
public function __construct(array $folderHashes)
{
$this->folderHashes = $folderHashes;
}
public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression
{
$constraints = [];
foreach ($queriedTables as $tableAlias => $tableName) {
if ($tableName !== 'sys_file') {
continue;
}
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($tableName);
$quotedHashes = array_map($connection->quote(...), $this->folderHashes);
$constraints[] = $expressionBuilder->in($tableAlias . '.folder_hash', $quotedHashes);
}
return $expressionBuilder->or(...$constraints);
}
}
@@ -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\Search\QueryRestrictions;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression;
use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Assumes identifiers carrying hierarchical information and
* filters files with identifiers starting with given identifier.
*/
class FolderIdentifierRestriction implements QueryRestrictionInterface
{
/**
* @var string
*/
private $folderIdentifier;
public function __construct(string $folderIdentifier)
{
$this->folderIdentifier = $folderIdentifier;
}
public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression
{
$constraints = [];
foreach ($queriedTables as $tableAlias => $tableName) {
if ($tableName !== 'sys_file') {
continue;
}
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($tableName);
$folderIdentifier = $connection->createQueryBuilder()->escapeLikeWildcards($this->folderIdentifier);
$constraints[] = $expressionBuilder->like(
$tableAlias . '.identifier',
$connection->quote($folderIdentifier . '%')
);
}
return $expressionBuilder->or(...$constraints);
}
}
@@ -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\Search\QueryRestrictions;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression;
use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\AbstractRestrictionContainer;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Restricts the result to available file mounts.
* No restriction is added if the user is admin.
*/
class FolderMountsRestriction extends AbstractRestrictionContainer
{
/**
* @var BackendUserAuthentication
*/
private $backendUser;
/**
* @var Folder[]|null
*/
private $folderMounts;
public function __construct(BackendUserAuthentication $backendUser)
{
$this->backendUser = $backendUser;
$this->populateRestrictions();
}
private function populateRestrictions(): void
{
if ($this->backendUser->isAdmin()) {
return;
}
foreach ($this->getFolderMounts() as $folder) {
$this->add(new FolderRestriction($folder, true));
}
}
/**
* Same as parent method, but using OR composite, as files in either mounted folder should be found.
*
* @param array $queriedTables Array of tables, where array key is table alias and value is a table name
* @param ExpressionBuilder $expressionBuilder Expression builder instance to add restrictions with
* @return CompositeExpression The result of query builder expression(s)
*/
public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression
{
if (!$this->backendUser->isAdmin() && empty($this->getFolderMounts())) {
// If the user isn't an admin but has no mounted folders, add an expression leading to an empty result
return $expressionBuilder->and('1=0');
}
$constraints = [];
foreach ($this->restrictions as $restriction) {
$constraints[] = $restriction->buildExpression($queriedTables, $expressionBuilder);
}
return $expressionBuilder->or(...$constraints);
}
/**
* @return Folder[]
*/
private function getFolderMounts(): array
{
if ($this->folderMounts !== null) {
return $this->folderMounts;
}
$this->folderMounts = [];
$fileMounts = $this->backendUser->getFileMountRecords();
foreach ($fileMounts as $fileMount) {
$this->folderMounts[] = GeneralUtility::makeInstance(ResourceFactory::class)->getFolderObjectFromCombinedIdentifier($fileMount['identifier'] ?? '');
}
return $this->folderMounts;
}
}
@@ -0,0 +1,83 @@
<?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\Search\QueryRestrictions;
use TYPO3\CMS\Core\Database\Query\Restriction\AbstractRestrictionContainer;
use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionInterface;
use TYPO3\CMS\Core\Resource\Folder;
/**
* Limits result to storage given by the folder
* and also restricts result to the given folder, respecting whether the storage
* has hierarchical identifiers or not.
*/
class FolderRestriction extends AbstractRestrictionContainer
{
/**
* @var Folder
*/
private $folder;
/**
* @var bool
*/
private $recursive;
public function __construct(Folder $folder, bool $recursive)
{
$this->folder = $folder;
$this->recursive = $recursive;
$this->populateRestrictions();
}
private function populateRestrictions(): void
{
$storage = $this->folder->getStorage();
$this->add(new StorageRestriction($storage));
if (!$this->recursive) {
$this->add($this->createFolderRestriction());
return;
}
if ($this->folder->getIdentifier() === $storage->getRootLevelFolder(false)->getIdentifier()) {
return;
}
if ($storage->hasHierarchicalIdentifiers()) {
$this->add($this->createHierarchicalFolderRestriction());
} else {
$this->add($this->createFolderRestriction());
}
}
private function createHierarchicalFolderRestriction(): QueryRestrictionInterface
{
return $this->recursive ? new FolderIdentifierRestriction($this->folder->getIdentifier()) : new FolderHashesRestriction([$this->folder->getHashedIdentifier()]);
}
private function createFolderRestriction(): QueryRestrictionInterface
{
$hashedFolderIdentifiers = [];
$hashedFolderIdentifiers[] = $this->folder->getHashedIdentifier();
if ($this->recursive) {
foreach ($this->folder->getSubfolders(0, 0, Folder::FILTER_MODE_NO_FILTERS, true) as $subFolder) {
$hashedFolderIdentifiers[] = $subFolder->getHashedIdentifier();
}
}
return new FolderHashesRestriction($hashedFolderIdentifiers);
}
}
@@ -0,0 +1,101 @@
<?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\Search\QueryRestrictions;
use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression;
use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionInterface;
use TYPO3\CMS\Core\Resource\Search\FileSearchDemand;
use TYPO3\CMS\Core\Schema\SearchableSchemaFieldsCollector;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Filters result by a given search term, respecting search fields defined in search demand or in TCA.
*/
readonly class SearchTermRestriction implements QueryRestrictionInterface
{
public function __construct(
private FileSearchDemand $searchDemand,
private QueryBuilder $queryBuilder,
) {}
public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression
{
$constraints = [];
foreach ($queriedTables as $tableAlias => $tableName) {
if (!in_array($tableName, ['sys_file', 'sys_file_metadata'])) {
continue;
}
$constraints[] = $this->makeQuerySearchByTable($tableName, $tableAlias);
}
return $expressionBuilder->or(...$constraints);
}
/**
* Build the MySql where clause by table.
*
* @param string $tableName Record table name
*/
private function makeQuerySearchByTable(string $tableName, string $tableAlias): CompositeExpression
{
$constraints = [];
$fieldsToSearchWithin = GeneralUtility::makeInstance(SearchableSchemaFieldsCollector::class)->getFields(
$tableName,
$this->searchDemand->getSearchFields()[$tableName] ?? []
);
if ($fieldsToSearchWithin->count() > 0) {
$searchTerm = (string)$this->searchDemand->getSearchTerm();
$searchTermParts = str_getcsv($searchTerm, ' ', '"', '\\');
foreach ($searchTermParts as $searchTermPart) {
$searchTermPart = trim($searchTermPart);
if ($searchTermPart === '') {
continue;
}
$constraintsForParts = [];
$like = '%' . $this->queryBuilder->escapeLikeWildcards($searchTermPart) . '%';
foreach ($fieldsToSearchWithin as $fieldName => $field) {
$constraintsForParts[] = $this->queryBuilder->expr()->and(
$this->queryBuilder->expr()->comparison(
sprintf(
'LOWER(%s)',
// Ensure to cast `$fieldName` to a text value, otherwise picky databases like
// postgres would complain about trying to use `LOWER()` on incompatible field
// like integer fields, something MariaDB/MySQL is silently allowed and hidden
// away from the consumer. We avoid doing database field type checks here for
// all or specific database and adding a value conversion by default for all
// fields to be on the safe side.
//
// The lower() construct here is used to enforce "case-insensitive" search for
// all database vendors unrelated to charset/collation configurations on field
// level.
$this->queryBuilder->expr()->castText($this->queryBuilder->quoteIdentifier($tableAlias . '.' . $fieldName))
),
'LIKE',
$this->queryBuilder->createNamedParameter(mb_strtolower($like))
)
);
}
$constraints[] = $this->queryBuilder->expr()->or(...$constraintsForParts);
}
}
return $this->queryBuilder->expr()->and(...$constraints);
}
}
@@ -0,0 +1,55 @@
<?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\Search\QueryRestrictions;
use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression;
use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionInterface;
use TYPO3\CMS\Core\Resource\ResourceStorage;
/**
* Limits search result to a give storage
*/
class StorageRestriction implements QueryRestrictionInterface
{
/**
* @var ResourceStorage
*/
private $storage;
public function __construct(ResourceStorage $storage)
{
$this->storage = $storage;
}
public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression
{
$constraints = [];
foreach ($queriedTables as $tableAlias => $tableName) {
if ($tableName !== 'sys_file') {
continue;
}
$constraints[] = $expressionBuilder->eq(
$tableAlias . '.storage',
(int)$this->storage->getUid()
);
}
return $expressionBuilder->or(...$constraints);
}
}