Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]
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 199 // We need to add these new parts to the beginning of the $parts list, not the end. 200 if ($type === 'js') { 201 $newparts = array(); 202 foreach ($yuimodules as $module) { 203 $newparts[] = $yuiversion . '/' . $module . '/' . $module . $filesuffix; 204 } 205 $newparts[] = 'yuiuseall/yuiuseall'; 206 $parts = array_merge($newparts, $parts); 207 } else { 208 $newparts = array(); 209 foreach ($yuimodules as $module) { 210 $candidate = $yuiversion . '/' . $module . '/assets/skins/sam/' . $module . '.css'; 211 if (!file_exists("$CFG->libdir/yuilib/$candidate")) { 212 continue; 213 } 214 $newparts[] = $candidate; 215 } 216 if ($newparts) { 217 $parts = array_merge($newparts, $parts); 218 } 219 } 220 } 221 222 continue; 223 } 224 if ($version === 'm') { 225 $version = 'moodle'; 226 } 227 if ($version === 'moodle') { 228 if (count($bits) <= 3) { 229 // This is an invalid module load attempt. 230 $content .= "\n// Incorrect moodle module inclusion. Not enough component information in {$part}.\n"; 231 continue; 232 } 233 $revision = (int)array_shift($bits); 234 if (!min_is_revision_valid_and_current($revision)) { 235 // A non-current revision means please don't cache the JS 236 $revision = -1; 237 $cache = false; 238 } 239 $frankenstyle = array_shift($bits); 240 $filename = array_pop($bits); 241 $modulename = $bits[0]; 242 $dir = core_component::get_component_directory($frankenstyle); 243 244 // For shifted YUI modules, we need the YUI module name in frankenstyle format. 245 $frankenstylemodulename = join('-', array($version, $frankenstyle, $modulename)); 246 $frankenstylefilename = preg_replace('/' . $modulename . '/', $frankenstylemodulename, $filename); 247 248 // Submodules are stored in a directory with the full submodule name. 249 // We need to remove the -debug.js, -min.js, and .js from the file name to calculate that directory name. 250 $frankenstyledirectoryname = str_replace(array('-min.js', '-debug.js', '.js', '.css'), '', $frankenstylefilename); 251 252 // By default, try and use the /yui/build directory. 253 $contentfile = $dir . '/yui/build/' . $frankenstyledirectoryname; 254 if ($mimetype == 'text/css') { 255 // CSS assets are in a slightly different place to the JS. 256 $contentfile = $contentfile . '/assets/skins/sam/' . $frankenstylefilename; 257 258 // Add the path to the bits to handle fallback for non-shifted assets. 259 $bits[] = 'assets'; 260 $bits[] = 'skins'; 261 $bits[] = 'sam'; 262 } else { 263 $contentfile = $contentfile . '/' . $frankenstylefilename; 264 } 265 266 // If the shifted versions don't exist, fall back to the non-shifted file. 267 if (!file_exists($contentfile) or !is_file($contentfile)) { 268 // We have to revert to the non-minified and non-debug versions. 269 $filename = preg_replace('/-(min|debug)\./', '.', $filename); 270 $contentfile = $dir . '/yui/' . join('/', $bits) . '/' . $filename; 271 } 272 } else if ($version === '2in3') { 273 $contentfile = "$CFG->libdir/yuilib/$part"; 274 275 } else if ($version == 'gallery') { 276 if (count($bits) <= 2) { 277 // This is an invalid module load attempt. 278 $content .= "\n// Incorrect moodle module inclusion. Not enough component information in {$part}.\n"; 279 continue; 280 } 281 $revision = (int)array_shift($bits); 282 if (!min_is_revision_valid_and_current($revision)) { 283 // A non-current revision means please don't cache the JS 284 $revision = -1; 285 $cache = false; 286 } 287 $contentfile = "$CFG->libdir/yuilib/gallery/" . join('/', $bits); 288 289 } else if ($version == 'yuiuseall') { 290 // Create global Y that is available in global scope, 291 // this is the trick behind original SimpleYUI. 292 $filecontent = "var Y = YUI().use('*');"; 293 294 } else { 295 // Allow support for revisions on YUI between official releases. 296 // We can just discard the subrevision since it is only used to invalidate the browser cache. 297 $yuipatchedversion = explode('_', $version); 298 $yuiversion = $yuipatchedversion[0]; 299 if ($yuiversion != $CFG->yui3version) { 300 $content .= "\n// Wrong yui version $part!\n"; 301 continue; 302 } 303 $newpart = explode('/', $part); 304 $newpart[0] = $yuiversion; 305 $part = implode('/', $newpart); 306 $contentfile = "$CFG->libdir/yuilib/$part"; 307 } 308 if (!file_exists($contentfile) or !is_file($contentfile)) { 309 $location = '$CFG->dirroot'.preg_replace('/^'.preg_quote($CFG->dirroot, '/').'/', '', $contentfile); 310 $content .= "\n// Combo resource $part ($location) not found!\n"; 311 continue; 312 } 313 314 if (empty($filecontent)) { 315 $filecontent = file_get_contents($contentfile); 316 } 317 $fmodified = filemtime($contentfile); 318 if ($fmodified > $lastmodified) { 319 $lastmodified = $fmodified; 320 } 321 322 $relroot = preg_replace('|^http.?://[^/]+|', '', $CFG->wwwroot); 323 $sep = ($slasharguments ? '/' : '?file='); 324 325 if ($mimetype === 'text/css') { 326 if ($version == 'moodle') { 327 // Search for all images in the file and replace with an appropriate link to the yui_image.php script 328 $imagebits = array( 329 $sep . $version, 330 $frankenstyle, 331 $modulename, 332 array_shift($bits), 333 '$1.$2' 334 ); 335 336 $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', $relroot . '/theme/yui_image.php' . implode('/', $imagebits), $filecontent); 337 } else if ($version == '2in3') { 338 // First we need to remove relative paths to images. These are used by YUI modules to make use of global assets. 339 // I've added this as a separate regex so it can be easily removed once 340 // YUI standardise there CSS methods 341 $filecontent = preg_replace('#(\.\./\.\./\.\./\.\./assets/skins/sam/)?([a-z0-9_-]+)\.(png|gif)#', '$2.$3', $filecontent); 342 343 // search for all images in yui2 CSS and serve them through the yui_image.php script 344 $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', $relroot.'/theme/yui_image.php'.$sep.$CFG->yui2version.'/$1.$2', $filecontent); 345 346 } else if ($version == 'gallery') { 347 // Replace any references to the CDN with a relative link. 348 $filecontent = preg_replace('#(' . preg_quote('http://yui.yahooapis.com/') . '(gallery-[^/]*/))#', '../../../../', $filecontent); 349 350 // Replace all relative image links with the a link to yui_image.php. 351 $filecontent = preg_replace('#(' . preg_quote('../../../../') . ')(gallery-[^/]*/assets/skins/sam/[a-z0-9_-]+)\.(png|gif)#', 352 $relroot . '/theme/yui_image.php' . $sep . '/gallery/' . $revision . '/$2.$3', $filecontent); 353 354 } else { 355 // First we need to remove relative paths to images. These are used by YUI modules to make use of global assets. 356 // I've added this as a separate regex so it can be easily removed once 357 // YUI standardise there CSS methods 358 $filecontent = preg_replace('#(\.\./\.\./\.\./\.\./assets/skins/sam/)?([a-z0-9_-]+)\.(png|gif)#', '$2.$3', $filecontent); 359 360 // search for all images in yui2 CSS and serve them through the yui_image.php script 361 $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', $relroot.'/theme/yui_image.php'.$sep.$version.'/$1.$2', $filecontent); 362 } 363 } 364 365 $content .= $filecontent; 366 } 367 368 if ($lastmodified == 0) { 369 $lastmodified = time(); 370 } 371 372 if ($cache) { 373 combo_send_cached($content, $mimetype, $etag, $lastmodified); 374 } else { 375 combo_send_uncached($content, $mimetype); 376 } 377 378 379 /** 380 * Send the JavaScript cached 381 * @param string $content 382 * @param string $mimetype 383 * @param string $etag 384 * @param int $lastmodified 385 */ 386 function combo_send_cached($content, $mimetype, $etag, $lastmodified) { 387 $lifetime = 60*60*24*360; // 1 year, we do not change YUI versions often, there are a few custom yui modules 388 389 header('Content-Disposition: inline; filename="combo"'); 390 header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT'); 391 header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT'); 392 header('Pragma: '); 393 header('Cache-Control: public, max-age='.$lifetime.', immutable'); 394 header('Accept-Ranges: none'); 395 header('Content-Type: '.$mimetype); 396 header('Etag: "'.$etag.'"'); 397 if (!min_enable_zlib_compression()) { 398 header('Content-Length: '.strlen($content)); 399 } 400 401 echo $content; 402 die; 403 } 404 405 /** 406 * Send the JavaScript uncached 407 * @param string $content 408 * @param string $mimetype 409 */ 410 function combo_send_uncached($content, $mimetype) { 411 header('Content-Disposition: inline; filename="combo"'); 412 header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT'); 413 header('Expires: '. gmdate('D, d M Y H:i:s', time() + 2) .' GMT'); 414 header('Pragma: '); 415 header('Accept-Ranges: none'); 416 header('Content-Type: '.$mimetype); 417 if (!min_enable_zlib_compression()) { 418 header('Content-Length: '.strlen($content)); 419 } 420 421 echo $content; 422 die; 423 } 424 425 function combo_not_found($message = '') { 426 header('HTTP/1.0 404 not found'); 427 if ($message) { 428 echo $message; 429 } else { 430 echo 'Combo resource not found, sorry.'; 431 } 432 die; 433 } 434 435 function combo_params() { 436 if (isset($_SERVER['QUERY_STRING']) and strpos($_SERVER['QUERY_STRING'], 'file=/') === 0) { 437 // url rewriting 438 $slashargument = substr($_SERVER['QUERY_STRING'], 6); 439 return array($slashargument, true); 440 441 } else if (isset($_SERVER['REQUEST_URI']) and strpos($_SERVER['REQUEST_URI'], '?') !== false) { 442 $parts = explode('?', $_SERVER['REQUEST_URI'], 2); 443 return array($parts[1], false); 444 445 } else if (isset($_SERVER['QUERY_STRING']) and strpos($_SERVER['QUERY_STRING'], '?') !== false) { 446 // note: buggy or misconfigured IIS does return the query string in REQUEST_URI 447 return array($_SERVER['QUERY_STRING'], false); 448 449 } else if ($slashargument = min_get_slash_argument(false)) { 450 $slashargument = ltrim($slashargument, '/'); 451 return array($slashargument, true); 452 453 } else { 454 // unsupported server, sorry! 455 combo_not_found('Unsupported server - query string can not be determined, try disabling YUI combo loading in admin settings.'); 456 } 457 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body