Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
/lib/ -> requirejs.php (source)

Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403]

   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 serving optimised JS for RequireJS.
  19   *
  20   * @package    core
  21   * @copyright  2015 Damyon Wiese
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  // Disable moodle specific debug messages and any errors in output,
  26  // comment out when debugging or better look into error log!
  27  define('NO_DEBUG_DISPLAY', true);
  28  
  29  // We need just the values from config.php and minlib.php.
  30  define('ABORT_AFTER_CONFIG', true);
  31  require('../config.php'); // This stops immediately at the beginning of lib/setup.php.
  32  require_once("$CFG->dirroot/lib/jslib.php");
  33  require_once("$CFG->dirroot/lib/classes/requirejs.php");
  34  
  35  $slashargument = min_get_slash_argument();
  36  if (!$slashargument) {
  37      // The above call to min_get_slash_argument should always work.
  38      die('Invalid request');
  39  }
  40  
  41  $slashargument = ltrim($slashargument, '/');
  42  if (substr_count($slashargument, '/') < 1) {
  43      header('HTTP/1.0 404 not found');
  44      die('Slash argument must contain both a revision and a file path');
  45  }
  46  // Split into revision and module name.
  47  list($rev, $file) = explode('/', $slashargument, 2);
  48  $rev  = min_clean_param($rev, 'INT');
  49  $file = '/' . min_clean_param($file, 'SAFEPATH');
  50  
  51  // Only load js files from the js modules folder from the components.
  52  $jsfiles = array();
  53  list($unused, $component, $module) = explode('/', $file, 3);
  54  
  55  // Use the caching only for meaningful revision numbers which prevents future cache poisoning.
  56  if ($rev > 0 and $rev < (time() + 60 * 60)) {
  57      // This is "production mode".
  58      // Some (huge) modules are better loaded lazily (when they are used). If we are requesting
  59      // one of these modules, only return the one module, not the combo.
  60      $lazysuffix = "-lazy.js";
  61      $lazyload = (strpos($module, $lazysuffix) !== false);
  62  
  63      if ($lazyload) {
  64          // We are lazy loading a single file - so include the component/filename pair in the etag.
  65          $etag = sha1($rev . '/' . $component . '/' . $module);
  66      } else {
  67          // We loading all (non-lazy) files - so only the rev makes this request unique.
  68          $etag = sha1($rev);
  69      }
  70  
  71      $candidate = $CFG->localcachedir . '/requirejs/' . $etag;
  72  
  73      if (file_exists($candidate)) {
  74          if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
  75              // We do not actually need to verify the etag value because our files
  76              // never change in cache because we increment the rev parameter.
  77              js_send_unmodified(filemtime($candidate), $etag);
  78          }
  79          js_send_cached($candidate, $etag, 'requirejs.php');
  80          exit(0);
  81  
  82      } else {
  83          $jsfiles = array();
  84          if ($lazyload) {
  85              $jsfiles = core_requirejs::find_one_amd_module($component, $module);
  86          } else {
  87              // Here we respond to the request by returning ALL amd modules. This saves
  88              // round trips in production.
  89  
  90              $jsfiles = core_requirejs::find_all_amd_modules();
  91          }
  92  
  93          $content = '';
  94          foreach ($jsfiles as $modulename => $jsfile) {
  95              $js = file_get_contents($jsfile);
  96              if ($js === false) {
  97                  error_log('Failed to load JavaScript file ' . $jsfile);
  98                  $js = "/* Failed to load JavaScript file {$jsfile}. */\n";
  99                  $content = $js . $content;
 100                  continue;
 101              }
 102              // Remove source map link.
 103              $js = preg_replace('~//# sourceMappingURL.*$~s', '', $js);
 104              $js = rtrim($js);
 105              $js .= "\n";
 106  
 107              if (preg_match('/define\(\s*(\[|function)/', $js)) {
 108                  // If the JavaScript module has been defined without specifying a name then we'll
 109                  // add the Moodle module name now.
 110                  $replace = 'define(\'' . $modulename . '\', ';
 111                  $search = 'define(';
 112                  // Replace only the first occurrence.
 113                  $js = implode($replace, explode($search, $js, 2));
 114              }
 115  
 116              $content .= $js;
 117          }
 118  
 119          js_write_cache_file_content($candidate, $content);
 120          // Verify nothing failed in cache file creation.
 121          clearstatcache();
 122          if (file_exists($candidate)) {
 123              js_send_cached($candidate, $etag, 'requirejs.php');
 124              exit(0);
 125          }
 126      }
 127  }
 128  
 129  // If we've made it here then we're in "dev mode" where everything is lazy loaded.
 130  // So all files will be served one at a time.
 131  $jsfiles = core_requirejs::find_one_amd_module($component, $module, false);
 132  
 133  if (!empty($jsfiles)) {
 134      $modulename = array_keys($jsfiles)[0];
 135      $jsfile = $jsfiles[$modulename];
 136      $shortfilename = str_replace($CFG->dirroot, '', $jsfile);
 137      $mapfile = $jsfile . '.map';
 138  
 139      if (file_exists($mapfile)) {
 140          // We've got a a source map file so we can return the minified file here and
 141          // the source map will be used by the browser to debug.
 142          $js = file_get_contents($jsfile);
 143          // Fix the source map link for the file.
 144          $js = preg_replace(
 145              '~//# sourceMappingURL.*$~s',
 146              "//# sourceMappingURL={$CFG->wwwroot}/lib/jssourcemap.php{$file}",
 147              $js
 148          );
 149          $js = rtrim($js);
 150      } else {
 151          // This file doesn't have a map file. We might be dealing with an older source file from
 152          // a plugin or previous version of Moodle so we should just return the full original source
 153          // like we used to.
 154          $originalsource = str_replace('/amd/build/', '/amd/src/', $jsfile);
 155          $originalsource = str_replace('.min.js', '.js', $originalsource);
 156          $js = file_get_contents($originalsource);
 157          $js = rtrim($js);
 158      }
 159  
 160      if (preg_match('/define\(\s*(\[|function)/', $js)) {
 161          // If the JavaScript module has been defined without specifying a name then we'll
 162          // add the Moodle module name now.
 163          $replace = 'define(\'' . $modulename . '\', ';
 164  
 165          // Replace only the first occurrence.
 166          $js = implode($replace, explode('define(', $js, 2));
 167      } else if (!preg_match('/define\s*\(/', $js)) {
 168          debugging('JS file: ' . $shortfilename . ' cannot be loaded, or does not contain a javascript' .
 169                    ' module in AMD format. "define()" not found.', DEBUG_DEVELOPER);
 170      }
 171  
 172      js_send_uncached($js, 'requirejs.php');
 173  } else {
 174      // We can't find the requested file.
 175      header('HTTP/1.0 404 not found');
 176  }