Skip to content

Commit 144a533

Browse files
authored
Merge pull request #16 from move-elevator/typo3-14
Typo3 14
2 parents 6833373 + c0b4779 commit 144a533

10 files changed

Lines changed: 1645 additions & 3021 deletions

Classes/EventListener/ContentMinifierEventListener.php

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,16 @@ final class ContentMinifierEventListener
1212
{
1313
public function __invoke(AfterCacheableContentIsGeneratedEvent $event): void
1414
{
15-
$event->getController()->content = $this->minify( // @phpstan-ignore-line
16-
$event->getController()->content // @phpstan-ignore-line
17-
);
15+
$event->setContent($this->minify($event->getContent()));
1816
}
1917

2018
/**
21-
* remove javascript inline comments
19+
* remove JavaScript inline comments
2220
* convert linebreaks to spaces
2321
* convert tabs to spaces
2422
* convert multiple spaces to one single space
2523
* remove spaces between tags, but ignore on some inline-tags
2624
* replace non-HTML5 conform closing tags
27-
* remove type attributes for styles and javascript
28-
* remove unnecessary whitespaces from class attributes
29-
* remove unnecessary whitespaces from JSON-LD
3025
*/
3126
private function minify(string $content): string
3227
{
@@ -39,7 +34,18 @@ private function minify(string $content): string
3934
'/" \/>/' => '">',
4035
];
4136

42-
$content = str_replace(
37+
$content = $this->removeUnnecessaryTypeAttributesForStyleAndScriptTags($content);
38+
$content = $this->removeUnnecessaryWhitespacesFromClassAttributes($content);
39+
$content = $this->removeUnnecessaryWhitespacesForJsonLdSchemas($content);
40+
$content = $this->removeCkeditorDataAttributesFromListItems($content);
41+
$content = $this->removeWhitespacesAfterTagStartAndBeforeTagClose($content);
42+
43+
return (string)preg_replace(array_keys($replacements), array_values($replacements), $content);
44+
}
45+
46+
private function removeUnnecessaryTypeAttributesForStyleAndScriptTags(string $content): string
47+
{
48+
return str_replace(
4349
[
4450
' type="text/css"',
4551
' type=\'text/css\'',
@@ -49,21 +55,54 @@ private function minify(string $content): string
4955
'',
5056
$content
5157
);
58+
}
59+
60+
/**
61+
* @see https://forge.typo3.org/issues/109002
62+
* @see https://github.com/ckeditor/ckeditor5/issues/19006
63+
*/
64+
private function removeCkeditorDataAttributesFromListItems(string $content): string
65+
{
66+
return (string)preg_replace(
67+
'/(<li)\s+data-list-item-id="[^"]*"/',
68+
'$1',
69+
$content
70+
);
71+
}
72+
73+
private function removeWhitespacesAfterTagStartAndBeforeTagClose(string $content): string
74+
{
75+
return (string)preg_replace_callback(
76+
'/<(h[1-6]|p|li|td|th|dt|dd|button|label)[^>]*>\K\s+|\s+(?=<\/(h[1-6]|p|li|td|th|dt|dd|button|label)>)/',
77+
static fn () => '',
78+
$content
79+
);
80+
}
5281

53-
$content = (string)preg_replace_callback(
82+
private function removeUnnecessaryWhitespacesFromClassAttributes(string $content): string
83+
{
84+
return (string)preg_replace_callback(
5485
'/class="([^"]+)"/',
5586
static function (array $matches) {
5687
$cleanedClassList = trim((string)preg_replace('/\s+/', ' ', $matches[1]));
5788
return 'class="' . $cleanedClassList . '"';
5889
},
5990
$content
6091
);
92+
}
6193

62-
$content = (string)preg_replace_callback(
94+
private function removeUnnecessaryWhitespacesForJsonLdSchemas(string $content): string
95+
{
96+
return (string)preg_replace_callback(
6397
'/<script\s+type="application\/ld\+json">(.*?)<\/script>/s',
6498
static function (array $matches) {
6599
$json = trim($matches[1]);
66-
$minifiedJson = json_encode(json_decode($json, true), JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
100+
101+
try {
102+
$minifiedJson = json_encode(json_decode($json, true), JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
103+
} catch (\JsonException) {
104+
return '';
105+
}
67106

68107
if ('null' === $minifiedJson) {
69108
return '';
@@ -73,7 +112,5 @@ static function (array $matches) {
73112
},
74113
$content
75114
);
76-
77-
return (string)preg_replace(array_keys($replacements), array_values($replacements), $content);
78115
}
79116
}

Classes/EventListener/LastDeploymentEventListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function __invoke(SystemInformationToolbarCollectorEvent $systemInformati
4949
}
5050

5151
$systemInformation->getToolbarItem()->addSystemInformation(
52-
$this->getLanguageService()->sL('LLL:EXT:' . Configuration::EXT_KEY->value . '/Resources/Private/Language/locallang_be.xlf:system_information.last_updated'),
52+
$this->getLanguageService()->sL('typo3_toolbox.be:system_information.last_updated'),
5353
$humanFormatDateTime,
5454
'actions-refresh',
5555
);

Classes/EventListener/SaveCloseButtonEventListener.php

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,51 @@
44

55
namespace MoveElevator\Typo3Toolbox\EventListener;
66

7-
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
87
use TYPO3\CMS\Backend\Template\Components\Buttons\InputButton;
9-
use TYPO3\CMS\Backend\Template\Components\ModifyButtonBarEvent;
8+
use TYPO3\CMS\Backend\Template\Components\{ButtonBar,ComponentFactory,ModifyButtonBarEvent};
109
use TYPO3\CMS\Core\Attribute\AsEventListener;
11-
use TYPO3\CMS\Core\Imaging\IconFactory;
12-
use TYPO3\CMS\Core\Imaging\IconSize;
10+
use TYPO3\CMS\Core\Imaging\{IconFactory,IconSize};
1311
use TYPO3\CMS\Core\Localization\LanguageService;
1412
use TYPO3\CMS\Core\Page\PageRenderer;
15-
use TYPO3\CMS\Core\Utility\GeneralUtility;
1613

1714
#[AsEventListener(identifier: 'moveElevator/modifyButtonBar')]
18-
final class SaveCloseButtonEventListener
15+
final readonly class SaveCloseButtonEventListener
1916
{
17+
public function __construct(
18+
private ComponentFactory $componentFactory,
19+
private IconFactory $iconFactory,
20+
private PageRenderer $pageRenderer,
21+
) {
22+
}
23+
2024
public function __invoke(ModifyButtonBarEvent $event): void
2125
{
2226
$buttons = $event->getButtons();
23-
$buttonBar = $event->getButtonBar();
2427
$saveButton = $buttons[ButtonBar::BUTTON_POSITION_LEFT][2][0] ?? null;
2528

2629
if ($saveButton instanceof InputButton) {
27-
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
2830
$language = $this->getLanguageService();
2931
$title = 'save';
3032

3133
if ($language instanceof LanguageService) {
32-
$title = $language->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.saveCloseDoc');
34+
$title = $language->sL('core.core:rm.saveCloseDoc');
3335
}
3436

35-
$saveCloseButton = $buttonBar->makeInputButton()
37+
$saveCloseButton = $this->componentFactory->createInputButton()
3638
->setName('_saveandclosedok')
3739
->setValue('1')
3840
->setForm($saveButton->getForm())
3941
->setDataAttributes([
4042
'js' => 'save-and-close-button',
4143
])
4244
->setTitle($title)
43-
->setIcon($iconFactory->getIcon('actions-document-save-close', IconSize::SMALL))
45+
->setIcon($this->iconFactory->getIcon('actions-document-save-close', IconSize::SMALL))
4446
->setShowLabelText(true);
4547

4648
$buttons[ButtonBar::BUTTON_POSITION_LEFT][2][] = $saveCloseButton;
4749
}
4850

49-
/** @var PageRenderer $pageRenderer */
50-
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
51-
$pageRenderer->loadJavaScriptModule('@move-elevator/typo3-toolbox/SaveAndClose.js');
51+
$this->pageRenderer->loadJavaScriptModule('@move-elevator/typo3-toolbox/SaveAndClose.js');
5252

5353
$event->setButtons($buttons);
5454
}

Classes/Page/AssetRenderer.php

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
namespace MoveElevator\Typo3Toolbox\Page;
66

77
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\ConsumableNonce;
8-
use TYPO3\CMS\Core\Utility\GeneralUtility;
9-
use TYPO3\CMS\Core\Utility\PathUtility;
108

119
readonly class AssetRenderer extends \TYPO3\CMS\Core\Page\AssetRenderer
1210
{
@@ -18,7 +16,7 @@ public function renderStyleSheets(bool $priority = false, string $endingSlash =
1816

1917
// Remove calculated inline styles from normal styles
2018
foreach ($inlineStyles as $key => $value) {
21-
if (true === array_key_exists($key, $normalStyles)) {
19+
if (true === array_key_exists((string) $key, $normalStyles)) {
2220
$this->assetCollector->removeStyleSheet($key);
2321
}
2422
}
@@ -46,14 +44,9 @@ public function renderStyleSheets(bool $priority = false, string $endingSlash =
4644
return $return;
4745
}
4846

49-
protected function getAbsoluteWebPath(string $file): string
47+
private function getAbsoluteWebPath(string $file): string
5048
{
51-
if (true === PathUtility::hasProtocolAndScheme($file)) {
52-
return $file;
53-
}
54-
55-
$file = PathUtility::getAbsoluteWebPath(GeneralUtility::getFileAbsFileName($file));
56-
57-
return GeneralUtility::createVersionNumberedFilename($file);
49+
$resource = $this->systemResourceFactory->createPublicResource($file);
50+
return (string)$this->resourcePublisher->generateUri($resource, null);
5851
}
5952
}

README.md

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
# TYPO3 extension `typo3_toolbox`
66

7-
[![Supported TYPO3 versions](https://badgen.net/badge/TYPO3/13/orange)]()
7+
[![Supported TYPO3 versions](https://badgen.net/badge/TYPO3/14/orange)]()
88
[![License](https://poser.pugx.org/move-elevator/typo3-toolbox/license)](LICENSE.md)
99
[![last commit](https://img.shields.io/github/last-commit/move-elevator/typo3-toolbox)](https://github.com/move-elevator/typo3-toolbox/commits)
1010

@@ -21,10 +21,12 @@ This extension provides several tools for TYPO3 integrators and developers.
2121
- Adds a sentry middleware and frontend module ...
2222
- Adds a custom TYPO3 page renderer template which removes some unnecessary spaces and changes the order of inline CSS injection
2323

24-
## Requirements
24+
## Version support
2525

26-
- PHP 8.4
27-
- TYPO3 13.4
26+
| Extension version | TYPO3 | PHP |
27+
|-------------------|-------|----------|
28+
| 2.x | 14.3 | 8.4, 8.5 |
29+
| 1.x | 13.4 | 8.4, 8.5 |
2830

2931
## Installation
3032

@@ -76,6 +78,24 @@ $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['typo3_toolbox']['sentryFrontendEnable
7678

7779
## Documentation
7880

81+
### Content Minifier
82+
83+
The `ContentMinifierEventListener` automatically minifies the HTML output of all cacheable frontend pages. It hooks into the TYPO3 `AfterCacheableContentIsGeneratedEvent` and is active by default — no configuration required.
84+
85+
#### Optimizations
86+
87+
| Optimization | Description |
88+
|----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
89+
| Remove JS inline comments | Strips `/** */` comments |
90+
| Collapse whitespace | Converts linebreaks, tabs, and multiple spaces into single spaces |
91+
| Remove inter-tag spaces | Removes spaces between HTML tags (preserves inline tags: `a`, `b`, `strong`, `img`, `em`, `i`, `span`, `small`, `big`) |
92+
| Fix self-closing tags | Converts `" />` to `">` for HTML5 conformity |
93+
| Remove redundant type attributes | Strips `type="text/css"` from `<style>` and `type="text/javascript"` from `<script>` tags |
94+
| Normalize class attributes | Collapses multiple spaces within `class` attribute values |
95+
| Minify JSON-LD schemas | Re-encodes `<script type="application/ld+json">` content as compact JSON; removes invalid schemas |
96+
| Remove CKEditor data attributes | Strips `data-list-item-id` attributes from `<li>` elements added by CKEditor 5 ([TYPO3#109002](https://forge.typo3.org/issues/109002), [CKEditor5#19006](https://github.com/ckeditor/ckeditor5/issues/19006)) |
97+
| Trim tag content whitespace | Removes leading/trailing whitespace inside `h1``h6`, `p`, `li`, `td`, `th`, `dt`, `dd`, `button`, and `label` tags |
98+
7999
### Middlewares
80100

81101
| Middleware | Path/ Parameter | Description |

Tests/CGL/phpstan.neon

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ parameters:
77

88
scanDirectories:
99
- ../../vendor
10-
- ../../vendor/phpunit/phpunit/src
1110
paths:
1211
- ../../Classes
1312
- ../../Configuration

Tests/CGL/rector.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
$rootPath.'/Classes',
2020
$rootPath.'/Configuration',
2121
])
22-
->withPhpVersion(PhpVersion::PHP_84)
22+
->withPhpVersion(PhpVersion::PHP_85)
2323
->withSets([
2424
Typo3SetList::CODE_QUALITY,
2525
Typo3SetList::GENERAL,
26-
Typo3LevelSetList::UP_TO_TYPO3_13,
27-
LevelSetList::UP_TO_PHP_84,
26+
Typo3LevelSetList::UP_TO_TYPO3_14,
27+
LevelSetList::UP_TO_PHP_85,
2828
])
2929
// To have a better analysis from PHPStan, we teach it here some more things
3030
->withPHPStanConfigs([

composer.json

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,27 @@
66
"require": {
77
"php": "^8.4",
88
"ext-intl": "*",
9-
"doctrine/dbal": "^4.2",
10-
"networkteam/sentry-client": "^5.2",
9+
"doctrine/dbal": "^4.4",
10+
"networkteam/sentry-client": "^6.0",
1111
"psr/http-factory": "^1.1",
1212
"psr/http-message": "^2.0",
1313
"psr/http-server-handler": "^1.0",
1414
"psr/http-server-middleware": "^1.0",
1515
"psr/log": "^3.0",
16-
"typo3/cms-adminpanel": "^13.4",
17-
"typo3/cms-backend": "^13.4",
18-
"typo3/cms-core": "^13.4",
19-
"typo3/cms-frontend": "^13.4",
20-
"typo3/cms-linkvalidator": "^13.4",
21-
"typo3fluid/fluid": "^4.3"
16+
"typo3/cms-adminpanel": "^14.3",
17+
"typo3/cms-backend": "^14.3",
18+
"typo3/cms-core": "^14.3",
19+
"typo3/cms-frontend": "^14.3",
20+
"typo3/cms-linkvalidator": "^14.3",
21+
"typo3fluid/fluid": "^5.3"
2222
},
2323
"require-dev": {
24-
"captainhook/captainhook": "^5.25",
25-
"eliashaeussler/version-bumper": "^3.0",
26-
"helhum/typo3-console": "^8.1",
27-
"move-elevator/composer-translation-validator": "^1.0",
28-
"phpunit/phpunit": "^11.0 || ^12.0",
29-
"typo3/cms-base-distribution": "^13.4",
30-
"typo3/cms-lowlevel": "^13.4"
24+
"captainhook/captainhook": "^5.29",
25+
"eliashaeussler/version-bumper": "^3.2",
26+
"helhum/typo3-console": "^8.3",
27+
"move-elevator/composer-translation-validator": "^1.4",
28+
"typo3/cms-base-distribution": "^14.3",
29+
"typo3/cms-lowlevel": "^14.3"
3130
},
3231
"autoload": {
3332
"psr-4": {

0 commit comments

Comments
 (0)