Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 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.
/admin/ -> renderer.php (source)

Differences Between: [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

   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   * Renderer for core_admin subsystem
  19   *
  20   * @package    core
  21   * @subpackage admin
  22   * @copyright  2011 David Mudrak <david@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  
  29  /**
  30   * Standard HTML output renderer for core_admin subsystem
  31   */
  32  class core_admin_renderer extends plugin_renderer_base {
  33  
  34      /**
  35       * Display the 'Do you acknowledge the terms of the GPL' page. The first page
  36       * during install.
  37       * @return string HTML to output.
  38       */
  39      public function install_licence_page() {
  40          global $CFG;
  41          $output = '';
  42  
  43          $copyrightnotice = text_to_html(get_string('gpl3'));
  44          $copyrightnotice = str_replace('target="_blank"', 'onclick="this.target=\'_blank\'"', $copyrightnotice); // extremely ugly validation hack
  45  
  46          $continue = new single_button(new moodle_url($this->page->url, array(
  47              'lang' => $CFG->lang, 'agreelicense' => 1)), get_string('continue'), 'get');
  48  
  49          $output .= $this->header();
  50          $output .= $this->heading('<a href="http://moodle.org">Moodle</a> - Modular Object-Oriented Dynamic Learning Environment');
  51          $output .= $this->heading(get_string('copyrightnotice'));
  52          $output .= $this->box($copyrightnotice, 'copyrightnotice');
  53          $output .= html_writer::empty_tag('br');
  54          $output .= $this->confirm(get_string('doyouagree'), $continue, "http://docs.moodle.org/dev/License");
  55          $output .= $this->footer();
  56  
  57          return $output;
  58      }
  59  
  60      /**
  61       * Display page explaining proper upgrade process,
  62       * there can not be any PHP file leftovers...
  63       *
  64       * @return string HTML to output.
  65       */
  66      public function upgrade_stale_php_files_page() {
  67          $output = '';
  68          $output .= $this->header();
  69          $output .= $this->heading(get_string('upgradestalefiles', 'admin'));
  70          $output .= $this->box_start('generalbox', 'notice');
  71          $output .= format_text(get_string('upgradestalefilesinfo', 'admin', get_docs_url('Upgrading')), FORMAT_MARKDOWN);
  72          $output .= html_writer::empty_tag('br');
  73          $output .= html_writer::tag('div', $this->single_button($this->page->url, get_string('reload'), 'get'), array('class' => 'buttons'));
  74          $output .= $this->box_end();
  75          $output .= $this->footer();
  76  
  77          return $output;
  78      }
  79  
  80      /**
  81       * Display the 'environment check' page that is displayed during install.
  82       * @param int $maturity
  83       * @param boolean $envstatus final result of the check (true/false)
  84       * @param array $environment_results array of results gathered
  85       * @param string $release moodle release
  86       * @return string HTML to output.
  87       */
  88      public function install_environment_page($maturity, $envstatus, $environment_results, $release) {
  89          global $CFG;
  90          $output = '';
  91  
  92          $output .= $this->header();
  93          $output .= $this->maturity_warning($maturity);
  94          $output .= $this->heading("Moodle $release");
  95          $output .= $this->release_notes_link();
  96  
  97          $output .= $this->environment_check_table($envstatus, $environment_results);
  98  
  99          if (!$envstatus) {
 100              $output .= $this->upgrade_reload(new moodle_url($this->page->url, array('agreelicense' => 1, 'lang' => $CFG->lang)));
 101          } else {
 102              $output .= $this->notification(get_string('environmentok', 'admin'), 'notifysuccess');
 103              $output .= $this->continue_button(new moodle_url($this->page->url, array(
 104                  'agreelicense' => 1, 'confirmrelease' => 1, 'lang' => $CFG->lang)));
 105          }
 106  
 107          $output .= $this->footer();
 108          return $output;
 109      }
 110  
 111      /**
 112       * Displays the list of plugins with unsatisfied dependencies
 113       *
 114       * @param double|string|int $version Moodle on-disk version
 115       * @param array $failed list of plugins with unsatisfied dependecies
 116       * @param moodle_url $reloadurl URL of the page to recheck the dependencies
 117       * @return string HTML
 118       */
 119      public function unsatisfied_dependencies_page($version, array $failed, moodle_url $reloadurl) {
 120          $output = '';
 121  
 122          $output .= $this->header();
 123          $output .= $this->heading(get_string('pluginscheck', 'admin'));
 124          $output .= $this->warning(get_string('pluginscheckfailed', 'admin', array('pluginslist' => implode(', ', array_unique($failed)))));
 125          $output .= $this->plugins_check_table(core_plugin_manager::instance(), $version, array('xdep' => true));
 126          $output .= $this->warning(get_string('pluginschecktodo', 'admin'));
 127          $output .= $this->continue_button($reloadurl);
 128  
 129          $output .= $this->footer();
 130  
 131          return $output;
 132      }
 133  
 134      /**
 135       * Display the 'You are about to upgrade Moodle' page. The first page
 136       * during upgrade.
 137       * @param string $strnewversion
 138       * @param int $maturity
 139       * @param string $testsite
 140       * @return string HTML to output.
 141       */
 142      public function upgrade_confirm_page($strnewversion, $maturity, $testsite) {
 143          $output = '';
 144  
 145          $continueurl = new moodle_url($this->page->url, array('confirmupgrade' => 1, 'cache' => 0));
 146          $continue = new single_button($continueurl, get_string('continue'), 'get');
 147          $cancelurl = new moodle_url('/admin/index.php');
 148  
 149          $output .= $this->header();
 150          $output .= $this->maturity_warning($maturity);
 151          $output .= $this->test_site_warning($testsite);
 152          $output .= $this->confirm(get_string('upgradesure', 'admin', $strnewversion), $continue, $cancelurl);
 153          $output .= $this->footer();
 154  
 155          return $output;
 156      }
 157  
 158      /**
 159       * Display the environment page during the upgrade process.
 160       * @param string $release
 161       * @param boolean $envstatus final result of env check (true/false)
 162       * @param array $environment_results array of results gathered
 163       * @return string HTML to output.
 164       */
 165      public function upgrade_environment_page($release, $envstatus, $environment_results) {
 166          global $CFG;
 167          $output = '';
 168  
 169          $output .= $this->header();
 170          $output .= $this->heading("Moodle $release");
 171          $output .= $this->release_notes_link();
 172          $output .= $this->environment_check_table($envstatus, $environment_results);
 173  
 174          if (!$envstatus) {
 175              $output .= $this->upgrade_reload(new moodle_url($this->page->url, array('confirmupgrade' => 1, 'cache' => 0)));
 176  
 177          } else {
 178              $output .= $this->notification(get_string('environmentok', 'admin'), 'notifysuccess');
 179  
 180              if (empty($CFG->skiplangupgrade) and current_language() !== 'en') {
 181                  $output .= $this->box(get_string('langpackwillbeupdated', 'admin'), 'generalbox', 'notice');
 182              }
 183  
 184              $output .= $this->continue_button(new moodle_url($this->page->url, array(
 185                  'confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0)));
 186          }
 187  
 188          $output .= $this->footer();
 189  
 190          return $output;
 191      }
 192  
 193      /**
 194       * Display the upgrade page that lists all the plugins that require attention.
 195       * @param core_plugin_manager $pluginman provides information about the plugins.
 196       * @param \core\update\checker $checker provides information about available updates.
 197       * @param int $version the version of the Moodle code from version.php.
 198       * @param bool $showallplugins
 199       * @param moodle_url $reloadurl
 200       * @param moodle_url $continueurl
 201       * @return string HTML to output.
 202       */
 203      public function upgrade_plugin_check_page(core_plugin_manager $pluginman, \core\update\checker $checker,
 204              $version, $showallplugins, $reloadurl, $continueurl) {
 205  
 206          $output = '';
 207  
 208          $output .= $this->header();
 209          $output .= $this->box_start('generalbox', 'plugins-check-page');
 210          $output .= html_writer::tag('p', get_string('pluginchecknotice', 'core_plugin'), array('class' => 'page-description'));
 211          $output .= $this->check_for_updates_button($checker, $reloadurl);
 212          $output .= $this->missing_dependencies($pluginman);
 213          $output .= $this->plugins_check_table($pluginman, $version, array('full' => $showallplugins));
 214          $output .= $this->box_end();
 215          $output .= $this->upgrade_reload($reloadurl);
 216  
 217          if ($pluginman->some_plugins_updatable()) {
 218              $output .= $this->container_start('upgradepluginsinfo');
 219              $output .= $this->help_icon('upgradepluginsinfo', 'core_admin', get_string('upgradepluginsfirst', 'core_admin'));
 220              $output .= $this->container_end();
 221          }
 222  
 223          $button = new single_button($continueurl, get_string('upgradestart', 'admin'), 'get', true);
 224          $button->class = 'continuebutton';
 225          $output .= $this->render($button);
 226          $output .= $this->footer();
 227  
 228          return $output;
 229      }
 230  
 231      /**
 232       * Display a page to confirm plugin installation cancelation.
 233       *
 234       * @param array $abortable list of \core\update\plugininfo
 235       * @param moodle_url $continue
 236       * @return string
 237       */
 238      public function upgrade_confirm_abort_install_page(array $abortable, moodle_url $continue) {
 239  
 240          $pluginman = core_plugin_manager::instance();
 241  
 242          if (empty($abortable)) {
 243              // The UI should not allow this.
 244              throw new moodle_exception('err_no_plugin_install_abortable', 'core_plugin');
 245          }
 246  
 247          $out = $this->output->header();
 248          $out .= $this->output->heading(get_string('cancelinstallhead', 'core_plugin'), 3);
 249          $out .= $this->output->container(get_string('cancelinstallinfo', 'core_plugin'), 'cancelinstallinfo');
 250  
 251          foreach ($abortable as $pluginfo) {
 252              $out .= $this->output->heading($pluginfo->displayname.' ('.$pluginfo->component.')', 4);
 253              $out .= $this->output->container(get_string('cancelinstallinfodir', 'core_plugin', $pluginfo->rootdir));
 254              if ($repotype = $pluginman->plugin_external_source($pluginfo->component)) {
 255                  $out .= $this->output->container(get_string('uninstalldeleteconfirmexternal', 'core_plugin', $repotype),
 256                      'alert alert-warning mt-2');
 257              }
 258          }
 259  
 260          $out .= $this->plugins_management_confirm_buttons($continue, $this->page->url);
 261          $out .= $this->output->footer();
 262  
 263          return $out;
 264      }
 265  
 266      /**
 267       * Display the admin notifications page.
 268       * @param int $maturity
 269       * @param bool $insecuredataroot warn dataroot is invalid
 270       * @param bool $errorsdisplayed warn invalid dispaly error setting
 271       * @param bool $cronoverdue warn cron not running
 272       * @param bool $dbproblems warn db has problems
 273       * @param bool $maintenancemode warn in maintenance mode
 274       * @param bool $buggyiconvnomb warn iconv problems
 275       * @param array|null $availableupdates array of \core\update\info objects or null
 276       * @param int|null $availableupdatesfetch timestamp of the most recent updates fetch or null (unknown)
 277       * @param string[] $cachewarnings An array containing warnings from the Cache API.
 278       * @param array $eventshandlers Events 1 API handlers.
 279       * @param bool $themedesignermode Warn about the theme designer mode.
 280       * @param bool $devlibdir Warn about development libs directory presence.
 281       * @param bool $mobileconfigured Whether the mobile web services have been enabled
 282       * @param bool $overridetossl Whether or not ssl is being forced.
 283       * @param bool $invalidforgottenpasswordurl Whether the forgotten password URL does not link to a valid URL.
 284       * @param bool $croninfrequent If true, warn that cron hasn't run in the past few minutes
 285       * @param bool $showcampaigncontent Whether the campaign content should be visible or not.
 286       * @param bool $showfeedbackencouragement Whether the feedback encouragement content should be displayed or not.
 287       *
 288       * @return string HTML to output.
 289       */
 290      public function admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed,
 291              $cronoverdue, $dbproblems, $maintenancemode, $availableupdates, $availableupdatesfetch,
 292              $buggyiconvnomb, $registered, array $cachewarnings = array(), $eventshandlers = 0,
 293              $themedesignermode = false, $devlibdir = false, $mobileconfigured = false,
 294              $overridetossl = false, $invalidforgottenpasswordurl = false, $croninfrequent = false,
 295              $showcampaigncontent = false, bool $showfeedbackencouragement = false) {
 296  
 297          global $CFG;
 298          $output = '';
 299  
 300          $output .= $this->header();
 301          $output .= $this->maturity_info($maturity);
 302          $output .= $this->legacy_log_store_writing_error();
 303          $output .= empty($CFG->disableupdatenotifications) ? $this->available_updates($availableupdates, $availableupdatesfetch) : '';
 304          $output .= $this->insecure_dataroot_warning($insecuredataroot);
 305          $output .= $this->development_libs_directories_warning($devlibdir);
 306          $output .= $this->themedesignermode_warning($themedesignermode);
 307          $output .= $this->display_errors_warning($errorsdisplayed);
 308          $output .= $this->buggy_iconv_warning($buggyiconvnomb);
 309          $output .= $this->cron_overdue_warning($cronoverdue);
 310          $output .= $this->cron_infrequent_warning($croninfrequent);
 311          $output .= $this->db_problems($dbproblems);
 312          $output .= $this->maintenance_mode_warning($maintenancemode);
 313          $output .= $this->overridetossl_warning($overridetossl);
 314          $output .= $this->cache_warnings($cachewarnings);
 315          $output .= $this->events_handlers($eventshandlers);
 316          $output .= $this->registration_warning($registered);
 317          $output .= $this->mobile_configuration_warning($mobileconfigured);
 318          $output .= $this->forgotten_password_url_warning($invalidforgottenpasswordurl);
 319          $output .= $this->userfeedback_encouragement($showfeedbackencouragement);
 320          $output .= $this->campaign_content($showcampaigncontent);
 321  
 322          //////////////////////////////////////////////////////////////////////////////////////////////////
 323          ////  IT IS ILLEGAL AND A VIOLATION OF THE GPL TO HIDE, REMOVE OR MODIFY THIS COPYRIGHT NOTICE ///
 324          $output .= $this->moodle_copyright();
 325          //////////////////////////////////////////////////////////////////////////////////////////////////
 326  
 327          $output .= $this->footer();
 328  
 329          return $output;
 330      }
 331  
 332      /**
 333       * Display the plugin management page (admin/plugins.php).
 334       *
 335       * The filtering options array may contain following items:
 336       *  bool contribonly - show only contributed extensions
 337       *  bool updatesonly - show only plugins with an available update
 338       *
 339       * @param core_plugin_manager $pluginman
 340       * @param \core\update\checker $checker
 341       * @param array $options filtering options
 342       * @return string HTML to output.
 343       */
 344      public function plugin_management_page(core_plugin_manager $pluginman, \core\update\checker $checker, array $options = array()) {
 345  
 346          $output = '';
 347  
 348          $output .= $this->header();
 349          $output .= $this->heading(get_string('pluginsoverview', 'core_admin'));
 350          $output .= $this->check_for_updates_button($checker, $this->page->url);
 351          $output .= $this->plugins_overview_panel($pluginman, $options);
 352          $output .= $this->plugins_control_panel($pluginman, $options);
 353          $output .= $this->footer();
 354  
 355          return $output;
 356      }
 357  
 358      /**
 359       * Renders a button to fetch for available updates.
 360       *
 361       * @param \core\update\checker $checker
 362       * @param moodle_url $reloadurl
 363       * @return string HTML
 364       */
 365      public function check_for_updates_button(\core\update\checker $checker, $reloadurl) {
 366  
 367          $output = '';
 368  
 369          if ($checker->enabled()) {
 370              $output .= $this->container_start('checkforupdates mb-4');
 371              $output .= $this->single_button(
 372                  new moodle_url($reloadurl, array('fetchupdates' => 1)),
 373                  get_string('checkforupdates', 'core_plugin')
 374              );
 375              if ($timefetched = $checker->get_last_timefetched()) {
 376                  $timefetched = userdate($timefetched, get_string('strftimedatetime', 'core_langconfig'));
 377                  $output .= $this->container(get_string('checkforupdateslast', 'core_plugin', $timefetched),
 378                      'lasttimefetched small text-muted mt-1');
 379              }
 380              $output .= $this->container_end();
 381          }
 382  
 383          return $output;
 384      }
 385  
 386      /**
 387       * Display a page to confirm the plugin uninstallation.
 388       *
 389       * @param core_plugin_manager $pluginman
 390       * @param \core\plugininfo\base $pluginfo
 391       * @param moodle_url $continueurl URL to continue after confirmation
 392       * @param moodle_url $cancelurl URL to to go if cancelled
 393       * @return string
 394       */
 395      public function plugin_uninstall_confirm_page(core_plugin_manager $pluginman, \core\plugininfo\base $pluginfo, moodle_url $continueurl, moodle_url $cancelurl) {
 396          $output = '';
 397  
 398          $pluginname = $pluginman->plugin_name($pluginfo->component);
 399  
 400          $confirm = '<p>' . get_string('uninstallconfirm', 'core_plugin', array('name' => $pluginname)) . '</p>';
 401          if ($extraconfirm = $pluginfo->get_uninstall_extra_warning()) {
 402              $confirm .= $extraconfirm;
 403          }
 404  
 405          $output .= $this->output->header();
 406          $output .= $this->output->heading(get_string('uninstalling', 'core_plugin', array('name' => $pluginname)));
 407          $output .= $this->output->confirm($confirm, $continueurl, $cancelurl);
 408          $output .= $this->output->footer();
 409  
 410          return $output;
 411      }
 412  
 413      /**
 414       * Display a page with results of plugin uninstallation and offer removal of plugin files.
 415       *
 416       * @param core_plugin_manager $pluginman
 417       * @param \core\plugininfo\base $pluginfo
 418       * @param progress_trace_buffer $progress
 419       * @param moodle_url $continueurl URL to continue to remove the plugin folder
 420       * @return string
 421       */
 422      public function plugin_uninstall_results_removable_page(core_plugin_manager $pluginman, \core\plugininfo\base $pluginfo,
 423                                                              progress_trace_buffer $progress, moodle_url $continueurl) {
 424          $output = '';
 425  
 426          $pluginname = $pluginman->plugin_name($pluginfo->component);
 427  
 428          // Do not show navigation here, they must click one of the buttons.
 429          $this->page->set_pagelayout('maintenance');
 430          $this->page->set_cacheable(false);
 431  
 432          $output .= $this->output->header();
 433          $output .= $this->output->heading(get_string('uninstalling', 'core_plugin', array('name' => $pluginname)));
 434  
 435          $output .= $this->output->box($progress->get_buffer(), 'generalbox uninstallresultmessage');
 436  
 437          $confirm = $this->output->container(get_string('uninstalldeleteconfirm', 'core_plugin',
 438              array('name' => $pluginname, 'rootdir' => $pluginfo->rootdir)), 'uninstalldeleteconfirm');
 439  
 440          if ($repotype = $pluginman->plugin_external_source($pluginfo->component)) {
 441              $confirm .= $this->output->container(get_string('uninstalldeleteconfirmexternal', 'core_plugin', $repotype),
 442                  'alert alert-warning mt-2');
 443          }
 444  
 445          // After any uninstall we must execute full upgrade to finish the cleanup!
 446          $output .= $this->output->confirm($confirm, $continueurl, new moodle_url('/admin/index.php'));
 447          $output .= $this->output->footer();
 448  
 449          return $output;
 450      }
 451  
 452      /**
 453       * Display a page with results of plugin uninstallation and inform about the need to remove plugin files manually.
 454       *
 455       * @param core_plugin_manager $pluginman
 456       * @param \core\plugininfo\base $pluginfo
 457       * @param progress_trace_buffer $progress
 458       * @return string
 459       */
 460      public function plugin_uninstall_results_page(core_plugin_manager $pluginman, \core\plugininfo\base $pluginfo, progress_trace_buffer $progress) {
 461          $output = '';
 462  
 463          $pluginname = $pluginfo->component;
 464  
 465          $output .= $this->output->header();
 466          $output .= $this->output->heading(get_string('uninstalling', 'core_plugin', array('name' => $pluginname)));
 467  
 468          $output .= $this->output->box($progress->get_buffer(), 'generalbox uninstallresultmessage');
 469  
 470          $output .= $this->output->box(get_string('uninstalldelete', 'core_plugin',
 471              array('name' => $pluginname, 'rootdir' => $pluginfo->rootdir)), 'generalbox uninstalldelete');
 472          $output .= $this->output->continue_button(new moodle_url('/admin/index.php'));
 473          $output .= $this->output->footer();
 474  
 475          return $output;
 476      }
 477  
 478      /**
 479       * Display the plugin management page (admin/environment.php).
 480       * @param array $versions
 481       * @param string $version
 482       * @param boolean $envstatus final result of env check (true/false)
 483       * @param array $environment_results array of results gathered
 484       * @return string HTML to output.
 485       */
 486      public function environment_check_page($versions, $version, $envstatus, $environment_results) {
 487          $output = '';
 488          $output .= $this->header();
 489  
 490          // Print the component download link
 491          $output .= html_writer::tag('div', html_writer::link(
 492                      new moodle_url('/admin/environment.php', array('action' => 'updatecomponent', 'sesskey' => sesskey())),
 493                      get_string('updatecomponent', 'admin')),
 494                  array('class' => 'reportlink'));
 495  
 496          // Heading.
 497          $output .= $this->heading(get_string('environment', 'admin'));
 498  
 499          // Box with info and a menu to choose the version.
 500          $output .= $this->box_start();
 501          $output .= html_writer::tag('div', get_string('adminhelpenvironment'));
 502          $select = new single_select(new moodle_url('/admin/environment.php'), 'version', $versions, $version, null);
 503          $select->label = get_string('moodleversion');
 504          $output .= $this->render($select);
 505          $output .= $this->box_end();
 506  
 507          // The results
 508          $output .= $this->environment_check_table($envstatus, $environment_results);
 509  
 510          $output .= $this->footer();
 511          return $output;
 512      }
 513  
 514      /**
 515       * Output a warning message, of the type that appears on the admin notifications page.
 516       * @param string $message the message to display.
 517       * @param string $type type class
 518       * @return string HTML to output.
 519       */
 520      protected function warning($message, $type = 'warning') {
 521          return $this->box($message, 'generalbox alert alert-' . $type);
 522      }
 523  
 524      /**
 525       * Render an appropriate message if dataroot is insecure.
 526       * @param bool $insecuredataroot
 527       * @return string HTML to output.
 528       */
 529      protected function insecure_dataroot_warning($insecuredataroot) {
 530          global $CFG;
 531  
 532          if ($insecuredataroot == INSECURE_DATAROOT_WARNING) {
 533              return $this->warning(get_string('datarootsecuritywarning', 'admin', $CFG->dataroot));
 534  
 535          } else if ($insecuredataroot == INSECURE_DATAROOT_ERROR) {
 536              return $this->warning(get_string('datarootsecurityerror', 'admin', $CFG->dataroot), 'danger');
 537  
 538          } else {
 539              return '';
 540          }
 541      }
 542  
 543      /**
 544       * Render a warning that a directory with development libs is present.
 545       *
 546       * @param bool $devlibdir True if the warning should be displayed.
 547       * @return string
 548       */
 549      protected function development_libs_directories_warning($devlibdir) {
 550  
 551          if ($devlibdir) {
 552              $moreinfo = new moodle_url('/report/security/index.php');
 553              $warning = get_string('devlibdirpresent', 'core_admin', ['moreinfourl' => $moreinfo->out()]);
 554              return $this->warning($warning, 'danger');
 555  
 556          } else {
 557              return '';
 558          }
 559      }
 560  
 561      /**
 562       * Render an appropriate message if dataroot is insecure.
 563       * @param bool $errorsdisplayed
 564       * @return string HTML to output.
 565       */
 566      protected function display_errors_warning($errorsdisplayed) {
 567          if (!$errorsdisplayed) {
 568              return '';
 569          }
 570  
 571          return $this->warning(get_string('displayerrorswarning', 'admin'));
 572      }
 573  
 574      /**
 575       * Render an appropriate message if themdesignermode is enabled.
 576       * @param bool $themedesignermode true if enabled
 577       * @return string HTML to output.
 578       */
 579      protected function themedesignermode_warning($themedesignermode) {
 580          if (!$themedesignermode) {
 581              return '';
 582          }
 583  
 584          return $this->warning(get_string('themedesignermodewarning', 'admin'));
 585      }
 586  
 587      /**
 588       * Render an appropriate message if iconv is buggy and mbstring missing.
 589       * @param bool $buggyiconvnomb
 590       * @return string HTML to output.
 591       */
 592      protected function buggy_iconv_warning($buggyiconvnomb) {
 593          if (!$buggyiconvnomb) {
 594              return '';
 595          }
 596  
 597          return $this->warning(get_string('warningiconvbuggy', 'admin'));
 598      }
 599  
 600      /**
 601       * Render an appropriate message if cron has not been run recently.
 602       * @param bool $cronoverdue
 603       * @return string HTML to output.
 604       */
 605      public function cron_overdue_warning($cronoverdue) {
 606          global $CFG;
 607          if (!$cronoverdue) {
 608              return '';
 609          }
 610  
 611          $check = new \tool_task\check\cronrunning();
 612          $result = $check->get_result();
 613          return $this->warning($result->get_summary() . '&nbsp;' . $this->help_icon('cron', 'admin'));
 614      }
 615  
 616      /**
 617       * Render an appropriate message if cron is not being run frequently (recommended every minute).
 618       *
 619       * @param bool $croninfrequent
 620       * @return string HTML to output.
 621       */
 622      public function cron_infrequent_warning(bool $croninfrequent) : string {
 623          global $CFG;
 624  
 625          if (!$croninfrequent) {
 626              return '';
 627          }
 628  
 629          $check = new \tool_task\check\cronrunning();
 630          $result = $check->get_result();
 631          return $this->warning($result->get_summary() . '&nbsp;' . $this->help_icon('cron', 'admin'));
 632      }
 633  
 634      /**
 635       * Render an appropriate message if there are any problems with the DB set-up.
 636       * @param bool $dbproblems
 637       * @return string HTML to output.
 638       */
 639      public function db_problems($dbproblems) {
 640          if (!$dbproblems) {
 641              return '';
 642          }
 643  
 644          return $this->warning($dbproblems);
 645      }
 646  
 647      /**
 648       * Renders cache warnings if there are any.
 649       *
 650       * @param string[] $cachewarnings
 651       * @return string
 652       */
 653      public function cache_warnings(array $cachewarnings) {
 654          if (!count($cachewarnings)) {
 655              return '';
 656          }
 657          return join("\n", array_map(array($this, 'warning'), $cachewarnings));
 658      }
 659  
 660      /**
 661       * Renders events 1 API handlers warning.
 662       *
 663       * @param array $eventshandlers
 664       * @return string
 665       */
 666      public function events_handlers($eventshandlers) {
 667          if ($eventshandlers) {
 668              $components = '';
 669              foreach ($eventshandlers as $eventhandler) {
 670                  $components .= $eventhandler->component . ', ';
 671              }
 672              $components = rtrim($components, ', ');
 673              return $this->warning(get_string('eventshandlersinuse', 'admin', $components));
 674          }
 675      }
 676  
 677      /**
 678       * Render an appropriate message if the site in in maintenance mode.
 679       * @param bool $maintenancemode
 680       * @return string HTML to output.
 681       */
 682      public function maintenance_mode_warning($maintenancemode) {
 683          if (!$maintenancemode) {
 684              return '';
 685          }
 686  
 687          $url = new moodle_url('/admin/settings.php', array('section' => 'maintenancemode'));
 688          $url = $url->out(); // get_string() does not support objects in params
 689  
 690          return $this->warning(get_string('sitemaintenancewarning2', 'admin', $url));
 691      }
 692  
 693      /**
 694       * Render a warning that ssl is forced because the site was on loginhttps.
 695       *
 696       * @param bool $overridetossl Whether or not ssl is being forced.
 697       * @return string
 698       */
 699      protected function overridetossl_warning($overridetossl) {
 700          if (!$overridetossl) {
 701              return '';
 702          }
 703          $warning = get_string('overridetossl', 'core_admin');
 704          return $this->warning($warning, 'warning');
 705      }
 706  
 707      /**
 708       * Display a warning about installing development code if necesary.
 709       * @param int $maturity
 710       * @return string HTML to output.
 711       */
 712      protected function maturity_warning($maturity) {
 713          if ($maturity == MATURITY_STABLE) {
 714              return ''; // No worries.
 715          }
 716  
 717          $maturitylevel = get_string('maturity' . $maturity, 'admin');
 718          return $this->warning(
 719                      $this->container(get_string('maturitycorewarning', 'admin', $maturitylevel)) .
 720                      $this->container($this->doc_link('admin/versions', get_string('morehelp'))),
 721                  'danger');
 722      }
 723  
 724      /*
 725       * If necessary, displays a warning about upgrading a test site.
 726       *
 727       * @param string $testsite
 728       * @return string HTML
 729       */
 730      protected function test_site_warning($testsite) {
 731  
 732          if (!$testsite) {
 733              return '';
 734          }
 735  
 736          $warning = (get_string('testsiteupgradewarning', 'admin', $testsite));
 737          return $this->warning($warning, 'danger');
 738      }
 739  
 740      /**
 741       * Output the copyright notice.
 742       * @return string HTML to output.
 743       */
 744      protected function moodle_copyright() {
 745          global $CFG;
 746  
 747          //////////////////////////////////////////////////////////////////////////////////////////////////
 748          ////  IT IS ILLEGAL AND A VIOLATION OF THE GPL TO HIDE, REMOVE OR MODIFY THIS COPYRIGHT NOTICE ///
 749          $copyrighttext = '<a href="http://moodle.org/">Moodle</a> '.
 750                           '<a href="http://docs.moodle.org/dev/Releases" title="'.$CFG->version.'">'.$CFG->release.'</a><br />'.
 751                           'Copyright &copy; 1999 onwards, Martin Dougiamas<br />'.
 752                           'and <a href="http://moodle.org/dev">many other contributors</a>.<br />'.
 753                           '<a href="http://docs.moodle.org/dev/License">GNU Public License</a>';
 754          //////////////////////////////////////////////////////////////////////////////////////////////////
 755  
 756          return $this->box($copyrighttext, 'copyright');
 757      }
 758  
 759      /**
 760       * Display a warning about installing development code if necesary.
 761       * @param int $maturity
 762       * @return string HTML to output.
 763       */
 764      protected function maturity_info($maturity) {
 765          if ($maturity == MATURITY_STABLE) {
 766              return ''; // No worries.
 767          }
 768  
 769          $level = 'warning';
 770  
 771          if ($maturity == MATURITY_ALPHA) {
 772              $level = 'danger';
 773          }
 774  
 775          $maturitylevel = get_string('maturity' . $maturity, 'admin');
 776          $warningtext = get_string('maturitycoreinfo', 'admin', $maturitylevel);
 777          $warningtext .= ' ' . $this->doc_link('admin/versions', get_string('morehelp'));
 778          return $this->warning($warningtext, $level);
 779      }
 780  
 781      /**
 782       * Displays the info about available Moodle core and plugin updates
 783       *
 784       * The structure of the $updates param has changed since 2.4. It contains not only updates
 785       * for the core itself, but also for all other installed plugins.
 786       *
 787       * @param array|null $updates array of (string)component => array of \core\update\info objects or null
 788       * @param int|null $fetch timestamp of the most recent updates fetch or null (unknown)
 789       * @return string
 790       */
 791      protected function available_updates($updates, $fetch) {
 792  
 793          $updateinfo = '';
 794          $someupdateavailable = false;
 795          if (is_array($updates)) {
 796              if (is_array($updates['core'])) {
 797                  $someupdateavailable = true;
 798                  $updateinfo .= $this->heading(get_string('updateavailable', 'core_admin'), 3);
 799                  foreach ($updates['core'] as $update) {
 800                      $updateinfo .= $this->moodle_available_update_info($update);
 801                  }
 802                  $updateinfo .= html_writer::tag('p', get_string('updateavailablerecommendation', 'core_admin'),
 803                      array('class' => 'updateavailablerecommendation'));
 804              }
 805              unset($updates['core']);
 806              // If something has left in the $updates array now, it is updates for plugins.
 807              if (!empty($updates)) {
 808                  $someupdateavailable = true;
 809                  $updateinfo .= $this->heading(get_string('updateavailableforplugin', 'core_admin'), 3);
 810                  $pluginsoverviewurl = new moodle_url('/admin/plugins.php', array('updatesonly' => 1));
 811                  $updateinfo .= $this->container(get_string('pluginsoverviewsee', 'core_admin',
 812                      array('url' => $pluginsoverviewurl->out())));
 813              }
 814          }
 815  
 816          if (!$someupdateavailable) {
 817              $now = time();
 818              if ($fetch and ($fetch <= $now) and ($now - $fetch < HOURSECS)) {
 819                  $updateinfo .= $this->heading(get_string('updateavailablenot', 'core_admin'), 3);
 820              }
 821          }
 822  
 823          $updateinfo .= $this->container_start('checkforupdates mt-1');
 824          $fetchurl = new moodle_url('/admin/index.php', array('fetchupdates' => 1, 'sesskey' => sesskey(), 'cache' => 0));
 825          $updateinfo .= $this->single_button($fetchurl, get_string('checkforupdates', 'core_plugin'));
 826          if ($fetch) {
 827              $updateinfo .= $this->container(get_string('checkforupdateslast', 'core_plugin',
 828                  userdate($fetch, get_string('strftimedatetime', 'core_langconfig'))));
 829          }
 830          $updateinfo .= $this->container_end();
 831  
 832          return $this->warning($updateinfo);
 833      }
 834  
 835      /**
 836       * Display a warning about not being registered on Moodle.org if necesary.
 837       *
 838       * @param boolean $registered true if the site is registered on Moodle.org
 839       * @return string HTML to output.
 840       */
 841      protected function registration_warning($registered) {
 842  
 843          if (!$registered && site_is_public()) {
 844              if (has_capability('moodle/site:config', context_system::instance())) {
 845                  $registerbutton = $this->single_button(new moodle_url('/admin/registration/index.php'),
 846                      get_string('register', 'admin'));
 847                  $str = 'registrationwarning';
 848              } else {
 849                  $registerbutton = '';
 850                  $str = 'registrationwarningcontactadmin';
 851              }
 852  
 853              return $this->warning( get_string($str, 'admin')
 854                      . '&nbsp;' . $this->help_icon('registration', 'admin') . $registerbutton ,
 855                  'error alert alert-danger');
 856          }
 857  
 858          return '';
 859      }
 860  
 861      /**
 862       * Return an admin page warning if site is not registered with moodle.org
 863       *
 864       * @return string
 865       */
 866      public function warn_if_not_registered() {
 867          return $this->registration_warning(\core\hub\registration::is_registered());
 868      }
 869  
 870      /**
 871       * Display a warning about the Mobile Web Services being disabled.
 872       *
 873       * @param boolean $mobileconfigured true if mobile web services are enabled
 874       * @return string HTML to output.
 875       */
 876      protected function mobile_configuration_warning($mobileconfigured) {
 877          $output = '';
 878          if (!$mobileconfigured) {
 879              $settingslink = new moodle_url('/admin/settings.php', ['section' => 'mobilesettings']);
 880              $configurebutton = $this->single_button($settingslink, get_string('enablemobilewebservice', 'admin'));
 881              $output .= $this->warning(get_string('mobilenotconfiguredwarning', 'admin') . '&nbsp;' . $configurebutton);
 882          }
 883  
 884          return $output;
 885      }
 886  
 887      /**
 888       * Display campaign content.
 889       *
 890       * @param bool $showcampaigncontent Whether the campaign content should be visible or not.
 891       * @return string the campaign content raw html.
 892       */
 893      protected function campaign_content(bool $showcampaigncontent): string {
 894          if (!$showcampaigncontent) {
 895              return '';
 896          }
 897  
 898          return $this->render_from_template('core/campaign_content', ['lang' => current_language()]);
 899      }
 900  
 901      /**
 902       * Display a warning about the forgotten password URL not linking to a valid URL.
 903       *
 904       * @param boolean $invalidforgottenpasswordurl true if the forgotten password URL is not valid
 905       * @return string HTML to output.
 906       */
 907      protected function forgotten_password_url_warning($invalidforgottenpasswordurl) {
 908          $output = '';
 909          if ($invalidforgottenpasswordurl) {
 910              $settingslink = new moodle_url('/admin/settings.php', ['section' => 'manageauths']);
 911              $configurebutton = $this->single_button($settingslink, get_string('check', 'moodle'));
 912              $output .= $this->warning(get_string('invalidforgottenpasswordurl', 'admin') . '&nbsp;' . $configurebutton,
 913                  'error alert alert-danger');
 914          }
 915  
 916          return $output;
 917      }
 918  
 919      /**
 920       * Helper method to render the information about the available Moodle update
 921       *
 922       * @param \core\update\info $updateinfo information about the available Moodle core update
 923       */
 924      protected function moodle_available_update_info(\core\update\info $updateinfo) {
 925  
 926          $boxclasses = 'moodleupdateinfo mb-2';
 927          $info = array();
 928  
 929          if (isset($updateinfo->release)) {
 930              $info[] = html_writer::tag('span', get_string('updateavailable_release', 'core_admin', $updateinfo->release),
 931                  array('class' => 'info release'));
 932          }
 933  
 934          if (isset($updateinfo->version)) {
 935              $info[] = html_writer::tag('span', get_string('updateavailable_version', 'core_admin', $updateinfo->version),
 936                  array('class' => 'info version'));
 937          }
 938  
 939          if (isset($updateinfo->maturity)) {
 940              $info[] = html_writer::tag('span', get_string('maturity'.$updateinfo->maturity, 'core_admin'),
 941                  array('class' => 'info maturity'));
 942              $boxclasses .= ' maturity'.$updateinfo->maturity;
 943          }
 944  
 945          if (isset($updateinfo->download)) {
 946              $info[] = html_writer::link($updateinfo->download, get_string('download'),
 947                  array('class' => 'info download btn btn-secondary'));
 948          }
 949  
 950          if (isset($updateinfo->url)) {
 951              $info[] = html_writer::link($updateinfo->url, get_string('updateavailable_moreinfo', 'core_plugin'),
 952                  array('class' => 'info more'));
 953          }
 954  
 955          $box  = $this->output->container_start($boxclasses);
 956          $box .= $this->output->container(implode(html_writer::tag('span', ' | ', array('class' => 'separator')), $info), '');
 957          $box .= $this->output->container_end();
 958  
 959          return $box;
 960      }
 961  
 962      /**
 963       * Display a link to the release notes.
 964       * @return string HTML to output.
 965       */
 966      protected function release_notes_link() {
 967          $releasenoteslink = get_string('releasenoteslink', 'admin', 'http://docs.moodle.org/dev/Releases');
 968          $releasenoteslink = str_replace('target="_blank"', 'onclick="this.target=\'_blank\'"', $releasenoteslink); // extremely ugly validation hack
 969          return $this->box($releasenoteslink, 'generalbox alert alert-info');
 970      }
 971  
 972      /**
 973       * Display the reload link that appears on several upgrade/install pages.
 974       * @return string HTML to output.
 975       */
 976      function upgrade_reload($url) {
 977          return html_writer::empty_tag('br') .
 978                  html_writer::tag('div',
 979                      html_writer::link($url, $this->pix_icon('i/reload', '', '', array('class' => 'icon icon-pre')) .
 980                              get_string('reload'), array('title' => get_string('reload'))),
 981                  array('class' => 'continuebutton')) . html_writer::empty_tag('br');
 982      }
 983  
 984      /**
 985       * Displays all known plugins and information about their installation or upgrade
 986       *
 987       * This default implementation renders all plugins into one big table. The rendering
 988       * options support:
 989       *     (bool)full = false: whether to display up-to-date plugins, too
 990       *     (bool)xdep = false: display the plugins with unsatisified dependecies only
 991       *
 992       * @param core_plugin_manager $pluginman provides information about the plugins.
 993       * @param int $version the version of the Moodle code from version.php.
 994       * @param array $options rendering options
 995       * @return string HTML code
 996       */
 997      public function plugins_check_table(core_plugin_manager $pluginman, $version, array $options = array()) {
 998          global $CFG;
 999          $plugininfo = $pluginman->get_plugins();
1000  
1001          if (empty($plugininfo)) {
1002              return '';
1003          }
1004  
1005          $options['full'] = isset($options['full']) ? (bool)$options['full'] : false;
1006          $options['xdep'] = isset($options['xdep']) ? (bool)$options['xdep'] : false;
1007  
1008          $table = new html_table();
1009          $table->id = 'plugins-check';
1010          $table->head = array(
1011              get_string('displayname', 'core_plugin').' / '.get_string('rootdir', 'core_plugin'),
1012              get_string('versiondb', 'core_plugin'),
1013              get_string('versiondisk', 'core_plugin'),
1014              get_string('requires', 'core_plugin'),
1015              get_string('source', 'core_plugin').' / '.get_string('status', 'core_plugin'),
1016          );
1017          $table->colclasses = array(
1018              'displayname', 'versiondb', 'versiondisk', 'requires', 'status',
1019          );
1020          $table->data = array();
1021  
1022          // Number of displayed plugins per type.
1023          $numdisplayed = array();
1024          // Number of plugins known to the plugin manager.
1025          $sumtotal = 0;
1026          // Number of plugins requiring attention.
1027          $sumattention = 0;
1028          // List of all components we can cancel installation of.
1029          $installabortable = $pluginman->list_cancellable_installations();
1030          // List of all components we can cancel upgrade of.
1031          $upgradeabortable = $pluginman->list_restorable_archives();
1032  
1033          foreach ($plugininfo as $type => $plugins) {
1034  
1035              $header = new html_table_cell($pluginman->plugintype_name_plural($type));
1036              $header->header = true;
1037              $header->colspan = count($table->head);
1038              $header = new html_table_row(array($header));
1039              $header->attributes['class'] = 'plugintypeheader type-' . $type;
1040  
1041              $numdisplayed[$type] = 0;
1042  
1043              if (empty($plugins) and $options['full']) {
1044                  $msg = new html_table_cell(get_string('noneinstalled', 'core_plugin'));
1045                  $msg->colspan = count($table->head);
1046                  $row = new html_table_row(array($msg));
1047                  $row->attributes['class'] .= 'msg msg-noneinstalled';
1048                  $table->data[] = $header;
1049                  $table->data[] = $row;
1050                  continue;
1051              }
1052  
1053              $plugintyperows = array();
1054  
1055              foreach ($plugins as $name => $plugin) {
1056                  $sumtotal++;
1057                  $row = new html_table_row();
1058                  $row->attributes['class'] = 'type-' . $plugin->type . ' name-' . $plugin->type . '_' . $plugin->name;
1059  
1060                  if ($this->page->theme->resolve_image_location('icon', $plugin->type . '_' . $plugin->name, null)) {
1061                      $icon = $this->output->pix_icon('icon', '', $plugin->type . '_' . $plugin->name, array('class' => 'smallicon pluginicon'));
1062                  } else {
1063                      $icon = '';
1064                  }
1065  
1066                  $displayname = new html_table_cell(
1067                      $icon.
1068                      html_writer::span($plugin->displayname, 'pluginname').
1069                      html_writer::div($plugin->get_dir(), 'plugindir text-muted small')
1070                  );
1071  
1072                  $versiondb = new html_table_cell($plugin->versiondb);
1073                  $versiondisk = new html_table_cell($plugin->versiondisk);
1074  
1075                  if ($isstandard = $plugin->is_standard()) {
1076                      $row->attributes['class'] .= ' standard';
1077                      $sourcelabel = html_writer::span(get_string('sourcestd', 'core_plugin'), 'sourcetext badge badge-secondary');
1078                  } else {
1079                      $row->attributes['class'] .= ' extension';
1080                      $sourcelabel = html_writer::span(get_string('sourceext', 'core_plugin'), 'sourcetext badge badge-info');
1081                  }
1082  
1083                  $coredependency = $plugin->is_core_dependency_satisfied($version);
1084                  $incompatibledependency = $plugin->is_core_compatible_satisfied($CFG->branch);
1085  
1086                  $otherpluginsdependencies = $pluginman->are_dependencies_satisfied($plugin->get_other_required_plugins());
1087                  $dependenciesok = $coredependency && $otherpluginsdependencies && $incompatibledependency;
1088  
1089                  $statuscode = $plugin->get_status();
1090                  $row->attributes['class'] .= ' status-' . $statuscode;
1091                  $statusclass = 'statustext badge ';
1092                  switch ($statuscode) {
1093                      case core_plugin_manager::PLUGIN_STATUS_NEW:
1094                          $statusclass .= $dependenciesok ? 'badge-success' : 'badge-warning';
1095                          break;
1096                      case core_plugin_manager::PLUGIN_STATUS_UPGRADE:
1097                          $statusclass .= $dependenciesok ? 'badge-info' : 'badge-warning';
1098                          break;
1099                      case core_plugin_manager::PLUGIN_STATUS_MISSING:
1100                      case core_plugin_manager::PLUGIN_STATUS_DOWNGRADE:
1101                      case core_plugin_manager::PLUGIN_STATUS_DELETE:
1102                          $statusclass .= 'badge-danger';
1103                          break;
1104                      case core_plugin_manager::PLUGIN_STATUS_NODB:
1105                      case core_plugin_manager::PLUGIN_STATUS_UPTODATE:
1106                          $statusclass .= $dependenciesok ? 'badge-light' : 'badge-warning';
1107                          break;
1108                  }
1109                  $status = html_writer::span(get_string('status_' . $statuscode, 'core_plugin'), $statusclass);
1110  
1111                  if (!empty($installabortable[$plugin->component])) {
1112                      $status .= $this->output->single_button(
1113                          new moodle_url($this->page->url, array('abortinstall' => $plugin->component, 'confirmplugincheck' => 0)),
1114                          get_string('cancelinstallone', 'core_plugin'),
1115                          'post',
1116                          array('class' => 'actionbutton cancelinstallone d-block mt-1')
1117                      );
1118                  }
1119  
1120                  if (!empty($upgradeabortable[$plugin->component])) {
1121                      $status .= $this->output->single_button(
1122                          new moodle_url($this->page->url, array('abortupgrade' => $plugin->component)),
1123                          get_string('cancelupgradeone', 'core_plugin'),
1124                          'post',
1125                          array('class' => 'actionbutton cancelupgradeone d-block mt-1')
1126                      );
1127                  }
1128  
1129                  $availableupdates = $plugin->available_updates();
1130                  if (!empty($availableupdates)) {
1131                      foreach ($availableupdates as $availableupdate) {
1132                          $status .= $this->plugin_available_update_info($pluginman, $availableupdate);
1133                      }
1134                  }
1135  
1136                  $status = new html_table_cell($sourcelabel.' '.$status);
1137                  if ($plugin->pluginsupported != null) {
1138                      $requires = new html_table_cell($this->required_column($plugin, $pluginman, $version, $CFG->branch));
1139                  } else {
1140                      $requires = new html_table_cell($this->required_column($plugin, $pluginman, $version));
1141                  }
1142  
1143                  $statusisboring = in_array($statuscode, array(
1144                          core_plugin_manager::PLUGIN_STATUS_NODB, core_plugin_manager::PLUGIN_STATUS_UPTODATE));
1145  
1146                  if ($options['xdep']) {
1147                      // we want to see only plugins with failed dependencies
1148                      if ($dependenciesok) {
1149                          continue;
1150                      }
1151  
1152                  } else if ($statusisboring and $dependenciesok and empty($availableupdates)) {
1153                      // no change is going to happen to the plugin - display it only
1154                      // if the user wants to see the full list
1155                      if (empty($options['full'])) {
1156                          continue;
1157                      }
1158  
1159                  } else {
1160                      $sumattention++;
1161                  }
1162  
1163                  // The plugin should be displayed.
1164                  $numdisplayed[$type]++;
1165                  $row->cells = array($displayname, $versiondb, $versiondisk, $requires, $status);
1166                  $plugintyperows[] = $row;
1167              }
1168  
1169              if (empty($numdisplayed[$type]) and empty($options['full'])) {
1170                  continue;
1171              }
1172  
1173              $table->data[] = $header;
1174              $table->data = array_merge($table->data, $plugintyperows);
1175          }
1176  
1177          // Total number of displayed plugins.
1178          $sumdisplayed = array_sum($numdisplayed);
1179  
1180          if ($options['xdep']) {
1181              // At the plugins dependencies check page, display the table only.
1182              return html_writer::table($table);
1183          }
1184  
1185          $out = $this->output->container_start('', 'plugins-check-info');
1186  
1187          if ($sumdisplayed == 0) {
1188              $out .= $this->output->heading(get_string('pluginchecknone', 'core_plugin'));
1189  
1190          } else {
1191              if (empty($options['full'])) {
1192                  $out .= $this->output->heading(get_string('plugincheckattention', 'core_plugin'));
1193              } else {
1194                  $out .= $this->output->heading(get_string('plugincheckall', 'core_plugin'));
1195              }
1196          }
1197  
1198          $out .= $this->output->container_start('actions mb-2');
1199  
1200          $installableupdates = $pluginman->filter_installable($pluginman->available_updates());
1201          if ($installableupdates) {
1202              $out .= $this->output->single_button(
1203                  new moodle_url($this->page->url, array('installupdatex' => 1)),
1204                  get_string('updateavailableinstallall', 'core_admin', count($installableupdates)),
1205                  'post',
1206                  array('class' => 'singlebutton updateavailableinstallall mr-1')
1207              );
1208          }
1209  
1210          if ($installabortable) {
1211              $out .= $this->output->single_button(
1212                  new moodle_url($this->page->url, array('abortinstallx' => 1, 'confirmplugincheck' => 0)),
1213                  get_string('cancelinstallall', 'core_plugin', count($installabortable)),
1214                  'post',
1215                  array('class' => 'singlebutton cancelinstallall mr-1')
1216              );
1217          }
1218  
1219          if ($upgradeabortable) {
1220              $out .= $this->output->single_button(
1221                  new moodle_url($this->page->url, array('abortupgradex' => 1)),
1222                  get_string('cancelupgradeall', 'core_plugin', count($upgradeabortable)),
1223                  'post',
1224                  array('class' => 'singlebutton cancelupgradeall mr-1')
1225              );
1226          }
1227  
1228          $out .= html_writer::div(html_writer::link(new moodle_url($this->page->url, array('showallplugins' => 0)),
1229              get_string('plugincheckattention', 'core_plugin')).' '.html_writer::span($sumattention, 'badge badge-light'),
1230              'btn btn-link mr-1');
1231  
1232          $out .= html_writer::div(html_writer::link(new moodle_url($this->page->url, array('showallplugins' => 1)),
1233              get_string('plugincheckall', 'core_plugin')).' '.html_writer::span($sumtotal, 'badge badge-light'),
1234              'btn btn-link mr-1');
1235  
1236          $out .= $this->output->container_end(); // End of .actions container.
1237          $out .= $this->output->container_end(); // End of #plugins-check-info container.
1238  
1239          if ($sumdisplayed > 0 or $options['full']) {
1240              $out .= html_writer::table($table);
1241          }
1242  
1243          return $out;
1244      }
1245  
1246      /**
1247       * Display the continue / cancel widgets for the plugins management pages.
1248       *
1249       * @param null|moodle_url $continue URL for the continue button, should it be displayed
1250       * @param null|moodle_url $cancel URL for the cancel link, defaults to the current page
1251       * @return string HTML
1252       */
1253      public function plugins_management_confirm_buttons(moodle_url $continue=null, moodle_url $cancel=null) {
1254  
1255          $out = html_writer::start_div('plugins-management-confirm-buttons');
1256  
1257          if (!empty($continue)) {
1258              $out .= $this->output->single_button($continue, get_string('continue'), 'post', array('class' => 'continue'));
1259          }
1260  
1261          if (empty($cancel)) {
1262              $cancel = $this->page->url;
1263          }
1264          $out .= html_writer::div(html_writer::link($cancel, get_string('cancel')), 'cancel');
1265  
1266          return $out;
1267      }
1268  
1269      /**
1270       * Displays the information about missing dependencies
1271       *
1272       * @param core_plugin_manager $pluginman
1273       * @return string
1274       */
1275      protected function missing_dependencies(core_plugin_manager $pluginman) {
1276  
1277          $dependencies = $pluginman->missing_dependencies();
1278  
1279          if (empty($dependencies)) {
1280              return '';
1281          }
1282  
1283          $available = array();
1284          $unavailable = array();
1285          $unknown = array();
1286  
1287          foreach ($dependencies as $component => $remoteinfo) {
1288              if ($remoteinfo === false) {
1289                  // The required version is not available. Let us check if there
1290                  // is at least some version in the plugins directory.
1291                  $remoteinfoanyversion = $pluginman->get_remote_plugin_info($component, ANY_VERSION, false);
1292                  if ($remoteinfoanyversion === false) {
1293                      $unknown[$component] = $component;
1294                  } else {
1295                      $unavailable[$component] = $remoteinfoanyversion;
1296                  }
1297              } else {
1298                  $available[$component] = $remoteinfo;
1299              }
1300          }
1301  
1302          $out  = $this->output->container_start('plugins-check-dependencies mb-4');
1303  
1304          if ($unavailable or $unknown) {
1305              $out .= $this->output->heading(get_string('misdepsunavail', 'core_plugin'));
1306              if ($unknown) {
1307                  $out .= $this->output->render((new \core\output\notification(get_string('misdepsunknownlist', 'core_plugin',
1308                      implode(', ', $unknown))))->set_show_closebutton(false));
1309              }
1310              if ($unavailable) {
1311                  $unavailablelist = array();
1312                  foreach ($unavailable as $component => $remoteinfoanyversion) {
1313                      $unavailablelistitem = html_writer::link('https://moodle.org/plugins/view.php?plugin='.$component,
1314                          '<strong>'.$remoteinfoanyversion->name.'</strong>');
1315                      if ($remoteinfoanyversion->version) {
1316                          $unavailablelistitem .= ' ('.$component.' &gt; '.$remoteinfoanyversion->version->version.')';
1317                      } else {
1318                          $unavailablelistitem .= ' ('.$component.')';
1319                      }
1320                      $unavailablelist[] = $unavailablelistitem;
1321                  }
1322                  $out .= $this->output->render((new \core\output\notification(get_string('misdepsunavaillist', 'core_plugin',
1323                      implode(', ', $unavailablelist))))->set_show_closebutton(false));
1324              }
1325              $out .= $this->output->container_start('plugins-check-dependencies-actions mb-4');
1326              $out .= ' '.html_writer::link(new moodle_url('/admin/tool/installaddon/'),
1327                  get_string('dependencyuploadmissing', 'core_plugin'), array('class' => 'btn btn-secondary'));
1328              $out .= $this->output->container_end(); // End of .plugins-check-dependencies-actions container.
1329          }
1330  
1331          if ($available) {
1332              $out .= $this->output->heading(get_string('misdepsavail', 'core_plugin'));
1333              $out .= $this->output->container_start('plugins-check-dependencies-actions mb-2');
1334  
1335              $installable = $pluginman->filter_installable($available);
1336              if ($installable) {
1337                  $out .= $this->output->single_button(
1338                      new moodle_url($this->page->url, array('installdepx' => 1)),
1339                      get_string('dependencyinstallmissing', 'core_plugin', count($installable)),
1340                      'post',
1341                      array('class' => 'singlebutton dependencyinstallmissing d-inline-block mr-1')
1342                  );
1343              }
1344  
1345              $out .= html_writer::div(html_writer::link(new moodle_url('/admin/tool/installaddon/'),
1346                  get_string('dependencyuploadmissing', 'core_plugin'), array('class' => 'btn btn-link')),
1347                  'dependencyuploadmissing d-inline-block mr-1');
1348  
1349              $out .= $this->output->container_end(); // End of .plugins-check-dependencies-actions container.
1350  
1351              $out .= $this->available_missing_dependencies_list($pluginman, $available);
1352          }
1353  
1354          $out .= $this->output->container_end(); // End of .plugins-check-dependencies container.
1355  
1356          return $out;
1357      }
1358  
1359      /**
1360       * Displays the list if available missing dependencies.
1361       *
1362       * @param core_plugin_manager $pluginman
1363       * @param array $dependencies
1364       * @return string
1365       */
1366      protected function available_missing_dependencies_list(core_plugin_manager $pluginman, array $dependencies) {
1367          global $CFG;
1368  
1369          $table = new html_table();
1370          $table->id = 'plugins-check-available-dependencies';
1371          $table->head = array(
1372              get_string('displayname', 'core_plugin'),
1373              get_string('release', 'core_plugin'),
1374              get_string('version', 'core_plugin'),
1375              get_string('supportedmoodleversions', 'core_plugin'),
1376              get_string('info', 'core'),
1377          );
1378          $table->colclasses = array('displayname', 'release', 'version', 'supportedmoodleversions', 'info');
1379          $table->data = array();
1380  
1381          foreach ($dependencies as $plugin) {
1382  
1383              $supportedmoodles = array();
1384              foreach ($plugin->version->supportedmoodles as $moodle) {
1385                  if ($CFG->branch == str_replace('.', '', $moodle->release)) {
1386                      $supportedmoodles[] = html_writer::span($moodle->release, 'badge badge-success');
1387                  } else {
1388                      $supportedmoodles[] = html_writer::span($moodle->release, 'badge badge-light');
1389                  }
1390              }
1391  
1392              $requriedby = $pluginman->other_plugins_that_require($plugin->component);
1393              if ($requriedby) {
1394                  foreach ($requriedby as $ix => $val) {
1395                      $inf = $pluginman->get_plugin_info($val);
1396                      if ($inf) {
1397                          $requriedby[$ix] = $inf->displayname.' ('.$inf->component.')';
1398                      }
1399                  }
1400                  $info = html_writer::div(
1401                      get_string('requiredby', 'core_plugin', implode(', ', $requriedby)),
1402                      'requiredby mb-1'
1403                  );
1404              } else {
1405                  $info = '';
1406              }
1407  
1408              $info .= $this->output->container_start('actions');
1409  
1410              $info .= html_writer::div(
1411                  html_writer::link('https://moodle.org/plugins/view.php?plugin='.$plugin->component,
1412                      get_string('misdepinfoplugin', 'core_plugin')),
1413                  'misdepinfoplugin d-inline-block mr-3 mb-1'
1414              );
1415  
1416              $info .= html_writer::div(
1417                  html_writer::link('https://moodle.org/plugins/pluginversion.php?id='.$plugin->version->id,
1418                      get_string('misdepinfoversion', 'core_plugin')),
1419                  'misdepinfoversion d-inline-block mr-3 mb-1'
1420              );
1421  
1422              $info .= html_writer::div(html_writer::link($plugin->version->downloadurl, get_string('download')),
1423                  'misdepdownload d-inline-block mr-3 mb-1');
1424  
1425              if ($pluginman->is_remote_plugin_installable($plugin->component, $plugin->version->version, $reason)) {
1426                  $info .= $this->output->single_button(
1427                      new moodle_url($this->page->url, array('installdep' => $plugin->component)),
1428                      get_string('dependencyinstall', 'core_plugin'),
1429                      'post',
1430                      array('class' => 'singlebutton dependencyinstall mr-3 mb-1')
1431                  );
1432              } else {
1433                  $reasonhelp = $this->info_remote_plugin_not_installable($reason);
1434                  if ($reasonhelp) {
1435                      $info .= html_writer::div($reasonhelp, 'reasonhelp dependencyinstall d-inline-block mr-3 mb-1');
1436                  }
1437              }
1438  
1439              $info .= $this->output->container_end(); // End of .actions container.
1440  
1441              $table->data[] = array(
1442                  html_writer::div($plugin->name, 'name').' '.html_writer::div($plugin->component, 'component text-muted small'),
1443                  $plugin->version->release,
1444                  $plugin->version->version,
1445                  implode(' ', $supportedmoodles),
1446                  $info
1447              );
1448          }
1449  
1450          return html_writer::table($table);
1451      }
1452  
1453      /**
1454       * Explain why {@link core_plugin_manager::is_remote_plugin_installable()} returned false.
1455       *
1456       * @param string $reason the reason code as returned by the plugin manager
1457       * @return string
1458       */
1459      protected function info_remote_plugin_not_installable($reason) {
1460  
1461          if ($reason === 'notwritableplugintype' or $reason === 'notwritableplugin') {
1462              return $this->output->help_icon('notwritable', 'core_plugin', get_string('notwritable', 'core_plugin'));
1463          }
1464  
1465          if ($reason === 'remoteunavailable') {
1466              return $this->output->help_icon('notdownloadable', 'core_plugin', get_string('notdownloadable', 'core_plugin'));
1467          }
1468  
1469          return false;
1470      }
1471  
1472      /**
1473       * Formats the information that needs to go in the 'Requires' column.
1474       * @param \core\plugininfo\base $plugin the plugin we are rendering the row for.
1475       * @param core_plugin_manager $pluginman provides data on all the plugins.
1476       * @param string $version
1477       * @param int $branch the current Moodle branch
1478       * @return string HTML code
1479       */
1480      protected function required_column(\core\plugininfo\base $plugin, core_plugin_manager $pluginman, $version, $branch = null) {
1481  
1482          $requires = array();
1483          $displayuploadlink = false;
1484          $displayupdateslink = false;
1485  
1486          $requirements = $pluginman->resolve_requirements($plugin, $version, $branch);
1487          foreach ($requirements as $reqname => $reqinfo) {
1488              if ($reqname === 'core') {
1489                  if ($reqinfo->status == $pluginman::REQUIREMENT_STATUS_OK) {
1490                      $class = 'requires-ok text-muted';
1491                      $label = '';
1492                  } else {
1493                      $class = 'requires-failed';
1494                      $label = html_writer::span(get_string('dependencyfails', 'core_plugin'), 'badge badge-danger');
1495                  }
1496  
1497                  if ($branch != null && !$plugin->is_core_compatible_satisfied($branch)) {
1498                      $requires[] = html_writer::tag('li',
1499                      html_writer::span(get_string('incompatibleversion', 'core_plugin', $branch), 'dep dep-core').
1500                      ' '.$label, array('class' => $class));
1501  
1502                  } else if ($branch != null && $plugin->pluginsupported != null) {
1503                      $requires[] = html_writer::tag('li',
1504                          html_writer::span(get_string('moodlebranch', 'core_plugin',
1505                          array('min' => $plugin->pluginsupported[0], 'max' => $plugin->pluginsupported[1])), 'dep dep-core').
1506                          ' '.$label, array('class' => $class));
1507  
1508                  } else if ($reqinfo->reqver != ANY_VERSION) {
1509                      $requires[] = html_writer::tag('li',
1510                          html_writer::span(get_string('moodleversion', 'core_plugin', $plugin->versionrequires), 'dep dep-core').
1511                          ' '.$label, array('class' => $class));
1512                  }
1513  
1514              } else {
1515                  $actions = array();
1516  
1517                  if ($reqinfo->status == $pluginman::REQUIREMENT_STATUS_OK) {
1518                      $label = '';
1519                      $class = 'requires-ok text-muted';
1520  
1521                  } else if ($reqinfo->status == $pluginman::REQUIREMENT_STATUS_MISSING) {
1522                      if ($reqinfo->availability == $pluginman::REQUIREMENT_AVAILABLE) {
1523                          $label = html_writer::span(get_string('dependencymissing', 'core_plugin'), 'badge badge-warning');
1524                          $label .= ' '.html_writer::span(get_string('dependencyavailable', 'core_plugin'), 'badge badge-warning');
1525                          $class = 'requires-failed requires-missing requires-available';
1526                          $actions[] = html_writer::link(
1527                              new moodle_url('https://moodle.org/plugins/view.php', array('plugin' => $reqname)),
1528                              get_string('misdepinfoplugin', 'core_plugin')
1529                          );
1530  
1531                      } else {
1532                          $label = html_writer::span(get_string('dependencymissing', 'core_plugin'), 'badge badge-danger');
1533                          $label .= ' '.html_writer::span(get_string('dependencyunavailable', 'core_plugin'),
1534                              'badge badge-danger');
1535                          $class = 'requires-failed requires-missing requires-unavailable';
1536                      }
1537                      $displayuploadlink = true;
1538  
1539                  } else if ($reqinfo->status == $pluginman::REQUIREMENT_STATUS_OUTDATED) {
1540                      if ($reqinfo->availability == $pluginman::REQUIREMENT_AVAILABLE) {
1541                          $label = html_writer::span(get_string('dependencyfails', 'core_plugin'), 'badge badge-warning');
1542                          $label .= ' '.html_writer::span(get_string('dependencyavailable', 'core_plugin'), 'badge badge-warning');
1543                          $class = 'requires-failed requires-outdated requires-available';
1544                          $displayupdateslink = true;
1545  
1546                      } else {
1547                          $label = html_writer::span(get_string('dependencyfails', 'core_plugin'), 'badge badge-danger');
1548                          $label .= ' '.html_writer::span(get_string('dependencyunavailable', 'core_plugin'),
1549                              'badge badge-danger');
1550                          $class = 'requires-failed requires-outdated requires-unavailable';
1551                      }
1552                      $displayuploadlink = true;
1553                  }
1554  
1555                  if ($reqinfo->reqver != ANY_VERSION) {
1556                      $str = 'otherpluginversion';
1557                  } else {
1558                      $str = 'otherplugin';
1559                  }
1560  
1561                  $requires[] = html_writer::tag('li', html_writer::span(
1562                      get_string($str, 'core_plugin', array('component' => $reqname, 'version' => $reqinfo->reqver)),
1563                      'dep dep-plugin').' '.$label.' '.html_writer::span(implode(' | ', $actions), 'actions'),
1564                      array('class' => $class)
1565                  );
1566              }
1567          }
1568  
1569          if (!$requires) {
1570              return '';
1571          }
1572  
1573          $out = html_writer::tag('ul', implode("\n", $requires), array('class' => 'm-0'));
1574  
1575          if ($displayuploadlink) {
1576              $out .= html_writer::div(
1577                  html_writer::link(
1578                      new moodle_url('/admin/tool/installaddon/'),
1579                      get_string('dependencyuploadmissing', 'core_plugin'),
1580                      array('class' => 'btn btn-secondary btn-sm m-1')
1581                  ),
1582                  'dependencyuploadmissing'
1583              );
1584          }
1585  
1586          if ($displayupdateslink) {
1587              $out .= html_writer::div(
1588                  html_writer::link(
1589                      new moodle_url($this->page->url, array('sesskey' => sesskey(), 'fetchupdates' => 1)),
1590                      get_string('checkforupdates', 'core_plugin'),
1591                      array('class' => 'btn btn-secondary btn-sm m-1')
1592                  ),
1593                  'checkforupdates'
1594              );
1595          }
1596  
1597          // Check if supports is present, and $branch is not in, only if $incompatible check was ok.
1598          if ($plugin->pluginsupported != null && $class == 'requires-ok' && $branch != null) {
1599              if ($pluginman->check_explicitly_supported($plugin, $branch) == $pluginman::VERSION_NOT_SUPPORTED) {
1600                  $out .= html_writer::div(get_string('notsupported', 'core_plugin', $branch));
1601              }
1602          }
1603  
1604          return $out;
1605  
1606      }
1607  
1608      /**
1609       * Prints an overview about the plugins - number of installed, number of extensions etc.
1610       *
1611       * @param core_plugin_manager $pluginman provides information about the plugins
1612       * @param array $options filtering options
1613       * @return string as usually
1614       */
1615      public function plugins_overview_panel(core_plugin_manager $pluginman, array $options = array()) {
1616  
1617          $plugininfo = $pluginman->get_plugins();
1618  
1619          $numtotal = $numextension = $numupdatable = $numinstallable = 0;
1620  
1621          foreach ($plugininfo as $type => $plugins) {
1622              foreach ($plugins as $name => $plugin) {
1623                  if ($res = $plugin->available_updates()) {
1624                      $numupdatable++;
1625                      foreach ($res as $updateinfo) {
1626                          if ($pluginman->is_remote_plugin_installable($updateinfo->component, $updateinfo->version, $reason, false)) {
1627                              $numinstallable++;
1628                              break;
1629                          }
1630                      }
1631                  }
1632                  if ($plugin->get_status() === core_plugin_manager::PLUGIN_STATUS_MISSING) {
1633                      continue;
1634                  }
1635                  $numtotal++;
1636                  if (!$plugin->is_standard()) {
1637                      $numextension++;
1638                  }
1639              }
1640          }
1641  
1642          $infoall = html_writer::link(
1643              new moodle_url($this->page->url, array('contribonly' => 0, 'updatesonly' => 0)),
1644              get_string('overviewall', 'core_plugin'),
1645              array('title' => get_string('filterall', 'core_plugin'))
1646          ).' '.html_writer::span($numtotal, 'badge number number-all');
1647  
1648          $infoext = html_writer::link(
1649              new moodle_url($this->page->url, array('contribonly' => 1, 'updatesonly' => 0)),
1650              get_string('overviewext', 'core_plugin'),
1651              array('title' => get_string('filtercontribonly', 'core_plugin'))
1652          ).' '.html_writer::span($numextension, 'badge number number-additional');
1653  
1654          if ($numupdatable) {
1655              $infoupdatable = html_writer::link(
1656                  new moodle_url($this->page->url, array('contribonly' => 0, 'updatesonly' => 1)),
1657                  get_string('overviewupdatable', 'core_plugin'),
1658                  array('title' => get_string('filterupdatesonly', 'core_plugin'))
1659              ).' '.html_writer::span($numupdatable, 'badge badge-info number number-updatable');
1660          } else {
1661              // No updates, or the notifications disabled.
1662              $infoupdatable = '';
1663          }
1664  
1665          $out = html_writer::start_div('', array('id' => 'plugins-overview-panel'));
1666  
1667          if (!empty($options['updatesonly'])) {
1668              $out .= $this->output->heading(get_string('overviewupdatable', 'core_plugin'), 3);
1669          } else if (!empty($options['contribonly'])) {
1670              $out .= $this->output->heading(get_string('overviewext', 'core_plugin'), 3);
1671          }
1672  
1673          if ($numinstallable) {
1674              $out .= $this->output->single_button(
1675                  new moodle_url($this->page->url, array('installupdatex' => 1)),
1676                  get_string('updateavailableinstallall', 'core_admin', $numinstallable),
1677                  'post',
1678                  array('class' => 'singlebutton updateavailableinstallall')
1679              );
1680          }
1681  
1682          $out .= html_writer::div($infoall, 'info info-all').
1683              html_writer::div($infoext, 'info info-ext').
1684              html_writer::div($infoupdatable, 'info info-updatable');
1685  
1686          $out .= html_writer::end_div(); // End of #plugins-overview-panel block.
1687  
1688          return $out;
1689      }
1690  
1691      /**
1692       * Displays all known plugins and links to manage them
1693       *
1694       * This default implementation renders all plugins into one big table.
1695       *
1696       * @param core_plugin_manager $pluginman provides information about the plugins.
1697       * @param array $options filtering options
1698       * @return string HTML code
1699       */
1700      public function plugins_control_panel(core_plugin_manager $pluginman, array $options = array()) {
1701  
1702          $plugininfo = $pluginman->get_plugins();
1703  
1704          // Filter the list of plugins according the options.
1705          if (!empty($options['updatesonly'])) {
1706              $updateable = array();
1707              foreach ($plugininfo as $plugintype => $pluginnames) {
1708                  foreach ($pluginnames as $pluginname => $pluginfo) {
1709                      $pluginavailableupdates = $pluginfo->available_updates();
1710                      if (!empty($pluginavailableupdates)) {
1711                          foreach ($pluginavailableupdates as $pluginavailableupdate) {
1712                              $updateable[$plugintype][$pluginname] = $pluginfo;
1713                          }
1714                      }
1715                  }
1716              }
1717              $plugininfo = $updateable;
1718          }
1719  
1720          if (!empty($options['contribonly'])) {
1721              $contribs = array();
1722              foreach ($plugininfo as $plugintype => $pluginnames) {
1723                  foreach ($pluginnames as $pluginname => $pluginfo) {
1724                      if (!$pluginfo->is_standard()) {
1725                          $contribs[$plugintype][$pluginname] = $pluginfo;
1726                      }
1727                  }
1728              }
1729              $plugininfo = $contribs;
1730          }
1731  
1732          if (empty($plugininfo)) {
1733              return '';
1734          }
1735  
1736          $table = new html_table();
1737          $table->id = 'plugins-control-panel';
1738          $table->head = array(
1739              get_string('displayname', 'core_plugin'),
1740              get_string('version', 'core_plugin'),
1741              get_string('availability', 'core_plugin'),
1742              get_string('actions', 'core_plugin'),
1743              get_string('notes','core_plugin'),
1744          );
1745          $table->headspan = array(1, 1, 1, 2, 1);
1746          $table->colclasses = array(
1747              'pluginname', 'version', 'availability', 'settings', 'uninstall', 'notes'
1748          );
1749  
1750          foreach ($plugininfo as $type => $plugins) {
1751              $heading = $pluginman->plugintype_name_plural($type);
1752              $pluginclass = core_plugin_manager::resolve_plugininfo_class($type);
1753              if ($manageurl = $pluginclass::get_manage_url()) {
1754                  $heading .= $this->output->action_icon($manageurl, new pix_icon('i/settings',
1755                      get_string('settings', 'core_plugin')));
1756              }
1757              $header = new html_table_cell(html_writer::tag('span', $heading, array('id'=>'plugin_type_cell_'.$type)));
1758              $header->header = true;
1759              $header->colspan = array_sum($table->headspan);
1760              $header = new html_table_row(array($header));
1761              $header->attributes['class'] = 'plugintypeheader type-' . $type;
1762              $table->data[] = $header;
1763  
1764              if (empty($plugins)) {
1765                  $msg = new html_table_cell(get_string('noneinstalled', 'core_plugin'));
1766                  $msg->colspan = array_sum($table->headspan);
1767                  $row = new html_table_row(array($msg));
1768                  $row->attributes['class'] .= 'msg msg-noneinstalled';
1769                  $table->data[] = $row;
1770                  continue;
1771              }
1772  
1773              foreach ($plugins as $name => $plugin) {
1774                  $row = new html_table_row();
1775                  $row->attributes['class'] = 'type-' . $plugin->type . ' name-' . $plugin->type . '_' . $plugin->name;
1776  
1777                  if ($this->page->theme->resolve_image_location('icon', $plugin->type . '_' . $plugin->name, null)) {
1778                      $icon = $this->output->pix_icon('icon', '', $plugin->type . '_' . $plugin->name, array('class' => 'icon pluginicon'));
1779                  } else {
1780                      $icon = $this->output->spacer();
1781                  }
1782                  $status = $plugin->get_status();
1783                  $row->attributes['class'] .= ' status-'.$status;
1784                  $pluginname  = html_writer::tag('div', $icon.$plugin->displayname, array('class' => 'displayname')).
1785                                 html_writer::tag('div', $plugin->component, array('class' => 'componentname'));
1786                  $pluginname  = new html_table_cell($pluginname);
1787  
1788                  $version = html_writer::div($plugin->versiondb, 'versionnumber');
1789                  if ((string)$plugin->release !== '') {
1790                      $version = html_writer::div($plugin->release, 'release').$version;
1791                  }
1792                  $version = new html_table_cell($version);
1793  
1794                  $isenabled = $plugin->is_enabled();
1795                  if (is_null($isenabled)) {
1796                      $availability = new html_table_cell('');
1797                  } else if ($isenabled) {
1798                      $row->attributes['class'] .= ' enabled';
1799                      $availability = new html_table_cell(get_string('pluginenabled', 'core_plugin'));
1800                  } else {
1801                      $row->attributes['class'] .= ' disabled';
1802                      $availability = new html_table_cell(get_string('plugindisabled', 'core_plugin'));
1803                  }
1804  
1805                  $settingsurl = $plugin->get_settings_url();
1806                  if (!is_null($settingsurl)) {
1807                      $settings = html_writer::link($settingsurl, get_string('settings', 'core_plugin'), array('class' => 'settings'));
1808                  } else {
1809                      $settings = '';
1810                  }
1811                  $settings = new html_table_cell($settings);
1812  
1813                  if ($uninstallurl = $pluginman->get_uninstall_url($plugin->component, 'overview')) {
1814                      $uninstall = html_writer::link($uninstallurl, get_string('uninstall', 'core_plugin'));
1815                  } else {
1816                      $uninstall = '';
1817                  }
1818                  $uninstall = new html_table_cell($uninstall);
1819  
1820                  if ($plugin->is_standard()) {
1821                      $row->attributes['class'] .= ' standard';
1822                      $source = '';
1823                  } else {
1824                      $row->attributes['class'] .= ' extension';
1825                      $source = html_writer::div(get_string('sourceext', 'core_plugin'), 'source badge badge-info');
1826                  }
1827  
1828                  if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
1829                      $msg = html_writer::div(get_string('status_missing', 'core_plugin'), 'statusmsg badge badge-danger');
1830                  } else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
1831                      $msg = html_writer::div(get_string('status_new', 'core_plugin'), 'statusmsg badge badge-success');
1832                  } else {
1833                      $msg = '';
1834                  }
1835  
1836                  $requriedby = $pluginman->other_plugins_that_require($plugin->component);
1837                  if ($requriedby) {
1838                      $requiredby = html_writer::tag('div', get_string('requiredby', 'core_plugin', implode(', ', $requriedby)),
1839                          array('class' => 'requiredby'));
1840                  } else {
1841                      $requiredby = '';
1842                  }
1843  
1844                  $updateinfo = '';
1845                  if (is_array($plugin->available_updates())) {
1846                      foreach ($plugin->available_updates() as $availableupdate) {
1847                          $updateinfo .= $this->plugin_available_update_info($pluginman, $availableupdate);
1848                      }
1849                  }
1850  
1851                  $notes = new html_table_cell($source.$msg.$requiredby.$updateinfo);
1852  
1853                  $row->cells = array(
1854                      $pluginname, $version, $availability, $settings, $uninstall, $notes
1855                  );
1856                  $table->data[] = $row;
1857              }
1858          }
1859  
1860          return html_writer::table($table);
1861      }
1862  
1863      /**
1864       * Helper method to render the information about the available plugin update
1865       *
1866       * @param core_plugin_manager $pluginman plugin manager instance
1867       * @param \core\update\info $updateinfo information about the available update for the plugin
1868       */
1869      protected function plugin_available_update_info(core_plugin_manager $pluginman, \core\update\info $updateinfo) {
1870  
1871          $boxclasses = 'pluginupdateinfo';
1872          $info = array();
1873  
1874          if (isset($updateinfo->release)) {
1875              $info[] = html_writer::div(
1876                  get_string('updateavailable_release', 'core_plugin', $updateinfo->release),
1877                  'info release'
1878              );
1879          }
1880  
1881          if (isset($updateinfo->maturity)) {
1882              $info[] = html_writer::div(
1883                  get_string('maturity'.$updateinfo->maturity, 'core_admin'),
1884                  'info maturity'
1885              );
1886              $boxclasses .= ' maturity'.$updateinfo->maturity;
1887          }
1888  
1889          if (isset($updateinfo->download)) {
1890              $info[] = html_writer::div(
1891                  html_writer::link($updateinfo->download, get_string('download')),
1892                  'info download'
1893              );
1894          }
1895  
1896          if (isset($updateinfo->url)) {
1897              $info[] = html_writer::div(
1898                  html_writer::link($updateinfo->url, get_string('updateavailable_moreinfo', 'core_plugin')),
1899                  'info more'
1900              );
1901          }
1902  
1903          $box = html_writer::start_div($boxclasses);
1904          $box .= html_writer::div(
1905              get_string('updateavailable', 'core_plugin', $updateinfo->version),
1906              'version'
1907          );
1908          $box .= html_writer::div(
1909              implode(html_writer::span(' ', 'separator'), $info),
1910              'infos'
1911          );
1912  
1913          if ($pluginman->is_remote_plugin_installable($updateinfo->component, $updateinfo->version, $reason, false)) {
1914              $box .= $this->output->single_button(
1915                  new moodle_url($this->page->url, array('installupdate' => $updateinfo->component,
1916                      'installupdateversion' => $updateinfo->version)),
1917                  get_string('updateavailableinstall', 'core_admin'),
1918                  'post',
1919                  array('class' => 'singlebutton updateavailableinstall')
1920              );
1921          } else {
1922              $reasonhelp = $this->info_remote_plugin_not_installable($reason);
1923              if ($reasonhelp) {
1924                  $box .= html_writer::div($reasonhelp, 'reasonhelp updateavailableinstall');
1925              }
1926          }
1927          $box .= html_writer::end_div();
1928  
1929          return $box;
1930      }
1931  
1932      /**
1933       * This function will render one beautiful table with all the environmental
1934       * configuration and how it suits Moodle needs.
1935       *
1936       * @param boolean $result final result of the check (true/false)
1937       * @param environment_results[] $environment_results array of results gathered
1938       * @return string HTML to output.
1939       */
1940      public function environment_check_table($result, $environment_results) {
1941          global $CFG;
1942  
1943          // Table headers
1944          $servertable = new html_table();//table for server checks
1945          $servertable->head  = array(
1946              get_string('name'),
1947              get_string('info'),
1948              get_string('report'),
1949              get_string('plugin'),
1950              get_string('status'),
1951          );
1952          $servertable->colclasses = array('centeralign name', 'centeralign info', 'leftalign report', 'leftalign plugin', 'centeralign status');
1953          $servertable->attributes['class'] = 'admintable environmenttable generaltable table-sm';
1954          $servertable->id = 'serverstatus';
1955  
1956          $serverdata = array('ok'=>array(), 'warn'=>array(), 'error'=>array());
1957  
1958          $othertable = new html_table();//table for custom checks
1959          $othertable->head  = array(
1960              get_string('info'),
1961              get_string('report'),
1962              get_string('plugin'),
1963              get_string('status'),
1964          );
1965          $othertable->colclasses = array('aligncenter info', 'alignleft report', 'alignleft plugin', 'aligncenter status');
1966          $othertable->attributes['class'] = 'admintable environmenttable generaltable table-sm';
1967          $othertable->id = 'otherserverstatus';
1968  
1969          $otherdata = array('ok'=>array(), 'warn'=>array(), 'error'=>array());
1970  
1971          // Iterate over each environment_result
1972          $continue = true;
1973          foreach ($environment_results as $environment_result) {
1974              $errorline   = false;
1975              $warningline = false;
1976              $stringtouse = '';
1977              if ($continue) {
1978                  $type = $environment_result->getPart();
1979                  $info = $environment_result->getInfo();
1980                  $status = $environment_result->getStatus();
1981                  $plugin = $environment_result->getPluginName();
1982                  $error_code = $environment_result->getErrorCode();
1983                  // Process Report field
1984                  $rec = new stdClass();
1985                  // Something has gone wrong at parsing time
1986                  if ($error_code) {
1987                      $stringtouse = 'environmentxmlerror';
1988                      $rec->error_code = $error_code;
1989                      $status = get_string('error');
1990                      $errorline = true;
1991                      $continue = false;
1992                  }
1993  
1994                  if ($continue) {
1995                      if ($rec->needed = $environment_result->getNeededVersion()) {
1996                          // We are comparing versions
1997                          $rec->current = $environment_result->getCurrentVersion();
1998                          if ($environment_result->getLevel() == 'required') {
1999                              $stringtouse = 'environmentrequireversion';
2000                          } else {
2001                              $stringtouse = 'environmentrecommendversion';
2002                          }
2003  
2004                      } else if ($environment_result->getPart() == 'custom_check') {
2005                          // We are checking installed & enabled things
2006                          if ($environment_result->getLevel() == 'required') {
2007                              $stringtouse = 'environmentrequirecustomcheck';
2008                          } else {
2009                              $stringtouse = 'environmentrecommendcustomcheck';
2010                          }
2011  
2012                      } else if ($environment_result->getPart() == 'php_setting') {
2013                          if ($status) {
2014                              $stringtouse = 'environmentsettingok';
2015                          } else if ($environment_result->getLevel() == 'required') {
2016                              $stringtouse = 'environmentmustfixsetting';
2017                          } else {
2018                              $stringtouse = 'environmentshouldfixsetting';
2019                          }
2020  
2021                      } else {
2022                          if ($environment_result->getLevel() == 'required') {
2023                              $stringtouse = 'environmentrequireinstall';
2024                          } else {
2025                              $stringtouse = 'environmentrecommendinstall';
2026                          }
2027                      }
2028  
2029                      // Calculate the status value
2030                      if ($environment_result->getBypassStr() != '') {            //Handle bypassed result (warning)
2031                          $status = get_string('bypassed');
2032                          $warningline = true;
2033                      } else if ($environment_result->getRestrictStr() != '') {   //Handle restricted result (error)
2034                          $status = get_string('restricted');
2035                          $errorline = true;
2036                      } else {
2037                          if ($status) {                                          //Handle ok result (ok)
2038                              $status = get_string('ok');
2039                          } else {
2040                              if ($environment_result->getLevel() == 'optional') {//Handle check result (warning)
2041                                  $status = get_string('check');
2042                                  $warningline = true;
2043                              } else {                                            //Handle error result (error)
2044                                  $status = get_string('check');
2045                                  $errorline = true;
2046                              }
2047                          }
2048                      }
2049                  }
2050  
2051                  // Build the text
2052                  $linkparts = array();
2053                  $linkparts[] = 'admin/environment';
2054                  $linkparts[] = $type;
2055                  if (!empty($info)){
2056                     $linkparts[] = $info;
2057                  }
2058                  // Plugin environments do not have docs pages yet.
2059                  if (empty($CFG->docroot) or $environment_result->plugin) {
2060                      $report = get_string($stringtouse, 'admin', $rec);
2061                  } else {
2062                      $report = $this->doc_link(join('/', $linkparts), get_string($stringtouse, 'admin', $rec), true);
2063                  }
2064                  // Enclose report text in div so feedback text will be displayed underneath it.
2065                  $report = html_writer::div($report);
2066  
2067                  // Format error or warning line
2068                  if ($errorline) {
2069                      $messagetype = 'error';
2070                      $statusclass = 'badge-danger';
2071                  } else if ($warningline) {
2072                      $messagetype = 'warn';
2073                      $statusclass = 'badge-warning';
2074                  } else {
2075                      $messagetype = 'ok';
2076                      $statusclass = 'badge-success';
2077                  }
2078                  $status = html_writer::span($status, 'badge ' . $statusclass);
2079                  // Here we'll store all the feedback found
2080                  $feedbacktext = '';
2081                  // Append the feedback if there is some
2082                  $feedbacktext .= $environment_result->strToReport($environment_result->getFeedbackStr(), $messagetype);
2083                  //Append the bypass if there is some
2084                  $feedbacktext .= $environment_result->strToReport($environment_result->getBypassStr(), 'warn');
2085                  //Append the restrict if there is some
2086                  $feedbacktext .= $environment_result->strToReport($environment_result->getRestrictStr(), 'error');
2087  
2088                  $report .= $feedbacktext;
2089  
2090                  // Add the row to the table
2091                  if ($environment_result->getPart() == 'custom_check'){
2092                      $otherdata[$messagetype][] = array ($info, $report, $plugin, $status);
2093                  } else {
2094                      $serverdata[$messagetype][] = array ($type, $info, $report, $plugin, $status);
2095                  }
2096              }
2097          }
2098  
2099          //put errors first in
2100          $servertable->data = array_merge($serverdata['error'], $serverdata['warn'], $serverdata['ok']);
2101          $othertable->data = array_merge($otherdata['error'], $otherdata['warn'], $otherdata['ok']);
2102  
2103          // Print table
2104          $output = '';
2105          $output .= $this->heading(get_string('serverchecks', 'admin'));
2106          $output .= html_writer::table($servertable);
2107          if (count($othertable->data)){
2108              $output .= $this->heading(get_string('customcheck', 'admin'));
2109              $output .= html_writer::table($othertable);
2110          }
2111  
2112          // Finally, if any error has happened, print the summary box
2113          if (!$result) {
2114              $output .= $this->box(get_string('environmenterrortodo', 'admin'), 'environmentbox errorbox');
2115          }
2116  
2117          return $output;
2118      }
2119  
2120      /**
2121       * Render a simple page for providing the upgrade key.
2122       *
2123       * @param moodle_url|string $url
2124       * @return string
2125       */
2126      public function upgradekey_form_page($url) {
2127  
2128          $output = '';
2129          $output .= $this->header();
2130          $output .= $this->container_start('upgradekeyreq');
2131          $output .= $this->heading(get_string('upgradekeyreq', 'core_admin'));
2132          $output .= html_writer::start_tag('form', array('method' => 'POST', 'action' => $url));
2133          $output .= html_writer::empty_tag('input', [
2134              'name' => 'upgradekey',
2135              'type' => 'password',
2136              'class' => 'form-control w-auto',
2137          ]);
2138          $output .= html_writer::empty_tag('input', [
2139              'type' => 'submit',
2140              'value' => get_string('submit'),
2141              'class' => 'btn btn-primary mt-3',
2142          ]);
2143          $output .= html_writer::end_tag('form');
2144          $output .= $this->container_end();
2145          $output .= $this->footer();
2146  
2147          return $output;
2148      }
2149  
2150      /**
2151       * Check to see if writing to the deprecated legacy log store is enabled.
2152       *
2153       * @return string An error message if writing to the legacy log store is enabled.
2154       */
2155      protected function legacy_log_store_writing_error() {
2156          $enabled = get_config('logstore_legacy', 'loglegacy');
2157          $plugins = explode(',', get_config('tool_log', 'enabled_stores'));
2158          $enabled = $enabled && in_array('logstore_legacy', $plugins);
2159  
2160          if ($enabled) {
2161              return $this->warning(get_string('legacylogginginuse'));
2162          }
2163      }
2164  
2165      /**
2166       * Display message about the benefits of registering on Moodle.org
2167       *
2168       * @return string
2169       */
2170      public function moodleorg_registration_message() {
2171  
2172          $out = format_text(get_string('registerwithmoodleorginfo', 'core_hub'), FORMAT_MARKDOWN);
2173  
2174          $out .= html_writer::link(
2175              new moodle_url('/admin/settings.php', ['section' => 'moodleservices']),
2176              $this->output->pix_icon('i/info', '').' '.get_string('registerwithmoodleorginfoapp', 'core_hub'),
2177              ['class' => 'btn btn-link', 'role' => 'opener', 'target' => '_href']
2178          );
2179  
2180          $out .= html_writer::link(
2181              HUB_MOODLEORGHUBURL,
2182              $this->output->pix_icon('i/stats', '').' '.get_string('registerwithmoodleorginfostats', 'core_hub'),
2183              ['class' => 'btn btn-link', 'role' => 'opener', 'target' => '_href']
2184          );
2185  
2186          $out .= html_writer::link(
2187              HUB_MOODLEORGHUBURL.'/sites',
2188              $this->output->pix_icon('i/location', '').' '.get_string('registerwithmoodleorginfosites', 'core_hub'),
2189              ['class' => 'btn btn-link', 'role' => 'opener', 'target' => '_href']
2190          );
2191  
2192          return $this->output->box($out);
2193      }
2194  
2195      /**
2196       * Display message about benefits of enabling the user feedback feature.
2197       *
2198       * @param bool $showfeedbackencouragement Whether the encouragement content should be displayed or not
2199       * @return string
2200       */
2201      protected function userfeedback_encouragement(bool $showfeedbackencouragement): string {
2202          $output = '';
2203  
2204          if ($showfeedbackencouragement) {
2205              $settingslink = new moodle_url('/admin/settings.php', ['section' => 'userfeedback']);
2206              $output .= $this->warning(get_string('userfeedbackencouragement', 'admin', $settingslink->out()), 'info');
2207          }
2208  
2209          return $output;
2210      }
2211  }