Merge branch 'wip-MDL-22955-m24' of git://github.com/samhemelryk/moodle

This commit is contained in:
Eloy Lafuente (stronk7) 2012-10-10 00:35:11 +02:00
commit dbaf9d448d
11 changed files with 443 additions and 46 deletions

View File

@ -683,7 +683,7 @@ class core_admin_renderer extends plugin_renderer_base {
$row = new html_table_row();
$row->attributes['class'] = 'type-' . $plugin->type . ' name-' . $plugin->type . '_' . $plugin->name;
if ($this->page->theme->resolve_image_location('icon', $plugin->type . '_' . $plugin->name)) {
if ($this->page->theme->resolve_image_location('icon', $plugin->type . '_' . $plugin->name, null)) {
$icon = $this->output->pix_icon('icon', '', $plugin->type . '_' . $plugin->name, array('class' => 'smallicon pluginicon'));
} else {
$icon = $this->output->pix_icon('spacer', '', 'moodle', array('class' => 'smallicon pluginicon noicon'));

View File

@ -442,6 +442,18 @@ $CFG->admin = 'admin';
//
// $CFG->disableupdatenotifications = true;
//
// As of version 2.4 Moodle serves icons as SVG images if the users browser appears
// to support SVG.
// For those wanting to control the serving of SVG images the following setting can
// be defined in your config.php.
// If it is not defined then the default (browser detection) will occur.
//
// To ensure they are always used when available:
// $CFG->svgicons = true;
//
// To ensure they are never used even when available:
// $CFG->svgicons = false;
//
//=========================================================================
// 8. SETTINGS FOR DEVELOPMENT SERVERS - not intended for production use!!!
//=========================================================================

View File

@ -198,7 +198,7 @@ class tiynce_subplugins_settings extends admin_setting {
$displayname = html_writer::tag('span', $namestr, array('class'=>'dimmed_text'));
}
if ($PAGE->theme->resolve_image_location('icon', 'tinymce_' . $name)) {
if ($PAGE->theme->resolve_image_location('icon', 'tinymce_' . $name, false)) {
$icon = $OUTPUT->pix_icon('icon', '', 'tinymce_' . $name, array('class' => 'smallicon pluginicon'));
} else {
$icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'smallicon pluginicon noicon'));

View File

@ -3784,7 +3784,7 @@ function file_pluginfile($relativepath, $forcedownload, $preview = null) {
}
// no redirect here because it is not cached
$theme = theme_config::load($themename);
$imagefile = $theme->resolve_image_location('u/'.$filename, 'moodle');
$imagefile = $theme->resolve_image_location('u/'.$filename, 'moodle', null);
send_file($imagefile, basename($imagefile), 60*60*24*14);
}

View File

@ -38,10 +38,17 @@ M.util.image_url = function(imagename, component) {
component = 'core';
}
var url = M.cfg.wwwroot + '/theme/image.php';
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;
if (!M.cfg.svgicons) {
url += '/_s';
}
url += '/' + 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;
url += '?theme=' + M.cfg.theme + '&component=' + component + '&rev=' + M.cfg.themerev + '&image=' + imagename;
if (!M.cfg.svgicons) {
url += '&svg=0';
}
}
return url;

View File

@ -334,6 +334,12 @@ class theme_config {
*/
public $supportscssoptimisation = true;
/**
* Used to determine whether we can serve SVG images or not.
* @var bool
*/
private $usesvg = null;
/**
* Load the config.php file for a particular theme, and return an instance
* of this class. (That is, this is a factory method.)
@ -942,6 +948,11 @@ class theme_config {
public function post_process($css) {
// now resolve all image locations
if (preg_match_all('/\[\[pix:([a-z_]+\|)?([^\]]+)\]\]/', $css, $matches, PREG_SET_ORDER)) {
// We are going to disable the use of SVG images when available in CSS background-image properties
// as support for it in browsers is at best quirky.
// When we choose to support SVG in background css we will need to remove this code and implement a solution that is
// either consistent or varies the URL for serving CSS depending upon SVG being used if available, or not.
$this->force_svg_use(false);
$replaced = array();
foreach ($matches as $match) {
if (isset($replaced[$match[0]])) {
@ -977,6 +988,7 @@ class theme_config {
global $CFG;
$params = array('theme'=>$this->name);
$svg = $this->use_svg_icons();
if (empty($component) or $component === 'moodle' or $component === 'core') {
$params['component'] = 'core';
@ -991,11 +1003,22 @@ class theme_config {
$params['image'] = $imagename;
$url = new moodle_url("$CFG->httpswwwroot/theme/image.php");
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);
$path = '/'.$params['theme'].'/'.$params['component'].'/'.$params['rev'].'/'.$params['image'];
if (!$svg) {
// We add a simple /_s to the start of the path.
// The underscore is used to ensure that it isn't a valid theme name.
$path = '/_s'.$path;
}
$url->set_slashargument($path, 'noparam', true);
} else {
$url = new moodle_url("$CFG->httpswwwroot/theme/image.php", $params);
if (!$svg) {
// We add an SVG param so that we know not to serve SVG images.
// We do this because all modern browsers support SVG and this param will one day be removed.
$params['svg'] = '0';
}
$url->params($params);
}
return $url;
@ -1003,26 +1026,41 @@ class theme_config {
/**
* Resolves the real image location.
*
* $svg was introduced as an arg in 2.4. It is important because not all supported browsers support the use of SVG
* and we need a way in which to turn it off.
* By default SVG won't be used unless asked for. This is done for two reasons:
* 1. It ensures that we don't serve svg images unless we really want to. The admin has selected to force them, of the users
* browser supports SVG.
* 2. We only serve SVG images from locations we trust. This must NOT include any areas where the image may have been uploaded
* by the user due to security concerns.
*
* @param string $image name of image, may contain relative path
* @param string $component
* @param bool $svg If set to true SVG images will also be looked for.
* @return string full file path
*/
public function resolve_image_location($image, $component) {
public function resolve_image_location($image, $component, $svg = false) {
global $CFG;
if (!is_bool($svg)) {
// If $svg isn't a bool then we need to decide for ourselves.
$svg = $this->use_svg_icons();
}
if ($component === 'moodle' or $component === 'core' or empty($component)) {
if ($imagefile = $this->image_exists("$this->dir/pix_core/$image")) {
if ($imagefile = $this->image_exists("$this->dir/pix_core/$image", $svg)) {
return $imagefile;
}
foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last
if ($imagefile = $this->image_exists("$parent_config->dir/pix_core/$image")) {
if ($imagefile = $this->image_exists("$parent_config->dir/pix_core/$image", $svg)) {
return $imagefile;
}
}
if ($imagefile = $this->image_exists("$CFG->dataroot/pix/$image")) {
if ($imagefile = $this->image_exists("$CFG->dataroot/pix/$image", $svg)) {
return $imagefile;
}
if ($imagefile = $this->image_exists("$CFG->dirroot/pix/$image")) {
if ($imagefile = $this->image_exists("$CFG->dirroot/pix/$image", $svg)) {
return $imagefile;
}
return null;
@ -1031,11 +1069,11 @@ class theme_config {
if ($image === 'favicon') {
return "$this->dir/pix/favicon.ico";
}
if ($imagefile = $this->image_exists("$this->dir/pix/$image")) {
if ($imagefile = $this->image_exists("$this->dir/pix/$image", $svg)) {
return $imagefile;
}
foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last
if ($imagefile = $this->image_exists("$parent_config->dir/pix/$image")) {
if ($imagefile = $this->image_exists("$parent_config->dir/pix/$image", $svg)) {
return $imagefile;
}
}
@ -1047,36 +1085,92 @@ class theme_config {
}
list($type, $plugin) = explode('_', $component, 2);
if ($imagefile = $this->image_exists("$this->dir/pix_plugins/$type/$plugin/$image")) {
if ($imagefile = $this->image_exists("$this->dir/pix_plugins/$type/$plugin/$image", $svg)) {
return $imagefile;
}
foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last
if ($imagefile = $this->image_exists("$parent_config->dir/pix_plugins/$type/$plugin/$image")) {
if ($imagefile = $this->image_exists("$parent_config->dir/pix_plugins/$type/$plugin/$image", $svg)) {
return $imagefile;
}
}
if ($imagefile = $this->image_exists("$CFG->dataroot/pix_plugins/$type/$plugin/$image")) {
if ($imagefile = $this->image_exists("$CFG->dataroot/pix_plugins/$type/$plugin/$image", $svg)) {
return $imagefile;
}
$dir = get_plugin_directory($type, $plugin);
if ($imagefile = $this->image_exists("$dir/pix/$image")) {
if ($imagefile = $this->image_exists("$dir/pix/$image", $svg)) {
return $imagefile;
}
return null;
}
}
/**
* Return true if we should look for SVG images as well.
*
* @staticvar bool $svg
* @return bool
*/
public function use_svg_icons() {
global $CFG;
if ($this->usesvg === null) {
if (!isset($CFG->svgicons) || !is_bool($CFG->svgicons)) {
// IE 5 - 8 don't support SVG at all.
if (empty($_SERVER['HTTP_USER_AGENT'])) {
// Can't be sure, just say no.
$this->usesvg = false;
} else if (preg_match('#MSIE +[5-8]\.#', $_SERVER['HTTP_USER_AGENT'])) {
// IE < 9 doesn't support SVG. Say no.
$this->usesvg = false;
} else if (preg_match('#Android +[0-2]\.#', $_SERVER['HTTP_USER_AGENT'])) {
// Android < 3 doesn't support SVG. Say no.
$this->usesvg = false;
} else {
// Presumed fine.
$this->usesvg = true;
}
} else {
// Force them on/off depending upon the setting.
$this->usesvg = $CFG->svgicons;
}
}
return $this->usesvg;
}
/**
* Forces the usesvg setting to either true or false, avoiding any decision making.
*
* This function should only ever be used when absolutely required, and before any generation of image URL's has occurred.
*
* @param bool $setting True to force the use of svg when available, null otherwise.
*/
private function force_svg_use($setting) {
$this->usesvg = (bool)$setting;
}
/**
* Checks if file with any image extension exists.
*
* The order to these images was adjusted prior to the release of 2.4
* At that point the were the following image counts in Moodle core:
*
* - png = 667 in pix dirs (1499 total)
* - gif = 385 in pix dirs (606 total)
* - jpg = 62 in pix dirs (74 total)
* - jpeg = 0 in pix dirs (1 total)
*
* There is work in progress to move towards SVG presently hence that has been prioritiesed.
*
* @param string $filepath
* @param bool $svg If set to true SVG images will also be looked for.
* @return string image name with extension
*/
private static function image_exists($filepath) {
if (file_exists("$filepath.gif")) {
return "$filepath.gif";
private static function image_exists($filepath, $svg = false) {
if ($svg && file_exists("$filepath.svg")) {
return "$filepath.svg";
} else if (file_exists("$filepath.png")) {
return "$filepath.png";
} else if (file_exists("$filepath.gif")) {
return "$filepath.gif";
} else if (file_exists("$filepath.jpg")) {
return "$filepath.jpg";
} else if (file_exists("$filepath.jpeg")) {

View File

@ -264,6 +264,7 @@ class page_requirements_manager {
'slasharguments' => (int)(!empty($CFG->slasharguments)),
'theme' => $page->theme->name,
'jsrev' => ((empty($CFG->cachejs) or empty($CFG->jsrev)) ? -1 : $CFG->jsrev),
'svgicons' => $page->theme->use_svg_icons()
);
if (debugging('', DEBUG_DEVELOPER)) {
$this->M_cfg['developerdebug'] = true;

View File

@ -133,3 +133,100 @@ class xhtml_container_stack_testcase extends advanced_testcase {
$this->assertDebuggingNotCalled();
}
}
/**
* Tests the theme config class.
*
* @copyright 2012 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class theme_config_testcase extends advanced_testcase {
/**
* This function will test directives used to serve SVG images to make sure
* this are making the right decisions.
*/
public function test_svg_image_use() {
global $CFG;
$this->resetAfterTest();
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$ua = $_SERVER['HTTP_USER_AGENT'];
} else {
$ua = null;
}
// The two required tests.
$this->assertTrue(file_exists($CFG->dirroot.'/pix/i/test.svg'));
$this->assertTrue(file_exists($CFG->dirroot.'/pix/i/test.png'));
$theme = theme_config::load(theme_config::DEFAULT_THEME);
// First up test the forced setting.
$imagefile = $theme->resolve_image_location('i/test', 'moodle', true);
$this->assertEquals('test.svg', basename($imagefile));
$imagefile = $theme->resolve_image_location('i/test', 'moodle', false);
$this->assertEquals('test.png', basename($imagefile));
// Now test the use of the svgicons config setting.
// We need to clone the theme as usesvg property is calculated only once.
$testtheme = clone $theme;
$CFG->svgicons = true;
$imagefile = $testtheme->resolve_image_location('i/test', 'moodle', null);
$this->assertEquals('test.svg', basename($imagefile));
$CFG->svgicons = false;
// We need to clone the theme as usesvg property is calculated only once.
$testtheme = clone $theme;
$imagefile = $testtheme->resolve_image_location('i/test', 'moodle', null);
$this->assertEquals('test.png', basename($imagefile));
unset($CFG->svgicons);
// Finally test a few user agents.
$useragents = array(
// IE7 on XP.
'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)' => false,
// IE8 on Vista.
'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)' => false,
// IE8 on Vista in compatability mode.
'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0)' => false,
// IE8 on Windows 7.
'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)' => false,
// IE9 on Windows 7.
'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)' => true,
// IE9 on Windows 7 in compatability mode.
'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/5.0)' => false,
// Chrome 11 on Windows.
'Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/11.0.652.0 Safari/534.17' => true,
// Chrome 22 on Windows.
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1' => true,
// Chrome 21 on Ubuntu 12.04.
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1' => true,
// Firefox 4 on Windows.
'Mozilla/5.0 (Windows NT 6.1; rv:1.9) Gecko/20100101 Firefox/4.0' => true,
// Firefox 15 on Windows.
'Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0.1' => true,
// Firefox 15 on Ubuntu.
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1' => true,
// Opera 12.02 on Ubuntu.
'Opera/9.80 (X11; Linux x86_64; U; en) Presto/2.10.289 Version/12.02' => true,
// Android browser pre 1.0
'Mozilla/5.0 (Linux; U; Android 0.5; en-us) AppleWebKit/522+ (KHTML, like Gecko) Safari/419.3' => false,
// Android browser 2.3 (HTC)
'Mozilla/5.0 (Linux; U; Android 2.3.5; en-us; HTC Vision Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1' => false,
// Android browser 3.0 (Motorola)
'Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13' => true
);
foreach ($useragents as $agent => $expected) {
$_SERVER['HTTP_USER_AGENT'] = $agent;
// We need to clone the theme as usesvg property is calculated only once.
$testtheme = clone $theme;
$imagefile = $testtheme->resolve_image_location('i/test', 'moodle', null);
$this->assertEquals($expected ? 'test.svg' : 'test.png', basename($imagefile),
'Incorrect image returned for user agent `'.$agent.'`');
}
if ($ua !== null) {
$_SERVER['HTTP_USER_AGENT'] = $ua;
}
}
}

BIN
pix/i/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

133
pix/i/test.svg Normal file
View File

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="114.88917"
height="114.88917"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="New document 1">
<defs
id="defs4">
<linearGradient
id="linearGradient3988">
<stop
style="stop-color:#000000;stop-opacity:0"
offset="0"
id="stop3990" />
<stop
style="stop-color:#ff8000;stop-opacity:1;"
offset="1"
id="stop3992" />
</linearGradient>
<linearGradient
id="linearGradient3982">
<stop
id="stop3984"
offset="0"
style="stop-color:#ffab00;stop-opacity:1;" />
<stop
id="stop3986"
offset="1"
style="stop-color:#ff8000;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3982"
id="radialGradient3980"
cx="360.68207"
cy="435.11246"
fx="360.68207"
fy="435.11246"
r="45.40649"
gradientTransform="matrix(1,0,0,0.98110214,0,8.2226956)"
gradientUnits="userSpaceOnUse"
spreadMethod="pad" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3988"
id="linearGradient4005"
x1="323.18604"
y1="445.29483"
x2="375.84158"
y2="514.34235"
gradientUnits="userSpaceOnUse" />
<filter
inkscape:collect="always"
id="filter4055"
color-interpolation-filters="sRGB">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="2.1197264"
id="feGaussianBlur4057" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.7480769"
inkscape:cx="-31.349998"
inkscape:cy="12.481512"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1855"
inkscape:window-height="1056"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-304.95364,-378.52595)">
<path
sodipodi:type="arc"
style="fill:url(#radialGradient3980);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path3204"
sodipodi:cx="360.68207"
sodipodi:cy="435.11246"
sodipodi:rx="44.90649"
sodipodi:ry="44.048405"
d="m 405.58856,435.11246 c 0,24.32726 -20.10532,44.0484 -44.90649,44.0484 -24.80117,0 -44.90649,-19.72114 -44.90649,-44.0484 0,-24.32726 20.10532,-44.04841 44.90649,-44.04841 24.80117,0 44.90649,19.72115 44.90649,44.04841 z" />
<path
sodipodi:type="arc"
id="path3994"
sodipodi:cx="374.98349"
sodipodi:cy="445.12344"
sodipodi:rx="52.343235"
sodipodi:ry="52.343235"
d="m 427.32673,445.12344 c 0,28.90837 -23.43487,52.34324 -52.34324,52.34324 -28.90837,0 -52.34324,-23.43487 -52.34324,-52.34324 0,-28.90837 23.43487,-52.34323 52.34324,-52.34323 28.90837,0 52.34324,23.43486 52.34324,52.34323 z"
transform="matrix(0.95405918,-0.29961823,0.29961823,0.95405918,-128.72531,123.64832)"
style="opacity:0.47736631;fill:url(#linearGradient4005);fill-opacity:1;filter:url(#filter4055)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -37,6 +37,13 @@ if ($slashargument = min_get_slash_argument()) {
if (substr_count($slashargument, '/') < 3) {
image_not_found();
}
if (strpos($slashargument, '_s/') === 0) {
// Can't use SVG
$slashargument = substr($slashargument, 3);
$usesvg = false;
} else {
$usesvg = true;
}
// image must be last because it may contain "/"
list($themename, $component, $rev, $image) = explode('/', $slashargument, 4);
$themename = min_clean_param($themename, 'SAFEDIR');
@ -49,6 +56,7 @@ if ($slashargument = min_get_slash_argument()) {
$component = min_optional_param('component', 'core', 'SAFEDIR');
$rev = min_optional_param('rev', -1, 'INT');
$image = min_optional_param('image', '', 'SAFEPATH');
$usesvg = (bool)min_optional_param('svg', '1', 'INT');
}
if (empty($component) or $component === 'moodle' or $component === 'core') {
@ -77,12 +85,15 @@ if ($rev > -1) {
image_not_found();
}
$cacheimage = false;
if (file_exists("$candidatelocation/$image.gif")) {
$cacheimage = "$candidatelocation/$image.gif";
$ext = 'gif';
if ($usesvg && file_exists("$candidatelocation/$image.svg")) {
$cacheimage = "$candidatelocation/$image.svg";
$ext = 'svg';
} else if (file_exists("$candidatelocation/$image.png")) {
$cacheimage = "$candidatelocation/$image.png";
$ext = 'png';
} else if (file_exists("$candidatelocation/$image.gif")) {
$cacheimage = "$candidatelocation/$image.gif";
$ext = 'gif';
} else if (file_exists("$candidatelocation/$image.jpg")) {
$cacheimage = "$candidatelocation/$image.jpg";
$ext = 'jpg';
@ -120,11 +131,38 @@ define('NO_UPGRADE_CHECK', true); // Ignore upgrade check
require("$CFG->dirroot/lib/setup.php");
$theme = theme_config::load($themename);
$imagefile = $theme->resolve_image_location($image, $component);
$rev = theme_get_revision();
$etag = sha1("$themename/$component/$rev/$image");
// We're not using SVG and there is no cached version of this file (in any format).
// As we're going to be caching a format other than svg, and because svg use is conditional we need to ensure that at the same
// time we cache a version of the SVG if it exists. If we don't do this other users who ask for SVG would not ever get it as
// there is a cached image already of another format.
// Remember this only gets run once before any candidate exists, and only if we want a cached revision.
if (!$usesvg && $rev > -1) {
$imagefile = $theme->resolve_image_location($image, $component, true);
if (!empty($imagefile) && is_readable($imagefile)) {
$cacheimage = cache_image($image, $imagefile, $candidatelocation);
$pathinfo = pathinfo($imagefile);
// There is no SVG equivilant, we've just successfully cached an image of another format.
if ($pathinfo['extension'] !== 'svg') {
// Serve the file as we would in a normal request.
if (connection_aborted()) {
die;
}
// make sure nothing failed
clearstatcache();
if (file_exists($cacheimage)) {
send_cached_image($cacheimage, $etag);
}
send_uncached_image($imagefile);
exit;
}
}
}
// Either SVG was requested or we've cached a SVG version and are ready to serve a regular format.
$imagefile = $theme->resolve_image_location($image, $component, $usesvg);
if (empty($imagefile) or !is_readable($imagefile)) {
if ($rev > -1) {
if (!file_exists($candidatelocation)) {
@ -139,23 +177,7 @@ if (empty($imagefile) or !is_readable($imagefile)) {
}
if ($rev > -1) {
$pathinfo = pathinfo($imagefile);
$cacheimage = "$candidatelocation/$image.".$pathinfo['extension'];
clearstatcache();
if (!file_exists(dirname($cacheimage))) {
@mkdir(dirname($cacheimage), $CFG->directorypermissions, true);
}
// Prevent serving of incomplete file from concurrent request,
// the rename() should be more atomic than copy().
ignore_user_abort(true);
if (@copy($imagefile, $cacheimage.'.tmp')) {
rename($cacheimage.'.tmp', $cacheimage);
@chmod($cacheimage, $CFG->filepermissions);
@unlink($cacheimage.'.tmp'); // just in case anything fails
}
ignore_user_abort(false);
$cacheimage = cache_image($image, $imagefile, $candidatelocation);
if (connection_aborted()) {
die;
}
@ -229,10 +251,12 @@ function image_not_found() {
function get_contenttype_from_ext($ext) {
switch ($ext) {
case 'gif':
return 'image/gif';
case 'svg':
return 'image/svg+xml';
case 'png':
return 'image/png';
case 'gif':
return 'image/gif';
case 'jpg':
case 'jpeg':
return 'image/jpeg';
@ -241,3 +265,32 @@ function get_contenttype_from_ext($ext) {
}
return 'document/unknown';
}
/**
* Caches a given image file.
*
* @param string $image The name of the image that was requested.
* @param string $imagefile The location of the image file we want to cache.
* @param string $candidatelocation The location to cache it in.
* @return string The path to the cached image.
*/
function cache_image($image, $imagefile, $candidatelocation) {
global $CFG;
$pathinfo = pathinfo($imagefile);
$cacheimage = "$candidatelocation/$image.".$pathinfo['extension'];
clearstatcache();
if (!file_exists(dirname($cacheimage))) {
@mkdir(dirname($cacheimage), $CFG->directorypermissions, true);
}
// Prevent serving of incomplete file from concurrent request,
// the rename() should be more atomic than copy().
ignore_user_abort(true);
if (@copy($imagefile, $cacheimage.'.tmp')) {
rename($cacheimage.'.tmp', $cacheimage);
@chmod($cacheimage, $CFG->filepermissions);
@unlink($cacheimage.'.tmp'); // just in case anything fails
}
return $cacheimage;
}