TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when a URI to a public resource can not be created
|
||||
*/
|
||||
final class CanNotGenerateUriException extends SystemResourceException {}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when a resolved resource is NOT public, but MUST be public (e.g. for URI generation)
|
||||
*/
|
||||
final class CanNotResolvePublicResourceException extends SystemResourceException {}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when all attempts to resolve a string to a unique system resource fail
|
||||
*/
|
||||
class CanNotResolveSystemResourceException extends SystemResourceException {}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when no specific identifier type can be detected
|
||||
*/
|
||||
final class CanNotResolveSystemResourceIdentifierException extends SystemResourceException {}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when an identifier type is detected, but the identifier contains errors
|
||||
*/
|
||||
final class InvalidSystemResourceIdentifierException extends SystemResourceException {}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when a resource is requested that has not been defined beforehand
|
||||
*/
|
||||
final class SystemResourceDefinitionNotFoundException extends SystemResourceException {}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* This exception is thrown when a referenced system resource (aka file)
|
||||
* does not exist, but information about this file is requested (like contents)
|
||||
*/
|
||||
final class SystemResourceDoesNotExistException extends SystemResourceException {}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?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\Exception;
|
||||
|
||||
use TYPO3\CMS\Core\Resource\Exception;
|
||||
|
||||
/**
|
||||
* This is the base exception. Catch this one to
|
||||
* gracefully proceed when errors happen in resource
|
||||
* creation or resource URI generation
|
||||
*/
|
||||
class SystemResourceException extends Exception {}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?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\Http;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use TYPO3\CMS\Core\Http\ApplicationType;
|
||||
use TYPO3\CMS\Core\Http\Uri;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
|
||||
/**
|
||||
* This is subject to change during v14 development. Do not use.
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
class CacheBustingUri extends Uri
|
||||
{
|
||||
public static function fromFileSystemPath(string $absolutePathToPotentialFile, UriInterface $baseUri, ?ApplicationType $applicationType = null): UriInterface
|
||||
{
|
||||
try {
|
||||
// The absolute path might be appended with a query string and/ or fragment
|
||||
// therefore we remove it here before we check for file existence
|
||||
$absolutePath = (new Uri($absolutePathToPotentialFile))->getPath();
|
||||
if (!is_file($absolutePath)) {
|
||||
return $baseUri;
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
// See https://review.typo3.org/75477 for the reason of this try/catch
|
||||
// likely we can remove this in the future, but it is kept for now to ensure BC
|
||||
return $baseUri;
|
||||
}
|
||||
$configAccessor = $applicationType?->isFrontend() ? 'FE' : 'BE';
|
||||
$rewriteFileName = (bool)($GLOBALS['TYPO3_CONF_VARS'][$configAccessor]['versionNumberInFilename'] ?? false);
|
||||
$fileModificationTime = filemtime($absolutePath);
|
||||
if ($rewriteFileName) {
|
||||
$nameParts = explode('.', $baseUri->getPath());
|
||||
$fileExtension = array_pop($nameParts);
|
||||
array_push($nameParts, $fileModificationTime, $fileExtension);
|
||||
$uri = $baseUri->withPath(implode('.', $nameParts));
|
||||
} else {
|
||||
$query = $baseUri->getQuery();
|
||||
$uri = $baseUri->withQuery($query . ($query !== '' ? '&' : '') . $fileModificationTime);
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
public static function fromFile(File $file, UriInterface $baseUri): UriInterface
|
||||
{
|
||||
$query = $baseUri->getQuery();
|
||||
return $baseUri->withQuery($query . ($query !== '' ? '&' : '') . $file->getSha1());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?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\Identifier;
|
||||
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\InvalidSystemResourceIdentifierException;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
|
||||
/**
|
||||
* This is subject to change during v14 development. Do not use.
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
final class FalResourceIdentifier extends SystemResourceIdentifier
|
||||
{
|
||||
public const string TYPE = 'FAL';
|
||||
|
||||
public function __construct(private readonly string $storageId, private readonly string $falIdentifier, string $givenIdentifier)
|
||||
{
|
||||
parent::__construct($givenIdentifier);
|
||||
if (!MathUtility::canBeInterpretedAsInteger($storageId)) {
|
||||
throw new InvalidSystemResourceIdentifierException(sprintf('Given identifier "%s" is invalid. Storage id must be integer.', $givenIdentifier), 1760433315);
|
||||
}
|
||||
}
|
||||
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return sprintf('%d:%s', $this->storageId, $this->falIdentifier);
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf(
|
||||
'%s:%d:%s',
|
||||
self::TYPE,
|
||||
$this->storageId,
|
||||
$this->falIdentifier,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?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\Identifier;
|
||||
|
||||
use TYPO3\CMS\Core\Package\PackageInterface;
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\InvalidSystemResourceIdentifierException;
|
||||
|
||||
/**
|
||||
* This is subject to change during v14 development. Do not use.
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
final class PackageResourceIdentifier extends SystemResourceIdentifier
|
||||
{
|
||||
public const string LEGACY_TYPE = 'EXT';
|
||||
public const string TYPE = 'PKG';
|
||||
|
||||
public function __construct(
|
||||
private readonly PackageInterface $package,
|
||||
private readonly string $relativePath,
|
||||
string $givenIdentifier,
|
||||
) {
|
||||
parent::__construct($givenIdentifier);
|
||||
}
|
||||
|
||||
public function getPackage(): PackageInterface
|
||||
{
|
||||
return $this->package;
|
||||
}
|
||||
|
||||
public function getRelativePath(): string
|
||||
{
|
||||
return $this->relativePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidSystemResourceIdentifierException
|
||||
*/
|
||||
public function withRelativePath(string $newPath): self
|
||||
{
|
||||
return new self(
|
||||
$this->package,
|
||||
$newPath,
|
||||
$this->givenIdentifier,
|
||||
);
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf(
|
||||
'%s:%s:%s',
|
||||
self::TYPE,
|
||||
$this->package->getValueFromComposerManifest('name') ?? $this->package->getPackageKey(),
|
||||
$this->relativePath,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?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\Identifier;
|
||||
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\InvalidSystemResourceIdentifierException;
|
||||
|
||||
/**
|
||||
* This is subject to change during v14 development. Do not use.
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
abstract class SystemResourceIdentifier implements \Stringable
|
||||
{
|
||||
/**
|
||||
* @throws InvalidSystemResourceIdentifierException
|
||||
*/
|
||||
public function __construct(public readonly string $givenIdentifier) {}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->givenIdentifier;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?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\Identifier;
|
||||
|
||||
use TYPO3\CMS\Core\Package\Exception\UnknownPackageException;
|
||||
use TYPO3\CMS\Core\Package\Exception\UnknownPackagePathException;
|
||||
use TYPO3\CMS\Core\Package\PackageInterface;
|
||||
use TYPO3\CMS\Core\Package\PackageManager;
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\CanNotResolveSystemResourceIdentifierException;
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\InvalidSystemResourceIdentifierException;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\PathUtility;
|
||||
|
||||
/**
|
||||
* This is subject to change during v14 development. Do not use.
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace,
|
||||
* with four exceptions due to deprecated legacy code, see Uri\ResourceViewHelper, PageRendererBackendSetupTrait,
|
||||
* AbstractItemProvider and ExtensionManagementUtility::resolvePackagePath()
|
||||
*/
|
||||
final readonly class SystemResourceIdentifierFactory
|
||||
{
|
||||
public function __construct(private PackageManager $packageManager) {}
|
||||
|
||||
/**
|
||||
* @throws InvalidSystemResourceIdentifierException
|
||||
* @throws CanNotResolveSystemResourceIdentifierException
|
||||
*/
|
||||
public function create(string $resourceIdentifier): SystemResourceIdentifier
|
||||
{
|
||||
$givenIdentifier = $resourceIdentifier;
|
||||
if (PathUtility::hasProtocolAndScheme($resourceIdentifier)) {
|
||||
$resourceIdentifier = sprintf('%s:%s', UriResourceIdentifier::TYPE, $resourceIdentifier);
|
||||
}
|
||||
[$identifierType] = explode(':', $resourceIdentifier, 2);
|
||||
return match ($identifierType) {
|
||||
PackageResourceIdentifier::LEGACY_TYPE => $this->createPackageResourceIdentifier($this->convertExtensionPathToPackageResourceIdentifier($resourceIdentifier), $resourceIdentifier),
|
||||
PackageResourceIdentifier::TYPE => $this->createPackageResourceIdentifier($resourceIdentifier),
|
||||
FalResourceIdentifier::TYPE => $this->createFalResourceIdentifier($resourceIdentifier),
|
||||
UriResourceIdentifier::TYPE => $this->createUriResourceIdentifier($givenIdentifier),
|
||||
default => throw new CanNotResolveSystemResourceIdentifierException(sprintf('Can not resolve system resource identifier "%s".', $resourceIdentifier), 1758700314),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidSystemResourceIdentifierException
|
||||
*/
|
||||
public function createFromPackagePath(string $packageKey, string $relativePath, string $givenIdentifier): PackageResourceIdentifier
|
||||
{
|
||||
return new PackageResourceIdentifier(
|
||||
$this->getPackageAndValidatePath($packageKey, $relativePath, $givenIdentifier),
|
||||
$relativePath,
|
||||
$givenIdentifier
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidSystemResourceIdentifierException
|
||||
*/
|
||||
private function createPackageResourceIdentifier(string $resourceIdentifier, ?string $originalIdentifier = null): PackageResourceIdentifier
|
||||
{
|
||||
[,$packageKey, $relativePath] = $this->parseIdentifier($resourceIdentifier);
|
||||
return $this->createFromPackagePath($packageKey, $relativePath, $originalIdentifier ?? $resourceIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidSystemResourceIdentifierException
|
||||
*/
|
||||
private function createFalResourceIdentifier(string $resourceIdentifier): FalResourceIdentifier
|
||||
{
|
||||
[,$storageId, $falIdentifier] = $this->parseIdentifier($resourceIdentifier);
|
||||
return new FalResourceIdentifier($storageId, $falIdentifier, $resourceIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidSystemResourceIdentifierException
|
||||
*/
|
||||
private function createUriResourceIdentifier(string $resourceIdentifier): UriResourceIdentifier
|
||||
{
|
||||
try {
|
||||
return new UriResourceIdentifier($resourceIdentifier);
|
||||
} catch (\Throwable $e) {
|
||||
throw new InvalidSystemResourceIdentifierException(sprintf('Can not resolve system resource identifier "%s". Invalid URI.', $resourceIdentifier), 1761732010, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{string, string, string}
|
||||
* @throws InvalidSystemResourceIdentifierException
|
||||
*/
|
||||
private function parseIdentifier(string $resourceIdentifier): array
|
||||
{
|
||||
$identifierParts = explode(':', $resourceIdentifier);
|
||||
if (count($identifierParts) !== 3) {
|
||||
throw new InvalidSystemResourceIdentifierException(sprintf('Given system resource identifier "%s" is invalid. An identifier consists of three parts, separated by a colon (":").', $resourceIdentifier), 1760386146);
|
||||
}
|
||||
return $identifierParts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidSystemResourceIdentifierException
|
||||
*/
|
||||
private function convertExtensionPathToPackageResourceIdentifier(string $extensionPath): string
|
||||
{
|
||||
try {
|
||||
$packageKey = $this->packageManager->extractPackageKeyFromPackagePath($extensionPath);
|
||||
} catch (UnknownPackageException|UnknownPackagePathException $e) {
|
||||
throw new InvalidSystemResourceIdentifierException(sprintf('Can not create system resource identifier from "%s".', $extensionPath), 1758884297, $e);
|
||||
}
|
||||
return sprintf(
|
||||
'%s:%s:%s',
|
||||
PackageResourceIdentifier::TYPE,
|
||||
$packageKey,
|
||||
substr($extensionPath, strlen($packageKey) + 5),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidSystemResourceIdentifierException
|
||||
*/
|
||||
private function getPackageAndValidatePath(string $packageKey, string $relativePath, string $givenIdentifier): PackageInterface
|
||||
{
|
||||
if ($relativePath === ''
|
||||
|| str_starts_with($relativePath, '/')
|
||||
|| !GeneralUtility::validPathStr($relativePath)
|
||||
) {
|
||||
throw new InvalidSystemResourceIdentifierException(sprintf('Relative package path "%s" must not be empty, must not start with a slash ("/") and must not contain invalid characters (e.g. ../ back path). (Given identifier "%s")', $relativePath, $givenIdentifier), 1763381514);
|
||||
}
|
||||
try {
|
||||
$package = $this->packageManager->getPackage($packageKey);
|
||||
} catch (UnknownPackageException $e) {
|
||||
throw new InvalidSystemResourceIdentifierException(sprintf('Package with key "%s" does not exist. (Given identifier "%s")', $packageKey, $givenIdentifier), 1763381504, $e);
|
||||
}
|
||||
return $package;
|
||||
}
|
||||
}
|
||||
@@ -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\SystemResource\Identifier;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use TYPO3\CMS\Core\Http\Uri;
|
||||
|
||||
/**
|
||||
* This is subject to change during v14 development. Do not use.
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
final class UriResourceIdentifier extends SystemResourceIdentifier
|
||||
{
|
||||
public const string TYPE = 'URI';
|
||||
private readonly UriInterface $uri;
|
||||
|
||||
public function __construct(string $givenIdentifier)
|
||||
{
|
||||
parent::__construct($givenIdentifier);
|
||||
if (str_starts_with($givenIdentifier, self::TYPE)) {
|
||||
$uri = substr($givenIdentifier, strlen(self::TYPE) + 1);
|
||||
}
|
||||
$this->uri = new Uri($uri ?? $givenIdentifier);
|
||||
}
|
||||
|
||||
public function getUri(): UriInterface
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
if ($this->isRelative()) {
|
||||
return $this->givenIdentifier;
|
||||
}
|
||||
return (string)$this->uri;
|
||||
}
|
||||
|
||||
private function isRelative(): bool
|
||||
{
|
||||
return $this->uri->getAuthority() === '';
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?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 TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Http\ApplicationType;
|
||||
use TYPO3\CMS\Core\Http\Uri;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\CanNotGenerateUriException;
|
||||
use TYPO3\CMS\Core\SystemResource\Http\CacheBustingUri;
|
||||
|
||||
/**
|
||||
* This is tightly coupled to DefaultSystemResourcePublisher and acts
|
||||
* as a helper to actually generate the URI for a public resource.
|
||||
* This helper and its interface only exists to not expose the absolute
|
||||
* path from the system resource objects directly.
|
||||
*
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
readonly class DefaultSystemResourceUriGenerator implements SystemResourceUriGeneratorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private string $publishingDirectory,
|
||||
private string $prefix,
|
||||
private ?ServerRequestInterface $request,
|
||||
private UriGenerationOptions $options,
|
||||
) {}
|
||||
|
||||
public function generateForPackageResource(
|
||||
ResourceUriBuildingContext $context,
|
||||
): UriInterface {
|
||||
$uri = $this->makeAbsolute(new Uri($this->calculateUriPath($context)));
|
||||
if (!$this->options->cacheBusting) {
|
||||
return $uri;
|
||||
}
|
||||
return CacheBustingUri::fromFileSystemPath(
|
||||
$context->absoluteResourcePath,
|
||||
$uri,
|
||||
$this->request ? ApplicationType::fromRequest($this->request) : null
|
||||
);
|
||||
}
|
||||
|
||||
public function generateForFile(File $file): UriInterface
|
||||
{
|
||||
$publicUrl = $file->getPublicUrl();
|
||||
if ($publicUrl === null) {
|
||||
throw new CanNotGenerateUriException(sprintf('Can not create a public Uri for a file %s', $file), 1758619473);
|
||||
}
|
||||
if (Environment::isCli()) {
|
||||
// On CLI FAL public URLs are always relative to public directory,
|
||||
// so we apply the prefix here, which is likely a "/" only,
|
||||
// unless calling code properly faked a request.
|
||||
$publicUrl = $this->prefix . $publicUrl;
|
||||
}
|
||||
$uri = $this->makeAbsolute(new Uri($publicUrl));
|
||||
if (!$this->options->cacheBusting) {
|
||||
return $uri;
|
||||
}
|
||||
return CacheBustingUri::fromFile(
|
||||
$file,
|
||||
$uri,
|
||||
);
|
||||
}
|
||||
|
||||
private function makeAbsolute(UriInterface $uri): UriInterface
|
||||
{
|
||||
if ($this->request === null || !$this->options->absoluteUri) {
|
||||
return $uri;
|
||||
}
|
||||
if ($uri->getHost() !== '') {
|
||||
return $uri;
|
||||
}
|
||||
$siteUri = new Uri($this->request->getAttribute('normalizedParams')->getSiteUrl());
|
||||
return $uri->withScheme($siteUri->getScheme())
|
||||
->withUserInfo($siteUri->getUserInfo())
|
||||
->withHost($siteUri->getHost())
|
||||
->withPort($siteUri->getPort());
|
||||
}
|
||||
|
||||
private function calculateUriPath(ResourceUriBuildingContext $context): string
|
||||
{
|
||||
if ($context->isSourcePublic) {
|
||||
return $this->prefix . substr($context->absoluteResourcePath, strlen(Environment::getPublicPath()) + 1);
|
||||
}
|
||||
return $this->prefix . $this->publishingDirectory . $context->uriPath;
|
||||
}
|
||||
}
|
||||
@@ -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\SystemResource\Publishing\FileSystem;
|
||||
|
||||
use TYPO3\CMS\Core\Package\Exception\PackageAssetsPublishingFailedException;
|
||||
|
||||
/**
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
interface FileSystemPublisherInterface
|
||||
{
|
||||
public function canPublish(string $source, string $target): bool;
|
||||
|
||||
/**
|
||||
* @throws PackageAssetsPublishingFailedException
|
||||
*/
|
||||
public function publishFolder(string $source, string $target): void;
|
||||
|
||||
/**
|
||||
* @throws PackageAssetsPublishingFailedException
|
||||
*/
|
||||
public function publishFile(string $source, string $target): void;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?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\FileSystem;
|
||||
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Package\Exception\PackageAssetsPublishingFailedException;
|
||||
use TYPO3\CMS\Core\Utility\File\FileSystem;
|
||||
|
||||
/**
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
final readonly class JunctionPublisher implements FileSystemPublisherInterface
|
||||
{
|
||||
private PublishingConfiguration $config;
|
||||
|
||||
public function __construct(private FileSystem $fileSystem)
|
||||
{
|
||||
$this->config = new PublishingConfiguration();
|
||||
}
|
||||
|
||||
public function canPublish(string $source, string $target): bool
|
||||
{
|
||||
return Environment::isWindows()
|
||||
&& $this->config->isLinkPublishingEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PackageAssetsPublishingFailedException
|
||||
*/
|
||||
public function publishFolder(string $source, string $target): void
|
||||
{
|
||||
$this->ensureJunctionExists($source, $target);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function publishFile(string $source, string $target): void
|
||||
{
|
||||
throw new \LogicException(self::class . ' can not be used to publish single files', 1772535297);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PackageAssetsPublishingFailedException
|
||||
*/
|
||||
private function ensureJunctionExists(string $target, string $junction): void
|
||||
{
|
||||
$e = null;
|
||||
if (!$this->fileSystem->isJunction($junction)) {
|
||||
try {
|
||||
$this->fileSystem->junction($target, $junction);
|
||||
} catch (IOException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
if ($e !== null || realpath($target) !== realpath($junction)) {
|
||||
throw new PackageAssetsPublishingFailedException(
|
||||
'junction',
|
||||
1717488535,
|
||||
$e,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?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\FileSystem;
|
||||
|
||||
use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
|
||||
use TYPO3\CMS\Core\Package\Exception\PackageAssetsPublishingFailedException;
|
||||
|
||||
/**
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
final readonly class MirrorPublisher implements FileSystemPublisherInterface
|
||||
{
|
||||
private PublishingConfiguration $config;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = new PublishingConfiguration();
|
||||
}
|
||||
|
||||
public function canPublish(string $source, string $target): bool
|
||||
{
|
||||
return $this->config->isMirrorPublishingEnabled();
|
||||
}
|
||||
|
||||
public function publishFolder(string $source, string $target): void
|
||||
{
|
||||
if (realpath($source) === realpath($target)) {
|
||||
throw new PackageAssetsPublishingFailedException(
|
||||
'mirror',
|
||||
1773140314,
|
||||
);
|
||||
}
|
||||
$symfonyFilesystem = new SymfonyFilesystem();
|
||||
$symfonyFilesystem->mirror(
|
||||
$source,
|
||||
$target,
|
||||
null,
|
||||
[
|
||||
'delete' => true,
|
||||
'override' => true,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function publishFile(string $source, string $target): void
|
||||
{
|
||||
if (!is_file($source)) {
|
||||
throw new \LogicException('Can not publish file, because source is not a file', 1772538042);
|
||||
}
|
||||
if (realpath($source) === realpath($target)) {
|
||||
throw new PackageAssetsPublishingFailedException(
|
||||
'mirror',
|
||||
1773140294,
|
||||
);
|
||||
}
|
||||
$symfonyFilesystem = new SymfonyFilesystem();
|
||||
$symfonyFilesystem->copy($source, $target, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?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\FileSystem;
|
||||
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
|
||||
/**
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
final readonly class PublishingConfiguration
|
||||
{
|
||||
private string $publishingType;
|
||||
|
||||
public function __construct(?string $publishingType = null)
|
||||
{
|
||||
$publishingType = $publishingType ?? $GLOBALS['TYPO3_CONF_VARS']['SYS']['SystemResources']['filesystemPublishingType'] ?? 'auto';
|
||||
if ($publishingType === 'auto') {
|
||||
$publishingType = Environment::getContext()->isDevelopment() ? 'link' : 'mirror';
|
||||
}
|
||||
$this->publishingType = $publishingType;
|
||||
}
|
||||
|
||||
public function isLinkPublishingEnabled(): bool
|
||||
{
|
||||
return $this->publishingType === 'link';
|
||||
}
|
||||
|
||||
public function isMirrorPublishingEnabled(): bool
|
||||
{
|
||||
return $this->publishingType === 'mirror';
|
||||
}
|
||||
|
||||
public function hasCustomPublishingType(): bool
|
||||
{
|
||||
return !$this->isMirrorPublishingEnabled() && !$this->isLinkPublishingEnabled();
|
||||
}
|
||||
|
||||
public function getCustomPublishingType(): string
|
||||
{
|
||||
if (!$this->hasCustomPublishingType()) {
|
||||
throw new \LogicException('There is no custom publishing type enabled', 1773138713);
|
||||
}
|
||||
return $this->publishingType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?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\FileSystem;
|
||||
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Package\Exception\PackageAssetsPublishingFailedException;
|
||||
use TYPO3\CMS\Core\Utility\File\FileSystem;
|
||||
|
||||
/**
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
final readonly class SymlinkPublisher implements FileSystemPublisherInterface
|
||||
{
|
||||
private PublishingConfiguration $config;
|
||||
|
||||
public function __construct(private FileSystem $fileSystem)
|
||||
{
|
||||
$this->config = new PublishingConfiguration();
|
||||
}
|
||||
|
||||
public function canPublish(string $source, string $target): bool
|
||||
{
|
||||
return !Environment::isWindows()
|
||||
&& $this->config->isLinkPublishingEnabled();
|
||||
}
|
||||
|
||||
public function publishFolder(string $source, string $target): void
|
||||
{
|
||||
$this->ensureSymlinkExists($source, $target, 'dir');
|
||||
}
|
||||
|
||||
public function publishFile(string $source, string $target): void
|
||||
{
|
||||
$this->ensureSymlinkExists($source, $target, 'file');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PackageAssetsPublishingFailedException
|
||||
*/
|
||||
private function ensureSymlinkExists(string $target, string $link, string $type): void
|
||||
{
|
||||
$success = true;
|
||||
if (!$this->isSymlinked($link, $type)) {
|
||||
$success = $this->fileSystem->relativeSymlink($target, $link);
|
||||
}
|
||||
$this->ensureIsValid($target, $link, $success);
|
||||
}
|
||||
|
||||
private function isSymlinked(string $link, string $type): bool
|
||||
{
|
||||
return match ($type) {
|
||||
'file' => $this->fileSystem->isSymlinkedFile($link),
|
||||
'dir' => $this->fileSystem->isSymlinkedDirectory($link),
|
||||
default => throw new \UnexpectedValueException(sprintf('Type can only be "file" or "dir", "%s" given.', $type), 1774611766),
|
||||
};
|
||||
}
|
||||
|
||||
private function ensureIsValid(string $target, string $link, bool $success): void
|
||||
{
|
||||
if (!$success || realpath($target) !== realpath($link)) {
|
||||
throw new PackageAssetsPublishingFailedException(
|
||||
'symlink',
|
||||
1717488536,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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\SystemResource\Publishing;
|
||||
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Package\PackageInterface;
|
||||
use TYPO3\CMS\Core\Package\Resource\Definition\DynamicPublicPrefixInterface;
|
||||
use TYPO3\CMS\Core\Package\Resource\Definition\PublicResourceDefinition;
|
||||
|
||||
/**
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
final readonly class ResourcePublishingContext
|
||||
{
|
||||
public string $prefix;
|
||||
public bool $isSourcePublic;
|
||||
public string $filesystemPath;
|
||||
|
||||
/**
|
||||
* Variable names are explicitly public API
|
||||
* for named variable access
|
||||
*/
|
||||
public function __construct(
|
||||
private PackageInterface $package,
|
||||
private PublicResourceDefinition $definition,
|
||||
) {
|
||||
$this->prefix = $definition->getPublicPrefix() instanceof DynamicPublicPrefixInterface
|
||||
? $definition->getPublicPrefix()->calculatePrefix($package, $definition)
|
||||
: $definition->getPublicPrefix();
|
||||
$this->isSourcePublic = str_starts_with($this->package->getPackagePath() . $this->definition->getRelativePath(), Environment::getPublicPath());
|
||||
$this->filesystemPath = $package->getPackagePath() . $definition->getRelativePath();
|
||||
}
|
||||
}
|
||||
@@ -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\SystemResource\Publishing;
|
||||
|
||||
use TYPO3\CMS\Core\Package\PackageInterface;
|
||||
use TYPO3\CMS\Core\Package\Resource\Definition\PublicResourceDefinition;
|
||||
use TYPO3\CMS\Core\SystemResource\Type\PublicPackageFile;
|
||||
|
||||
/**
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
final readonly class ResourceUriBuildingContext
|
||||
{
|
||||
public string $absoluteResourcePath;
|
||||
public string $uriPath;
|
||||
public bool $isSourcePublic;
|
||||
|
||||
/**
|
||||
* Variable names are explicitly public API
|
||||
* for named variable access
|
||||
*/
|
||||
public function __construct(
|
||||
public PublicPackageFile $resource,
|
||||
public PackageInterface $package,
|
||||
public PublicResourceDefinition $definition,
|
||||
) {
|
||||
$this->absoluteResourcePath = $this->package->getPackagePath() . $this->resource->getRelativePath();
|
||||
$publishingContext = new ResourcePublishingContext(
|
||||
package: $package,
|
||||
definition: $definition
|
||||
);
|
||||
$this->isSourcePublic = $publishingContext->isSourcePublic;
|
||||
$this->uriPath = $publishingContext->prefix . substr($this->resource->getRelativePath(), strlen($this->definition->getRelativePath()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?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 TYPO3\CMS\Core\Messaging\FlashMessageQueue;
|
||||
use TYPO3\CMS\Core\Package\PackageInterface;
|
||||
use TYPO3\CMS\Core\SystemResource\Type\PublicResourceInterface;
|
||||
|
||||
/**
|
||||
* Implementations of this interface can publish public extension resources (once implemented)
|
||||
* and therefore also generate URIs to those published resources.
|
||||
* E.g. publish resources directly to a CDN and then generate CDS URIs
|
||||
* to those resources.
|
||||
*/
|
||||
interface SystemResourcePublisherInterface
|
||||
{
|
||||
public function publishResources(PackageInterface $package): FlashMessageQueue;
|
||||
|
||||
public function generateUri(PublicResourceInterface $publicResource, ?ServerRequestInterface $request, ?UriGenerationOptions $options = null): UriInterface;
|
||||
}
|
||||
@@ -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\SystemResource\Publishing;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
|
||||
/**
|
||||
* This is an implementation detail to allow not exposing the absolute file path
|
||||
* to extension resources directly, but only to the resource publisher
|
||||
* when generating URLs to the _assets directory.
|
||||
*
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
interface SystemResourceUriGeneratorInterface
|
||||
{
|
||||
public function generateForPackageResource(
|
||||
ResourceUriBuildingContext $context,
|
||||
): UriInterface;
|
||||
public function generateForFile(File $file): UriInterface;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Options for system resource URI generation.
|
||||
* These might change, by adding more options,
|
||||
* which means the variable names MUST be kept
|
||||
* (or properly deprecated) as they are public API.
|
||||
* Also, this object MUST be crated using named arguments.
|
||||
*/
|
||||
final readonly class UriGenerationOptions
|
||||
{
|
||||
/**
|
||||
* Variable names are explicitly public API
|
||||
* for named variable access
|
||||
*
|
||||
* Some or all of these options might to be applicable
|
||||
* to specific implementations SystemResourcePublisherInterface,
|
||||
* which means, that if other resource publishing strategies
|
||||
* are configured, that changing these options might not
|
||||
* influence the resulting URI
|
||||
*/
|
||||
public function __construct(
|
||||
public ?string $uriPrefix = null,
|
||||
public bool $absoluteUri = false,
|
||||
public bool $cacheBusting = true,
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Http\Uri;
|
||||
use TYPO3\CMS\Core\Package\Resource\Definition\PublicResourceDefinition;
|
||||
use TYPO3\CMS\Core\Package\VirtualAppPackage;
|
||||
use TYPO3\CMS\Core\Resource\Exception as FalException;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
use TYPO3\CMS\Core\Resource\Folder;
|
||||
use TYPO3\CMS\Core\Resource\ProcessedFile;
|
||||
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
||||
use TYPO3\CMS\Core\Resource\StorageRepository;
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\CanNotResolvePublicResourceException;
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\CanNotResolveSystemResourceException;
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\CanNotResolveSystemResourceIdentifierException;
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\InvalidSystemResourceIdentifierException;
|
||||
use TYPO3\CMS\Core\SystemResource\Identifier\FalResourceIdentifier;
|
||||
use TYPO3\CMS\Core\SystemResource\Identifier\PackageResourceIdentifier;
|
||||
use TYPO3\CMS\Core\SystemResource\Identifier\SystemResourceIdentifierFactory;
|
||||
use TYPO3\CMS\Core\SystemResource\Identifier\UriResourceIdentifier;
|
||||
use TYPO3\CMS\Core\SystemResource\Type\PackageResource;
|
||||
use TYPO3\CMS\Core\SystemResource\Type\PublicPackageFile;
|
||||
use TYPO3\CMS\Core\SystemResource\Type\PublicResourceInterface;
|
||||
use TYPO3\CMS\Core\SystemResource\Type\StaticResourceInterface;
|
||||
use TYPO3\CMS\Core\SystemResource\Type\SystemResourceInterface;
|
||||
use TYPO3\CMS\Core\SystemResource\Type\UriResource;
|
||||
use TYPO3\CMS\Core\Utility\PathUtility;
|
||||
|
||||
/**
|
||||
* This is the heart of system resource handling and
|
||||
* the most important API to be used in userland code
|
||||
* and throughout the core.
|
||||
*
|
||||
* This class should be final, but is not, because it
|
||||
* needs to be mocked in tests.
|
||||
*/
|
||||
#[Autoconfigure(public: true)]
|
||||
readonly class SystemResourceFactory
|
||||
{
|
||||
public function __construct(
|
||||
private SystemResourceIdentifierFactory $identifierFactory,
|
||||
private ?StorageRepository $storageRepository,
|
||||
private ?ResourceFactory $resourceFactory,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Use this method to obtain a resource that is public,
|
||||
* which means, that a URI can be generated for it.
|
||||
*
|
||||
* Always use it when the goal is to generate a URI,
|
||||
* as it checks, whether the resolved resource can/ is
|
||||
* indeed published or not and throws an exception otherwise.
|
||||
*
|
||||
* @throws CanNotResolveSystemResourceException
|
||||
* @throws CanNotResolvePublicResourceException
|
||||
*/
|
||||
public function createPublicResource(string $resourceString): PublicResourceInterface
|
||||
{
|
||||
$resource = $this->createResource($resourceString);
|
||||
if (!$resource instanceof PublicResourceInterface || !$resource->isPublished()) {
|
||||
throw new CanNotResolvePublicResourceException(sprintf('Resolved resource "%s" is not a public resource. Given resource identifier: "%s"', $resource, $resourceString), 1758098512);
|
||||
}
|
||||
return $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method, when generating a URI is not required
|
||||
* for the resource and only e.g. file contents like for templates
|
||||
* is required.
|
||||
*
|
||||
* @throws CanNotResolveSystemResourceException
|
||||
* @throws InvalidSystemResourceIdentifierException
|
||||
*/
|
||||
public function createResource(string $resourceString): StaticResourceInterface
|
||||
{
|
||||
try {
|
||||
return $this->createFromIdentifier($resourceString);
|
||||
} catch (CanNotResolveSystemResourceIdentifierException $e) {
|
||||
if (str_starts_with($resourceString, Environment::getProjectPath())
|
||||
|| (PathUtility::isAbsolutePath($resourceString) && file_exists($resourceString))
|
||||
) {
|
||||
throw new CanNotResolveSystemResourceException('Absolute paths are not allowed as resource identifiers', 1760618195);
|
||||
}
|
||||
$resourceString = ltrim($resourceString, '/');
|
||||
$falResource = $this->createFromLegacyFalPath($resourceString, $e);
|
||||
return $falResource ?? $this->createResourceFromRelativePublicPath($resourceString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CanNotResolveSystemResourceException
|
||||
* @throws InvalidSystemResourceIdentifierException
|
||||
*/
|
||||
private function createResourceFromRelativePublicPath(string $relativePublicPath): SystemResourceInterface
|
||||
{
|
||||
$absoluteResourcePath = Environment::getPublicPath() . '/' . $relativePublicPath;
|
||||
// The file must actually exist, only then we can be sure our
|
||||
// following string manipulation is correct. Otherwise, it could be some unexpected
|
||||
// string and the manipulation will lead to unexpected results
|
||||
// Strip potentially available query string and fragment from the path before checking, though
|
||||
try {
|
||||
$strippedAbsolutePath = (new Uri($absoluteResourcePath))->getPath();
|
||||
} catch (\InvalidArgumentException) {
|
||||
$strippedAbsolutePath = null;
|
||||
}
|
||||
if (!file_exists($strippedAbsolutePath ?? $absoluteResourcePath)) {
|
||||
throw new CanNotResolveSystemResourceException(sprintf('Can not resolve relative public path "%s" to a system resource', $relativePublicPath), 1759740281);
|
||||
}
|
||||
$packageIdentifier = $this->identifierFactory->createFromPackagePath(
|
||||
VirtualAppPackage::APP_PACKAGE_KEY,
|
||||
substr($absoluteResourcePath, strlen(Environment::getProjectPath()) + 1),
|
||||
$relativePublicPath,
|
||||
);
|
||||
return $this->createFromPackageIdentifier($packageIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CanNotResolveSystemResourceException
|
||||
* @throws InvalidSystemResourceIdentifierException
|
||||
* @throws CanNotResolveSystemResourceIdentifierException
|
||||
*/
|
||||
private function createFromIdentifier(string $potentialIdentifier): StaticResourceInterface
|
||||
{
|
||||
$identifier = $this->identifierFactory->create($potentialIdentifier);
|
||||
return match (get_class($identifier)) {
|
||||
UriResourceIdentifier::class => new UriResource($identifier),
|
||||
PackageResourceIdentifier::class => $this->createFromPackageIdentifier($identifier),
|
||||
FalResourceIdentifier::class => $this->createFromFalIdentifier($identifier),
|
||||
default => throw new InvalidSystemResourceIdentifierException(sprintf('Can not resolve "%s" to a system resource. Unknown SystemResourceIdentifier', $potentialIdentifier), 1759393674),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CanNotResolveSystemResourceException
|
||||
*/
|
||||
private function createFromLegacyFalPath(string $resourceString, \Throwable $e): ?StaticResourceInterface
|
||||
{
|
||||
if (!str_starts_with($resourceString, $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'])) {
|
||||
// For legacy resolving, we do not even try resolving any other path than
|
||||
// one starting with configured $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
$potentialFalPath = $resourceString;
|
||||
$storageUid = $this->storageRepository?->findBestMatchingStorageByLocalPath($potentialFalPath);
|
||||
} catch (\Throwable) {
|
||||
$storageUid = 0;
|
||||
}
|
||||
if ($storageUid <= 0) {
|
||||
return null;
|
||||
}
|
||||
$storage = $this->storageRepository?->findByUid($storageUid);
|
||||
if ($storage === null) {
|
||||
throw new CanNotResolveSystemResourceException(sprintf('Can not resolve "%s" to a system resource, storage %d does not exist', $resourceString, $storageUid), 1758627596, $e);
|
||||
}
|
||||
$file = null;
|
||||
try {
|
||||
$file = $storage->getFile($potentialFalPath);
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
$this->ensureValidFalResource($file, $resourceString);
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CanNotResolveSystemResourceException
|
||||
*/
|
||||
private function createFromFalIdentifier(FalResourceIdentifier $resourceUri): PublicResourceInterface
|
||||
{
|
||||
try {
|
||||
$file = $this->resourceFactory?->retrieveFileOrFolderObject($resourceUri->getIdentifier());
|
||||
$this->ensureValidFalResource($file, (string)$resourceUri);
|
||||
return $file;
|
||||
} catch (FalException $e) {
|
||||
throw new CanNotResolveSystemResourceException(sprintf('Can not resolve "%s" to a system resource', $resourceUri), 1759397430, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CanNotResolveSystemResourceException
|
||||
*/
|
||||
private function ensureValidFalResource(ProcessedFile|File|Folder|null $falResource, string $resourceUri): void
|
||||
{
|
||||
if (!$falResource instanceof File || $falResource->getStorage()->getUid() === 0) {
|
||||
throw new CanNotResolveSystemResourceException(sprintf('Can not resolve file with URI "%s"', $resourceUri), 1758700078);
|
||||
}
|
||||
}
|
||||
|
||||
private function createFromPackageIdentifier(PackageResourceIdentifier $packageIdentifier): SystemResourceInterface
|
||||
{
|
||||
$resourceDefinition = $packageIdentifier->getPackage()->getResources()->definitionForPath($packageIdentifier->getRelativePath());
|
||||
if ($resourceDefinition instanceof PublicResourceDefinition) {
|
||||
return new PublicPackageFile($packageIdentifier, $resourceDefinition);
|
||||
}
|
||||
return new PackageResource($packageIdentifier, $resourceDefinition);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?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\Type;
|
||||
|
||||
use TYPO3\CMS\Core\Package\Resource\Definition\ResourceDefinitionInterface;
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\SystemResourceDoesNotExistException;
|
||||
use TYPO3\CMS\Core\SystemResource\Identifier\PackageResourceIdentifier;
|
||||
use TYPO3\CMS\Core\Type\File\FileInfo;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\PathUtility;
|
||||
|
||||
/**
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
class PackageResource implements SystemResourceInterface
|
||||
{
|
||||
private ?FileInfo $fileInfo = null;
|
||||
|
||||
public function __construct(
|
||||
protected readonly PackageResourceIdentifier $identifier,
|
||||
protected readonly ResourceDefinitionInterface $resourceDefinition,
|
||||
) {}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return PathUtility::pathinfo($this->identifier->getRelativePath())['basename'];
|
||||
}
|
||||
|
||||
public function getNameWithoutExtension(): string
|
||||
{
|
||||
return PathUtility::pathinfo($this->identifier->getRelativePath())['filename'];
|
||||
}
|
||||
|
||||
public function getExtension(): string
|
||||
{
|
||||
return PathUtility::pathinfo($this->identifier->getRelativePath())['extension'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SystemResourceDoesNotExistException
|
||||
*/
|
||||
public function getContents(): string
|
||||
{
|
||||
$fileInfo = $this->getValidatedFileInfo();
|
||||
$content = file_get_contents($fileInfo->getPathname());
|
||||
if ($content === false) {
|
||||
throw new SystemResourceDoesNotExistException(sprintf('Can not get contents from referenced system resource "%s" (resolved as "%s")', $this->identifier->givenIdentifier, $this), 1758714587);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SystemResourceDoesNotExistException
|
||||
*/
|
||||
public function getMimeType(): string
|
||||
{
|
||||
$fileInfo = $this->getValidatedFileInfo();
|
||||
$mimeType = $fileInfo->getMimeType();
|
||||
if ($mimeType === false) {
|
||||
throw new SystemResourceDoesNotExistException(sprintf('Can not get mime type from referenced system resource "%s" (resolved as "%s")', $this->identifier->givenIdentifier, $this), 1758786841);
|
||||
}
|
||||
return $mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SystemResourceDoesNotExistException
|
||||
*/
|
||||
public function getHash(): string
|
||||
{
|
||||
$fileInfo = $this->getValidatedFileInfo();
|
||||
return md5_file($fileInfo->getPathname());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SystemResourceDoesNotExistException
|
||||
*/
|
||||
private function getValidatedFileInfo(): FileInfo
|
||||
{
|
||||
if ($this->fileInfo !== null) {
|
||||
return $this->fileInfo;
|
||||
}
|
||||
$fileInfo = GeneralUtility::makeInstance(FileInfo::class, $this->identifier->getPackage()->getPackagePath() . $this->identifier->getRelativePath());
|
||||
if (!$fileInfo->isFile()) {
|
||||
throw new SystemResourceDoesNotExistException(sprintf('Referenced system resource "%s" (resolved as "%s") does not exist, or is not a file', $this->identifier->givenIdentifier, $this), 1758785343);
|
||||
}
|
||||
return $this->fileInfo = $fileInfo;
|
||||
}
|
||||
|
||||
public function getResourceIdentifier(): string
|
||||
{
|
||||
return (string)$this->identifier;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->getResourceIdentifier();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?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\Type;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use TYPO3\CMS\Core\Package\Resource\Definition\PublicResourceDefinition;
|
||||
use TYPO3\CMS\Core\SystemResource\Identifier\PackageResourceIdentifier;
|
||||
use TYPO3\CMS\Core\SystemResource\Publishing\ResourceUriBuildingContext;
|
||||
use TYPO3\CMS\Core\SystemResource\Publishing\SystemResourceUriGeneratorInterface;
|
||||
|
||||
/**
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
final class PublicPackageFile extends PackageResource implements PublicResourceInterface
|
||||
{
|
||||
public function __construct(
|
||||
PackageResourceIdentifier $identifier,
|
||||
PublicResourceDefinition $resourceDefinition,
|
||||
) {
|
||||
parent::__construct($identifier, $resourceDefinition);
|
||||
}
|
||||
|
||||
public function getPublicUri(SystemResourceUriGeneratorInterface $uriGenerator): UriInterface
|
||||
{
|
||||
// This is fine, as the type is enforced in constructor
|
||||
assert($this->resourceDefinition instanceof PublicResourceDefinition);
|
||||
return $uriGenerator->generateForPackageResource(
|
||||
new ResourceUriBuildingContext(
|
||||
resource: $this,
|
||||
package: $this->identifier->getPackage(),
|
||||
definition: $this->resourceDefinition,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function getRelativePath(): string
|
||||
{
|
||||
return $this->identifier->getRelativePath();
|
||||
}
|
||||
|
||||
public function isPublished(): bool
|
||||
{
|
||||
return $this->identifier->getPackage()->getResources()->isPublicPath($this->identifier->getRelativePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal This API is only meant for very limited use cases,
|
||||
* e.g. for building URIs for Vite dev server, where the dev server actually
|
||||
* publishes (exposes) all (private) source files, so that they can be processed on the fly
|
||||
* Do *not* use within TYPO3 core or other third party extensions
|
||||
*/
|
||||
public static function fromPackageResource(PackageResource $packageResource): self
|
||||
{
|
||||
if ($packageResource instanceof PublicResourceInterface) {
|
||||
throw new \LogicException('It is pointless to create a public resource from an already public resource', 1761217630);
|
||||
}
|
||||
return new self(
|
||||
$packageResource->identifier,
|
||||
new PublicResourceDefinition(
|
||||
$packageResource->resourceDefinition->getRelativePath()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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\SystemResource\Type;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use TYPO3\CMS\Core\SystemResource\Publishing\SystemResourceUriGeneratorInterface;
|
||||
|
||||
/**
|
||||
* This interface is public API and can be referenced in third party code
|
||||
* or throughout the TYPO3 core.
|
||||
* Implementations of this interface are internal though
|
||||
* and must only happen in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
interface PublicResourceInterface extends StaticResourceInterface
|
||||
{
|
||||
/**
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
public function getPublicUri(SystemResourceUriGeneratorInterface $uriGenerator): UriInterface;
|
||||
|
||||
/**
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
public function isPublished(): bool;
|
||||
}
|
||||
@@ -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\SystemResource\Type;
|
||||
|
||||
/**
|
||||
* All static resources in TYPO3 have one thing in common
|
||||
* and that is, that they can be identified with a unique string.
|
||||
* This means they can be constructed from a string by SystemResourceFactory,
|
||||
* and they can be cast to a unique string representation.
|
||||
*
|
||||
* Currently absolute and relative URLs (UriResource), files within an extension directory (PackageResource),
|
||||
* and file abstraction layer (FAL) files serve as static resources
|
||||
* to be referenced e.g. as public URL (see PublicResourceInterface)
|
||||
*
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
interface StaticResourceInterface extends \Stringable
|
||||
{
|
||||
public function getResourceIdentifier(): string;
|
||||
}
|
||||
@@ -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\SystemResource\Type;
|
||||
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\SystemResourceDoesNotExistException;
|
||||
|
||||
/**
|
||||
* This interface is public API and can be referenced in third party code
|
||||
* or throughout the TYPO3 core.
|
||||
* Implementations of this interface are internal though
|
||||
* and must only happen in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
interface SystemResourceInterface extends StaticResourceInterface
|
||||
{
|
||||
/**
|
||||
* @throws SystemResourceDoesNotExistException
|
||||
*/
|
||||
public function getContents(): string;
|
||||
public function getName(): string;
|
||||
public function getNameWithoutExtension(): string;
|
||||
public function getExtension(): string;
|
||||
public function getMimeType(): string;
|
||||
public function getHash(): string;
|
||||
}
|
||||
@@ -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\SystemResource\Type;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use TYPO3\CMS\Core\SystemResource\Identifier\UriResourceIdentifier;
|
||||
use TYPO3\CMS\Core\SystemResource\Publishing\SystemResourceUriGeneratorInterface;
|
||||
|
||||
/**
|
||||
* @internal Only to be used in TYPO3\CMS\Core\SystemResource namespace
|
||||
*/
|
||||
final readonly class UriResource implements StaticResourceInterface, PublicResourceInterface
|
||||
{
|
||||
public function __construct(private UriResourceIdentifier $identifier) {}
|
||||
|
||||
public function getPublicUri(SystemResourceUriGeneratorInterface $uriGenerator): UriInterface
|
||||
{
|
||||
return $this->identifier->getUri();
|
||||
}
|
||||
|
||||
public function isPublished(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->getResourceIdentifier();
|
||||
}
|
||||
|
||||
public function getResourceIdentifier(): string
|
||||
{
|
||||
return (string)$this->identifier;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user