Commit 436dbeec authored by Sam Hemelryk's avatar Sam Hemelryk
Browse files

MDL-22955 theme: Added ability to use SVG format for icons

parent ccd90e76
...@@ -683,7 +683,7 @@ class core_admin_renderer extends plugin_renderer_base { ...@@ -683,7 +683,7 @@ class core_admin_renderer extends plugin_renderer_base {
$row = new html_table_row(); $row = new html_table_row();
$row->attributes['class'] = 'type-' . $plugin->type . ' name-' . $plugin->type . '_' . $plugin->name; $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')); $icon = $this->output->pix_icon('icon', '', $plugin->type . '_' . $plugin->name, array('class' => 'smallicon pluginicon'));
} else { } else {
$icon = $this->output->pix_icon('spacer', '', 'moodle', array('class' => 'smallicon pluginicon noicon')); $icon = $this->output->pix_icon('spacer', '', 'moodle', array('class' => 'smallicon pluginicon noicon'));
......
...@@ -442,6 +442,18 @@ $CFG->admin = 'admin'; ...@@ -442,6 +442,18 @@ $CFG->admin = 'admin';
// //
// $CFG->disableupdatenotifications = true; // $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!!! // 8. SETTINGS FOR DEVELOPMENT SERVERS - not intended for production use!!!
//========================================================================= //=========================================================================
......
...@@ -198,7 +198,7 @@ class tiynce_subplugins_settings extends admin_setting { ...@@ -198,7 +198,7 @@ class tiynce_subplugins_settings extends admin_setting {
$displayname = html_writer::tag('span', $namestr, array('class'=>'dimmed_text')); $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')); $icon = $OUTPUT->pix_icon('icon', '', 'tinymce_' . $name, array('class' => 'smallicon pluginicon'));
} else { } else {
$icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'smallicon pluginicon noicon')); $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'smallicon pluginicon noicon'));
......
...@@ -3779,7 +3779,7 @@ function file_pluginfile($relativepath, $forcedownload, $preview = null) { ...@@ -3779,7 +3779,7 @@ function file_pluginfile($relativepath, $forcedownload, $preview = null) {
} }
// no redirect here because it is not cached // no redirect here because it is not cached
$theme = theme_config::load($themename); $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); send_file($imagefile, basename($imagefile), 60*60*24*14);
} }
......
...@@ -38,10 +38,17 @@ M.util.image_url = function(imagename, component) { ...@@ -38,10 +38,17 @@ M.util.image_url = function(imagename, component) {
component = 'core'; component = 'core';
} }
var url = M.cfg.wwwroot + '/theme/image.php';
if (M.cfg.themerev > 0 && M.cfg.slasharguments == 1) { 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 { } 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; return url;
......
...@@ -334,6 +334,12 @@ class theme_config { ...@@ -334,6 +334,12 @@ class theme_config {
*/ */
public $supportscssoptimisation = true; 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 * Load the config.php file for a particular theme, and return an instance
* of this class. (That is, this is a factory method.) * of this class. (That is, this is a factory method.)
...@@ -977,6 +983,7 @@ class theme_config { ...@@ -977,6 +983,7 @@ class theme_config {
global $CFG; global $CFG;
$params = array('theme'=>$this->name); $params = array('theme'=>$this->name);
$svg = $this->use_svg_icons();
if (empty($component) or $component === 'moodle' or $component === 'core') { if (empty($component) or $component === 'moodle' or $component === 'core') {
$params['component'] = 'core'; $params['component'] = 'core';
...@@ -991,11 +998,22 @@ class theme_config { ...@@ -991,11 +998,22 @@ class theme_config {
$params['image'] = $imagename; $params['image'] = $imagename;
$url = new moodle_url("$CFG->httpswwwroot/theme/image.php");
if (!empty($CFG->slasharguments) and $rev > 0) { if (!empty($CFG->slasharguments) and $rev > 0) {
$url = new moodle_url("$CFG->httpswwwroot/theme/image.php"); $path = '/'.$params['theme'].'/'.$params['component'].'/'.$params['rev'].'/'.$params['image'];
$url->set_slashargument('/'.$params['theme'].'/'.$params['component'].'/'.$params['rev'].'/'.$params['image'], 'noparam', true); 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 { } 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; return $url;
...@@ -1003,26 +1021,41 @@ class theme_config { ...@@ -1003,26 +1021,41 @@ class theme_config {
/** /**
* Resolves the real image location. * 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 $image name of image, may contain relative path
* @param string $component * @param string $component
* @param bool $svg If set to true SVG images will also be looked for.
* @return string full file path * @return string full file path
*/ */
public function resolve_image_location($image, $component) { public function resolve_image_location($image, $component, $svg = false) {
global $CFG; 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 ($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; return $imagefile;
} }
foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last 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; return $imagefile;
} }
} }
if ($imagefile = $this->image_exists("$CFG->dataroot/pix/$image")) { if ($imagefile = $this->image_exists("$CFG->dataroot/pix/$image", $svg)) {
return $imagefile; return $imagefile;
} }
if ($imagefile = $this->image_exists("$CFG->dirroot/pix/$image")) { if ($imagefile = $this->image_exists("$CFG->dirroot/pix/$image", $svg)) {
return $imagefile; return $imagefile;
} }
return null; return null;
...@@ -1031,11 +1064,11 @@ class theme_config { ...@@ -1031,11 +1064,11 @@ class theme_config {
if ($image === 'favicon') { if ($image === 'favicon') {
return "$this->dir/pix/favicon.ico"; 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; return $imagefile;
} }
foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last 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; return $imagefile;
} }
} }
...@@ -1047,36 +1080,81 @@ class theme_config { ...@@ -1047,36 +1080,81 @@ class theme_config {
} }
list($type, $plugin) = explode('_', $component, 2); 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; return $imagefile;
} }
foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last 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; 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; return $imagefile;
} }
$dir = get_plugin_directory($type, $plugin); $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 $imagefile;
} }
return null; 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;
}
/** /**
* Checks if file with any image extension exists. * 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 string $filepath
* @param bool $svg If set to true SVG images will also be looked for.
* @return string image name with extension * @return string image name with extension
*/ */
private static function image_exists($filepath) { private static function image_exists($filepath, $svg = false) {
if (file_exists("$filepath.gif")) { if ($svg && file_exists("$filepath.svg")) {
return "$filepath.gif"; return "$filepath.svg";
} else if (file_exists("$filepath.png")) { } else if (file_exists("$filepath.png")) {
return "$filepath.png"; return "$filepath.png";
} else if (file_exists("$filepath.gif")) {
return "$filepath.gif";
} else if (file_exists("$filepath.jpg")) { } else if (file_exists("$filepath.jpg")) {
return "$filepath.jpg"; return "$filepath.jpg";
} else if (file_exists("$filepath.jpeg")) { } else if (file_exists("$filepath.jpeg")) {
......
...@@ -264,6 +264,7 @@ class page_requirements_manager { ...@@ -264,6 +264,7 @@ class page_requirements_manager {
'slasharguments' => (int)(!empty($CFG->slasharguments)), 'slasharguments' => (int)(!empty($CFG->slasharguments)),
'theme' => $page->theme->name, 'theme' => $page->theme->name,
'jsrev' => ((empty($CFG->cachejs) or empty($CFG->jsrev)) ? -1 : $CFG->jsrev), 'jsrev' => ((empty($CFG->cachejs) or empty($CFG->jsrev)) ? -1 : $CFG->jsrev),
'svgicons' => $page->theme->use_svg_icons()
); );
if (debugging('', DEBUG_DEVELOPER)) { if (debugging('', DEBUG_DEVELOPER)) {
$this->M_cfg['developerdebug'] = true; $this->M_cfg['developerdebug'] = true;
......
...@@ -133,3 +133,100 @@ class xhtml_container_stack_testcase extends advanced_testcase { ...@@ -133,3 +133,100 @@ class xhtml_container_stack_testcase extends advanced_testcase {
$this->assertDebuggingNotCalled(); $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;
}
}
}
\ No newline at end of file
<?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>
...@@ -37,6 +37,13 @@ if ($slashargument = min_get_slash_argument()) { ...@@ -37,6 +37,13 @@ if ($slashargument = min_get_slash_argument()) {
if (substr_count($slashargument, '/') < 3) { if (substr_count($slashargument, '/') < 3) {
image_not_found(); image_not_found();
} }
if (strpos($slashargument, '_s/') === 0) {
// Can't use SVG
$slashargument = substr($slashargument, 3);