. /** * This file is responsible for serving the one theme and plugin images. * * @package moodlecore * @copyright 2009 Petr Skoda (skodak) {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ // disable moodle specific debug messages and any errors in output, // comment out when debugging or better look into error log! 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 $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 (empty($component) or empty($image)) { image_not_found(); } if (file_exists("$CFG->dirroot/theme/$themename/config.php")) { // exists } else if (!empty($CFG->themedir) and file_exists("$CFG->themedir/$themename/config.php")) { // exists } else { image_not_found(); } $candidatelocation = "$CFG->cachedir/theme/$themename/pix/$component"; if ($rev > -1) { if (file_exists("$candidatelocation/$image.error")) { // this is a major speedup if there are multiple missing images, // the only problem is that random requests may pollute our cache. image_not_found(); } $cacheimage = false; if (file_exists("$candidatelocation/$image.gif")) { $cacheimage = "$candidatelocation/$image.gif"; $ext = 'gif'; } else if (file_exists("$candidatelocation/$image.png")) { $cacheimage = "$candidatelocation/$image.png"; $ext = 'png'; } else if (file_exists("$candidatelocation/$image.jpg")) { $cacheimage = "$candidatelocation/$image.jpg"; $ext = 'jpg'; } else if (file_exists("$candidatelocation/$image.jpeg")) { $cacheimage = "$candidatelocation/$image.jpeg"; $ext = 'jpeg'; } else if (file_exists("$candidatelocation/$image.ico")) { $cacheimage = "$candidatelocation/$image.ico"; $ext = 'ico'; } if ($cacheimage) { 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 $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('Content-Type: '.$mimetype); die; } send_cached_image($cacheimage, $rev); } } //================================================================================= // ok, now we need to start normal moodle script, we need to load all libs and $DB define('ABORT_AFTER_CONFIG_CANCEL', true); define('NO_MOODLE_COOKIES', true); // Session not used here 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(); if (empty($imagefile) or !is_readable($imagefile)) { if ($rev > -1) { // make note we can not find this file $cacheimage = "$candidatelocation/$image.error"; $fp = fopen($cacheimage, 'w'); fclose($fp); } image_not_found(); } if ($rev > -1) { $pathinfo = pathinfo($imagefile); $cacheimage = "$candidatelocation/$image.".$pathinfo['extension']; if (!file_exists($cacheimage)) { // 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($cacheimage)); copy($imagefile, $cacheimage); } send_cached_image($cacheimage, $rev); } else { send_uncached_image($imagefile); } //================================================================================= //=== utility functions == // 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) { $lifetime = 60*60*24*30; // 30 days $pathinfo = pathinfo($imagepath); $imagename = $pathinfo['filename'].'.'.$pathinfo['extension']; $mimetype = get_contenttype_from_ext($pathinfo['extension']); header('Etag: '.md5("$rev/$imagepath")); header('Content-Disposition: inline; filename="'.$imagename.'"'); 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('Accept-Ranges: none'); header('Content-Type: '.$mimetype); header('Content-Length: '.filesize($imagepath)); // no need to gzip already compressed images ;-) readfile($imagepath); die; } function send_uncached_image($imagepath) { $pathinfo = pathinfo($imagepath); $imagename = $pathinfo['filename'].'.'.$pathinfo['extension']; $mimetype = get_contenttype_from_ext($pathinfo['extension']); header('Content-Disposition: inline; filename="'.$imagename.'"'); header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT'); header('Expires: '. gmdate('D, d M Y H:i:s', time() + 15) .' GMT'); header('Pragma: '); header('Accept-Ranges: none'); header('Content-Type: '.$mimetype); header('Content-Length: '.filesize($imagepath)); readfile($imagepath); die; } function image_not_found() { header('HTTP/1.0 404 not found'); die('Image was not found, sorry.'); } function get_contenttype_from_ext($ext) { switch ($ext) { case 'gif': return 'image/gif'; case 'png': return 'image/png'; case 'jpg': case 'jpeg': return 'image/jpeg'; case 'ico': return 'image/vnd.microsoft.icon'; } return 'document/unknown'; }