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,172 @@
<?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\SystemResource\Publishing;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UriInterface;
use Symfony\Component\DependencyInjection\Attribute\AsAlias;
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Http\NormalizedParams;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
use TYPO3\CMS\Core\Package\Exception\PackageAssetsPublishingFailedException;
use TYPO3\CMS\Core\Package\PackageInterface;
use TYPO3\CMS\Core\SystemResource\Exception\CanNotGenerateUriException;
use TYPO3\CMS\Core\SystemResource\Publishing\FileSystem\FileSystemPublisherInterface;
use TYPO3\CMS\Core\SystemResource\Type\PublicResourceInterface;
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* This implementation publishes (when implemented) public assets from extension
* packages to the public _assets directory using a hash as directory name.
* Subsequently, it can also generate URIs to public resource objects within that _assets folder
*
* @internal Never use or reference it directly, use SystemResourcePublisherInterface to inject it (or a proper replacement).
*/
#[Autoconfigure(public: true), AsAlias(SystemResourcePublisherInterface::class, public: true)]
final readonly class DefaultSystemResourcePublisher implements SystemResourcePublisherInterface
{
private const string PUBLISHING_DIRECTORY = '_assets/';
private const string PUBLISHING_DIRECTORY_INSTALL = '_assets_install/';
/**
* @var FileSystemPublisherInterface[]
*/
private array $fileSystemPublishers;
private string $publishingDirectory;
public function __construct(
array $fileSystemPublishers = [],
bool $failsafe = false,
) {
$this->fileSystemPublishers = $fileSystemPublishers;
$this->publishingDirectory = $failsafe ? self::PUBLISHING_DIRECTORY_INSTALL : self::PUBLISHING_DIRECTORY;
}
public function publishResources(
PackageInterface $package,
): FlashMessageQueue {
$queue = new FlashMessageQueue('asset:publish');
$resourceDefinitions = $package
->getResources()
->getPublicResourceDefinitions();
foreach ($resourceDefinitions as $definition) {
$publishingContext = new ResourcePublishingContext(
package: $package,
definition: $definition,
);
if ($publishingContext->isSourcePublic) {
continue;
}
$publicResourcesPath = Environment::getPublicPath() . '/' . $this->publishingDirectory . $publishingContext->prefix;
GeneralUtility::mkdir_deep(dirname($publicResourcesPath));
try {
foreach ($this->fileSystemPublishers as $publisher) {
if (!$publisher->canPublish($publishingContext->filesystemPath, $publicResourcesPath)) {
continue;
}
if (is_file($publishingContext->filesystemPath)) {
$publisher->publishFile($publishingContext->filesystemPath, $publicResourcesPath);
} else {
if (!is_dir($publishingContext->filesystemPath)) {
$queue->addMessage(new FlashMessage(
sprintf(
'Did not publish public resource for extension "%s".'
. chr(10)
. 'The source file/directory "%s" does not exist.',
$package->getPackageKey(),
substr($publishingContext->filesystemPath, strlen(Environment::getProjectPath())),
),
$package->getPackageKey(),
ContextualFeedbackSeverity::INFO,
));
break;
}
$publisher->publishFolder($publishingContext->filesystemPath, $publicResourcesPath);
}
break;
}
} catch (PackageAssetsPublishingFailedException $e) {
$queue->addMessage(new FlashMessage(
sprintf(
'Could not publish public resources for extension "%s" by using the "%s" strategy.'
. chr(10)
. 'Check whether the target directory "%s" already exists'
. chr(10)
. 'and TYPO3 has permissions to write inside the "_assets" directory.',
$package->getPackageKey(),
$e->publishingStrategy,
'.' . substr($publicResourcesPath, strlen(Environment::getProjectPath())),
),
$package->getPackageKey(),
ContextualFeedbackSeverity::ERROR,
));
}
}
return $queue;
}
/**
* @throws CanNotGenerateUriException
*/
public function generateUri(PublicResourceInterface $publicResource, ?ServerRequestInterface $request, ?UriGenerationOptions $options = null): UriInterface
{
if (!$publicResource->isPublished()) {
throw new CanNotGenerateUriException(sprintf('Can not generate Uri for an unpublished resource %s', $publicResource), 1761211273);
}
$request ??= $GLOBALS['TYPO3_REQUEST'] ?? null;
$options ??= new UriGenerationOptions();
return $publicResource->getPublicUri(
new DefaultSystemResourceUriGenerator(
$this->publishingDirectory,
$this->extractPublicPrefixFromRequest($request, $options->uriPrefix),
$request,
$options,
)
);
}
private function extractPublicPrefixFromRequest(?ServerRequestInterface $request, ?string $publicPrefix): string
{
if ($publicPrefix !== null) {
return $publicPrefix;
}
if ($request === null) {
return '/';
}
$normalizedParams = $request->getAttribute('normalizedParams');
return $this->getFrontendUrlPrefix($request->getAttribute('frontend.typoscript')?->getConfigArray(), $normalizedParams)
?? $normalizedParams->getSitePath();
}
private function getFrontendUrlPrefix(?array $typoScriptConfigArray, NormalizedParams $normalizedParams): ?string
{
if ($typoScriptConfigArray === null) {
return null;
}
if ($typoScriptConfigArray['forceAbsoluteUrls'] ?? false) {
return $normalizedParams->getSiteUrl();
}
$absRefPrefix = trim($typoScriptConfigArray['absRefPrefix'] ?? '');
return $absRefPrefix === 'auto' ? $normalizedParams->getSitePath() : $absRefPrefix;
}
}