Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[BUGFIX] Adjust CSS import paths in inline CSS
Extracts CSS path fixing functionality of ResourceCompressor to a separate class in order to be used in other contexts. PageRenderer uses the new object. Resolves: #91935 Releases: master, 10.4 Change-Id: Icba71dcd0ceb110fb2398b2839310fb1952d0601 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/65720 Tested-by: TYPO3com <noreply@typo3.com> Tested-by: Benni Mack <benni@typo3.org> Reviewed-by: Benni Mack <benni@typo3.org>
- Loading branch information
Showing
4 changed files
with
198 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
82 changes: 82 additions & 0 deletions
82
typo3/sysext/core/Classes/Resource/RelativeCssPathFixer.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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\Resource; | ||
|
||
use TYPO3\CMS\Core\Utility\GeneralUtility; | ||
|
||
/** | ||
* This fixes import paths in CSS files if their location changes, | ||
* e.g. when inlining or compressing css | ||
* | ||
* @internal This class is not part of the TYPO3 API. | ||
*/ | ||
class RelativeCssPathFixer | ||
{ | ||
/** | ||
* Fixes the relative paths inside of url() references in CSS files | ||
* | ||
* @param string $contents Data to process | ||
* @param string $newDir directory referenced from current location | ||
* @return string Processed data | ||
*/ | ||
public function fixRelativeUrlPaths(string $contents, string $newDir): string | ||
{ | ||
// Replace "url()" paths | ||
if (stripos($contents, 'url') !== false) { | ||
$regex = '/url(\\(\\s*["\']?(?!\\/)([^"\']+)["\']?\\s*\\))/iU'; | ||
$contents = $this->findAndReplaceUrlPathsByRegex($contents, $regex, $newDir, '(\'|\')'); | ||
} | ||
// Replace "@import" paths | ||
if (stripos($contents, '@import') !== false) { | ||
$regex = '/@import\\s*(["\']?(?!\\/)([^"\']+)["\']?)/i'; | ||
$contents = $this->findAndReplaceUrlPathsByRegex($contents, $regex, $newDir, '"|"'); | ||
} | ||
return $contents; | ||
} | ||
|
||
/** | ||
* Finds and replaces all URLs by using a given regex | ||
* | ||
* @param string $contents Data to process | ||
* @param string $regex Regex used to find URLs in content | ||
* @param string $newDir Path to prepend to the original file | ||
* @param string $wrap Wrap around replaced values | ||
* @return string Processed data | ||
*/ | ||
protected function findAndReplaceUrlPathsByRegex(string $contents, string $regex, string $newDir, string $wrap = '|'): string | ||
{ | ||
$matches = []; | ||
$replacements = []; | ||
$wrapParts = explode('|', $wrap); | ||
preg_match_all($regex, $contents, $matches); | ||
foreach ($matches[2] as $matchCount => $match) { | ||
// remove '," or white-spaces around | ||
$match = trim($match, '\'" '); | ||
// we must not rewrite paths containing ":" or "url(", e.g. data URIs (see RFC 2397) | ||
if (strpos($match, ':') === false && !preg_match('/url\\s*\\(/i', $match)) { | ||
$newPath = GeneralUtility::resolveBackPath($newDir . $match); | ||
$replacements[$matches[1][$matchCount]] = $wrapParts[0] . $newPath . $wrapParts[1]; | ||
} | ||
} | ||
// replace URL paths in content | ||
if (!empty($replacements)) { | ||
$contents = str_replace(array_keys($replacements), array_values($replacements), $contents); | ||
} | ||
return $contents; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
typo3/sysext/core/Tests/Unit/Resource/RelativeCssPathFixerTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<?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\Tests\Unit\Resource; | ||
|
||
use TYPO3\CMS\Core\Resource\RelativeCssPathFixer; | ||
|
||
/** | ||
* Testcase for the RelativeCssPathFixer class | ||
*/ | ||
class RelativeCssPathFixerTest extends BaseTestCase | ||
{ | ||
/** | ||
* @var bool Restore Environment after tests | ||
*/ | ||
protected $backupEnvironment = true; | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public function fixRelativeUrlPathsDataProvider(): array | ||
{ | ||
return [ | ||
'@import from fileadmin with relative' => [ | ||
'@import url(../tests/test.css); body { background: #ffffff; }', | ||
'/fileadmin/css/', | ||
'@import url(\'/fileadmin/tests/test.css\'); body { background: #ffffff; }', | ||
], | ||
'@import from fileadmin with no relative' => [ | ||
'@import url(test.css); body { background: #ffffff; }', | ||
'fileadmin/css/', | ||
'@import url(\'fileadmin/css/test.css\'); body { background: #ffffff; }', | ||
], | ||
'@import from sitepackage with no relative' => [ | ||
'@import url(test.css); body { background: #ffffff; }', | ||
'typo3conf/ext/sitepackage/Resources/Public/Css/', | ||
'@import url(\'typo3conf/ext/sitepackage/Resources/Public/Css/test.css\'); body { background: #ffffff; }', | ||
], | ||
'url() from sitepackage with relative' => [ | ||
'@font-face { | ||
font-family: "Testfont" | ||
src: url("../fonts/testfont.woff2") format("woff2"), | ||
url("../fonts/testfont.woff") format("woff"); | ||
}', | ||
'../../../typo3conf/ext/sitepackage/Resources/Public/Css/', | ||
'@font-face { | ||
font-family: "Testfont" | ||
src: url(\'../../../typo3conf/ext/sitepackage/Resources/Public/fonts/testfont.woff2\') format("woff2"), | ||
url(\'../../../typo3conf/ext/sitepackage/Resources/Public/fonts/testfont.woff\') format("woff"); | ||
}', | ||
], | ||
'url() from fileadmin with no relative' => [ | ||
'@font-face { | ||
font-family: "Testfont" | ||
src: url("../fonts/testfont.woff2") format("woff2"), | ||
url("../fonts/testfont.woff") format("woff"); | ||
}', | ||
'fileadmin/css/', | ||
'@font-face { | ||
font-family: "Testfont" | ||
src: url(\'fileadmin/fonts/testfont.woff2\') format("woff2"), | ||
url(\'fileadmin/fonts/testfont.woff\') format("woff"); | ||
}', | ||
], | ||
]; | ||
} | ||
|
||
/** | ||
* @test | ||
* @dataProvider fixRelativeUrlPathsDataProvider | ||
* @param string $css css input | ||
* @param string $newDir new Directory of css file | ||
* @param string $expected expected adjusted import path | ||
*/ | ||
public function fixRelativeUrlPaths(string $css, string $newDir, string $expected): void | ||
{ | ||
$subject = new RelativeCssPathFixer(); | ||
$fixedCssPath = $subject->fixRelativeUrlPaths($css, $newDir); | ||
self::assertSame($expected, $fixedCssPath); | ||
} | ||
} |