Skip to content

Commit

Permalink
[mms] Abstract CSS caching/compressions into Horde_Themes_Css framework.
Browse files Browse the repository at this point in the history
  • Loading branch information
slusarz committed Feb 4, 2014
1 parent d231a4f commit fdef052
Show file tree
Hide file tree
Showing 9 changed files with 455 additions and 130 deletions.
59 changes: 59 additions & 0 deletions framework/Core/lib/Horde/Core/Factory/CssCache.php
@@ -0,0 +1,59 @@
<?php
/**
* Copyright 2014 Horde LLC (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2014 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @link http://pear.horde.org/index.php?package=Core
* @package Core
*/

/**
* A Horde_Injector based factory for creating the CSS caching object.
*
* @author Michael Slusarz <slusarz@horde.org>
* @category Horde
* @copyright 2014 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @link http://pear.horde.org/index.php?package=Core
* @package Core
* @since 2.12.0
*/
class Horde_Core_Factory_CssCache extends Horde_Core_Factory_Injector
{
/**
*/
public function create(Horde_Injector $injector)
{
global $conf;

$driver = empty($conf['cachecss'])
? 'none'
: strtolower($conf['cachecssparams']['driver']);

switch ($driver) {
case 'filesystem':
$driver = 'Horde_Themes_Css_Cache_File';
$params = $conf['cachecssparams'];
break;

case 'horde_cache':
$driver = 'Horde_Themes_Css_Cache_HordeCache';
$params = $conf['cachecssparams'];
break;

case 'none':
default:
$driver = 'Horde_Themes_Css_Cache_Null';
$params = array();
break;
}

return new $driver($params);
}

}
1 change: 1 addition & 0 deletions framework/Core/lib/Horde/Registry.php
Expand Up @@ -400,6 +400,7 @@ public function __construct($session_flags = 0, array $args = array())
'getRequestConfiguration',
),
'Horde_Core_Auth_Signup' => 'Horde_Core_Factory_AuthSignup',
'Horde_Core_CssCache' => 'Horde_Core_Factory_CssCache',
'Horde_Core_JavascriptCache' => 'Horde_Core_Factory_JavascriptCache',
'Horde_Core_Perms' => 'Horde_Core_Factory_PermsCore',
'Horde_Dav_Server' => 'Horde_Core_Factory_DavServer',
Expand Down
146 changes: 16 additions & 130 deletions framework/Core/lib/Horde/Themes/Css.php
Expand Up @@ -85,7 +85,7 @@ public function addThemeStylesheet($file)
*/
public function getStylesheetUrls(array $opts = array())
{
global $conf, $injector, $prefs, $registry;
global $conf, $injector, $prefs;

$theme = isset($opts['theme'])
? $opts['theme']
Expand All @@ -95,64 +95,11 @@ public function getStylesheetUrls(array $opts = array())
return array();
}

$cache_type = !empty($opts['nocache']) || empty($conf['cachecss'])
? 'none'
: $conf['cachecssparams']['driver'];
$cache_ob = empty($opts['nocache'])
? $injector->getInstance('Horde_Core_CssCache')
: new Horde_Themes_Css_Cache_Null();

if ($cache_type == 'none') {
$css_out = array();
foreach ($css as $file) {
$url = Horde::url($file['uri'], true, -1);
$css_out[] = (is_null($file['app']) || empty($conf['cachecssparams']['url_version_param']))
? $url
: $url->add('v', hash('sha1', $registry->getVersion($file['app'])));
}
return $css_out;
}

if (!empty($conf['cachecssparams']['filemtime'])) {
foreach ($css as &$val) {
$val['mtime'] = @filemtime($val['fs']);
}
}

$out = '';
$sig = hash('sha1', serialize($css) . $this->_cacheid);

switch ($cache_type) {
case 'filesystem':
$css_filename = '/static/' . $sig . '.css';
$css_path = $registry->get('fileroot', 'horde') . $css_filename;
$css_url = Horde::url($registry->get('webroot', 'horde') . $css_filename, true, array('append_session' => -1));
$exists = file_exists($css_path);
break;

case 'horde_cache':
$cache = $injector->getInstance('Horde_Cache');

// Do lifetime checking here, not on cache display page.
$exists = $cache->exists($sig, empty($conf['cachecssparams']['lifetime']) ? 0 : $conf['cachecssparams']['lifetime']);
$css_url = Horde::getCacheUrl('css', array('cid' => $sig));
break;
}

if (!$exists) {
$out = $this->loadCssFiles($css);

switch ($cache_type) {
case 'filesystem':
if (!file_put_contents($css_path, $out)) {
throw new Horde_Exception('Could not write cached CSS file to disk.');
}
break;

case 'horde_cache':
$cache->set($sig, $out);
break;
}
}

return array($css_url);
return $cache_ob->process($css, $this->_cacheid);
}

/**
Expand Down Expand Up @@ -182,8 +129,10 @@ public function getStylesheetUrls(array $opts = array())
*/
public function getStylesheets($theme = '', array $opts = array())
{
if (($theme === '') && isset($GLOBALS['prefs'])) {
$theme = $GLOBALS['prefs']->getValue('theme');
global $injector, $prefs, $registry;

if (($theme === '') && isset($prefs)) {
$theme = $prefs->getValue('theme');
}

$add_css = $css_out = array();
Expand All @@ -194,7 +143,7 @@ public function getStylesheets($theme = '', array $opts = array())
$css_list = array_unique(array_merge($css_list, array_keys($this->_cssThemeFiles)));

$curr_app = empty($opts['app'])
? $GLOBALS['registry']->getApp()
? $registry->getApp()
: $opts['app'];
$mask = empty($opts['nohorde'])
? 0
Expand All @@ -203,7 +152,7 @@ public function getStylesheets($theme = '', array $opts = array())
? null
: $opts['sub'];

$cache = $GLOBALS['injector']->getInstance('Horde_Core_Factory_ThemesCache')->create($curr_app, $theme);
$cache = $injector->getInstance('Horde_Core_Factory_ThemesCache')->create($curr_app, $theme);
$this->_cacheid = $cache->getCacheId();

/* Add external stylesheets first, since they are ALWAYS overwritable
Expand All @@ -230,7 +179,7 @@ public function getStylesheets($theme = '', array $opts = array())
}

/* Add user-defined additional stylesheets. */
$hooks = $GLOBALS['injector']->getInstance('Horde_Core_Hooks');
$hooks = $injector->getInstance('Horde_Core_Hooks');
try {
$add_css = array_merge($add_css, $hooks->callHook('cssfiles', 'horde', array($theme)));
} catch (Horde_Exception_HookNotSet $e) {}
Expand Down Expand Up @@ -293,80 +242,17 @@ public function getBaseStylesheetList()
/**
* Loads CSS files, cleans up the input, and concatenates to a string.
*
* @deprecated Use Horde_Themes_Css_Compress instead.
*
* @param array $files List of CSS files as returned from
* getStylesheets().
*
* @return string CSS data.
*/
public function loadCssFiles($files)
{
global $browser, $conf;

$dataurl = (empty($conf['nobase64_img']) && $browser->hasFeature('dataurl'));
$out = '';

foreach ($files as $file) {
$data = file_get_contents($file['fs']);
$path = substr($file['uri'], 0, strrpos($file['uri'], '/') + 1);
$url = array();

try {
$css_parser = new Horde_Css_Parser($data);
} catch (Exception $e) {
/* If the CSS is broken, log error and output as-is. */
Horde::log($e, 'ERR');
$out .= $data;
continue;
}

foreach ($css_parser->doc->getContents() as $val) {
if ($val instanceof Sabberworm\CSS\Property\Import) {
$ob = Horde_Themes_Element::fromUri($path . $val->getLocation()->getURL()->getString());
$out .= $this->loadCssFiles(array(array(
'app' => null,
'fs' => $ob->fs,
'uri' => $ob->uri
)));
$css_parser->doc->remove($val);
}
}

foreach ($css_parser->doc->getAllRuleSets() as $val) {
foreach ($val->getRules('background-') as $val2) {
$item = $val2->getValue();

if ($item instanceof Sabberworm\CSS\Value\URL) {
$url[] = $item;
} elseif ($item instanceof Sabberworm\CSS\Value\RuleValueList) {
foreach ($item->getListComponents() as $val3) {
if ($val3 instanceof Sabberworm\CSS\Value\URL) {
$url[] = $val3;
}
}
}
}
}

foreach ($url as $val) {
$url_ob = $val->getURL();
$url_str = $url_ob->getString();

if (Horde_Url_Data::isData($url_str)) {
$url_ob->setString($url_str);
} else {
if ($dataurl) {
/* Limit data to 16 KB in stylesheets. */
$url_ob->setString(Horde_Themes_Image::base64ImgData($path . $url_str, 16384));
} else {
$url_ob->setString($path . $url_str);
}
}
}

$out .= $css_parser->compress();
}

return $out;
$compress = new Horde_Themes_Css_Compress();
return $compress->compress($files);
}

}
60 changes: 60 additions & 0 deletions framework/Core/lib/Horde/Themes/Css/Cache.php
@@ -0,0 +1,60 @@
<?php
/**
* Copyright 2014 Horde LLC (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2014 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Core
*/

/**
* Object handling storage of cached CSS data.
*
* @author Michael Slusarz <slusarz@horde.org>
* @category Horde
* @copyright 2014 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Core
* @since 2.12.0
*/
abstract class Horde_Themes_Css_Cache
{
/**
* Configuration parameters.
*
* @var array
*/
protected $_params;

/**
* Constructor.
*
* @param array $params Configuration parameters.
*/
public function __construct(array $params = array())
{
$this->_params = $params;
}

/**
* Process a list of CSS files.
*
* @param array $css See Horde_Themes_Css#getStylesheets().
* @param string $cacheid Cache ID.
*
* @return array The list of URLs to display (Horde_Url objects).
*/
abstract public function process($css, $cacheid);

/**
* Perform garbage collection.
*/
public function gc()
{
}

}

0 comments on commit fdef052

Please sign in to comment.