Merge branch 'w19_MDL-32683_m23_publiccache' of git://github.com/skodak/moodle
This commit is contained in:
+32
-1
@@ -108,4 +108,35 @@ function min_enable_zlib_compression() {
|
||||
@ini_set('zlib.output_compression', 65536);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the slashargument part of the URL.
|
||||
* Note: ".php" is NOT allowed in slasharguments!
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function min_get_slash_argument() {
|
||||
// Note: This code has to work in the same cases as normal get_slash_argument(),
|
||||
// but at the same time it may be simpler because we do not have to deal
|
||||
// with encodings and other tricky stuff.
|
||||
|
||||
$relativepath = '';
|
||||
|
||||
if (stripos($_SERVER['SERVER_SOFTWARE'], 'iis') !== false) {
|
||||
if (isset($_SERVER['PATH_INFO']) and $_SERVER['PATH_INFO'] !== '') {
|
||||
$relativepath = urldecode($_SERVER['PATH_INFO']);
|
||||
}
|
||||
} else {
|
||||
if (isset($_SERVER['PATH_INFO'])) {
|
||||
$relativepath = $_SERVER['PATH_INFO'];
|
||||
}
|
||||
}
|
||||
|
||||
$matches = null;
|
||||
if (preg_match('|^.+\.php(.*)$|i', $relativepath, $matches)) {
|
||||
$relativepath = $matches[1];
|
||||
}
|
||||
|
||||
return $relativepath;
|
||||
}
|
||||
|
||||
+45
-12
@@ -25,6 +25,8 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
//NOTE: do not verify MOODLE_INTERNAL here, this is used from themes too
|
||||
|
||||
/**
|
||||
* Stores CSS in a file at the given path.
|
||||
*
|
||||
@@ -85,21 +87,33 @@ function css_store_css(theme_config $theme, $csspath, array $cssfiles) {
|
||||
*
|
||||
* @param string $themename The name of the theme we are sending CSS for.
|
||||
* @param string $rev The revision to ensure we utilise the cache.
|
||||
* @param string $etag The revision to ensure we utilise the cache.
|
||||
* @param bool $slasharguments
|
||||
*/
|
||||
function css_send_ie_css($themename, $rev) {
|
||||
$lifetime = 60*60*24*30; // 30 days
|
||||
function css_send_ie_css($themename, $rev, $etag, $slasharguments) {
|
||||
global $CFG;
|
||||
|
||||
$lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often
|
||||
|
||||
$relroot = preg_replace('|^http.?://[^/]+|', '', $CFG->wwwroot);
|
||||
|
||||
$css = "/** Unfortunately IE6/7 does not support more than 4096 selectors in one CSS file, which means we have to use some ugly hacks :-( **/";
|
||||
$css .= "\n@import url(styles.php?theme=$themename&rev=$rev&type=plugins);";
|
||||
$css .= "\n@import url(styles.php?theme=$themename&rev=$rev&type=parents);";
|
||||
$css .= "\n@import url(styles.php?theme=$themename&rev=$rev&type=theme);";
|
||||
if ($slasharguments) {
|
||||
$css .= "\n@import url($relroot/styles.php/$themename/$rev/plugins);";
|
||||
$css .= "\n@import url($relroot/styles.php/$themename/$rev/parents);";
|
||||
$css .= "\n@import url($relroot/styles.php/$themename/$rev/theme);";
|
||||
} else {
|
||||
$css .= "\n@import url($relroot/styles.php?theme=$themename&rev=$rev&type=plugins);";
|
||||
$css .= "\n@import url($relroot/styles.php?theme=$themename&rev=$rev&type=parents);";
|
||||
$css .= "\n@import url($relroot/styles.php?theme=$themename&rev=$rev&type=theme);";
|
||||
}
|
||||
|
||||
header('Etag: '.md5($rev));
|
||||
header('Etag: '.$etag);
|
||||
header('Content-Disposition: inline; filename="styles.php"');
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Pragma: ');
|
||||
header('Cache-Control: max-age='.$lifetime);
|
||||
header('Cache-Control: public, max-age='.$lifetime);
|
||||
header('Accept-Ranges: none');
|
||||
header('Content-Type: text/css; charset=utf-8');
|
||||
header('Content-Length: '.strlen($css));
|
||||
@@ -115,16 +129,17 @@ function css_send_ie_css($themename, $rev) {
|
||||
* request, then optimised/minified, and finally cached for serving.
|
||||
*
|
||||
* @param string $csspath The path to the CSS file we want to serve.
|
||||
* @param string $rev The revision to make sure we utilise any caches.
|
||||
* @param string $etag The revision to make sure we utilise any caches.
|
||||
*/
|
||||
function css_send_cached_css($csspath, $rev) {
|
||||
$lifetime = 60*60*24*30; // 30 days
|
||||
function css_send_cached_css($csspath, $etag) {
|
||||
$lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often
|
||||
|
||||
header('Etag: '.$etag);
|
||||
header('Content-Disposition: inline; filename="styles.php"');
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($csspath)) .' GMT');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Pragma: ');
|
||||
header('Cache-Control: max-age='.$lifetime);
|
||||
header('Cache-Control: public, max-age='.$lifetime);
|
||||
header('Accept-Ranges: none');
|
||||
header('Content-Type: text/css; charset=utf-8');
|
||||
if (!min_enable_zlib_compression()) {
|
||||
@@ -176,6 +191,24 @@ function css_send_uncached_css($css) {
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send file not modified headers
|
||||
* @param int $lastmodified
|
||||
* @param string $etag
|
||||
*/
|
||||
function css_send_unmodified($lastmodified, $etag) {
|
||||
$lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often
|
||||
header('HTTP/1.1 304 Not Modified');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Cache-Control: public, max-age='.$lifetime);
|
||||
header('Content-Type: text/css; charset=utf-8');
|
||||
header('Etag: '.$etag);
|
||||
if ($lastmodified) {
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
|
||||
}
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a 404 message about CSS not being found.
|
||||
*/
|
||||
@@ -3630,4 +3663,4 @@ class css_style_paddingleft extends css_style_padding {
|
||||
public function consolidate_to() {
|
||||
return 'padding';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,14 +33,15 @@ M.str = M.str || {};
|
||||
* @return {String}
|
||||
*/
|
||||
M.util.image_url = function(imagename, component) {
|
||||
var url = M.cfg.wwwroot + '/theme/image.php?theme=' + M.cfg.theme + '&image=' + imagename;
|
||||
|
||||
if (M.cfg.themerev > 0) {
|
||||
url = url + '&rev=' + M.cfg.themerev;
|
||||
if (component == '' || component == 'moodle' || component == 'core') {
|
||||
component = 'core';
|
||||
}
|
||||
|
||||
if (component && component != '' && component != 'moodle' && component != 'core') {
|
||||
url = url + '&component=' + component;
|
||||
if (M.cfg.themerev > 0 && M.cfg.slasharguments == 1) {
|
||||
var url = M.cfg.wwwroot + '/theme/image.php/' + M.cfg.theme + '/' + component + '/' + M.cfg.themerev + '/' + imagename;
|
||||
} else {
|
||||
var url = M.cfg.wwwroot + '/theme/image.php?theme=' + M.cfg.theme + '&component=' + component + '&rev=' + M.cfg.themerev + '&image=' + imagename;
|
||||
}
|
||||
|
||||
return url;
|
||||
|
||||
+38
-51
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@@ -18,8 +17,7 @@
|
||||
/**
|
||||
* This file is serving optimised JS
|
||||
*
|
||||
* @package core
|
||||
* @subpackage lib
|
||||
* @package core_lib
|
||||
* @copyright 2010 Petr Skoda (skodak)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@@ -31,15 +29,22 @@ define('NO_DEBUG_DISPLAY', true);
|
||||
// we need just the values from config.php and minlib.php
|
||||
define('ABORT_AFTER_CONFIG', true);
|
||||
require('../config.php'); // this stops immediately at the beginning of lib/setup.php
|
||||
require_once("$CFG->dirroot/lib/jslib.php");
|
||||
|
||||
ini_set('zlib.output_compression', 'Off');
|
||||
if ($slashargument = min_get_slash_argument()) {
|
||||
$slashargument = ltrim($slashargument, '/');
|
||||
if (substr_count($slashargument, '/') < 1) {
|
||||
image_not_found();
|
||||
}
|
||||
// image must be last because it may contain "/"
|
||||
list($rev, $file) = explode('/', $slashargument, 2);
|
||||
$rev = min_clean_param($rev, 'INT');
|
||||
$file = '/'.min_clean_param($file, 'SAFEPATH');
|
||||
|
||||
// setup include path
|
||||
set_include_path($CFG->libdir . '/minify/lib' . PATH_SEPARATOR . get_include_path());
|
||||
require_once('Minify.php');
|
||||
|
||||
$file = min_optional_param('file', '', 'RAW');
|
||||
$rev = min_optional_param('rev', 0, 'INT');
|
||||
} else {
|
||||
$rev = min_optional_param('rev', 0, 'INT');
|
||||
$file = min_optional_param('file', '', 'RAW');
|
||||
}
|
||||
|
||||
// some security first - pick only files with .js extension in dirroot
|
||||
$jsfiles = array();
|
||||
@@ -70,50 +75,32 @@ if (!$jsfiles) {
|
||||
die();
|
||||
}
|
||||
|
||||
minify($jsfiles);
|
||||
$etag = sha1($rev.implode(',', $jsfiles));
|
||||
$candidate = $CFG->cachedir.'/js/'.$etag;
|
||||
|
||||
function minify($files) {
|
||||
global $CFG;
|
||||
if ($rev > -1) {
|
||||
if (file_exists($candidate)) {
|
||||
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 parameter
|
||||
js_send_unmodified(filemtime($candidate), $etag);
|
||||
}
|
||||
js_send_cached($candidate, $etag);
|
||||
|
||||
$cachedir = $CFG->cachedir.'/js';
|
||||
// make sure the cache dir exist
|
||||
if (!file_exists($cachedir)) {
|
||||
@mkdir($cachedir, $CFG->directorypermissions, true);
|
||||
} else {
|
||||
if (!file_exists(dirname($candidate))) {
|
||||
@mkdir(dirname($candidate), $CFG->directorypermissions, true);
|
||||
}
|
||||
$fp = fopen($candidate, 'w');
|
||||
fwrite($fp, js_minify($jsfiles));
|
||||
fclose($fp);
|
||||
js_send_cached($candidate, $etag);
|
||||
}
|
||||
|
||||
if (0 === stripos(PHP_OS, 'win')) {
|
||||
Minify::setDocRoot(); // IIS may need help
|
||||
}
|
||||
Minify::setCache($cachedir, true);
|
||||
|
||||
$options = array(
|
||||
// Maximum age to cache
|
||||
'maxAge' => (60*60*24*20),
|
||||
// The files to minify
|
||||
'files' => $files
|
||||
);
|
||||
|
||||
try {
|
||||
Minify::serve('Files', $options);
|
||||
die();
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
$error = str_replace("\r", ' ', $error);
|
||||
$error = str_replace("\n", ' ', $error);
|
||||
}
|
||||
|
||||
// minification failed - try to inform the developer and include the non-minified version
|
||||
$js = <<<EOD
|
||||
try {console.log('Error: Minimisation of javascript failed!');} catch (e) {}
|
||||
|
||||
// Error: $error
|
||||
// Problem detected during javascript minimisation, please review the following code
|
||||
// =================================================================================
|
||||
|
||||
|
||||
EOD;
|
||||
echo $js;
|
||||
foreach ($files as $jsfile) {
|
||||
echo file_get_contents($jsfile)."\n";
|
||||
} else {
|
||||
$content = '';
|
||||
foreach ($jsfiles as $jsfile) {
|
||||
$content .= file_get_contents($jsfile)."\n";
|
||||
}
|
||||
js_send_uncached($candidate, $etag);
|
||||
}
|
||||
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This file contains various javascript related functions,
|
||||
* all functions here are self contained and can be used in ABORT_AFTER_CONFIG scripts.
|
||||
*
|
||||
* @package core_lib
|
||||
* @copyright 2012 Petr Skoda (skodak) {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
//NOTE: do not verify MOODLE_INTERNAL here, this is used from themes too
|
||||
|
||||
/**
|
||||
* Send javascript file content with as much caching as possible
|
||||
* @param string $jspath
|
||||
* @param string $etag
|
||||
* @param string $filename
|
||||
*/
|
||||
function js_send_cached($jspath, $etag, $filename = 'javascript.php') {
|
||||
require(__DIR__ . '/xsendfilelib.php');
|
||||
|
||||
$lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often
|
||||
|
||||
header('Etag: '.$etag);
|
||||
header('Content-Disposition: inline; filename="'.$filename.'"');
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($jspath)) .' GMT');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Pragma: ');
|
||||
header('Cache-Control: public, max-age='.$lifetime);
|
||||
header('Accept-Ranges: none');
|
||||
header('Content-Type: application/javascript; charset=utf-8');
|
||||
|
||||
if (xsendfile($jspath)) {
|
||||
die;
|
||||
}
|
||||
|
||||
if (!min_enable_zlib_compression()) {
|
||||
header('Content-Length: '.filesize($jspath));
|
||||
}
|
||||
|
||||
readfile($jspath);
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send javascript without any caching
|
||||
* @param string $js
|
||||
* @param string $filename
|
||||
*/
|
||||
function js_send_uncached($js, $filename = 'javascript.php') {
|
||||
header('Content-Disposition: inline; filename="'.$filename.'"');
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + 2) .' GMT');
|
||||
header('Pragma: ');
|
||||
header('Accept-Ranges: none');
|
||||
header('Content-Type: application/javascript; charset=utf-8');
|
||||
header('Content-Length: '.strlen($js));
|
||||
|
||||
echo $js;
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send file not modified headers
|
||||
* @param int $lastmodified
|
||||
* @param string $etag
|
||||
*/
|
||||
function js_send_unmodified($lastmodified, $etag) {
|
||||
$lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often
|
||||
header('HTTP/1.1 304 Not Modified');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Cache-Control: public, max-age='.$lifetime);
|
||||
header('Content-Type: application/javascript; charset=utf-8');
|
||||
header('Etag: '.$etag);
|
||||
if ($lastmodified) {
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
|
||||
}
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minify javascript files
|
||||
* @param array $files
|
||||
* @return string
|
||||
*/
|
||||
function js_minify($files) {
|
||||
// setup include path
|
||||
set_include_path(__DIR__ . '/minify/lib' . PATH_SEPARATOR . get_include_path());
|
||||
require_once('Minify.php');
|
||||
|
||||
if (empty($files)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (0 === stripos(PHP_OS, 'win')) {
|
||||
Minify::setDocRoot(); // IIS may need help
|
||||
}
|
||||
// disable all caching, we do it in moodle
|
||||
Minify::setCache(null, false);
|
||||
|
||||
$options = array(
|
||||
'bubbleCssImports' => false,
|
||||
// Don't gzip content we just want text for storage
|
||||
'encodeOutput' => false,
|
||||
// Maximum age to cache, not used but required
|
||||
'maxAge' => 1800,
|
||||
// The files to minify
|
||||
'files' => $files,
|
||||
// Turn orr URI rewriting
|
||||
'rewriteCssUris' => false,
|
||||
// This returns the CSS rather than echoing it for display
|
||||
'quiet' => true
|
||||
);
|
||||
|
||||
$error = 'unknown';
|
||||
try {
|
||||
$result = Minify::serve('Files', $options);
|
||||
if ($result['success']) {
|
||||
return $result['content'];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
$error = str_replace("\r", ' ', $error);
|
||||
$error = str_replace("\n", ' ', $error);
|
||||
}
|
||||
|
||||
// minification failed - try to inform the theme developer and include the non-minified version
|
||||
$js = <<<EOD
|
||||
try {console.log('Error: Minimisation of javascript failed!');} catch (e) {}
|
||||
|
||||
// Error: $error
|
||||
// Problem detected during javascript minimisation, please review the following code
|
||||
// =================================================================================
|
||||
|
||||
|
||||
EOD;
|
||||
foreach ($files as $jsfile) {
|
||||
$js .= file_get_contents($jsfile)."\n";
|
||||
}
|
||||
return $js;
|
||||
}
|
||||
+39
-11
@@ -518,8 +518,14 @@ class theme_config {
|
||||
$rev = theme_get_revision();
|
||||
|
||||
if ($rev > -1) {
|
||||
$params = array('theme'=>$this->name,'rev'=>$rev, 'type'=>'editor');
|
||||
return new moodle_url($CFG->httpswwwroot.'/theme/styles.php', $params);
|
||||
if (!empty($CFG->slasharguments)) {
|
||||
$url = new moodle_url("$CFG->httpswwwroot/theme/styles.php");
|
||||
$url->set_slashargument('/'.$this->name.'/'.$rev.'/editor', 'noparam', true);
|
||||
return $url;
|
||||
} else {
|
||||
$params = array('theme'=>$this->name,'rev'=>$rev, 'type'=>'editor');
|
||||
return new moodle_url($CFG->httpswwwroot.'/theme/styles.php', $params);
|
||||
}
|
||||
} else {
|
||||
$params = array('theme'=>$this->name, 'type'=>'editor');
|
||||
return new moodle_url($CFG->httpswwwroot.'/theme/styles_debug.php', $params);
|
||||
@@ -589,7 +595,13 @@ class theme_config {
|
||||
$urls[] = new moodle_url($CFG->httpswwwroot.'/theme/styles.php', array('theme'=>$this->name,'rev'=>$rev, 'type'=>'parents'));
|
||||
$urls[] = new moodle_url($CFG->httpswwwroot.'/theme/styles.php', array('theme'=>$this->name,'rev'=>$rev, 'type'=>'theme'));
|
||||
} else {
|
||||
$urls[] = new moodle_url($CFG->httpswwwroot.'/theme/styles.php', array('theme'=>$this->name,'rev'=>$rev));
|
||||
if (!empty($CFG->slasharguments)) {
|
||||
$url = new moodle_url("$CFG->httpswwwroot/theme/styles.php");
|
||||
$url->set_slashargument('/'.$this->name.'/'.$rev.'/all', 'noparam', true);
|
||||
$urls[] = $url;
|
||||
} else {
|
||||
$urls[] = new moodle_url($CFG->httpswwwroot.'/theme/styles.php', array('theme'=>$this->name,'rev'=>$rev, 'type'=>'all'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// find out the current CSS and cache it now for 5 seconds
|
||||
@@ -763,7 +775,13 @@ class theme_config {
|
||||
$params = array('theme'=>$this->name,'rev'=>$rev);
|
||||
$params['type'] = $inhead ? 'head' : 'footer';
|
||||
|
||||
return new moodle_url($CFG->httpswwwroot.'/theme/javascript.php', $params);
|
||||
if (!empty($CFG->slasharguments) and $rev > 0) {
|
||||
$url = new moodle_url("$CFG->httpswwwroot/theme/javascript.php");
|
||||
$url->set_slashargument('/'.$this->name.'/'.$rev.'/'.$params['type'], 'noparam', true);
|
||||
return $url;
|
||||
} else {
|
||||
return new moodle_url($CFG->httpswwwroot.'/theme/javascript.php', $params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -872,8 +890,6 @@ class theme_config {
|
||||
* @return string The processed CSS.
|
||||
*/
|
||||
public function post_process($css) {
|
||||
global $CFG;
|
||||
|
||||
// now resolve all image locations
|
||||
if (preg_match_all('/\[\[pix:([a-z_]+\|)?([^\]]+)\]\]/', $css, $matches, PREG_SET_ORDER)) {
|
||||
$replaced = array();
|
||||
@@ -886,7 +902,7 @@ class theme_config {
|
||||
$component = rtrim($match[1], '|');
|
||||
$imageurl = $this->pix_url($imagename, $component)->out(false);
|
||||
// we do not need full url because the image.php is always in the same dir
|
||||
$imageurl = str_replace("$CFG->httpswwwroot/theme/", '', $imageurl);
|
||||
$imageurl = preg_replace('|^http.?://[^/]+|', '', $imageurl);
|
||||
$css = str_replace($match[0], $imageurl, $css);
|
||||
}
|
||||
}
|
||||
@@ -910,17 +926,29 @@ class theme_config {
|
||||
public function pix_url($imagename, $component) {
|
||||
global $CFG;
|
||||
|
||||
$params = array('theme'=>$this->name, 'image'=>$imagename);
|
||||
$params = array('theme'=>$this->name);
|
||||
|
||||
if (empty($component) or $component === 'moodle' or $component === 'core') {
|
||||
$params['component'] = 'core';
|
||||
} else {
|
||||
$params['component'] = $component;
|
||||
}
|
||||
|
||||
$rev = theme_get_revision();
|
||||
if ($rev != -1) {
|
||||
$params['rev'] = $rev;
|
||||
}
|
||||
if (!empty($component) and $component !== 'moodle'and $component !== 'core') {
|
||||
$params['component'] = $component;
|
||||
|
||||
$params['image'] = $imagename;
|
||||
|
||||
if (!empty($CFG->slasharguments) and $rev > 0) {
|
||||
$url = new moodle_url("$CFG->httpswwwroot/theme/image.php");
|
||||
$url->set_slashargument('/'.$params['theme'].'/'.$params['component'].'/'.$params['rev'].'/'.$params['image'], 'noparam', true);
|
||||
} else {
|
||||
$url = new moodle_url("$CFG->httpswwwroot/theme/image.php", $params);
|
||||
}
|
||||
|
||||
return new moodle_url("$CFG->httpswwwroot/theme/image.php", $params);
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -152,6 +152,8 @@ class page_requirements_manager {
|
||||
public function __construct() {
|
||||
global $CFG;
|
||||
|
||||
$sep = empty($CFG->slasharguments) ? '?' : '/';
|
||||
|
||||
require_once("$CFG->libdir/yui/phploader/phploader/loader.php");
|
||||
|
||||
$this->yui3loader = new stdClass();
|
||||
@@ -174,8 +176,8 @@ class page_requirements_manager {
|
||||
} else {
|
||||
$this->yui3loader->base = $CFG->httpswwwroot . '/lib/yui/'. $CFG->yui3version . '/build/';
|
||||
$this->yui2loader->base = $CFG->httpswwwroot . '/lib/yui/'. $CFG->yui2version . '/build/';
|
||||
$this->yui3loader->comboBase = $CFG->httpswwwroot . '/theme/yui_combo.php?';
|
||||
$this->yui2loader->comboBase = $CFG->httpswwwroot . '/theme/yui_combo.php?';
|
||||
$this->yui3loader->comboBase = $CFG->httpswwwroot . '/theme/yui_combo.php'.$sep;
|
||||
$this->yui2loader->comboBase = $CFG->httpswwwroot . '/theme/yui_combo.php'.$sep;
|
||||
}
|
||||
|
||||
// enable combo loader? this significantly helps with caching and performance!
|
||||
@@ -201,8 +203,8 @@ class page_requirements_manager {
|
||||
$this->M_yui_loader->groups = array(
|
||||
'moodle' => array(
|
||||
'name' => 'moodle',
|
||||
'base' => $CFG->httpswwwroot . '/theme/yui_combo.php?moodle/'.$jsrev.'/',
|
||||
'comboBase' => $CFG->httpswwwroot . '/theme/yui_combo.php?',
|
||||
'base' => $CFG->httpswwwroot . '/theme/yui_combo.php'.$sep.'moodle/'.$jsrev.'/',
|
||||
'comboBase' => $CFG->httpswwwroot . '/theme/yui_combo.php'.$sep,
|
||||
'combine' => $this->yui3loader->combine,
|
||||
'filter' => '',
|
||||
'ext' => false,
|
||||
@@ -218,7 +220,7 @@ class page_requirements_manager {
|
||||
'local' => array(
|
||||
'name' => 'gallery',
|
||||
'base' => $CFG->wwwroot.'/lib/yui/gallery/',
|
||||
'comboBase' => $CFG->httpswwwroot . '/theme/yui_combo.php?',
|
||||
'comboBase' => $CFG->httpswwwroot . '/theme/yui_combo.php'.$sep,
|
||||
'combine' => $this->yui3loader->combine,
|
||||
'filter' => $this->M_yui_loader->filter,
|
||||
'ext' => false,
|
||||
@@ -316,6 +318,7 @@ class page_requirements_manager {
|
||||
'sesskey' => sesskey(),
|
||||
'loadingicon' => $renderer->pix_url('i/loading_small', 'moodle')->out(false),
|
||||
'themerev' => theme_get_revision(),
|
||||
'slasharguments' => (int)(!empty($CFG->slasharguments)),
|
||||
'theme' => $page->theme->name,
|
||||
'jsrev' => ((empty($CFG->cachejs) or empty($CFG->jsrev)) ? -1 : $CFG->jsrev),
|
||||
);
|
||||
@@ -401,8 +404,14 @@ class page_requirements_manager {
|
||||
throw new coding_exception('Attempt to require a JavaScript file that does not exist.', $url);
|
||||
}
|
||||
}
|
||||
if (!empty($CFG->cachejs) and !empty($CFG->jsrev) and strpos($url, '/lib/editor/') !== 0 and substr($url, -3) === '.js') {
|
||||
return new moodle_url($CFG->httpswwwroot.'/lib/javascript.php', array('file'=>$url, 'rev'=>$CFG->jsrev));
|
||||
if (!empty($CFG->cachejs) and !empty($CFG->jsrev) and $CFG->jsrev > 0 and strpos($url, '/lib/editor/') !== 0 and substr($url, -3) === '.js') {
|
||||
if (empty($CFG->slasharguments)) {
|
||||
return new moodle_url($CFG->httpswwwroot.'/lib/javascript.php', array('rev'=>$CFG->jsrev, 'file'=>$url));
|
||||
} else {
|
||||
$returnurl = new moodle_url($CFG->httpswwwroot.'/lib/javascript.php');
|
||||
$returnurl->set_slashargument('/'.$CFG->jsrev.$url);
|
||||
return $returnurl;
|
||||
}
|
||||
} else {
|
||||
return new moodle_url($CFG->httpswwwroot.$url);
|
||||
}
|
||||
|
||||
+35
-14
@@ -32,12 +32,30 @@ define('NO_DEBUG_DISPLAY', true);
|
||||
define('ABORT_AFTER_CONFIG', true);
|
||||
require('../config.php'); // this stops immediately at the beginning of lib/setup.php
|
||||
|
||||
$themename = min_optional_param('theme', 'standard', 'SAFEDIR');
|
||||
$component = min_optional_param('component', 'moodle', 'SAFEDIR');
|
||||
$image = min_optional_param('image', '', 'SAFEPATH');
|
||||
$rev = min_optional_param('rev', -1, 'INT');
|
||||
if ($slashargument = min_get_slash_argument()) {
|
||||
$slashargument = ltrim($slashargument, '/');
|
||||
if (substr_count($slashargument, '/') < 3) {
|
||||
image_not_found();
|
||||
}
|
||||
// image must be last because it may contain "/"
|
||||
list($themename, $component, $rev, $image) = explode('/', $slashargument, 4);
|
||||
$themename = min_clean_param($themename, 'SAFEDIR');
|
||||
$component = min_clean_param($component, 'SAFEDIR');
|
||||
$rev = min_clean_param($rev, 'INT');
|
||||
$image = min_clean_param($image, 'SAFEPATH');
|
||||
|
||||
if (empty($component) or empty($image)) {
|
||||
} else {
|
||||
$themename = min_optional_param('theme', 'standard', 'SAFEDIR');
|
||||
$component = min_optional_param('component', 'core', 'SAFEDIR');
|
||||
$rev = min_optional_param('rev', -1, 'INT');
|
||||
$image = min_optional_param('image', '', 'SAFEPATH');
|
||||
}
|
||||
|
||||
if (empty($component) or $component === 'moodle' or $component === 'core') {
|
||||
$component = 'moodle';
|
||||
}
|
||||
|
||||
if (empty($image)) {
|
||||
image_not_found();
|
||||
}
|
||||
|
||||
@@ -50,6 +68,7 @@ if (file_exists("$CFG->dirroot/theme/$themename/config.php")) {
|
||||
}
|
||||
|
||||
$candidatelocation = "$CFG->cachedir/theme/$themename/pix/$component";
|
||||
$etag = sha1("$themename/$component/$rev/$image");
|
||||
|
||||
if ($rev > -1) {
|
||||
if (file_exists("$candidatelocation/$image.error")) {
|
||||
@@ -78,15 +97,16 @@ if ($rev > -1) {
|
||||
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 parameter
|
||||
$lifetime = 60*60*24*30; // 30 days
|
||||
$lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often
|
||||
$mimetype = get_contenttype_from_ext($ext);
|
||||
header('HTTP/1.1 304 Not Modified');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Cache-Control: max-age='.$lifetime);
|
||||
header('Cache-Control: public, max-age='.$lifetime);
|
||||
header('Content-Type: '.$mimetype);
|
||||
header('Etag: '.$etag);
|
||||
die;
|
||||
}
|
||||
send_cached_image($cacheimage, $rev);
|
||||
send_cached_image($cacheimage, $etag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +123,7 @@ $theme = theme_config::load($themename);
|
||||
$imagefile = $theme->resolve_image_location($image, $component);
|
||||
|
||||
$rev = theme_get_revision();
|
||||
$etag = sha1("$themename/$component/$rev/$image");
|
||||
|
||||
if (empty($imagefile) or !is_readable($imagefile)) {
|
||||
if ($rev > -1) {
|
||||
@@ -126,7 +147,7 @@ if ($rev > -1) {
|
||||
}
|
||||
copy($imagefile, $cacheimage);
|
||||
}
|
||||
send_cached_image($cacheimage, $rev);
|
||||
send_cached_image($cacheimage, $etag);
|
||||
|
||||
} else {
|
||||
send_uncached_image($imagefile);
|
||||
@@ -138,22 +159,22 @@ if ($rev > -1) {
|
||||
// we are not using filelib because we need to fine tune all header
|
||||
// parameters to get the best performance.
|
||||
|
||||
function send_cached_image($imagepath, $rev) {
|
||||
function send_cached_image($imagepath, $etag) {
|
||||
global $CFG;
|
||||
require("$CFG->dirroot/lib/xsendfilelib.php");
|
||||
|
||||
$lifetime = 60*60*24*30; // 30 days
|
||||
$lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often
|
||||
$pathinfo = pathinfo($imagepath);
|
||||
$imagename = $pathinfo['filename'].'.'.$pathinfo['extension'];
|
||||
|
||||
$mimetype = get_contenttype_from_ext($pathinfo['extension']);
|
||||
|
||||
header('Etag: '.md5("$rev/$imagepath"));
|
||||
header('Etag: '.$etag);
|
||||
header('Content-Disposition: inline; filename="'.$imagename.'"');
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($imagepath)) .' GMT');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Pragma: ');
|
||||
header('Cache-Control: max-age='.$lifetime);
|
||||
header('Cache-Control: public, max-age='.$lifetime);
|
||||
header('Accept-Ranges: none');
|
||||
header('Content-Type: '.$mimetype);
|
||||
header('Content-Length: '.filesize($imagepath));
|
||||
|
||||
+28
-115
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@@ -18,7 +17,7 @@
|
||||
/**
|
||||
* This file is responsible for serving the one huge CSS of each theme.
|
||||
*
|
||||
* @package moodlecore
|
||||
* @package core
|
||||
* @copyright 2009 Petr Skoda (skodak) {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@@ -31,10 +30,24 @@ define('NO_DEBUG_DISPLAY', true);
|
||||
// we need just the values from config.php and minlib.php
|
||||
define('ABORT_AFTER_CONFIG', true);
|
||||
require('../config.php'); // this stops immediately at the beginning of lib/setup.php
|
||||
require_once("$CFG->dirroot/lib/jslib.php");
|
||||
|
||||
$themename = min_optional_param('theme', 'standard', 'SAFEDIR');
|
||||
$rev = min_optional_param('rev', 0, 'INT');
|
||||
$type = min_optional_param('type', 'head', 'RAW');
|
||||
if ($slashargument = min_get_slash_argument()) {
|
||||
$slashargument = ltrim($slashargument, '/');
|
||||
if (substr_count($slashargument, '/') < 2) {
|
||||
image_not_found();
|
||||
}
|
||||
// image must be last because it may contain "/"
|
||||
list($themename, $rev, $type) = explode('/', $slashargument, 3);
|
||||
$themename = min_clean_param($themename, 'SAFEDIR');
|
||||
$rev = min_clean_param($rev, 'INT');
|
||||
$type = min_clean_param($type, 'SAFEDIR');
|
||||
|
||||
} else {
|
||||
$themename = min_optional_param('theme', 'standard', 'SAFEDIR');
|
||||
$rev = min_optional_param('rev', 0, 'INT');
|
||||
$type = min_optional_param('type', 'head', 'RAW');
|
||||
}
|
||||
|
||||
if ($type !== 'head' and $type !== 'footer') {
|
||||
header('HTTP/1.0 404 not found');
|
||||
@@ -51,19 +64,15 @@ if (file_exists("$CFG->dirroot/theme/$themename/config.php")) {
|
||||
}
|
||||
|
||||
$candidate = "$CFG->cachedir/theme/$themename/javascript_$type.js";
|
||||
$etag = sha1("$themename/$rev/$type");
|
||||
|
||||
if ($rev > -1 and file_exists($candidate)) {
|
||||
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 parameter
|
||||
$lifetime = 60*60*24*30; // 30 days
|
||||
header('HTTP/1.1 304 Not Modified');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Cache-Control: max-age='.$lifetime);
|
||||
header('Content-Type: application/javascript; charset=utf-8');
|
||||
die;
|
||||
js_send_unmodified(filemtime($candidate), $etag);
|
||||
}
|
||||
send_cached_js($candidate, $rev);
|
||||
js_send_cached($candidate, $etag);
|
||||
}
|
||||
|
||||
//=================================================================================
|
||||
@@ -74,118 +83,22 @@ define('NO_MOODLE_COOKIES', true); // Session not used here
|
||||
define('NO_UPGRADE_CHECK', true); // Ignore upgrade check
|
||||
|
||||
require("$CFG->dirroot/lib/setup.php");
|
||||
// setup include path
|
||||
set_include_path($CFG->libdir . '/minify/lib' . PATH_SEPARATOR . get_include_path());
|
||||
require_once('Minify.php');
|
||||
|
||||
$theme = theme_config::load($themename);
|
||||
|
||||
$rev = theme_get_revision();
|
||||
$etag = sha1("$themename/$rev/$type");
|
||||
|
||||
if ($rev > -1) {
|
||||
// note: cache reset might have purged our cache dir structure,
|
||||
// make sure we do not use stale file stat cache in the next check_dir_exists()
|
||||
clearstatcache();
|
||||
check_dir_exists(dirname($candidate));
|
||||
$fp = fopen($candidate, 'w');
|
||||
fwrite($fp, minify($theme->javascript_files($type)));
|
||||
fwrite($fp, js_minify($theme->javascript_files($type)));
|
||||
fclose($fp);
|
||||
send_cached_js($candidate);
|
||||
js_send_cached($candidate, $etag);
|
||||
|
||||
} else {
|
||||
send_uncached_js($theme->javascript_content($type));
|
||||
}
|
||||
|
||||
//=================================================================================
|
||||
//=== utility functions ==
|
||||
// we are not using filelib because we need to fine tune all header
|
||||
// parameters to get the best performance.
|
||||
|
||||
function send_cached_js($jspath) {
|
||||
global $CFG;
|
||||
require("$CFG->dirroot/lib/xsendfilelib.php");
|
||||
|
||||
$lifetime = 60*60*24*30; // 30 days
|
||||
|
||||
header('Content-Disposition: inline; filename="javascript.php"');
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($jspath)) .' GMT');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Pragma: ');
|
||||
header('Cache-Control: max-age='.$lifetime);
|
||||
header('Accept-Ranges: none');
|
||||
header('Content-Type: application/javascript; charset=utf-8');
|
||||
|
||||
if (xsendfile($jspath)) {
|
||||
die;
|
||||
}
|
||||
|
||||
if (!min_enable_zlib_compression()) {
|
||||
header('Content-Length: '.filesize($jspath));
|
||||
}
|
||||
|
||||
readfile($jspath);
|
||||
die;
|
||||
}
|
||||
|
||||
function send_uncached_js($js) {
|
||||
header('Content-Disposition: inline; filename="javascript.php"');
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + 2) .' GMT');
|
||||
header('Pragma: ');
|
||||
header('Accept-Ranges: none');
|
||||
header('Content-Type: application/javascript; charset=utf-8');
|
||||
header('Content-Length: '.strlen($js));
|
||||
|
||||
echo $js;
|
||||
die;
|
||||
}
|
||||
|
||||
function minify($files) {
|
||||
if (empty($files)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (0 === stripos(PHP_OS, 'win')) {
|
||||
Minify::setDocRoot(); // IIS may need help
|
||||
}
|
||||
// disable all caching, we do it in moodle
|
||||
Minify::setCache(null, false);
|
||||
|
||||
$options = array(
|
||||
'bubbleCssImports' => false,
|
||||
// Don't gzip content we just want text for storage
|
||||
'encodeOutput' => false,
|
||||
// Maximum age to cache, not used but required
|
||||
'maxAge' => 1800,
|
||||
// The files to minify
|
||||
'files' => $files,
|
||||
// Turn orr URI rewriting
|
||||
'rewriteCssUris' => false,
|
||||
// This returns the CSS rather than echoing it for display
|
||||
'quiet' => true
|
||||
);
|
||||
|
||||
$error = 'unknown';
|
||||
try {
|
||||
$result = Minify::serve('Files', $options);
|
||||
if ($result['success']) {
|
||||
return $result['content'];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
$error = str_replace("\r", ' ', $error);
|
||||
$error = str_replace("\n", ' ', $error);
|
||||
}
|
||||
|
||||
// minification failed - try to inform the theme developer and include the non-minified version
|
||||
$js = <<<EOD
|
||||
try {console.log('Error: Minimisation of theme javascript failed!');} catch (e) {}
|
||||
|
||||
// Error: $error
|
||||
// Problem detected during javascript minimisation, please review the following code
|
||||
// =================================================================================
|
||||
|
||||
|
||||
EOD;
|
||||
foreach ($files as $jsfile) {
|
||||
$js .= file_get_contents($jsfile)."\n";
|
||||
}
|
||||
return $js;
|
||||
js_send_uncached($theme->javascript_content($type));
|
||||
}
|
||||
|
||||
+24
-12
@@ -33,9 +33,22 @@ define('ABORT_AFTER_CONFIG', true);
|
||||
require('../config.php'); // this stops immediately at the beginning of lib/setup.php
|
||||
require_once($CFG->dirroot.'/lib/csslib.php');
|
||||
|
||||
$themename = min_optional_param('theme', 'standard', 'SAFEDIR');
|
||||
$type = min_optional_param('type', 'all', 'SAFEDIR');
|
||||
$rev = min_optional_param('rev', 0, 'INT');
|
||||
if ($slashargument = min_get_slash_argument()) {
|
||||
$slashargument = ltrim($slashargument, '/');
|
||||
if (substr_count($slashargument, '/') < 2) {
|
||||
image_not_found();
|
||||
}
|
||||
// image must be last because it may contain "/"
|
||||
list($themename, $rev, $type) = explode('/', $slashargument, 3);
|
||||
$themename = min_clean_param($themename, 'SAFEDIR');
|
||||
$rev = min_clean_param($rev, 'INT');
|
||||
$type = min_clean_param($type, 'SAFEDIR');
|
||||
|
||||
} else {
|
||||
$themename = min_optional_param('theme', 'standard', 'SAFEDIR');
|
||||
$rev = min_optional_param('rev', 0, 'INT');
|
||||
$type = min_optional_param('type', 'all', 'SAFEDIR');
|
||||
}
|
||||
|
||||
if (!in_array($type, array('all', 'ie', 'editor', 'plugins', 'parents', 'theme'))) {
|
||||
header('HTTP/1.0 404 not found');
|
||||
@@ -52,23 +65,19 @@ if (file_exists("$CFG->dirroot/theme/$themename/config.php")) {
|
||||
}
|
||||
|
||||
if ($type === 'ie') {
|
||||
css_send_ie_css($themename, $rev);
|
||||
css_send_ie_css($themename, $rev, $etag, !empty($slashargument));
|
||||
}
|
||||
|
||||
$candidatesheet = "$CFG->cachedir/theme/$themename/css/$type.css";
|
||||
$etag = sha1("$themename/$rev/$type");
|
||||
|
||||
if (file_exists($candidatesheet)) {
|
||||
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 parameter
|
||||
$lifetime = 60*60*24*30; // 30 days
|
||||
header('HTTP/1.1 304 Not Modified');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Cache-Control: max-age='.$lifetime);
|
||||
header('Content-Type: text/css; charset=utf-8');
|
||||
die;
|
||||
css_send_unmodified(filemtime($candidatesheet), $etag);
|
||||
}
|
||||
css_send_cached_css($candidatesheet, $rev);
|
||||
css_send_cached_css($candidatesheet, $etag);
|
||||
}
|
||||
|
||||
//=================================================================================
|
||||
@@ -82,6 +91,9 @@ require("$CFG->dirroot/lib/setup.php");
|
||||
|
||||
$theme = theme_config::load($themename);
|
||||
|
||||
$rev = theme_get_revision();
|
||||
$etag = sha1("$themename/$rev/$type");
|
||||
|
||||
if ($type === 'editor') {
|
||||
$cssfiles = $theme->editor_css_files();
|
||||
css_store_css($theme, $candidatesheet, $cssfiles);
|
||||
@@ -106,4 +118,4 @@ if ($type === 'editor') {
|
||||
$cssfile = "$CFG->cachedir/theme/$themename/css/all.css";
|
||||
css_store_css($theme, $cssfile, $allfiles);
|
||||
}
|
||||
css_send_cached_css($candidatesheet, $rev);
|
||||
css_send_cached_css($candidatesheet, $etag);
|
||||
|
||||
+36
-13
@@ -33,10 +33,13 @@ define('ABORT_AFTER_CONFIG', true);
|
||||
require('../config.php'); // this stops immediately at the beginning of lib/setup.php
|
||||
|
||||
// get special url parameters
|
||||
if (!$parts = combo_params()) {
|
||||
|
||||
list($parts, $slasharguments) = combo_params();
|
||||
if (!$parts) {
|
||||
combo_not_found();
|
||||
}
|
||||
|
||||
$etag = sha1($parts);
|
||||
$parts = trim($parts, '&');
|
||||
|
||||
// find out what we are serving - only one type per request
|
||||
@@ -53,16 +56,18 @@ if (substr($parts, -3) === '.js') {
|
||||
// If-Modified-Since header, we can send back a 304 Not Modified since the
|
||||
// content never changes (the rev number is increased any time the content changes)
|
||||
if (strpos($parts, '/-1/') === false and (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']))) {
|
||||
$lifetime = 60*60*24*30; // 30 days
|
||||
$lifetime = 60*60*24*360; // 1 year, we do not change YUI versions often, there are a few custom yui modules
|
||||
header('HTTP/1.1 304 Not Modified');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Cache-Control: max-age='.$lifetime);
|
||||
header('Cache-Control: public, max-age='.$lifetime);
|
||||
header('Content-Type: '.$mimetype);
|
||||
header('Etag: '.$etag);
|
||||
die;
|
||||
}
|
||||
|
||||
$parts = explode('&', $parts);
|
||||
$cache = true;
|
||||
$lastmodified = 0;
|
||||
|
||||
foreach ($parts as $part) {
|
||||
if (empty($part)) {
|
||||
@@ -108,13 +113,20 @@ foreach ($parts as $part) {
|
||||
continue;
|
||||
}
|
||||
$filecontent = file_get_contents($contentfile);
|
||||
$fmodified = filemtime($contentfile);
|
||||
if ($fmodified > $lastmodified) {
|
||||
$lastmodified = $fmodified;
|
||||
}
|
||||
|
||||
$relroot = preg_replace('|^http.?://[^/]+|', '', $CFG->wwwroot);
|
||||
$sep = ($slasharguments ? '/' : '?file=');
|
||||
|
||||
if ($mimetype === 'text/css') {
|
||||
if ($version == 'moodle') {
|
||||
$filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/'.$frankenstyle.'/'.array_shift($bits).'/$1.$2', $filecontent);
|
||||
$filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', $relroot.'/theme/yui_image.php'.$sep.$version.'/'.$frankenstyle.'/'.array_shift($bits).'/$1.$2', $filecontent);
|
||||
} else if ($version == 'gallery') {
|
||||
// search for all images in gallery module CSS and serve them through the yui_image.php script
|
||||
$filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/'.$bits[0].'/'.$bits[1].'/$1.$2', $filecontent);
|
||||
$filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', $relroot.'/theme/yui_image.php'.$sep.$version.'/'.$bits[0].'/'.$bits[1].'/$1.$2', $filecontent);
|
||||
} else {
|
||||
// First we need to remove relative paths to images. These are used by YUI modules to make use of global assets.
|
||||
// I've added this as a separate regex so it can be easily removed once
|
||||
@@ -122,15 +134,19 @@ foreach ($parts as $part) {
|
||||
$filecontent = preg_replace('#(\.\./\.\./\.\./\.\./assets/skins/sam/)?([a-z0-9_-]+)\.(png|gif)#', '$2.$3', $filecontent);
|
||||
|
||||
// search for all images in yui2 CSS and serve them through the yui_image.php script
|
||||
$filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/$1.$2', $filecontent);
|
||||
$filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', $relroot.'/theme/yui_image.php'.$sep.$version.'/$1.$2', $filecontent);
|
||||
}
|
||||
}
|
||||
|
||||
$content .= $filecontent;
|
||||
}
|
||||
|
||||
if ($lastmodified == 0) {
|
||||
$lastmodified = time();
|
||||
}
|
||||
|
||||
if ($cache) {
|
||||
combo_send_cached($content, $mimetype);
|
||||
combo_send_cached($content, $mimetype, $etag, $lastmodified);
|
||||
} else {
|
||||
combo_send_uncached($content, $mimetype);
|
||||
}
|
||||
@@ -140,17 +156,20 @@ if ($cache) {
|
||||
* Send the JavaScript cached
|
||||
* @param string $content
|
||||
* @param string $mimetype
|
||||
* @param string $etag
|
||||
* @param int $lastmodified
|
||||
*/
|
||||
function combo_send_cached($content, $mimetype) {
|
||||
$lifetime = 60*60*24*30; // 30 days
|
||||
function combo_send_cached($content, $mimetype, $etag, $lastmodified) {
|
||||
$lifetime = 60*60*24*360; // 1 year, we do not change YUI versions often, there are a few custom yui modules
|
||||
|
||||
header('Content-Disposition: inline; filename="combo"');
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Pragma: ');
|
||||
header('Cache-Control: max-age='.$lifetime);
|
||||
header('Accept-Ranges: none');
|
||||
header('Content-Type: '.$mimetype);
|
||||
header('Etag: '.$etag);
|
||||
if (!min_enable_zlib_compression()) {
|
||||
header('Content-Length: '.strlen($content));
|
||||
}
|
||||
@@ -193,10 +212,14 @@ function combo_params() {
|
||||
// note: buggy or misconfigured IIS does return the query string in REQUEST_URL
|
||||
if (isset($_SERVER['REQUEST_URI']) and strpos($_SERVER['REQUEST_URI'], '?') !== false) {
|
||||
$parts = explode('?', $_SERVER['REQUEST_URI'], 2);
|
||||
return $parts[1];
|
||||
return array($parts[1], false);
|
||||
|
||||
} else if (isset($_SERVER['QUERY_STRING'])) {
|
||||
return $_SERVER['QUERY_STRING'];
|
||||
} else if (isset($_SERVER['QUERY_STRING']) and strpos($_SERVER['QUERY_STRING'], '?') !== false) {
|
||||
return array($_SERVER['QUERY_STRING'], false);
|
||||
|
||||
} else if ($slashargument = min_get_slash_argument()) {
|
||||
$slashargument = ltrim($slashargument, '/');
|
||||
return array($slashargument, true);
|
||||
|
||||
} else {
|
||||
// unsupported server, sorry!
|
||||
|
||||
+36
-17
@@ -32,7 +32,11 @@ define('NO_DEBUG_DISPLAY', true);
|
||||
define('ABORT_AFTER_CONFIG', true);
|
||||
require('../config.php'); // this stops immediately at the beginning of lib/setup.php
|
||||
|
||||
$path = min_optional_param('file', '', 'SAFEPATH');
|
||||
if ($slashargument = min_get_slash_argument()) {
|
||||
$path = ltrim($slashargument, '/');
|
||||
} else {
|
||||
$path = min_optional_param('file', '', 'SAFEPATH');
|
||||
}
|
||||
|
||||
$parts = explode('/', $path);
|
||||
$version = array_shift($parts);
|
||||
@@ -65,35 +69,50 @@ if (!file_exists($imagepath)) {
|
||||
yui_image_not_found();
|
||||
}
|
||||
|
||||
yui_image_cached($imagepath);
|
||||
$pathinfo = pathinfo($imagepath);
|
||||
$imagename = $pathinfo['filename'].'.'.$pathinfo['extension'];
|
||||
|
||||
switch($pathinfo['extension']) {
|
||||
case 'gif' : $mimetype = 'image/gif'; break;
|
||||
case 'png' : $mimetype = 'image/png'; break;
|
||||
case 'jpg' : $mimetype = 'image/jpeg'; break;
|
||||
case 'jpeg' : $mimetype = 'image/jpeg'; break;
|
||||
case 'ico' : $mimetype = 'image/vnd.microsoft.icon'; break;
|
||||
default: $mimetype = 'document/unknown';
|
||||
}
|
||||
|
||||
// if they are requesting a revision that's not -1, and they have supplied an
|
||||
// If-Modified-Since header, we can send back a 304 Not Modified since the
|
||||
// content never changes (the rev number is increased any time the content changes)
|
||||
if (strpos($parts, '/-1/') === false and (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']))) {
|
||||
$lifetime = 60*60*24*360; // 1 year, we do not change YUI versions often, there are a few custom yui modules
|
||||
header('HTTP/1.1 304 Not Modified');
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($imagepath)) .' GMT');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Cache-Control: public, max-age='.$lifetime);
|
||||
header('Content-Type: '.$mimetype);
|
||||
header('Etag: '.$etag);
|
||||
die;
|
||||
}
|
||||
|
||||
yui_image_cached($imagepath, $imagename, $mimetype, $etag);
|
||||
|
||||
|
||||
|
||||
function yui_image_cached($imagepath) {
|
||||
function yui_image_cached($imagepath, $imagename, $mimetype, $etag) {
|
||||
global $CFG;
|
||||
require("$CFG->dirroot/lib/xsendfilelib.php");
|
||||
|
||||
$lifetime = 60*60*24*300; // 300 days === forever
|
||||
$pathinfo = pathinfo($imagepath);
|
||||
$imagename = $pathinfo['filename'].'.'.$pathinfo['extension'];
|
||||
|
||||
switch($pathinfo['extension']) {
|
||||
case 'gif' : $mimetype = 'image/gif'; break;
|
||||
case 'png' : $mimetype = 'image/png'; break;
|
||||
case 'jpg' : $mimetype = 'image/jpeg'; break;
|
||||
case 'jpeg' : $mimetype = 'image/jpeg'; break;
|
||||
case 'ico' : $mimetype = 'image/vnd.microsoft.icon'; break;
|
||||
default: $mimetype = 'document/unknown';
|
||||
}
|
||||
$lifetime = 60*60*24*360; // 1 year, we do not change YUI versions often, there are a few custom yui modules
|
||||
|
||||
header('Content-Disposition: inline; filename="'.$imagename.'"');
|
||||
header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($imagepath)) .' GMT');
|
||||
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
|
||||
header('Pragma: ');
|
||||
header('Cache-Control: max-age=315360000');
|
||||
header('Cache-Control: public, max-age=315360000');
|
||||
header('Accept-Ranges: none');
|
||||
header('Content-Type: '.$mimetype);
|
||||
header('Content-Length: '.filesize($imagepath));
|
||||
header('Etag: '.$etag);
|
||||
|
||||
if (xsendfile($imagepath)) {
|
||||
die;
|
||||
|
||||
Reference in New Issue
Block a user