Commit a302f92a authored by Cédric Anne's avatar Cédric Anne Committed by Johan Cwiklinski

Make CSS cacheable by browsers and proxies (#5898)

parent 2aa97c64
......@@ -45,4 +45,15 @@ include_once GLPI_ROOT . '/inc/config.php';
$css = Html::compileScss($_GET);
header('Content-Type: text/css');
$is_cacheable = !isset($_GET['debug']) && !isset($_GET['nocache']);
if ($is_cacheable) {
// Makes CSS cacheable by browsers and proxies
$max_age = WEEK_TIMESTAMP;
header_remove('Pragma');
header('Cache-Control: public');
header('Cache-Control: max-age=' . $max_age);
header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + $max_age));
}
echo $css;
......@@ -46,7 +46,12 @@ echo "<html lang=\"{$CFG_GLPI["languages"][$_SESSION['glpilanguage']][3]}\">";
<title>GLPI</title>
<?php
echo Html::scss('glpi-legacy');
echo Html::scss('css/legacy');
if (isset($_SESSION['glpihighcontrast_css']) && $_SESSION['glpihighcontrast_css']) {
echo Html::scss('css/highcontrast');
}
$theme = isset($_SESSION['glpipalette']) ? $_SESSION['glpipalette'] : 'auror';
echo Html::scss('css/palettes/' . $theme);
echo Html::script($CFG_GLPI["root_doc"].'/script.js');
?>
......
......@@ -1279,7 +1279,12 @@ class Html {
}
// CSS link
echo Html::scss('glpi-legacy');
echo Html::scss('css/legacy');
if (isset($_SESSION['glpihighcontrast_css']) && $_SESSION['glpihighcontrast_css']) {
echo Html::scss('css/highcontrast');
}
$theme = isset($_SESSION['glpipalette']) ? $_SESSION['glpipalette'] : 'auror';
echo Html::scss('css/palettes/' . $theme);
echo "<link rel='shortcut icon' type='images/x-icon' href='".
$CFG_GLPI["root_doc"]."/pics/favicon.ico' >\n";
......@@ -4936,6 +4941,9 @@ class Html {
$file = $url;
$url = self::getPrefixedUrl('/front/css.php');
$url .= '?file=' . $file;
if ($_SESSION['glpi_use_mode'] == Session::DEBUG_MODE) {
$url .= '&debug';
}
}
return self::csslink($url, $options);
......@@ -6678,12 +6686,10 @@ class Html {
$appCache = Toolbox::getAppCache();
$ckey = isset($args['v']) ? $args['v'] : GLPI_SCHEMA_VERSION;
$is_debug = $_SESSION['glpi_use_mode'] == Session::DEBUG_MODE;
$files = [];
$scss = new Compiler();
$scss->setFormatter('Leafo\ScssPhp\Formatter\Crunched');
if ($is_debug || isset($args['debug'])) {
if (isset($args['debug'])) {
$ckey .= '_sourcemap';
$scss->setSourceMap(Compiler::SOURCE_MAP_INLINE);
$scss->setSourceMapOptions(
......@@ -6694,77 +6700,56 @@ class Html {
);
}
if (!isset($args['file']) || $args['file'] == 'main_styles') {
$files[] = 'css/glpi';
} else if ($args['file'] == 'glpi-legacy') {
$ckey .= '_legacy';
$files[] = 'css/legacy';
$file = isset($args['file']) ? $args['file'] : 'css/glpi';
if (isset($_SESSION['glpihighcontrast_css'])
&& $_SESSION['glpihighcontrast_css']) {
$ckey .= '_highcontrast';
$files[] = 'css/highcontrast';
}
$ckey .= '_' . $file;
$ckey = 'css_' . md5($ckey);
// CSS theme
$theme = 'auror';
if (isset($_SESSION["glpipalette"])) {
$theme = $_SESSION['glpipalette'];
}
$ckey .= '_' . $theme;
$files[] = 'css/palettes/' . $theme;
} else {
if (!Toolbox::startsWith($args['file'], 'css/')) {
$args['file'] = '/css/' . $args['file'];
}
$ckey .= '_' . md5($args['file']);
if (!Toolbox::endsWith($file, '.scss')) {
// Prevent include of file if ext is not .scss
$file .= '.scss';
}
$filename = realpath(GLPI_ROOT . $args['file'] . '.scss');
if (!Toolbox::startsWith($filename, realpath(GLPI_ROOT))) {
// Prevent import of a file from ouside GLPI dir
// or not ending with .scss
return '';
}
// Requested file path
$path = GLPI_ROOT . '/' . $file;
// Alternate file path (prefixed by a "_", i.e. "_highcontrast.scss").
$pathargs = explode('/', $file);
$pathargs[] = '_' . array_pop($pathargs);
$pathalt = GLPI_ROOT . '/' . implode('/', $pathargs);
if (!file_exists($path) && !file_exists($pathalt)) {
Toolbox::logWarning('Requested file ' . $path . ' does not exists.');
return '';
}
if (!file_exists($path)) {
$path = $pathalt;
}
$files[] = $args['file'];
// Prevent import of a file from ouside GLPI dir
$path = realpath($path);
if (!Toolbox::startsWith($path, realpath(GLPI_ROOT))) {
Toolbox::logWarning('Requested file ' . $path . ' is outside GLPI file tree.');
return '';
}
$ckey = md5($ckey);
$import = '';
$import = '@import "' . $file . '";';
$fckey = md5($file);
$hashfile = self::getScssFileHash($path);
foreach ($files as $file) {
$path = GLPI_ROOT . "/$file";
if (!Toolbox::endsWith($file, '.scss')) {
$path .= ".scss";
}
$pathargs = explode('/', $file);
$pathargs[] = '_' . array_pop($pathargs);
$pathalt = GLPI_ROOT . '/' . implode('/', $pathargs) . '.scss';
if (file_exists($path) || file_exists($pathalt)) {
if (!file_exists($path)) {
$path = $pathalt;
}
$import .= '@import "' . $file . '";';
$fckey = md5($file);
$hashfile = self::getScssFileHash($path);
//check if files has changed
if ($appCache->has($fckey)) {
$hash = $appCache->get($fckey);
if ($hashfile != $hash) {
//file has changed
Toolbox::logDebug("$file has changed, reloading");
$args['reload'] = true;
$appCache->set($fckey, $hashfile);
}
} else {
Toolbox::logDebug("$file is new, loading");
$appCache->set($fckey, $hashfile);
}
} else {
Toolbox::logWarning('Requested file ' . $path . ' does not exists.');
//check if files has changed
if ($appCache->has($fckey)) {
$hash = $appCache->get($fckey);
if ($hashfile != $hash) {
//file has changed
Toolbox::logDebug("$file has changed, reloading");
$args['reload'] = true;
$appCache->set($fckey, $hashfile);
}
} else {
Toolbox::logDebug("$file is new, loading");
$appCache->set($fckey, $hashfile);
}
$scss->addImportPath(GLPI_ROOT);
......
......@@ -86,7 +86,12 @@ if (!file_exists(GLPI_CONFIG_DIR . "/db.yaml")) {
echo "<meta name='viewport' content='width=device-width, initial-scale=1'/>";
// Appel CSS
echo Html::scss('glpi-legacy');
echo Html::scss('css/legacy');
if (isset($_SESSION['glpihighcontrast_css']) && $_SESSION['glpihighcontrast_css']) {
echo Html::scss('css/highcontrast');
}
$theme = isset($_SESSION['glpipalette']) ? $_SESSION['glpipalette'] : 'auror';
echo Html::scss('css/palettes/' . $theme);
// font awesome icons
echo Html::css('public/lib/fontawesome-free/css/all.css');
......
......@@ -807,7 +807,7 @@ class Main extends AbstractController implements ControllerInterface
*
* @return void
*
* @Glpi\Annotation\Route(name="asset", pattern="/assets/css/{file}")
* @Glpi\Annotation\Route(name="asset", pattern="/assets/css/{file:.*}")
*/
public function asset(Request $request, Response $response, array $args)
{
......@@ -815,6 +815,24 @@ class Main extends AbstractController implements ControllerInterface
$css = \Html::compileScss(array_merge($args, $get));
$response = $response->withHeader('Content-type', 'text/css');
// Makes CSS cacheable by browsers and proxies
$is_cacheable = !isset($get['debug']) && !isset($get['nocache']);
if ($is_cacheable) {
// For now it is nearly impossible to change automatic cache headers policy related to session
// as session is started prior to kernel and router instanciation.
// Setting header "Pragma" to empty value permits to use browser cache but this is a workaround that
// may not always work.
//
// TODO When it will be possible, replace this by usage of "session_cache_limiter('');"
// before "session_start()" for this route (or even better, do not use session for this route).
$response = $response->withHeader('Pragma', null);
$max_age = WEEK_TIMESTAMP;
$response = $response->withHeader('Cache-Control', ['public', 'max-age=' . $max_age]);
$response = $response->withHeader('Expires', gmdate('D, d M Y H:i:s \G\M\T', time() + $max_age));
}
$body = $response->getBody();
$body->write($css);
return $response;
......
......@@ -47,7 +47,7 @@
<link rel="stylesheet" href="{{ CFG_GLPI.root_doc }}/{{ lib_path }}/coreui/css/coreui-standalone.css" type="text/css" media="screen" />
<link rel="stylesheet" href="{{ CFG_GLPI.root_doc }}/{{ lib_path }}/fontawesome-free/css/all.css" type="text/css" media="screen" />
<link rel="stylesheet" href="{{ path_for('asset', {'file': 'glpi'}) }}" type="text/css" media="screen" />
<link rel="stylesheet" href="{{ path_for('asset', {'file': 'css/glpi'}) }}{{ glpi_debug ? '?debug' : '' }}" type="text/css" media="screen" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic" />
......
......@@ -82,8 +82,8 @@ class CompileScssCommand extends Command {
$files = $input->getOption('file');
if (empty($files)) {
$files[] = 'main_styles'; // Compile main styles if no file option is set.
$files[] = 'glpi-legacy'; // Compile legacy styles if no file option is set.
$files[] = 'css/glpi'; // Compile main styles if no file option is set.
$files[] = 'css/legacy'; // Compile legacy styles if no file option is set.
}
foreach ($files as $file) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment