mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 22:08:20 +01:00
Merge branch 'MDL-59148-master-7' of git://github.com/andrewnicols/moodle
This commit is contained in:
commit
185455dcc3
@ -329,6 +329,26 @@ function css_send_cached_css_content($csscontent, $etag) {
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends CSS directly and disables all caching.
|
||||
* The Content-Length of the body is also included, but the script is not ended.
|
||||
*
|
||||
* @param string $css The CSS content to send
|
||||
* @param int $expiry The anticipated expiry of the file
|
||||
*/
|
||||
function css_send_temporary_css($css) {
|
||||
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||||
header('Pragma: no-cache');
|
||||
header('Expires: 0');
|
||||
header('Content-Disposition: inline; filename="styles_debug.php"');
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
|
||||
header('Accept-Ranges: none');
|
||||
header('Content-Type: text/css; charset=utf-8');
|
||||
header('Content-Length: ' . strlen($css));
|
||||
|
||||
echo $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends CSS directly without caching it.
|
||||
*
|
||||
@ -382,4 +402,4 @@ function css_send_unmodified($lastmodified, $etag) {
|
||||
function css_send_css_not_found() {
|
||||
header('HTTP/1.0 404 not found');
|
||||
die('CSS was not found, sorry.');
|
||||
}
|
||||
}
|
||||
|
@ -363,6 +363,12 @@ class theme_config {
|
||||
*/
|
||||
public $editor_sheets = array();
|
||||
|
||||
/**
|
||||
* @var bool Whether a fallback version of the stylesheet will be used
|
||||
* whilst the final version is generated.
|
||||
*/
|
||||
public $usefallback = false;
|
||||
|
||||
/**
|
||||
* @var array The names of all the javascript files this theme that you would
|
||||
* like included from head, in order. Give the names of the files without .js.
|
||||
@ -724,7 +730,7 @@ class theme_config {
|
||||
}
|
||||
|
||||
$configurable = array(
|
||||
'parents', 'sheets', 'parents_exclude_sheets', 'plugins_exclude_sheets',
|
||||
'parents', 'sheets', 'parents_exclude_sheets', 'plugins_exclude_sheets', 'usefallback',
|
||||
'javascripts', 'javascripts_footer', 'parents_exclude_javascripts',
|
||||
'layouts', 'enable_dock', 'enablecourseajax', 'requiredblocks',
|
||||
'rendererfactory', 'csspostprocess', 'editor_sheets', 'rarrow', 'larrow', 'uarrow', 'darrow',
|
||||
@ -1102,6 +1108,19 @@ class theme_config {
|
||||
return $cache->set($key, $csscontent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the post processed CSS content has been cached.
|
||||
*
|
||||
* @return bool Whether the post-processed CSS is available in the cache.
|
||||
*/
|
||||
public function has_css_cached_content() {
|
||||
|
||||
$key = $this->get_css_cache_key();
|
||||
$cache = cache::make('core', 'postprocessedcss');
|
||||
|
||||
return $cache->has($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return cached post processed CSS content.
|
||||
*
|
||||
@ -2218,6 +2237,15 @@ class theme_config {
|
||||
$this->rtlmode = $inrtl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the theme is being served in RTL mode.
|
||||
*
|
||||
* @return bool True when in RTL mode.
|
||||
*/
|
||||
public function get_rtl_mode() {
|
||||
return $this->rtlmode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if file with any image extension exists.
|
||||
*
|
||||
|
@ -29,6 +29,7 @@ require_once(__DIR__ . '/lib.php');
|
||||
$THEME->name = 'boost';
|
||||
$THEME->sheets = [];
|
||||
$THEME->editor_sheets = [];
|
||||
$THEME->usefallback = true;
|
||||
$THEME->scss = function($theme) {
|
||||
return theme_boost_get_main_scss_content($theme);
|
||||
};
|
||||
|
@ -28,6 +28,7 @@ $THEME->parents = array('clean', 'bootstrapbase');
|
||||
$THEME->doctype = 'html5';
|
||||
$THEME->sheets = array('custom');
|
||||
$THEME->lessfile = 'moodle';
|
||||
$THEME->usefallback = true;
|
||||
$THEME->parents_exclude_sheets = array('bootstrapbase' => array('moodle'), 'clean' => array('custom'));
|
||||
$THEME->lessvariablescallback = 'theme_more_less_variables';
|
||||
$THEME->extralesscallback = 'theme_more_extra_less';
|
||||
|
254
theme/styles.php
254
theme/styles.php
@ -77,6 +77,7 @@ if (is_null($themesubrev)) {
|
||||
$themesubrev = min_clean_param($themesubrev, 'INT');
|
||||
}
|
||||
|
||||
// Check that type fits into the expected values.
|
||||
if ($type === 'editor') {
|
||||
// The editor CSS is never chunked.
|
||||
$chunk = null;
|
||||
@ -96,28 +97,17 @@ if (file_exists("$CFG->dirroot/theme/$themename/config.php")) {
|
||||
}
|
||||
|
||||
$candidatedir = "$CFG->localcachedir/theme/$rev/$themename/css";
|
||||
$etag = "$rev/$themename/$type/$themesubrev";
|
||||
$candidatename = ($themesubrev > 0) ? "{$type}_{$themesubrev}" : $type;
|
||||
if (!$usesvg) {
|
||||
// Add to the sheet name, one day we'll be able to just drop this.
|
||||
$candidatedir .= '/nosvg';
|
||||
$etag .= '/nosvg';
|
||||
}
|
||||
$candidatesheet = "{$candidatedir}/" . theme_styles_get_filename($type, $themesubrev, $usesvg);
|
||||
$chunkedcandidatesheet = "{$candidatedir}/" . theme_styles_get_filename($type, $themesubrev, $usesvg, $chunk);
|
||||
$etag = theme_styles_get_etag($themename, $rev, $type, $themesubrev, $usesvg, $chunk);
|
||||
|
||||
if ($chunk !== null) {
|
||||
$etag .= '/chunk'.$chunk;
|
||||
$candidatename .= '.'.$chunk;
|
||||
}
|
||||
$candidatesheet = "$candidatedir/$candidatename.css";
|
||||
$etag = sha1($etag);
|
||||
|
||||
if (file_exists($candidatesheet)) {
|
||||
if (file_exists($chunkedcandidatesheet)) {
|
||||
if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
|
||||
// We do not actually need to verify the etag value because our files
|
||||
// never change in cache because we increment the rev counter.
|
||||
css_send_unmodified(filemtime($candidatesheet), $etag);
|
||||
css_send_unmodified(filemtime($chunkedcandidatesheet), $etag);
|
||||
}
|
||||
css_send_cached_css($candidatesheet, $etag);
|
||||
css_send_cached_css($chunkedcandidatesheet, $etag);
|
||||
}
|
||||
|
||||
// Ok, now we need to start normal moodle script, we need to load all libs and $DB.
|
||||
@ -139,94 +129,208 @@ $cache = true;
|
||||
// If the client is requesting a revision that doesn't match both
|
||||
// the global theme revision and the theme specific revision then
|
||||
// tell the browser not to cache this style sheet because it's
|
||||
// likely being regnerated.
|
||||
// likely being regenerated.
|
||||
if ($themerev <= 0 or $themerev != $rev or $themesubrev != $currentthemesubrev) {
|
||||
$rev = $themerev;
|
||||
$cache = false;
|
||||
|
||||
$candidatedir = "$CFG->localcachedir/theme/$rev/$themename/css";
|
||||
$etag = "$rev/$themename/$type/$themesubrev";
|
||||
$candidatename = ($themesubrev > 0) ? "{$type}_{$themesubrev}" : $type;
|
||||
if (!$usesvg) {
|
||||
// Add to the sheet name, one day we'll be able to just drop this.
|
||||
$candidatedir .= '/nosvg';
|
||||
$etag .= '/nosvg';
|
||||
}
|
||||
|
||||
if ($chunk !== null) {
|
||||
$etag .= '/chunk'.$chunk;
|
||||
$candidatename .= '.'.$chunk;
|
||||
}
|
||||
$candidatesheet = "$candidatedir/$candidatename.css";
|
||||
$etag = sha1($etag);
|
||||
$candidatesheet = "{$candidatedir}/" . theme_styles_get_filename($type, $themesubrev, $usesvg);
|
||||
$chunkedcandidatesheet = "{$candidatedir}/" . theme_styles_get_filename($type, $themesubrev, $usesvg, $chunk);
|
||||
$etag = theme_styles_get_etag($themename, $rev, $type, $themesubrev, $usesvg, $chunk);
|
||||
}
|
||||
|
||||
make_localcache_directory('theme', false);
|
||||
|
||||
if ($type === 'editor') {
|
||||
$csscontent = $theme->get_css_content_editor();
|
||||
css_store_css($theme, "$candidatedir/editor.css", $csscontent, false);
|
||||
css_store_css($theme, $candidatesheet, $csscontent, false);
|
||||
|
||||
} else {
|
||||
// Fetch a lock whilst the CSS is fetched as this can be slow and CPU intensive.
|
||||
// Each client should wait for one to finish the compilation before starting the compiler.
|
||||
$lockfactory = \core\lock\lock_config::get_lock_factory('core_theme_get_css_content');
|
||||
$lock = $lockfactory->get_lock($themename, rand(90, 120));
|
||||
|
||||
if (file_exists($candidatesheet)) {
|
||||
// The file was built while we waited for the lock, we release the lock and serve the file.
|
||||
if ($lock) {
|
||||
$lock->release();
|
||||
}
|
||||
|
||||
if ($cache) {
|
||||
css_send_cached_css($candidatesheet, $etag);
|
||||
} else {
|
||||
css_send_uncached_css(file_get_contents($candidatesheet));
|
||||
}
|
||||
if ($cache) {
|
||||
css_send_cached_css($candidatesheet, $etag);
|
||||
} else {
|
||||
css_send_uncached_css(file_get_contents($candidatesheet));
|
||||
}
|
||||
|
||||
// The lock is still held, and the sheet still does not exist.
|
||||
// Compile the CSS content.
|
||||
}
|
||||
|
||||
if (($fallbacksheet = theme_styles_fallback_content($theme)) && !$theme->has_css_cached_content()) {
|
||||
// The theme is not yet available and a fallback is available.
|
||||
// Return the fallback immediately, specifying the Content-Length, then generate in the background.
|
||||
$css = file_get_contents($fallbacksheet);
|
||||
css_send_temporary_css($css);
|
||||
|
||||
// The fallback content has now been sent.
|
||||
// There will be an attempt to generate the content, but it should not be served.
|
||||
// The Content-Length above means that the client will disregard it anyway.
|
||||
$sendaftergeneration = false;
|
||||
|
||||
// There may be another client currently holding a lock and generating the stylesheet.
|
||||
// Use a very low lock timeout as the connection will be ended immediately afterwards.
|
||||
$locktimeout = 1;
|
||||
} else {
|
||||
// There is no fallback content to be issued here, therefore the generated content must be output.
|
||||
$sendaftergeneration = true;
|
||||
|
||||
// Use a realistic lock timeout as the intention is to avoid lock contention.
|
||||
$locktimeout = rand(90, 120);
|
||||
}
|
||||
|
||||
// Attempt to fetch the lock.
|
||||
$lockfactory = \core\lock\lock_config::get_lock_factory('core_theme_get_css_content');
|
||||
$lock = $lockfactory->get_lock($themename, $locktimeout);
|
||||
|
||||
if ($sendaftergeneration || $lock) {
|
||||
// Either the lock was successful, or the lock was unsuccessful but the content *must* be sent.
|
||||
if (!file_exists($chunkedcandidatesheet)) {
|
||||
// The content does not exist locally.
|
||||
// Generate and save it.
|
||||
$candidatesheet = theme_styles_generate_and_store($theme, $rev, $themesubrev, $candidatedir);
|
||||
}
|
||||
|
||||
if ($lock) {
|
||||
$lock->release();
|
||||
}
|
||||
|
||||
if ($sendaftergeneration) {
|
||||
if (!$cache) {
|
||||
// Do not pollute browser caches if invalid revision requested,
|
||||
// let's ignore legacy IE breakage here too.
|
||||
css_send_uncached_css(file_get_contents($candidatesheet));
|
||||
|
||||
} else if ($chunk !== null and file_exists($chunkedcandidatesheet)) {
|
||||
// Greetings stupid legacy IEs!
|
||||
css_send_cached_css($chunkedcandidatesheet, $etag);
|
||||
|
||||
} else {
|
||||
// Real browsers - this is the expected result!
|
||||
css_send_cached_css($candidatesheet, $etag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the theme CSS and store it.
|
||||
*
|
||||
* @param theme_config $theme The theme to be generated
|
||||
* @param int $rev The theme revision
|
||||
* @param int $themesubrev The theme sub-revision
|
||||
* @param string $candidatedir The directory that it should be stored in
|
||||
* @return string The path that the primary (non-chunked) CSS was written to
|
||||
*/
|
||||
function theme_styles_generate_and_store($theme, $rev, $themesubrev, $candidatedir) {
|
||||
global $CFG;
|
||||
|
||||
// Generate the content first.
|
||||
if (!$csscontent = $theme->get_css_cached_content()) {
|
||||
$csscontent = $theme->get_css_content();
|
||||
$theme->set_css_content_cache($csscontent);
|
||||
}
|
||||
|
||||
if ($theme->get_rtl_mode()) {
|
||||
$type = "all-rtl";
|
||||
} else {
|
||||
$type = "all";
|
||||
}
|
||||
|
||||
// Determine the candidatesheet path.
|
||||
// Note: Do not pass any value for chunking as this is calcualted during css storage.
|
||||
$candidatesheet = "{$candidatedir}/" . theme_styles_get_filename($type, $themesubrev, $theme->use_svg_icons());
|
||||
|
||||
// Determine the chunking URL.
|
||||
// Note, this will be removed when support for IE9 is removed.
|
||||
$relroot = preg_replace('|^http.?://[^/]+|', '', $CFG->wwwroot);
|
||||
if (!empty($slashargument)) {
|
||||
if ($usesvg) {
|
||||
$chunkurl = "{$relroot}/theme/styles.php/{$themename}/{$rev}/$type";
|
||||
if (!empty(min_get_slash_argument())) {
|
||||
if ($theme->use_svg_icons()) {
|
||||
$chunkurl = "{$relroot}/theme/styles.php/{$theme->name}/{$rev}/$type";
|
||||
} else {
|
||||
$chunkurl = "{$relroot}/theme/styles.php/_s/{$themename}/{$rev}/$type";
|
||||
$chunkurl = "{$relroot}/theme/styles.php/_s/{$theme->name}/{$rev}/$type";
|
||||
}
|
||||
} else {
|
||||
if ($usesvg) {
|
||||
$chunkurl = "{$relroot}/theme/styles.php?theme={$themename}&rev={$rev}&type=$type";
|
||||
if ($theme->use_svg_icons()) {
|
||||
$chunkurl = "{$relroot}/theme/styles.php?theme={$theme->name}&rev={$rev}&type=$type";
|
||||
} else {
|
||||
$chunkurl = "{$relroot}/theme/styles.php?theme={$themename}&rev={$rev}&type=$type&svg=0";
|
||||
$chunkurl = "{$relroot}/theme/styles.php?theme={$theme->name}&rev={$rev}&type=$type&svg=0";
|
||||
}
|
||||
}
|
||||
|
||||
css_store_css($theme, "$candidatedir/$candidatename.css", $csscontent, true, $chunkurl);
|
||||
// Store the CSS.
|
||||
css_store_css($theme, $candidatesheet, $csscontent, true, $chunkurl);
|
||||
|
||||
if ($lock) {
|
||||
// Now that the CSS has been generated and/or stored, release the lock.
|
||||
// This will allow waiting clients to use the newly generated and stored CSS.
|
||||
$lock->release();
|
||||
// Store the fallback CSS in the temp directory.
|
||||
// This file is used as a fallback when waiting for a theme to compile and is not versioned in any way.
|
||||
$fallbacksheet = make_temp_directory("theme/{$theme->name}")
|
||||
. "/"
|
||||
. theme_styles_get_filename($type, 0, $theme->use_svg_icons());
|
||||
css_store_css($theme, $fallbacksheet, $csscontent, true, $chunkurl);
|
||||
|
||||
return $candidatesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the preferred fallback content location if available.
|
||||
*
|
||||
* @param theme_config $theme The theme to be generated
|
||||
* @return string The path to the fallback sheet on disk
|
||||
*/
|
||||
function theme_styles_fallback_content($theme) {
|
||||
global $CFG;
|
||||
|
||||
if (!$theme->usefallback) {
|
||||
// This theme does not support fallbacks.
|
||||
return false;
|
||||
}
|
||||
|
||||
$type = $theme->get_rtl_mode() ? 'all-rtl' : 'all';
|
||||
$filename = theme_styles_get_filename($type);
|
||||
|
||||
$fallbacksheet = "{$CFG->tempdir}/theme/{$theme->name}/{$filename}";
|
||||
if (file_exists($fallbacksheet)) {
|
||||
return $fallbacksheet;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$cache) {
|
||||
// Do not pollute browser caches if invalid revision requested,
|
||||
// let's ignore legacy IE breakage here too.
|
||||
css_send_uncached_css($csscontent);
|
||||
/**
|
||||
* Get the filename for the specified configuration.
|
||||
*
|
||||
* @param string $type The requested sheet type
|
||||
* @param int $themesubrev The theme sub-revision
|
||||
* @param bool $usesvg Whether SVGs are allowed
|
||||
* @param int $chunk The chunk number if specified
|
||||
* @return string The filename for this sheet
|
||||
*/
|
||||
function theme_styles_get_filename($type, $themesubrev = 0, $usesvg = true, $chunk = null) {
|
||||
$filename = $type;
|
||||
$filename .= ($themesubrev > 0) ? "_{$themesubrev}" : '';
|
||||
$filename .= $usesvg ? '' : '-nosvg';
|
||||
$filename .= $chunk ? ".{$chunk}" : '';
|
||||
|
||||
} else if ($chunk !== null and file_exists($candidatesheet)) {
|
||||
// Greetings stupid legacy IEs!
|
||||
css_send_cached_css($candidatesheet, $etag);
|
||||
|
||||
} else {
|
||||
// Real browsers - this is the expected result!
|
||||
css_send_cached_css_content($csscontent, $etag);
|
||||
return "{$filename}.css";
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the correct etag for the specified configuration.
|
||||
*
|
||||
* @param string $themename The name of the theme
|
||||
* @param int $rev The revision number
|
||||
* @param string $type The requested sheet type
|
||||
* @param int $themesubrev The theme sub-revision
|
||||
* @param bool $usesvg Whether SVGs are allowed
|
||||
* @param int $chunk The chunk number if specified
|
||||
* @return string The etag to use for this request
|
||||
*/
|
||||
function theme_styles_get_etag($themename, $rev, $type, $themesubrev, $usesvg, $chunk) {
|
||||
$etag = [$rev, $themename, $type, $themesubrev];
|
||||
|
||||
if (!$usesvg) {
|
||||
$etag[] = 'nosvg';
|
||||
}
|
||||
|
||||
if ($chunk) {
|
||||
$etag[] = "chunk{$chunk}";
|
||||
}
|
||||
|
||||
return sha1(implode('/', $etag));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user