Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions lib/Horde/Themes/Css/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,34 @@ public function __construct(array $params = [])
*/
abstract public function process($css, $cacheid);

/**
* Split off the stylesheets that are not local files (e.g. a CDN URL
* added by the cssfiles hook). These cannot be compressed, so they are
* linked as is, before the cached file. An absolute URL backed by a
* readable local file is kept in the cached file.
*
* @param array $css See Horde_Themes_Css#getStylesheets().
*
* @return array Two elements: the list of external URLs (Horde_Url
* objects) and the list of local stylesheets.
*/
protected function _splitExternal($css)
{
$external = $local = [];

foreach ($css as $val) {
if (isset($val['uri'])
&& preg_match('#^https?://#i', (string) $val['uri'])
&& !(isset($val['fs']) && is_readable($val['fs']))) {
$external[] = Horde::url($val['uri'], true, -1);
} else {
$local[] = $val;
}
}

return [$external, $local];
}

/**
* Perform garbage collection.
*/
Expand Down
9 changes: 7 additions & 2 deletions lib/Horde/Themes/Css/Cache/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public function process($css, $cacheid)
{
global $registry, $injector;

[$external, $css] = $this->_splitExternal($css);
if (!count($css)) {
return $external;
}

if (!empty($this->_params['filemtime'])) {
foreach ($css as &$val) {
$val['mtime'] = @filemtime($val['fs']);
Expand Down Expand Up @@ -71,9 +76,9 @@ public function process($css, $cacheid)
}
}

return [
return array_merge($external, [
Horde::url(($registry->get('staticuri', 'horde') ?? '') . '/' . $filename, true, ['append_session' => -1]),
];
]);
}

/**
Expand Down
9 changes: 7 additions & 2 deletions lib/Horde/Themes/Css/Cache/HordeCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public function process($css, $cacheid)
{
global $injector;

[$external, $css] = $this->_splitExternal($css);
if (!count($css)) {
return $external;
}

if (!empty($this->_params['filemtime'])) {
foreach ($css as &$val) {
$val['mtime'] = @filemtime($val['fs']);
Expand Down Expand Up @@ -62,9 +67,9 @@ public function process($css, $cacheid)
$cache->set($sig, $compress->compress($css, $logger));
}

return [
return array_merge($external, [
Horde::getCacheUrl('css', ['cid' => $sig]),
];
]);
}

}
82 changes: 82 additions & 0 deletions test/Unit/Themes/Css/CacheTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

declare(strict_types=1);

/**
* Tests for Horde_Themes_Css_Cache.
*
* @category Horde
* @package Core
*/

namespace Horde\Core\Test\Unit\Themes\Css;

use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\CoversClass;
use Horde_Registry;
use Horde_Themes_Css_Cache;

#[CoversClass(Horde_Themes_Css_Cache::class)]
class CacheTest extends TestCase
{
protected function setUp(): void
{
$GLOBALS['registry'] = $this->createStub(Horde_Registry::class);
$GLOBALS['registry']->method('get')->willReturn('/horde');
}

protected function tearDown(): void
{
unset($GLOBALS['registry']);
}

private function getCache(): Horde_Themes_Css_Cache
{
return new class extends Horde_Themes_Css_Cache {
public function process($css, $cacheid)
{
return $this->_splitExternal($css);
}
};
}

public function testSplitExternal(): void
{
$cdn = 'https://cdn.example.org/styles/main.css';
$local = ['app' => 'horde', 'fs' => '/srv/themes/x/screen.css', 'uri' => '/themes/x/screen.css'];

[$external, $rest] = $this->getCache()->process([
['app' => 'horde', 'fs' => $cdn, 'uri' => $cdn],
$local,
], '');

$this->assertCount(1, $external);
$this->assertSame($cdn, $external[0]->toString(false, true));
$this->assertSame([$local], $rest);
}

public function testSplitExternalKeepsAbsoluteUrlWithLocalFile(): void
{
$fs = tempnam(sys_get_temp_dir(), 'css');
$local = ['app' => 'horde', 'fs' => $fs, 'uri' => 'https://static.example.org/themes/x/screen.css'];

try {
[$external, $rest] = $this->getCache()->process([$local], '');
} finally {
unlink($fs);
}

$this->assertSame([], $external);
$this->assertSame([$local], $rest);
}

public function testSplitExternalWithoutExternal(): void
{
$local = ['app' => 'horde', 'fs' => '/srv/themes/x/screen.css', 'uri' => '/themes/x/screen.css'];

[$external, $rest] = $this->getCache()->process([$local], '');

$this->assertSame([], $external);
$this->assertSame([$local], $rest);
}
}
Loading