Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 401 and 402] [Versions 401 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   * Class registration
  19   *
  20   * @package    core
  21   * @copyright  2017 Marina Glancy
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core\hub;
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  use moodle_exception;
  29  use moodle_url;
  30  use context_system;
  31  use stdClass;
  32  use html_writer;
  33  
  34  /**
  35   * Methods to use when registering the site at the moodle sites directory.
  36   *
  37   * @package    core
  38   * @copyright  2017 Marina Glancy
  39   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  40   */
  41  class registration {
  42  
  43      /** @var array Fields used in a site registration form.
  44       * IMPORTANT: any new fields with non-empty defaults have to be added to CONFIRM_NEW_FIELDS */
  45      const FORM_FIELDS = ['policyagreed', 'language', 'countrycode', 'privacy',
  46          'contactemail', 'contactable', 'emailalert', 'emailalertemail', 'commnews', 'commnewsemail',
  47          'contactname', 'name', 'description', 'imageurl', 'contactphone', 'regioncode', 'geolocation', 'street'];
  48  
  49      /** @var List of new FORM_FIELDS or siteinfo fields added indexed by the version when they were added.
  50       * If site was already registered, admin will be promted to confirm new registration data manually. Until registration is manually confirmed,
  51       * the scheduled task updating registration will be paused.
  52       * Keys of this array are not important as long as they increment, use current date to avoid confusions.
  53       */
  54      const CONFIRM_NEW_FIELDS = [
  55          2017092200 => [
  56              'commnews', // Receive communication news. This was added in 3.4 and is "On" by default. Admin must confirm or opt-out.
  57              'mobileservicesenabled', 'mobilenotificationsenabled', 'registereduserdevices', 'registeredactiveuserdevices' // Mobile stats added in 3.4.
  58          ],
  59          // Analytics stats added in Moodle 3.7.
  60          2019022200 => ['analyticsenabledmodels', 'analyticspredictions', 'analyticsactions', 'analyticsactionsnotuseful'],
  61          // Active users stats added in Moodle 3.9.
  62          2020022600 => ['activeusers', 'activeparticipantnumberaverage'],
  63      ];
  64  
  65      /** @var Site privacy: not displayed */
  66      const HUB_SITENOTPUBLISHED = 'notdisplayed';
  67  
  68      /** @var Site privacy: public */
  69      const HUB_SITENAMEPUBLISHED = 'named';
  70  
  71      /** @var Site privacy: public and global */
  72      const HUB_SITELINKPUBLISHED = 'linked';
  73  
  74      /** @var stdClass cached site registration information */
  75      protected static $registration = null;
  76  
  77      /**
  78       * Get site registration
  79       *
  80       * @param bool $confirmed
  81       * @return stdClass|null
  82       */
  83      protected static function get_registration($confirmed = true) {
  84          global $DB;
  85  
  86          if (self::$registration === null) {
  87              self::$registration = $DB->get_record('registration_hubs', ['huburl' => HUB_MOODLEORGHUBURL]) ?: null;
  88          }
  89  
  90          if (self::$registration && (bool)self::$registration->confirmed == (bool)$confirmed) {
  91              return self::$registration;
  92          }
  93  
  94          return null;
  95      }
  96  
  97      /**
  98       * Same as get_registration except it throws exception if site not registered
  99       *
 100       * @return stdClass
 101       * @throws \moodle_exception
 102       */
 103      public static function require_registration() {
 104          if ($registration = self::get_registration()) {
 105              return $registration;
 106          }
 107          if (has_capability('moodle/site:config', context_system::instance())) {
 108              throw new moodle_exception('registrationwarning', 'admin', new moodle_url('/admin/registration/index.php'));
 109          } else {
 110              throw new moodle_exception('registrationwarningcontactadmin', 'admin');
 111          }
 112      }
 113  
 114      /**
 115       * Checks if site is registered
 116       *
 117       * @return bool
 118       */
 119      public static function is_registered() {
 120          return self::get_registration() ? true : false;
 121      }
 122  
 123      /**
 124       * Returns registration token
 125       *
 126       * @param int $strictness if set to MUST_EXIST and site is not registered will throw an exception
 127       * @return null
 128       * @throws moodle_exception
 129       */
 130      public static function get_token($strictness = IGNORE_MISSING) {
 131          if ($strictness == MUST_EXIST) {
 132              $registration = self::require_registration();
 133          } else if (!$registration = self::get_registration()) {
 134              return null;
 135          }
 136          return $registration->token;
 137      }
 138  
 139      /**
 140       * When was the registration last updated
 141       *
 142       * @return int|null timestamp or null if site is not registered
 143       */
 144      public static function get_last_updated() {
 145          if ($registration = self::get_registration()) {
 146              return $registration->timemodified;
 147          }
 148          return null;
 149      }
 150  
 151      /**
 152       * Calculates and prepares site information to send to the sites directory as a part of registration.
 153       *
 154       * @param array $defaults default values for inputs in the registration form (if site was never registered before)
 155       * @return array site info
 156       */
 157      public static function get_site_info($defaults = []) {
 158          global $CFG, $DB;
 159          require_once($CFG->libdir . '/badgeslib.php');
 160          require_once($CFG->dirroot . "/course/lib.php");
 161  
 162          $siteinfo = array();
 163          foreach (self::FORM_FIELDS as $field) {
 164              $siteinfo[$field] = get_config('hub', 'site_'.$field);
 165              if ($siteinfo[$field] === false) {
 166                  $siteinfo[$field] = array_key_exists($field, $defaults) ? $defaults[$field] : null;
 167              }
 168          }
 169  
 170          // Statistical data.
 171          $siteinfo['courses'] = $DB->count_records('course') - 1;
 172          $siteinfo['users'] = $DB->count_records('user', array('deleted' => 0));
 173          $siteinfo['activeusers'] = $DB->count_records_select('user', 'deleted = ? AND lastlogin > ?', [0, time() - DAYSECS * 30]);
 174          $siteinfo['enrolments'] = $DB->count_records('role_assignments');
 175          $siteinfo['posts'] = $DB->count_records('forum_posts');
 176          $siteinfo['questions'] = $DB->count_records('question');
 177          $siteinfo['resources'] = $DB->count_records('resource');
 178          $siteinfo['badges'] = $DB->count_records_select('badge', 'status <> ' . BADGE_STATUS_ARCHIVED);
 179          $siteinfo['issuedbadges'] = $DB->count_records('badge_issued');
 180          $siteinfo['participantnumberaverage'] = average_number_of_participants();
 181          $siteinfo['activeparticipantnumberaverage'] = average_number_of_participants(true, time() - DAYSECS * 30);
 182          $siteinfo['modulenumberaverage'] = average_number_of_courses_modules();
 183  
 184          // Version and url.
 185          $siteinfo['moodlerelease'] = $CFG->release;
 186          $siteinfo['url'] = $CFG->wwwroot;
 187  
 188          // Mobile related information.
 189          $siteinfo['mobileservicesenabled'] = 0;
 190          $siteinfo['mobilenotificationsenabled'] = 0;
 191          $siteinfo['registereduserdevices'] = 0;
 192          $siteinfo['registeredactiveuserdevices'] = 0;
 193          if (!empty($CFG->enablewebservices) && !empty($CFG->enablemobilewebservice)) {
 194              $siteinfo['mobileservicesenabled'] = 1;
 195              $siteinfo['registereduserdevices'] = $DB->count_records('user_devices');
 196              $airnotifierextpath = $CFG->dirroot . '/message/output/airnotifier/externallib.php';
 197              if (file_exists($airnotifierextpath)) { // Maybe some one uninstalled the plugin.
 198                  require_once($airnotifierextpath);
 199                  $siteinfo['mobilenotificationsenabled'] = \message_airnotifier_external::is_system_configured();
 200                  $siteinfo['registeredactiveuserdevices'] = $DB->count_records('message_airnotifier_devices', array('enable' => 1));
 201              }
 202          }
 203  
 204          // Analytics related data follow.
 205          $siteinfo['analyticsenabledmodels'] = \core_analytics\stats::enabled_models();
 206          $siteinfo['analyticspredictions'] = \core_analytics\stats::predictions();
 207          $siteinfo['analyticsactions'] = \core_analytics\stats::actions();
 208          $siteinfo['analyticsactionsnotuseful'] = \core_analytics\stats::actions_not_useful();
 209  
 210          // IMPORTANT: any new fields in siteinfo have to be added to the constant CONFIRM_NEW_FIELDS.
 211  
 212          return $siteinfo;
 213      }
 214  
 215      /**
 216       * Human-readable summary of data that will be sent to the sites directory.
 217       *
 218       * @param array $siteinfo result of get_site_info()
 219       * @return string
 220       */
 221      public static function get_stats_summary($siteinfo) {
 222          $fieldsneedconfirm = self::get_new_registration_fields();
 223          $summary = html_writer::tag('p', get_string('sendfollowinginfo_help', 'hub')) .
 224              html_writer::start_tag('ul');
 225  
 226          $mobileservicesenabled = $siteinfo['mobileservicesenabled'] ? get_string('yes') : get_string('no');
 227          $mobilenotificationsenabled = $siteinfo['mobilenotificationsenabled'] ? get_string('yes') : get_string('no');
 228          $moodlerelease = $siteinfo['moodlerelease'];
 229          if (preg_match('/^(\d+\.\d.*?)[\. ]/', $moodlerelease, $matches)) {
 230              $moodlerelease = $matches[1];
 231          }
 232          $senddata = [
 233              'moodlerelease' => get_string('sitereleasenum', 'hub', $moodlerelease),
 234              'courses' => get_string('coursesnumber', 'hub', $siteinfo['courses']),
 235              'users' => get_string('usersnumber', 'hub', $siteinfo['users']),
 236              'activeusers' => get_string('activeusersnumber', 'hub', $siteinfo['activeusers']),
 237              'enrolments' => get_string('roleassignmentsnumber', 'hub', $siteinfo['enrolments']),
 238              'posts' => get_string('postsnumber', 'hub', $siteinfo['posts']),
 239              'questions' => get_string('questionsnumber', 'hub', $siteinfo['questions']),
 240              'resources' => get_string('resourcesnumber', 'hub', $siteinfo['resources']),
 241              'badges' => get_string('badgesnumber', 'hub', $siteinfo['badges']),
 242              'issuedbadges' => get_string('issuedbadgesnumber', 'hub', $siteinfo['issuedbadges']),
 243              'participantnumberaverage' => get_string('participantnumberaverage', 'hub',
 244                  format_float($siteinfo['participantnumberaverage'], 2)),
 245              'activeparticipantnumberaverage' => get_string('activeparticipantnumberaverage', 'hub',
 246                  format_float($siteinfo['activeparticipantnumberaverage'], 2)),
 247              'modulenumberaverage' => get_string('modulenumberaverage', 'hub',
 248                  format_float($siteinfo['modulenumberaverage'], 2)),
 249              'mobileservicesenabled' => get_string('mobileservicesenabled', 'hub', $mobileservicesenabled),
 250              'mobilenotificationsenabled' => get_string('mobilenotificationsenabled', 'hub', $mobilenotificationsenabled),
 251              'registereduserdevices' => get_string('registereduserdevices', 'hub', $siteinfo['registereduserdevices']),
 252              'registeredactiveuserdevices' => get_string('registeredactiveuserdevices', 'hub', $siteinfo['registeredactiveuserdevices']),
 253              'analyticsenabledmodels' => get_string('analyticsenabledmodels', 'hub', $siteinfo['analyticsenabledmodels']),
 254              'analyticspredictions' => get_string('analyticspredictions', 'hub', $siteinfo['analyticspredictions']),
 255              'analyticsactions' => get_string('analyticsactions', 'hub', $siteinfo['analyticsactions']),
 256              'analyticsactionsnotuseful' => get_string('analyticsactionsnotuseful', 'hub', $siteinfo['analyticsactionsnotuseful']),
 257          ];
 258  
 259          foreach ($senddata as $key => $str) {
 260              $class = in_array($key, $fieldsneedconfirm) ? ' needsconfirmation mark' : '';
 261              $summary .= html_writer::tag('li', $str, ['class' => 'site' . $key . $class]);
 262          }
 263          $summary .= html_writer::end_tag('ul');
 264          return $summary;
 265      }
 266  
 267      /**
 268       * Save registration info locally so it can be retrieved when registration needs to be updated
 269       *
 270       * @param stdClass $formdata data from {@link site_registration_form}
 271       */
 272      public static function save_site_info($formdata) {
 273          foreach (self::FORM_FIELDS as $field) {
 274              set_config('site_' . $field, $formdata->$field, 'hub');
 275          }
 276          // Even if the connection with the sites directory fails, admin has manually submitted the form which means they don't need
 277          // to be redirected to the site registration page any more.
 278          set_config('site_regupdateversion', max(array_keys(self::CONFIRM_NEW_FIELDS)), 'hub');
 279      }
 280  
 281      /**
 282       * Updates site registration when "Update reigstration" button is clicked by admin
 283       */
 284      public static function update_manual() {
 285          global $DB;
 286  
 287          if (!$registration = self::get_registration()) {
 288              return false;
 289          }
 290  
 291          $siteinfo = self::get_site_info();
 292          try {
 293              api::update_registration($siteinfo);
 294          } catch (moodle_exception $e) {
 295              if (!self::is_registered()) {
 296                  // Token was rejected during registration update and site and locally stored token was reset,
 297                  // proceed to site registration. This method will redirect away.
 298                  self::register('');
 299              }
 300              \core\notification::add(get_string('errorregistrationupdate', 'hub', $e->getMessage()),
 301                  \core\output\notification::NOTIFY_ERROR);
 302              return false;
 303          }
 304          $DB->update_record('registration_hubs', ['id' => $registration->id, 'timemodified' => time()]);
 305          \core\notification::add(get_string('siteregistrationupdated', 'hub'),
 306              \core\output\notification::NOTIFY_SUCCESS);
 307          self::$registration = null;
 308          return true;
 309      }
 310  
 311      /**
 312       * Updates site registration via cron
 313       *
 314       * @throws moodle_exception
 315       */
 316      public static function update_cron() {
 317          global $DB;
 318  
 319          if (!$registration = self::get_registration()) {
 320              mtrace(get_string('registrationwarning', 'admin'));
 321              return;
 322          }
 323  
 324          if (self::get_new_registration_fields()) {
 325              mtrace(get_string('pleaserefreshregistrationnewdata', 'admin'));
 326              return;
 327          }
 328  
 329          $siteinfo = self::get_site_info();
 330          api::update_registration($siteinfo);
 331          $DB->update_record('registration_hubs', ['id' => $registration->id, 'timemodified' => time()]);
 332          mtrace(get_string('siteregistrationupdated', 'hub'));
 333          self::$registration = null;
 334      }
 335  
 336      /**
 337       * Confirms registration by the sites directory.
 338       *
 339       * @param string $token
 340       * @param string $newtoken
 341       * @param string $hubname
 342       * @throws moodle_exception
 343       */
 344      public static function confirm_registration($token, $newtoken, $hubname) {
 345          global $DB;
 346  
 347          $registration = self::get_registration(false);
 348          if (!$registration || $registration->token !== $token) {
 349              throw new moodle_exception('wrongtoken', 'hub', new moodle_url('/admin/registration/index.php'));
 350          }
 351          $record = ['id' => $registration->id];
 352          $record['token'] = $newtoken;
 353          $record['confirmed'] = 1;
 354          $record['hubname'] = $hubname;
 355          $record['timemodified'] = time();
 356          $DB->update_record('registration_hubs', $record);
 357          self::$registration = null;
 358  
 359          $siteinfo = self::get_site_info();
 360          if (strlen(http_build_query($siteinfo)) > 1800) {
 361              // Update registration again because the initial request was too long and could have been truncated.
 362              api::update_registration($siteinfo);
 363              self::$registration = null;
 364          }
 365  
 366          // Finally, allow other plugins to perform actions once a site is registered for first time.
 367          $pluginsfunction = get_plugins_with_function('post_site_registration_confirmed');
 368          foreach ($pluginsfunction as $plugins) {
 369              foreach ($plugins as $pluginfunction) {
 370                  $pluginfunction($registration->id);
 371              }
 372          }
 373      }
 374  
 375      /**
 376       * Retrieve the options for site privacy form element to use in registration form
 377       * @return array
 378       */
 379      public static function site_privacy_options() {
 380          return [
 381              self::HUB_SITENOTPUBLISHED => get_string('siteprivacynotpublished', 'hub'),
 382              self::HUB_SITENAMEPUBLISHED => get_string('siteprivacypublished', 'hub'),
 383              self::HUB_SITELINKPUBLISHED => get_string('siteprivacylinked', 'hub')
 384          ];
 385      }
 386  
 387      /**
 388       * Registers a site
 389       *
 390       * This method will make sure that unconfirmed registration record is created and then redirect to
 391       * registration script on the sites directory.
 392       * The sites directory will check that the site is accessible, register it and redirect back
 393       * to /admin/registration/confirmregistration.php
 394       *
 395       * @param string $returnurl
 396       * @throws \coding_exception
 397       */
 398      public static function register($returnurl) {
 399          global $DB, $SESSION;
 400  
 401          if (self::is_registered()) {
 402              // Caller of this method must make sure that site is not registered.
 403              throw new \coding_exception('Site already registered');
 404          }
 405  
 406          $hub = self::get_registration(false);
 407          if (empty($hub)) {
 408              // Create a new record in 'registration_hubs'.
 409              $hub = new stdClass();
 410              $hub->token = get_site_identifier();
 411              $hub->secret = $hub->token;
 412              $hub->huburl = HUB_MOODLEORGHUBURL;
 413              $hub->hubname = 'moodle';
 414              $hub->confirmed = 0;
 415              $hub->timemodified = time();
 416              $hub->id = $DB->insert_record('registration_hubs', $hub);
 417              self::$registration = null;
 418          }
 419  
 420          $params = self::get_site_info();
 421  
 422          // The most conservative limit for the redirect URL length is 2000 characters. Only pass parameters before
 423          // we reach this limit. The next registration update will update all fields.
 424          // We will also update registration after we receive confirmation from moodle.net.
 425          $url = new moodle_url(HUB_MOODLEORGHUBURL . '/local/hub/siteregistration.php',
 426              ['token' => $hub->token, 'url' => $params['url']]);
 427          foreach ($params as $key => $value) {
 428              if (strlen($url->out(false, [$key => $value])) > 2000) {
 429                  break;
 430              }
 431              $url->param($key, $value);
 432          }
 433  
 434          $SESSION->registrationredirect = $returnurl;
 435          redirect($url);
 436      }
 437  
 438      /**
 439       * Unregister site
 440       *
 441       * @param bool $unpublishalladvertisedcourses
 442       * @param bool $unpublishalluploadedcourses
 443       * @return bool
 444       */
 445      public static function unregister($unpublishalladvertisedcourses, $unpublishalluploadedcourses) {
 446          global $DB;
 447  
 448          if (!$hub = self::get_registration()) {
 449              return true;
 450          }
 451  
 452          // Course unpublish went ok, unregister the site now.
 453          try {
 454              api::unregister_site();
 455          } catch (moodle_exception $e) {
 456              \core\notification::add(get_string('unregistrationerror', 'hub', $e->getMessage()),
 457                  \core\output\notification::NOTIFY_ERROR);
 458              return false;
 459          }
 460  
 461          $DB->delete_records('registration_hubs', array('id' => $hub->id));
 462          self::$registration = null;
 463          return true;
 464      }
 465  
 466      /**
 467       * Resets the registration token without changing site identifier so site can be re-registered
 468       *
 469       * @return bool
 470       */
 471      public static function reset_token() {
 472          global $DB;
 473          if (!$hub = self::get_registration()) {
 474              return true;
 475          }
 476          $DB->delete_records('registration_hubs', array('id' => $hub->id));
 477          self::$registration = null;
 478      }
 479  
 480      /**
 481       * Generate a new token for the site that is not registered
 482       *
 483       * @param string $token
 484       * @throws moodle_exception
 485       */
 486      public static function reset_site_identifier($token) {
 487          global $DB, $CFG;
 488  
 489          $registration = self::get_registration(false);
 490          if (!$registration || $registration->token != $token) {
 491              throw new moodle_exception('wrongtoken', 'hub',
 492                 new moodle_url('/admin/registration/index.php'));
 493          }
 494  
 495          $DB->delete_records('registration_hubs', array('id' => $registration->id));
 496          self::$registration = null;
 497  
 498          $CFG->siteidentifier = null;
 499          get_site_identifier();
 500      }
 501  
 502      /**
 503       * Returns information about the sites directory.
 504       *
 505       * Example of the return array:
 506       * {
 507       *     "courses": 384,
 508       *     "description": "Official moodle sites directory",
 509       *     "downloadablecourses": 0,
 510       *     "enrollablecourses": 0,
 511       *     "hublogo": 1,
 512       *     "language": "en",
 513       *     "name": "moodle",
 514       *     "sites": 274175,
 515       *     "url": "https://stats.moodle.org",
 516       *     "imgurl": "https://stats.moodle.org/local/hub/webservice/download.php?filetype=hubscreenshot"
 517       * }
 518       *
 519       * @return array|null
 520       */
 521      public static function get_moodlenet_info() {
 522          try {
 523              return api::get_hub_info();
 524          } catch (moodle_exception $e) {
 525              // Ignore error, we only need it for displaying information about the sites directory.
 526              // If this request fails, it's not a big deal.
 527              return null;
 528          }
 529      }
 530  
 531      /**
 532       * Does admin need to be redirected to the registration page after install?
 533       *
 534       * @param bool|null $markasviewed if set to true will mark the registration form as viewed and admin will not be redirected
 535       *     to the registration form again (regardless of whether the site was registered or not).
 536       * @return bool
 537       */
 538      public static function show_after_install($markasviewed = null) {
 539          global $CFG;
 540          if (self::is_registered()) {
 541              $showregistration = false;
 542              $markasviewed = true;
 543          } else {
 544              $showregistration = !empty($CFG->registrationpending);
 545              if ($showregistration && !site_is_public()) {
 546                  // If it's not a public site, don't redirect to registration, it won't work anyway.
 547                  $showregistration = false;
 548                  $markasviewed = true;
 549              }
 550          }
 551          if ($markasviewed !== null) {
 552              set_config('registrationpending', !$markasviewed);
 553          }
 554          return $showregistration;
 555      }
 556  
 557      /**
 558       * Returns the list of the fields in the registration form that were added since registration or last manual update
 559       *
 560       * If this list is not empty the scheduled task will be paused and admin will be reminded to update registration manually.
 561       *
 562       * @return array
 563       */
 564      public static function get_new_registration_fields() {
 565          $fieldsneedconfirm = [];
 566          if (!self::is_registered()) {
 567              // Nothing to update if site is not registered.
 568              return $fieldsneedconfirm;
 569          }
 570  
 571          $lastupdated = (int)get_config('hub', 'site_regupdateversion');
 572          foreach (self::CONFIRM_NEW_FIELDS as $version => $fields) {
 573              if ($version > $lastupdated) {
 574                  $fieldsneedconfirm = array_merge($fieldsneedconfirm, $fields);
 575              }
 576          }
 577          return $fieldsneedconfirm;
 578      }
 579  
 580      /**
 581       * Redirect to the site registration form if it's a new install or registration needs updating
 582       *
 583       * @param string|moodle_url $url
 584       */
 585      public static function registration_reminder($url) {
 586          if (defined('BEHAT_SITE_RUNNING') && BEHAT_SITE_RUNNING) {
 587              // No redirection during behat runs.
 588              return;
 589          }
 590          if (!has_capability('moodle/site:config', context_system::instance())) {
 591              return;
 592          }
 593          if (self::show_after_install() || self::get_new_registration_fields()) {
 594              $returnurl = new moodle_url($url);
 595              redirect(new moodle_url('/admin/registration/index.php', ['returnurl' => $returnurl->out_as_local_url(false)]));
 596          }
 597      }
 598  }