TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:00 +02:00
commit f9941541b7
1178 changed files with 135377 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
<?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\Backend\CodeEditor;
use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
/**
* Represents an addon for CodeMirror
* @internal
*/
class Addon
{
protected string $identifier;
protected ?JavaScriptModuleInstruction $module = null;
protected ?JavaScriptModuleInstruction $keymap = null;
/**
* @var array
*/
protected $modes = [];
/**
* @var array
*/
protected $options = [];
/**
* @var array
*/
protected $cssFiles = [];
public function __construct(
string $identifier,
?JavaScriptModuleInstruction $module = null,
?JavaScriptModuleInstruction $keymap = null
) {
$this->identifier = $identifier;
$this->module = $module;
$this->keymap = $keymap;
}
public function getIdentifier(): string
{
return $this->identifier;
}
public function getModule(): ?JavaScriptModuleInstruction
{
return $this->module;
}
public function getKeymap(): ?JavaScriptModuleInstruction
{
return $this->keymap;
}
public function setOptions(array $options): Addon
{
$this->options = $options;
return $this;
}
public function getOptions(): array
{
return $this->options;
}
public function setCssFiles(array $cssFiles): Addon
{
$this->cssFiles = $cssFiles;
return $this;
}
public function getCssFiles(): array
{
return $this->cssFiles;
}
}
+146
View File
@@ -0,0 +1,146 @@
<?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\Backend\CodeEditor;
use TYPO3\CMS\Backend\CodeEditor\Registry\AddonRegistry;
use TYPO3\CMS\Backend\CodeEditor\Registry\ModeRegistry;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
use TYPO3\CMS\Core\Package\Cache\PackageDependentCacheIdentifier;
use TYPO3\CMS\Core\Package\PackageManager;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Provides necessary code to set up a code editor instance in FormEngine
* @internal
* @todo: refactor to use DI
*/
class CodeEditor implements SingletonInterface
{
protected ?array $configuration = null;
/**
* Registers the configuration and bootstraps the modes / addons.
*
* @throws \InvalidArgumentException
*/
public function registerConfiguration(): void
{
$configuration = $this->buildConfiguration();
if (isset($configuration['modes'])) {
$modeRegistry = GeneralUtility::makeInstance(ModeRegistry::class);
foreach ($configuration['modes'] as $formatCode => $mode) {
$modeInstance = GeneralUtility::makeInstance(Mode::class, $mode['module'])->setFormatCode($formatCode);
if (!empty($mode['extensions']) && is_array($mode['extensions'])) {
$modeInstance->bindToFileExtensions($mode['extensions']);
}
if (isset($mode['default']) && $mode['default'] === true) {
$modeInstance->setAsDefault();
}
$modeRegistry->register($modeInstance);
}
}
$addonRegistry = GeneralUtility::makeInstance(AddonRegistry::class);
if (isset($configuration['addons'])) {
foreach ($configuration['addons'] as $identifier => $addon) {
$addonInstance = GeneralUtility::makeInstance(Addon::class, $identifier, $addon['module'] ?? null, $addon['keymap'] ?? null);
if (!empty($addon['cssFiles']) && is_array($addon['cssFiles'])) {
$addonInstance->setCssFiles($addon['cssFiles']);
}
if (!empty($addon['options']) && is_array($addon['options'])) {
$addonInstance->setOptions($addon['options']);
}
$addonRegistry->register($addonInstance);
}
}
}
/**
* Compiles the configuration for code editor. Configuration is stored in caching framework.
*
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
* @throws \TYPO3\CMS\Core\Cache\Exception\InvalidDataException
* @throws \InvalidArgumentException
*/
protected function buildConfiguration(): array
{
if ($this->configuration !== null) {
return $this->configuration;
}
$this->configuration = [
'modes' => [],
'addons' => [],
];
$cache = $this->getCache();
$packageManager = GeneralUtility::makeInstance(PackageManager::class);
$cacheIdentifier = $this->generateCacheIdentifier($packageManager);
$configurationFromCache = $cache->get($cacheIdentifier);
if ($configurationFromCache !== false) {
$this->configuration = $configurationFromCache;
} else {
$packages = $packageManager->getActivePackages();
foreach ($packages as $package) {
$configurationPath = $package->getPackagePath() . 'Configuration/Backend/T3editor';
$modesFileNameForPackage = $configurationPath . '/Modes.php';
if (is_file($modesFileNameForPackage)) {
$definedModes = require $modesFileNameForPackage;
if (is_array($definedModes)) {
$this->configuration['modes'] = array_merge($this->configuration['modes'], $definedModes);
}
}
$addonsFileNameForPackage = $configurationPath . '/Addons.php';
if (is_file($addonsFileNameForPackage)) {
$definedAddons = require $addonsFileNameForPackage;
if (is_array($definedAddons)) {
$this->configuration['addons'] = array_merge($this->configuration['addons'], $definedAddons);
}
}
}
$cache->set($cacheIdentifier, $this->configuration);
}
return $this->configuration;
}
protected function generateCacheIdentifier(PackageManager $packageManager): string
{
return (new PackageDependentCacheIdentifier($packageManager))->withPrefix('T3editorConfiguration')->toString();
}
/**
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
* @throws \InvalidArgumentException
*/
protected function getCache(): FrontendInterface
{
return GeneralUtility::makeInstance(CacheManager::class)->getCache('assets');
}
}
@@ -0,0 +1,26 @@
<?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\Backend\CodeEditor\Exception;
use TYPO3\CMS\Core\Exception;
/**
* Exception thrown for invalid modes
* @internal
*/
class InvalidModeException extends Exception {}
+90
View File
@@ -0,0 +1,90 @@
<?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\Backend\CodeEditor;
use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
/**
* Represents a mode for CodeMirror
* @internal
*/
class Mode
{
protected JavaScriptModuleInstruction $module;
/**
* @var string
*/
protected $formatCode = '';
/**
* @var array
*/
protected $fileExtensions = [];
/**
* @var bool
*/
protected $isDefault = false;
public function __construct(JavaScriptModuleInstruction $module)
{
$this->module = $module;
}
public function getModule(): JavaScriptModuleInstruction
{
return $this->module;
}
public function getFormatCode(): string
{
return $this->formatCode;
}
public function setFormatCode(string $formatCode): Mode
{
$this->formatCode = $formatCode;
return $this;
}
public function bindToFileExtensions(array $fileExtensions): Mode
{
$this->fileExtensions = $fileExtensions;
return $this;
}
public function getBoundFileExtensions(): array
{
return $this->fileExtensions;
}
public function setAsDefault(): Mode
{
$this->isDefault = true;
return $this;
}
public function isDefault(): bool
{
return $this->isDefault;
}
}
@@ -0,0 +1,61 @@
<?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\Backend\CodeEditor\Registry;
use TYPO3\CMS\Backend\CodeEditor\Addon;
use TYPO3\CMS\Core\SingletonInterface;
/**
* Registers and holds code editor modes
* @internal
*/
class AddonRegistry implements SingletonInterface
{
/**
* @var Addon[]
*/
protected $registeredAddons = [];
/**
* Registers addons for global use in code editor
*/
public function register(Addon $addon): AddonRegistry
{
$this->registeredAddons[] = $addon;
return $this;
}
public function getAddons(): array
{
return $this->registeredAddons;
}
/**
* @param Addon[] $addons
*/
public function compileSettings(array $addons): array
{
$settings = [];
foreach ($addons as $addon) {
$settings = array_merge($settings, $addon->getOptions());
}
return $settings;
}
}
@@ -0,0 +1,99 @@
<?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\Backend\CodeEditor\Registry;
use TYPO3\CMS\Backend\CodeEditor\Exception\InvalidModeException;
use TYPO3\CMS\Backend\CodeEditor\Mode;
use TYPO3\CMS\Core\SingletonInterface;
/**
* Registers and holds code editor modes
* @internal
*/
class ModeRegistry implements SingletonInterface
{
/**
* @var Mode[]
*/
protected array $registeredModes = [];
protected Mode $defaultMode;
/**
* Registers modes for code editor
*/
public function register(Mode $mode): ModeRegistry
{
$this->registeredModes[$mode->getFormatCode()] = $mode;
if ($mode->isDefault()) {
$this->defaultMode = $mode;
}
return $this;
}
/**
* Removes registered modes
*/
public function unregister(string $formatCode): ModeRegistry
{
if (isset($this->registeredModes[$formatCode])) {
unset($this->registeredModes[$formatCode]);
}
return $this;
}
public function isRegistered(string $formatCode): bool
{
return isset($this->registeredModes[$formatCode]);
}
/**
* @throws InvalidModeException
*/
public function getByFormatCode(string $formatCode): Mode
{
foreach ($this->registeredModes as $mode) {
if ($mode->getFormatCode() === $formatCode) {
return $mode;
}
}
throw new InvalidModeException('Tried to get unregistered code editor mode by format code "' . $formatCode . '"', 1499710203);
}
/**
* @throws InvalidModeException
*/
public function getByFileExtension(string $fileExtension): Mode
{
foreach ($this->registeredModes as $mode) {
if (in_array($fileExtension, $mode->getBoundFileExtensions(), true)) {
return $mode;
}
}
throw new InvalidModeException('Cannot find a registered mode for requested file extension "' . $fileExtension . '"', 1500306488);
}
public function getDefaultMode(): Mode
{
return $this->defaultMode;
}
}