Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.
/rss/ -> file.php (source)

Differences Between: [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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   * rss/file.php - entry point to serve rss streams
  19   *
  20   * This script simply checks the parameters to construct a $USER
  21   * then finds and calls a function in the relevant component to
  22   * actually check security and create the RSS stream
  23   *
  24   * @package    core_rss
  25   * @category   rss
  26   * @copyright  1999 onwards Martin Dougiamas {@link http://moodle.com}
  27   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  28   */
  29  
  30  /** NO_DEBUG_DISPLAY - bool, Disable moodle debug and error messages. Set to false to see any errors during RSS generation */
  31  define('NO_DEBUG_DISPLAY', true);
  32  
  33  /** NO_MOODLE_COOKIES - bool, Disable the use of sessions/cookies - we recreate $USER for every call. */
  34  define('NO_MOODLE_COOKIES', true);
  35  
  36  require_once('../config.php');
  37  require_once($CFG->libdir.'/filelib.php');
  38  require_once($CFG->libdir.'/rsslib.php');
  39  
  40  // RSS feeds must be enabled site-wide.
  41  if (empty($CFG->enablerssfeeds)) {
  42      rss_error();
  43  }
  44  
  45  // All the arguments are in the path.
  46  $relativepath = get_file_argument();
  47  if (!$relativepath) {
  48      rss_error();
  49  }
  50  
  51  // Extract relative path components into variables.
  52  $args = explode('/', trim($relativepath, '/'));
  53  if (count($args) < 5) {
  54      rss_error();
  55  }
  56  
  57  $contextid   = (int)$args[0];
  58  $token  = clean_param($args[1], PARAM_ALPHANUM);
  59  $componentname = clean_param($args[2], PARAM_FILE);
  60  
  61  // Check if they have requested a 1.9 RSS feed.
  62  // If token is an int it is a user id (1.9 request).
  63  // If token contains any letters it is a token (2.0 request).
  64  $inttoken = intval($token);
  65  if ($token === "$inttoken") {
  66      // They have requested a feed using a 1.9 url. redirect them to the 2.0 url using the guest account.
  67  
  68      $instanceid  = clean_param($args[3], PARAM_INT);
  69  
  70      // 1.9 URL puts course id where the context id is in 2.0 URLs.
  71      $courseid = $contextid;
  72      unset($contextid);
  73  
  74      // Find the context id.
  75      if ($course = $DB->get_record('course', array('id' => $courseid))) {
  76          $modinfo = get_fast_modinfo($course);
  77  
  78          foreach ($modinfo->get_instances_of($componentname) as $modinstanceid => $cm) {
  79              if ($modinstanceid == $instanceid) {
  80                  $context = context_module::instance($cm->id, IGNORE_MISSING);
  81                  break;
  82              }
  83          }
  84      }
  85  
  86      if (empty($context)) {
  87          // This shouldnt happen. something bad is going on.
  88          rss_error();
  89      }
  90  
  91      // Make sure that $CFG->siteguest is set.
  92      if (empty($CFG->siteguest)) {
  93          if (!$guestid = $DB->get_field('user', 'id', array('username' => 'guest', 'mnethostid' => $CFG->mnet_localhost_id))) {
  94              // Guest does not exist yet, weird.
  95              rss_error();
  96          }
  97          set_config('siteguest', $guestid);
  98      }
  99      $guesttoken = rss_get_token($CFG->siteguest);
 100  
 101      // Change forum to mod_forum (for example).
 102      $componentname = 'mod_'.$componentname;
 103  
 104      $url = $PAGE->url;
 105      $url->set_slashargument("/{$context->id}/$guesttoken/$componentname/$instanceid/rss.xml");
 106  
 107      // Redirect to the 2.0 rss URL.
 108      redirect($url);
 109  } else {
 110      // Authenticate the user from the token.
 111      $userid = rss_get_userid_from_token($token);
 112      if (!$userid) {
 113          rss_error('rsserrorauth', 'rss.xml', 0, '403 Forbidden');
 114      }
 115  }
 116  
 117  // Check the context actually exists.
 118  try {
 119      list($context, $course, $cm) = get_context_info_array($contextid);
 120  } catch (dml_missing_record_exception $e) {
 121      rss_error();
 122  }
 123  
 124  $PAGE->set_context($context);
 125  
 126  $user = get_complete_user_data('id', $userid);
 127  
 128  // Let enrol plugins deal with new enrolments if necessary.
 129  enrol_check_plugins($user);
 130  
 131  \core\session\manager::set_user($user); // For login and capability checks.
 132  
 133  try {
 134      $autologinguest = true;
 135      $setwantsurltome = true;
 136      $preventredirect = true;
 137      require_course_login($course, $autologinguest, $cm, $setwantsurltome, $preventredirect);
 138  } catch (Exception $e) {
 139      if (isguestuser()) {
 140          rss_error('rsserrorguest', 'rss.xml', 0, '403 Forbidden');
 141      } else {
 142          rss_error('rsserrorauth', 'rss.xml', 0, '403 Forbidden');
 143      }
 144  }
 145  
 146  // Work out which component in Moodle we want (from the frankenstyle name).
 147  $componentdir = core_component::get_component_directory($componentname);
 148  list($type, $plugin) = core_component::normalize_component($componentname);
 149  
 150  // Call the component to check/update the feed and tell us the path to the cached file.
 151  $pathname = null;
 152  
 153  if (file_exists($componentdir)) {
 154      require_once("$componentdir/rsslib.php");
 155      $functionname = $plugin.'_rss_get_feed';
 156  
 157      if (function_exists($functionname)) {
 158          // The $pathname will be null if there was a problem (eg user doesn't have the necessary capabilities).
 159          // NOTE:the component providing the feed must do its own capability checks and security.
 160          try {
 161              $pathname = $functionname($context, $args);
 162          } catch (Exception $e) {
 163              rss_error('rsserror');
 164          }
 165      }
 166  }
 167  
 168  // Check that file exists.
 169  if (empty($pathname) || !file_exists($pathname)) {
 170      rss_error();
 171  }
 172  
 173  // Send the RSS file to the user!
 174  send_file($pathname, 'rss.xml', 3600);   // Cached by browsers for 1 hour.
 175  
 176  /**
 177   * Sends an error formatted as an rss file and then exits
 178   *
 179   * @package core_rss
 180   * @category rss
 181   *
 182   * @param string $error the error type, default is rsserror
 183   * @param string $filename the name of the file to created
 184   * @param int $unused
 185   * @param string $statuscode http 1.1 statuscode indicicating the error
 186   * @uses exit
 187   */
 188  function rss_error($error='rsserror', $filename='rss.xml', $unused=0, $statuscode='404 Not Found') {
 189      header("HTTP/1.1 $statuscode");
 190      header('Content-Disposition: inline; filename="'.$filename.'"');
 191      header('Content-Type: application/xml');
 192      echo rss_geterrorxmlfile($error);
 193      exit;
 194  }