Search moodle.org's
Developer Documentation


Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.
  • /theme/ -> font.php (source)

    Differences Between: [Versions 32 and 39]

       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 the fonts used in CSS.
      19   *
      20   * Note: it is recommended to use only WOFF (Web Open Font Format) fonts.
      21   *
      22   * @package   core
      23   * @copyright 2013 Petr Skoda (skodak)  {@link http://skodak.org}
      24   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      25   */
      26  
      27  // Disable moodle specific debug messages and any errors in output,
      28  // comment out when debugging or better look into error log!
      29  define('NO_DEBUG_DISPLAY', true);
      30  
      31  define('ABORT_AFTER_CONFIG', true);
      32  require('../config.php');
      33  
      34  if ($slashargument = min_get_slash_argument()) {
      35      $slashargument = ltrim($slashargument, '/');
      36      if (substr_count($slashargument, '/') < 3) {
      37          font_not_found();
      38      }
      39      list($themename, $component, $rev, $font) = explode('/', $slashargument, 4);
      40      $themename = min_clean_param($themename, 'SAFEDIR');
      41      $component = min_clean_param($component, 'SAFEDIR');
      42      $rev       = min_clean_param($rev, 'INT');
      43      $font      = min_clean_param($font, 'RAW');
      44  
      45  } else {
      46      $themename = min_optional_param('theme', 'standard', 'SAFEDIR');
      47      $component = min_optional_param('component', 'core', 'SAFEDIR');
      48      $rev       = min_optional_param('rev', -1, 'INT');
      49      $font      = min_optional_param('font', '', 'RAW');
      50  }
      51  
      52  if (!$font) {
      53      font_not_found();
      54  }
      55  
      56  if ($to = strpos($font, '?')) {
      57      $font = substr($font, 0, $to);
      58  }
      59  
      60  if (empty($component) or $component === 'moodle' or $component === 'core') {
      61      $component = 'core';
      62  }
      63  
      64  if (preg_match('/^[a-z0-9_-]+\.woff2$/i', $font, $matches)) {
      65      $font = $matches[0];
      66      $mimetype = 'application/font-woff2';
      67  
      68  } else if (preg_match('/^[a-z0-9_-]+\.woff$/i', $font, $matches)) {
      69      // This is the real standard!
      70      $font = $matches[0];
      71      $mimetype = 'application/font-woff';
      72  
      73  } else if (preg_match('/^[a-z0-9_-]+\.ttf$/i', $font, $matches)) {
      74      $font = $matches[0];
      75      $mimetype = 'application/x-font-ttf';
      76  
      77  } else if (preg_match('/^[a-z0-9_-]+\.otf$/i', $font, $matches)) {
      78      $font = $matches[0];
      79      $mimetype = 'application/x-font-opentype';
      80  
      81  } else if (preg_match('/^[a-z0-9_-]+\.eot$/i', $font, $matches)) {
      82      // IE8 must die!!!
      83      $font = $matches[0];
      84      $mimetype = 'application/vnd.ms-fontobject';
      85  } else if (preg_match('/^[a-z0-9_-]+\.svg$/i', $font, $matches)) {
      86      $font = $matches[0];
      87      $mimetype = 'image/svg+xml';
      88  
      89  } else {
      90      font_not_found();
      91  }
      92  
      93  if (file_exists("$CFG->dirroot/theme/$themename/config.php")) {
      94      // Normal theme exists.
      95  } else if (!empty($CFG->themedir) and file_exists("$CFG->themedir/$themename/config.php")) {
      96      // Theme exists in alternative location.
      97  } else {
      98      font_not_found();
      99  }
     100  
     101  $candidatelocation = "$CFG->localcachedir/theme/$rev/$themename/fonts/$component";
     102  $etag = sha1("$rev/$themename/$component/$font");
     103  
     104  if ($rev > 0) {
     105      if (file_exists("$candidatelocation/$font.error")) {
     106          font_not_found();
     107      }
     108  
     109      if (file_exists("$candidatelocation/$font")) {
     110          if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
     111              // We do not actually need to verify the etag value because our files
     112              // never change in cache because we increment the rev parameter.
     113              // 90 days only - based on Moodle point release cadence being every 3 months.
     114              $lifetime = 60 * 60 * 24 * 90;
     115              header('HTTP/1.1 304 Not Modified');
     116              header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
     117              header('Cache-Control: public, max-age='.$lifetime);
     118              header('Content-Type: '.$mimetype);
     119              header('Etag: "'.$etag.'"');
     120              die;
     121          }
     122          send_cached_font("$candidatelocation/$font", $etag, $font, $mimetype);
     123      }
     124  }
     125  
     126  // Ok, now we need to start normal moodle script, we need to load all libs and $DB.
     127  define('ABORT_AFTER_CONFIG_CANCEL', true);
     128  
     129  define('NO_MOODLE_COOKIES', true); // Session not used here.
     130  define('NO_UPGRADE_CHECK', true);  // Ignore upgrade check.
     131  
     132  require("$CFG->dirroot/lib/setup.php");
     133  
     134  $theme = theme_config::load($themename);
     135  $themerev = theme_get_revision();
     136  
     137  $fontfile = $theme->resolve_font_location($font, $component);
     138  
     139  if ($themerev <= 0 or $rev != $themerev) {
     140      // Do not send caching headers if they do not request current revision,
     141      // we do not want to pollute browser caches with outdated fonts.
     142      if (empty($fontfile) or !is_readable($fontfile)) {
     143          font_not_found();
     144      }
     145      send_uncached_font($fontfile, $font, $mimetype);
     146  }
     147  
     148  make_localcache_directory('theme', false);
     149  
     150  if (empty($fontfile) or !is_readable($fontfile)) {
     151      if (!file_exists($candidatelocation)) {
     152          @mkdir($candidatelocation, $CFG->directorypermissions, true);
     153      }
     154      // Make note we can not find this file.
     155      $cachefont = "$candidatelocation/$font.error";
     156      $fp = fopen($cachefont, 'w');
     157      fclose($fp);
     158      font_not_found();
     159  }
     160  
     161  $cachefont = cache_font($font, $fontfile, $candidatelocation);
     162  if (connection_aborted()) {
     163      die;
     164  }
     165  // Make sure nothing failed.
     166  clearstatcache();
     167  if (file_exists($cachefont)) {
     168      send_cached_font($cachefont, $etag, $font, $mimetype);
     169  }
     170  
     171  send_uncached_font($fontfile, $font, $mimetype);
     172  
     173  
     174  
     175  // Utility functions.
     176  
     177  function send_cached_font($fontpath, $etag, $font, $mimetype) {
     178      global $CFG;
     179      require("$CFG->dirroot/lib/xsendfilelib.php");
     180  
     181      // 90 days only - based on Moodle point release cadence being every 3 months.
     182      $lifetime = 60 * 60 * 24 * 90;
     183  
     184      header('Etag: "'.$etag.'"');
     185      header('Content-Disposition: inline; filename="'.$font.'"');
     186      header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($fontpath)) .' GMT');
     187      header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
     188      header('Pragma: ');
     189      header('Cache-Control: public, max-age='.$lifetime.', immutable');
     190      header('Accept-Ranges: none');
     191      header('Content-Type: '.$mimetype);
     192      header('Content-Length: '.filesize($fontpath));
     193  
     194      if (xsendfile($fontpath)) {
     195          die;
     196      }
     197  
     198      // No need to gzip already compressed fonts.
     199  
     200      readfile($fontpath);
     201      die;
     202  }
     203  
     204  function send_uncached_font($fontpath, $font, $mimetype) {
     205      header('Content-Disposition: inline; filename="'.$font.'"');
     206      header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
     207      header('Expires: '. gmdate('D, d M Y H:i:s', time() + 15) .' GMT');
     208      header('Pragma: ');
     209      header('Accept-Ranges: none');
     210      header('Content-Type: '.$mimetype);
     211      header('Content-Length: '.filesize($fontpath));
     212  
     213      readfile($fontpath);
     214      die;
     215  }
     216  
     217  function font_not_found() {
     218      header('HTTP/1.0 404 not found');
     219      die('font was not found, sorry.');
     220  }
     221  
     222  /**
     223   * Caches a given font file.
     224   *
     225   * @param string $font The name of the font that was requested.
     226   * @param string $fontfile The location of the font file we want to cache.
     227   * @param string $candidatelocation The location to cache it in.
     228   * @return string The path to the cached font.
     229   */
     230  function cache_font($font, $fontfile, $candidatelocation) {
     231      global $CFG;
     232      $cachefont = "$candidatelocation/$font";
     233  
     234      clearstatcache();
     235      if (!file_exists($candidatelocation)) {
     236          @mkdir($candidatelocation, $CFG->directorypermissions, true);
     237      }
     238  
     239      // Prevent serving of incomplete file from concurrent request,
     240      // the rename() should be more atomic than copy().
     241      ignore_user_abort(true);
     242      if (@copy($fontfile, $cachefont.'.tmp')) {
     243          rename($cachefont.'.tmp', $cachefont);
     244          @chmod($cachefont, $CFG->filepermissions);
     245          @unlink($cachefont.'.tmp'); // Just in case anything fails.
     246      }
     247      return $cachefont;
     248  }
    

    Search This Site: