Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * This file is responsible for serving of yui Javascript and CSS 19 * 20 * @package core 21 * @copyright 2009 Petr Skoda (skodak) {@link http://skodak.org} 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 26 // disable moodle specific debug messages and any errors in output, 27 // comment out when debugging or better look into error log! 28 define('NO_DEBUG_DISPLAY', true); 29 30 // we need just the values from config.php and minlib.php 31 define('ABORT_AFTER_CONFIG', true); 32 require('../config.php'); // this stops immediately at the beginning of lib/setup.php 33 34 // get special url parameters 35 36 list($parts, $slasharguments) = combo_params(); 37 if (!$parts) { 38 combo_not_found(); 39 } 40 41 $parts = trim($parts, '&'); 42 43 // Remove any duplicate parts, since each file only needs to be loaded once (which also helps reduce total file size). 44 $parts = implode('&', array_unique(explode('&', $parts))); 45 46 // Limit length of parts to match the YUI loader limit of 1024, to prevent loading an arbitrary number of files. 47 if (strlen($parts) > 1024) { 48 $parts = substr($parts, 0, 1024); 49 50 // If the shortened $parts has been cut off mid-way through a filename, trim back to the end of the previous filename. 51 if (substr($parts, -3) !== '.js' && substr($parts, -4) !== '.css') { 52 $parts = substr($parts, 0, strrpos($parts, '&')); 53 } 54 } 55 56 // find out what we are serving - only one type per request 57 $content = ''; 58 if (substr($parts, -3) === '.js') { 59 $mimetype = 'application/javascript'; 60 } else if (substr($parts, -4) === '.css') { 61 $mimetype = 'text/css'; 62 } else { 63 combo_not_found(); 64 } 65 66 $etag = sha1($parts); 67 68 // if they are requesting a revision that's not -1, and they have supplied an 69 // If-Modified-Since header, we can send back a 304 Not Modified since the 70 // content never changes (the rev number is increased any time the content changes) 71 if (strpos($parts, '/-1/') === false and (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']))) { 72 $lifetime = 60*60*24*360; // 1 year, we do not change YUI versions often, there are a few custom yui modules 73 header('HTTP/1.1 304 Not Modified'); 74 header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT'); 75 header('Cache-Control: public, max-age='.$lifetime); 76 header('Content-Type: '.$mimetype); 77 header('Etag: "'.$etag.'"'); 78 die; 79 } 80 81 $parts = explode('&', $parts); 82 $cache = true; 83 $lastmodified = 0; 84 85 while (count($parts)) { 86 $part = array_shift($parts); 87 if (empty($part)) { 88 continue; 89 } 90 $filecontent = ''; 91 $part = min_clean_param($part, 'SAFEPATH'); 92 $bits = explode('/', $part); 93 if (count($bits) < 2) { 94 $content .= "\n// Wrong combo resource $part!\n"; 95 continue; 96 } 97 98 $version = array_shift($bits); 99 if ($version === 'rollup') { 100 $yuipatchedversion = explode('_', array_shift($bits)); 101 $revision = $yuipatchedversion[0]; 102 $rollupname = array_shift($bits); 103 104 if (strpos($rollupname, 'yui-moodlesimple') !== false) { 105 if (substr($rollupname, -3) === '.js') { 106 // Determine which version of this rollup should be used. 107 $filesuffix = '.js'; 108 preg_match('/(-(debug|min))?\.js/', $rollupname, $matches); 109 if (isset($matches[1])) { 110 $filesuffix = $matches[0]; 111 } 112 113 $type = 'js'; 114 } else if (substr($rollupname, -4) === '.css') { 115 $type = 'css'; 116 } else { 117 continue; 118 } 119 120 // Allow support for revisions on YUI between official releases. 121 // We can just discard the subrevision since it is only used to invalidate the browser cache. 122 $yuipatchedversion = explode('_', $revision); 123 $yuiversion = $yuipatchedversion[0]; 124 125 $yuimodules = array( 126 'yui', 127 'oop', 128 'event-custom-base', 129 'dom-core', 130 'dom-base', 131 'color-base', 132 'dom-style', 133 'selector-native', 134 'selector', 135 'node-core', 136 'node-base', 137 'event-base', 138 'event-base-ie', 139 'pluginhost-base', 140 'pluginhost-config', 141 'event-delegate', 142 'node-event-delegate', 143 'node-pluginhost', 144 'dom-screen', 145 'node-screen', 146 'node-style', 147 'querystring-stringify-simple', 148 'io-base', 149 'json-parse', 150 'transition', 151 'selector-css2', 152 'selector-css3', 153 'dom-style-ie', 154 155 // Some extras we use everywhere. 156 'escape', 157 158 'attribute-core', 159 'event-custom-complex', 160 'base-core', 161 'attribute-base', 162 'attribute-extras', 163 'attribute-observable', 164 'base-observable', 165 'base-base', 166 'base-pluginhost', 167 'base-build', 168 'event-synthetic', 169 170 'attribute-complex', 171 'event-mouseenter', 172 'event-key', 173 'event-outside', 174 'event-focus', 175 'classnamemanager', 176 'widget-base', 177 'widget-htmlparser', 178 'widget-skin', 179 'widget-uievents', 180 'widget-stdmod', 181 'widget-position', 182 'widget-position-align', 183 'widget-stack', 184 'widget-position-constrain', 185 'overlay', 186 187 'widget-autohide', 188 'button-core', 189 'button-plugin', 190 'widget-buttons', 191 'widget-modality', 192 'panel', 193 'yui-throttle', 194 'dd-ddm-base', 195 'dd-drag', 196 'dd-plugin', 197 198 // Cache is used by moodle-core-tooltip which we include everywhere. 199 'cache-base', 200 ); 201 202 // We need to add these new parts to the beginning of the $parts list, not the end. 203 if ($type === 'js') { 204 $newparts = array(); 205 foreach ($yuimodules as $module) { 206 $newparts[] = $yuiversion . '/' . $module . '/' . $module . $filesuffix; 207 } 208 $newparts[] = 'yuiuseall/yuiuseall'; 209 $parts = array_merge($newparts, $parts); 210 } else { 211 $newparts = array(); 212 foreach ($yuimodules as $module) { 213 $candidate = $yuiversion . '/' . $module . '/assets/skins/sam/' . $module . '.css'; 214 if (!file_exists("$CFG->libdir/yuilib/$candidate")) { 215 continue; 216 } 217 $newparts[] = $candidate; 218 } 219 if ($newparts) { 220 $parts = array_merge($newparts, $parts); 221 } 222 } 223 } 224 225 continue; 226 } 227 if ($version === 'm') { 228 $version = 'moodle'; 229 } 230 if ($version === 'moodle') { 231 if (count($bits) <= 3) { 232 // This is an invalid module load attempt. 233 $content .= "\n// Incorrect moodle module inclusion. Not enough component information in {$part}.\n"; 234 continue; 235 } 236 $revision = (int)array_shift($bits); 237 if ($revision === -1) { 238 // Revision -1 says please don't cache the JS 239 $cache = false; 240 } 241 $frankenstyle = array_shift($bits); 242 $filename = array_pop($bits); 243 $modulename = $bits[0]; 244 $dir = core_component::get_component_directory($frankenstyle); 245 246 // For shifted YUI modules, we need the YUI module name in frankenstyle format. 247 $frankenstylemodulename = join('-', array($version, $frankenstyle, $modulename)); 248 $frankenstylefilename = preg_replace('/' . $modulename . '/', $frankenstylemodulename, $filename); 249 250 // Submodules are stored in a directory with the full submodule name. 251 // We need to remove the -debug.js, -min.js, and .js from the file name to calculate that directory name. 252 $frankenstyledirectoryname = str_replace(array('-min.js', '-debug.js', '.js', '.css'), '', $frankenstylefilename); 253 254 // By default, try and use the /yui/build directory. 255 $contentfile = $dir . '/yui/build/' . $frankenstyledirectoryname; 256 if ($mimetype == 'text/css') { 257 // CSS assets are in a slightly different place to the JS. 258 $contentfile = $contentfile . '/assets/skins/sam/' . $frankenstylefilename; 259 260 // Add the path to the bits to handle fallback for non-shifted assets. 261 $bits[] = 'assets'; 262 $bits[] = 'skins'; 263 $bits[] = 'sam'; 264 } else { 265 $contentfile = $contentfile . '/' . $frankenstylefilename; 266 } 267 268 // If the shifted versions don't exist, fall back to the non-shifted file. 269 if (!file_exists($contentfile) or !is_file($contentfile)) { 270 // We have to revert to the non-minified and non-debug versions. 271 $filename = preg_replace('/-(min|debug)\./', '.', $filename); 272 $contentfile = $dir . '/yui/' . join('/', $bits) . '/' . $filename; 273 } 274 } else if ($version === '2in3') { 275 $contentfile = "$CFG->libdir/yuilib/$part"; 276 277 } else if ($version == 'gallery') { 278 if (count($bits) <= 2) { 279 // This is an invalid module load attempt. 280 $content .= "\n// Incorrect moodle module inclusion. Not enough component information in {$part}.\n"; 281 continue; 282 } 283 $revision = (int)array_shift($bits); 284 if ($revision === -1) { 285 // Revision -1 says please don't cache the JS 286 $cache = false; 287 } 288 $contentfile = "$CFG->libdir/yuilib/gallery/" . join('/', $bits); 289 290 } else if ($version == 'yuiuseall') { 291 // Create global Y that is available in global scope, 292 // this is the trick behind original SimpleYUI. 293 $filecontent = "var Y = YUI().use('*');"; 294 295 } else { 296 // Allow support for revisions on YUI between official releases. 297 // We can just discard the subrevision since it is only used to invalidate the browser cache. 298 $yuipatchedversion = explode('_', $version); 299 $yuiversion = $yuipatchedversion[0]; 300 if ($yuiversion != $CFG->yui3version) { 301 $content .= "\n// Wrong yui version $part!\n"; 302 continue; 303 } 304 $newpart = explode('/', $part); 305 $newpart[0] = $yuiversion; 306 $part = implode('/', $newpart); 307 $contentfile = "$CFG->libdir/yuilib/$part"; 308 } 309 if (!file_exists($contentfile) or !is_file($contentfile)) { 310 $location = '$CFG->dirroot'.preg_replace('/^'.preg_quote($CFG->dirroot, '/').'/', '', $contentfile); 311 $content .= "\n// Combo resource $part ($location) not found!\n"; 312 continue; 313 } 314 315 if (empty($filecontent)) { 316 $filecontent = file_get_contents($contentfile); 317 } 318 $fmodified = filemtime($contentfile); 319 if ($fmodified > $lastmodified) { 320 $lastmodified = $fmodified; 321 } 322 323 $relroot = preg_replace('|^http.?://[^/]+|', '', $CFG->wwwroot); 324 $sep = ($slasharguments ? '/' : '?file='); 325 326 if ($mimetype === 'text/css') { 327 if ($version == 'moodle') { 328 // Search for all images in the file and replace with an appropriate link to the yui_image.php script 329 $imagebits = array( 330 $sep . $version, 331 $frankenstyle, 332 $modulename, 333 array_shift($bits), 334 '$1.$2' 335 ); 336 337 $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', $relroot . '/theme/yui_image.php' . implode('/', $imagebits), $filecontent); 338 } else if ($version == '2in3') { 339 // First we need to remove relative paths to images. These are used by YUI modules to make use of global assets. 340 // I've added this as a separate regex so it can be easily removed once 341 // YUI standardise there CSS methods 342 $filecontent = preg_replace('#(\.\./\.\./\.\./\.\./assets/skins/sam/)?([a-z0-9_-]+)\.(png|gif)#', '$2.$3', $filecontent); 343 344 // search for all images in yui2 CSS and serve them through the yui_image.php script 345 $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', $relroot.'/theme/yui_image.php'.$sep.$CFG->yui2version.'/$1.$2', $filecontent); 346 347 } else if ($version == 'gallery') { 348 // Replace any references to the CDN with a relative link. 349 $filecontent = preg_replace('#(' . preg_quote('http://yui.yahooapis.com/') . '(gallery-[^/]*/))#', '../../../../', $filecontent); 350 351 // Replace all relative image links with the a link to yui_image.php. 352 $filecontent = preg_replace('#(' . preg_quote('../../../../') . ')(gallery-[^/]*/assets/skins/sam/[a-z0-9_-]+)\.(png|gif)#', 353 $relroot . '/theme/yui_image.php' . $sep . '/gallery/' . $revision . '/$2.$3', $filecontent); 354 355 } else { 356 // First we need to remove relative paths to images. These are used by YUI modules to make use of global assets. 357 // I've added this as a separate regex so it can be easily removed once 358 // YUI standardise there CSS methods 359 $filecontent = preg_replace('#(\.\./\.\./\.\./\.\./assets/skins/sam/)?([a-z0-9_-]+)\.(png|gif)#', '$2.$3', $filecontent); 360 361 // search for all images in yui2 CSS and serve them through the yui_image.php script 362 $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', $relroot.'/theme/yui_image.php'.$sep.$version.'/$1.$2', $filecontent); 363 } 364 } 365 366 $content .= $filecontent; 367 } 368 369 if ($lastmodified == 0) { 370 $lastmodified = time(); 371 } 372 373 if ($cache) { 374 combo_send_cached($content, $mimetype, $etag, $lastmodified); 375 } else { 376 combo_send_uncached($content, $mimetype); 377 } 378 379 380 /** 381 * Send the JavaScript cached 382 * @param string $content 383 * @param string $mimetype 384 * @param string $etag 385 * @param int $lastmodified 386 */ 387 function combo_send_cached($content, $mimetype, $etag, $lastmodified) { 388 $lifetime = 60*60*24*360; // 1 year, we do not change YUI versions often, there are a few custom yui modules 389 390 header('Content-Disposition: inline; filename="combo"'); 391 header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT'); 392 header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT'); 393 header('Pragma: '); 394 header('Cache-Control: public, max-age='.$lifetime.', immutable'); 395 header('Accept-Ranges: none'); 396 header('Content-Type: '.$mimetype); 397 header('Etag: "'.$etag.'"'); 398 if (!min_enable_zlib_compression()) { 399 header('Content-Length: '.strlen($content)); 400 } 401 402 echo $content; 403 die; 404 } 405 406 /** 407 * Send the JavaScript uncached 408 * @param string $content 409 * @param string $mimetype 410 */ 411 function combo_send_uncached($content, $mimetype) { 412 header('Content-Disposition: inline; filename="combo"'); 413 header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT'); 414 header('Expires: '. gmdate('D, d M Y H:i:s', time() + 2) .' GMT'); 415 header('Pragma: '); 416 header('Accept-Ranges: none'); 417 header('Content-Type: '.$mimetype); 418 if (!min_enable_zlib_compression()) { 419 header('Content-Length: '.strlen($content)); 420 } 421 422 echo $content; 423 die; 424 } 425 426 function combo_not_found($message = '') { 427 header('HTTP/1.0 404 not found'); 428 if ($message) { 429 echo $message; 430 } else { 431 echo 'Combo resource not found, sorry.'; 432 } 433 die; 434 } 435 436 function combo_params() { 437 if (isset($_SERVER['QUERY_STRING']) and strpos($_SERVER['QUERY_STRING'], 'file=/') === 0) { 438 // url rewriting 439 $slashargument = substr($_SERVER['QUERY_STRING'], 6); 440 return array($slashargument, true); 441 442 } else if (isset($_SERVER['REQUEST_URI']) and strpos($_SERVER['REQUEST_URI'], '?') !== false) { 443 $parts = explode('?', $_SERVER['REQUEST_URI'], 2); 444 return array($parts[1], false); 445 446 } else if (isset($_SERVER['QUERY_STRING']) and strpos($_SERVER['QUERY_STRING'], '?') !== false) { 447 // note: buggy or misconfigured IIS does return the query string in REQUEST_URI 448 return array($_SERVER['QUERY_STRING'], false); 449 450 } else if ($slashargument = min_get_slash_argument(false)) { 451 $slashargument = ltrim($slashargument, '/'); 452 return array($slashargument, true); 453 454 } else { 455 // unsupported server, sorry! 456 combo_not_found('Unsupported server - query string can not be determined, try disabling YUI combo loading in admin settings.'); 457 } 458 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body