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