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