0a59aa81fb23a5a8680c48e795dd36fc04d0ec2a
[moodle.git] / admin / renderer.php
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/>.
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  */
26 defined('MOODLE_INTERNAL') || die();
29 /**
30  * Standard HTML output renderer for core_admin subsystem
31  */
32 class core_admin_renderer extends plugin_renderer_base {
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 = '';
43         $copyrightnotice = text_to_html(get_string('gpl3'));
44         $copyrightnotice = str_replace('target="_blank"', 'onclick="this.target=\'_blank\'"', $copyrightnotice); // extremely ugly validation hack
46         $continue = new single_button(new moodle_url($this->page->url, array(
47             'lang' => $CFG->lang, 'agreelicense' => 1)), get_string('continue'), 'get');
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();
57         return $output;
58     }
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();
77         return $output;
78     }
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 = '';
92         $output .= $this->header();
93         $output .= $this->maturity_warning($maturity);
94         $output .= $this->heading("Moodle $release");
95         $output .= $this->release_notes_link();
97         $output .= $this->environment_check_table($envstatus, $environment_results);
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         }
107         $output .= $this->footer();
108         return $output;
109     }
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 = '';
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);
129         $output .= $this->footer();
131         return $output;
132     }
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 = '';
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');
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();
155         return $output;
156     }
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 = '';
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);
174         if (!$envstatus) {
175             $output .= $this->upgrade_reload(new moodle_url($this->page->url, array('confirmupgrade' => 1, 'cache' => 0)));
177         } else {
178             $output .= $this->notification(get_string('environmentok', 'admin'), 'notifysuccess');
180             if (empty($CFG->skiplangupgrade) and current_language() !== 'en') {
181                 $output .= $this->box(get_string('langpackwillbeupdated', 'admin'), 'generalbox', 'notice');
182             }
184             $output .= $this->continue_button(new moodle_url($this->page->url, array(
185                 'confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0)));
186         }
188         $output .= $this->footer();
190         return $output;
191     }
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) {
206         $output = '';
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);
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         }
223         $button = new single_button($continueurl, get_string('upgradestart', 'admin'), 'get');
224         $button->class = 'continuebutton';
225         $output .= $this->render($button);
226         $output .= $this->footer();
228         return $output;
229     }
231     /**
232      * Display a page to confirm plugin installation cancelation.
233      *
234      * @param bool|string $plugin true if cancelling all, component name otherwsie
235      * @param moodle_url $continue
236      * @return string
237      */
238     public function upgrade_confirm_abort_install_page($plugin, moodle_url $continue) {
240         $pluginman = core_plugin_manager::instance();
241         $abortable = array();
243         if ($plugin === true) {
244             foreach ($pluginman->get_plugins() as $type => $pluginfos) {
245                 foreach ($pluginfos as $pluginfo) {
246                     if ($pluginman->can_cancel_plugin_installation($pluginfo)) {
247                         $abortable[] = $pluginfo;
248                     }
249                 }
250             }
252         } else {
253             $pluginfo = $pluginman->get_plugin_info($plugin);
254             if ($pluginman->can_cancel_plugin_installation($pluginfo)) {
255                 $abortable[] = $pluginfo;
256             }
257         }
259         if (empty($abortable)) {
260             // The UI should not allow this.
261             throw new moodle_exception('err_no_plugin_install_abortable', 'core_plugin');
262         }
264         $out = $this->output->header();
265         $out .= $this->output->heading(get_string('cancelinstallhead', 'core_plugin'), 3);
266         $out .= $this->output->container(get_string('cancelinstallinfo', 'core_plugin'), 'cancelinstallinfo');
268         foreach ($abortable as $pluginfo) {
269             $out .= $this->output->heading($pluginfo->displayname.' ('.$pluginfo->component.')', 4);
270             $out .= $this->output->container(get_string('cancelinstallinfodir', 'core_plugin', $pluginfo->rootdir));
271             if ($repotype = $pluginman->plugin_external_source($pluginfo->component)) {
272                 $out .= $this->output->container(get_string('uninstalldeleteconfirmexternal', 'core_plugin', $repotype),
273                     'uninstalldeleteconfirmexternal');
274             }
275         }
277         $out .= $this->plugins_management_confirm_buttons($continue, $this->page->url);
278         $out .= $this->output->footer();
280         return $out;
281     }
283     /**
284      * Display the admin notifications page.
285      * @param int $maturity
286      * @param bool $insecuredataroot warn dataroot is invalid
287      * @param bool $errorsdisplayed warn invalid dispaly error setting
288      * @param bool $cronoverdue warn cron not running
289      * @param bool $dbproblems warn db has problems
290      * @param bool $maintenancemode warn in maintenance mode
291      * @param bool $buggyiconvnomb warn iconv problems
292      * @param array|null $availableupdates array of \core\update\info objects or null
293      * @param int|null $availableupdatesfetch timestamp of the most recent updates fetch or null (unknown)
294      * @param string[] $cachewarnings An array containing warnings from the Cache API.
295      *
296      * @return string HTML to output.
297      */
298     public function admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed,
299             $cronoverdue, $dbproblems, $maintenancemode, $availableupdates, $availableupdatesfetch,
300             $buggyiconvnomb, $registered, array $cachewarnings = array()) {
301         global $CFG;
302         $output = '';
304         $output .= $this->header();
305         $output .= $this->maturity_info($maturity);
306         $output .= empty($CFG->disableupdatenotifications) ? $this->available_updates($availableupdates, $availableupdatesfetch) : '';
307         $output .= $this->insecure_dataroot_warning($insecuredataroot);
308         $output .= $this->display_errors_warning($errorsdisplayed);
309         $output .= $this->buggy_iconv_warning($buggyiconvnomb);
310         $output .= $this->cron_overdue_warning($cronoverdue);
311         $output .= $this->db_problems($dbproblems);
312         $output .= $this->maintenance_mode_warning($maintenancemode);
313         $output .= $this->cache_warnings($cachewarnings);
314         $output .= $this->registration_warning($registered);
316         //////////////////////////////////////////////////////////////////////////////////////////////////
317         ////  IT IS ILLEGAL AND A VIOLATION OF THE GPL TO HIDE, REMOVE OR MODIFY THIS COPYRIGHT NOTICE ///
318         $output .= $this->moodle_copyright();
319         //////////////////////////////////////////////////////////////////////////////////////////////////
321         $output .= $this->footer();
323         return $output;
324     }
326     /**
327      * Display the plugin management page (admin/plugins.php).
328      *
329      * The filtering options array may contain following items:
330      *  bool contribonly - show only contributed extensions
331      *  bool updatesonly - show only plugins with an available update
332      *
333      * @param core_plugin_manager $pluginman
334      * @param \core\update\checker $checker
335      * @param array $options filtering options
336      * @return string HTML to output.
337      */
338     public function plugin_management_page(core_plugin_manager $pluginman, \core\update\checker $checker, array $options = array()) {
340         $output = '';
342         $output .= $this->header();
343         $output .= $this->heading(get_string('pluginsoverview', 'core_admin'));
344         $output .= $this->check_for_updates_button($checker, $this->page->url);
345         $output .= $this->plugins_overview_panel($pluginman, $options);
346         $output .= $this->plugins_control_panel($pluginman, $options);
347         $output .= $this->footer();
349         return $output;
350     }
352     /**
353      * Renders a button to fetch for available updates.
354      *
355      * @param \core\update\checker $checker
356      * @param moodle_url $reloadurl
357      * @return string HTML
358      */
359     public function check_for_updates_button(\core\update\checker $checker, $reloadurl) {
361         $output = '';
363         if ($checker->enabled()) {
364             $output .= $this->container_start('checkforupdates');
365             $output .= $this->single_button(
366                 new moodle_url($reloadurl, array('fetchremote' => 1)),
367                 get_string('checkforupdates', 'core_plugin')
368             );
369             if ($timefetched = $checker->get_last_timefetched()) {
370                 $timefetched = userdate($timefetched, get_string('strftimedatetime', 'core_langconfig'));
371                 $output .= $this->container(get_string('checkforupdateslast', 'core_plugin', $timefetched), 'lasttimefetched');
372             }
373             $output .= $this->container_end();
374         }
376         return $output;
377     }
379     /**
380      * Display a page to confirm the plugin uninstallation.
381      *
382      * @param core_plugin_manager $pluginman
383      * @param \core\plugininfo\base $pluginfo
384      * @param moodle_url $continueurl URL to continue after confirmation
385      * @param moodle_url $cancelurl URL to to go if cancelled
386      * @return string
387      */
388     public function plugin_uninstall_confirm_page(core_plugin_manager $pluginman, \core\plugininfo\base $pluginfo, moodle_url $continueurl, moodle_url $cancelurl) {
389         $output = '';
391         $pluginname = $pluginman->plugin_name($pluginfo->component);
393         $confirm = '<p>' . get_string('uninstallconfirm', 'core_plugin', array('name' => $pluginname)) . '</p>';
394         if ($extraconfirm = $pluginfo->get_uninstall_extra_warning()) {
395             $confirm .= $extraconfirm;
396         }
398         $output .= $this->output->header();
399         $output .= $this->output->heading(get_string('uninstalling', 'core_plugin', array('name' => $pluginname)));
400         $output .= $this->output->confirm($confirm, $continueurl, $cancelurl);
401         $output .= $this->output->footer();
403         return $output;
404     }
406     /**
407      * Display a page with results of plugin uninstallation and offer removal of plugin files.
408      *
409      * @param core_plugin_manager $pluginman
410      * @param \core\plugininfo\base $pluginfo
411      * @param progress_trace_buffer $progress
412      * @param moodle_url $continueurl URL to continue to remove the plugin folder
413      * @return string
414      */
415     public function plugin_uninstall_results_removable_page(core_plugin_manager $pluginman, \core\plugininfo\base $pluginfo,
416                                                             progress_trace_buffer $progress, moodle_url $continueurl) {
417         $output = '';
419         $pluginname = $pluginman->plugin_name($pluginfo->component);
421         // Do not show navigation here, they must click one of the buttons.
422         $this->page->set_pagelayout('maintenance');
423         $this->page->set_cacheable(false);
425         $output .= $this->output->header();
426         $output .= $this->output->heading(get_string('uninstalling', 'core_plugin', array('name' => $pluginname)));
428         $output .= $this->output->box($progress->get_buffer(), 'generalbox uninstallresultmessage');
430         $confirm = $this->output->container(get_string('uninstalldeleteconfirm', 'core_plugin',
431             array('name' => $pluginname, 'rootdir' => $pluginfo->rootdir)), 'uninstalldeleteconfirm');
433         if ($repotype = $pluginman->plugin_external_source($pluginfo->component)) {
434             $confirm .= $this->output->container(get_string('uninstalldeleteconfirmexternal', 'core_plugin', $repotype),
435                 'uninstalldeleteconfirmexternal');
436         }
438         // After any uninstall we must execute full upgrade to finish the cleanup!
439         $output .= $this->output->confirm($confirm, $continueurl, new moodle_url('/admin/index.php'));
440         $output .= $this->output->footer();
442         return $output;
443     }
445     /**
446      * Display a page with results of plugin uninstallation and inform about the need to remove plugin files manually.
447      *
448      * @param core_plugin_manager $pluginman
449      * @param \core\plugininfo\base $pluginfo
450      * @param progress_trace_buffer $progress
451      * @return string
452      */
453     public function plugin_uninstall_results_page(core_plugin_manager $pluginman, \core\plugininfo\base $pluginfo, progress_trace_buffer $progress) {
454         $output = '';
456         $pluginname = $pluginfo->component;
458         $output .= $this->output->header();
459         $output .= $this->output->heading(get_string('uninstalling', 'core_plugin', array('name' => $pluginname)));
461         $output .= $this->output->box($progress->get_buffer(), 'generalbox uninstallresultmessage');
463         $output .= $this->output->box(get_string('uninstalldelete', 'core_plugin',
464             array('name' => $pluginname, 'rootdir' => $pluginfo->rootdir)), 'generalbox uninstalldelete');
465         $output .= $this->output->continue_button(new moodle_url('/admin/index.php'));
466         $output .= $this->output->footer();
468         return $output;
469     }
471     /**
472      * Display the plugin management page (admin/environment.php).
473      * @param array $versions
474      * @param string $version
475      * @param boolean $envstatus final result of env check (true/false)
476      * @param array $environment_results array of results gathered
477      * @return string HTML to output.
478      */
479     public function environment_check_page($versions, $version, $envstatus, $environment_results) {
480         $output = '';
481         $output .= $this->header();
483         // Print the component download link
484         $output .= html_writer::tag('div', html_writer::link(
485                     new moodle_url('/admin/environment.php', array('action' => 'updatecomponent', 'sesskey' => sesskey())),
486                     get_string('updatecomponent', 'admin')),
487                 array('class' => 'reportlink'));
489         // Heading.
490         $output .= $this->heading(get_string('environment', 'admin'));
492         // Box with info and a menu to choose the version.
493         $output .= $this->box_start();
494         $output .= html_writer::tag('div', get_string('adminhelpenvironment'));
495         $select = new single_select(new moodle_url('/admin/environment.php'), 'version', $versions, $version, null);
496         $select->label = get_string('moodleversion');
497         $output .= $this->render($select);
498         $output .= $this->box_end();
500         // The results
501         $output .= $this->environment_check_table($envstatus, $environment_results);
503         $output .= $this->footer();
504         return $output;
505     }
507     /**
508      * Output a warning message, of the type that appears on the admin notifications page.
509      * @param string $message the message to display.
510      * @param string $type type class
511      * @return string HTML to output.
512      */
513     protected function warning($message, $type = 'warning') {
514         return $this->box($message, 'generalbox admin' . $type);
515     }
517     /**
518      * Render an appropriate message if dataroot is insecure.
519      * @param bool $insecuredataroot
520      * @return string HTML to output.
521      */
522     protected function insecure_dataroot_warning($insecuredataroot) {
523         global $CFG;
525         if ($insecuredataroot == INSECURE_DATAROOT_WARNING) {
526             return $this->warning(get_string('datarootsecuritywarning', 'admin', $CFG->dataroot));
528         } else if ($insecuredataroot == INSECURE_DATAROOT_ERROR) {
529             return $this->warning(get_string('datarootsecurityerror', 'admin', $CFG->dataroot), 'error');
531         } else {
532             return '';
533         }
534     }
536     /**
537      * Render an appropriate message if dataroot is insecure.
538      * @param bool $errorsdisplayed
539      * @return string HTML to output.
540      */
541     protected function display_errors_warning($errorsdisplayed) {
542         if (!$errorsdisplayed) {
543             return '';
544         }
546         return $this->warning(get_string('displayerrorswarning', 'admin'));
547     }
549     /**
550      * Render an appropriate message if iconv is buggy and mbstring missing.
551      * @param bool $buggyiconvnomb
552      * @return string HTML to output.
553      */
554     protected function buggy_iconv_warning($buggyiconvnomb) {
555         if (!$buggyiconvnomb) {
556             return '';
557         }
559         return $this->warning(get_string('warningiconvbuggy', 'admin'));
560     }
562     /**
563      * Render an appropriate message if cron has not been run recently.
564      * @param bool $cronoverdue
565      * @return string HTML to output.
566      */
567     public function cron_overdue_warning($cronoverdue) {
568         global $CFG;
569         if (!$cronoverdue) {
570             return '';
571         }
573         if (empty($CFG->cronclionly)) {
574             $url = new moodle_url('/admin/cron.php');
575             if (!empty($CFG->cronremotepassword)) {
576                 $url = new moodle_url('/admin/cron.php', array('password' => $CFG->cronremotepassword));
577             }
579             return $this->warning(get_string('cronwarning', 'admin', $url->out()) . '&nbsp;' .
580                     $this->help_icon('cron', 'admin'));
581         }
583         // $CFG->cronclionly is not empty: cron can run only from CLI.
584         return $this->warning(get_string('cronwarningcli', 'admin') . '&nbsp;' .
585                 $this->help_icon('cron', 'admin'));
586     }
588     /**
589      * Render an appropriate message if there are any problems with the DB set-up.
590      * @param bool $dbproblems
591      * @return string HTML to output.
592      */
593     public function db_problems($dbproblems) {
594         if (!$dbproblems) {
595             return '';
596         }
598         return $this->warning($dbproblems);
599     }
601     /**
602      * Renders cache warnings if there are any.
603      *
604      * @param string[] $cachewarnings
605      * @return string
606      */
607     public function cache_warnings(array $cachewarnings) {
608         if (!count($cachewarnings)) {
609             return '';
610         }
611         return join("\n", array_map(array($this, 'warning'), $cachewarnings));
612     }
614     /**
615      * Render an appropriate message if the site in in maintenance mode.
616      * @param bool $maintenancemode
617      * @return string HTML to output.
618      */
619     public function maintenance_mode_warning($maintenancemode) {
620         if (!$maintenancemode) {
621             return '';
622         }
624         $url = new moodle_url('/admin/settings.php', array('section' => 'maintenancemode'));
625         $url = $url->out(); // get_string() does not support objects in params
627         return $this->warning(get_string('sitemaintenancewarning2', 'admin', $url));
628     }
630     /**
631      * Display a warning about installing development code if necesary.
632      * @param int $maturity
633      * @return string HTML to output.
634      */
635     protected function maturity_warning($maturity) {
636         if ($maturity == MATURITY_STABLE) {
637             return ''; // No worries.
638         }
640         $maturitylevel = get_string('maturity' . $maturity, 'admin');
641         return $this->warning(
642                     $this->container(get_string('maturitycorewarning', 'admin', $maturitylevel)) .
643                     $this->container($this->doc_link('admin/versions', get_string('morehelp'))),
644                 'error');
645     }
647     /*
648      * If necessary, displays a warning about upgrading a test site.
649      *
650      * @param string $testsite
651      * @return string HTML
652      */
653     protected function test_site_warning($testsite) {
655         if (!$testsite) {
656             return '';
657         }
659         $warning = (get_string('testsiteupgradewarning', 'admin', $testsite));
660         return $this->warning($warning, 'error');
661     }
663     /**
664      * Output the copyright notice.
665      * @return string HTML to output.
666      */
667     protected function moodle_copyright() {
668         global $CFG;
670         //////////////////////////////////////////////////////////////////////////////////////////////////
671         ////  IT IS ILLEGAL AND A VIOLATION OF THE GPL TO HIDE, REMOVE OR MODIFY THIS COPYRIGHT NOTICE ///
672         $copyrighttext = '<a href="http://moodle.org/">Moodle</a> '.
673                          '<a href="http://docs.moodle.org/dev/Releases" title="'.$CFG->version.'">'.$CFG->release.'</a><br />'.
674                          'Copyright &copy; 1999 onwards, Martin Dougiamas<br />'.
675                          'and <a href="http://moodle.org/dev">many other contributors</a>.<br />'.
676                          '<a href="http://docs.moodle.org/dev/License">GNU Public License</a>';
677         //////////////////////////////////////////////////////////////////////////////////////////////////
679         return $this->box($copyrighttext, 'copyright');
680     }
682     /**
683      * Display a warning about installing development code if necesary.
684      * @param int $maturity
685      * @return string HTML to output.
686      */
687     protected function maturity_info($maturity) {
688         if ($maturity == MATURITY_STABLE) {
689             return ''; // No worries.
690         }
692         $level = 'warning';
694         if ($maturity == MATURITY_ALPHA) {
695             $level = 'error';
696         }
698         $maturitylevel = get_string('maturity' . $maturity, 'admin');
699         $warningtext = get_string('maturitycoreinfo', 'admin', $maturitylevel);
700         $warningtext .= ' ' . $this->doc_link('admin/versions', get_string('morehelp'));
701         return $this->warning($warningtext, $level);
702     }
704     /**
705      * Displays the info about available Moodle core and plugin updates
706      *
707      * The structure of the $updates param has changed since 2.4. It contains not only updates
708      * for the core itself, but also for all other installed plugins.
709      *
710      * @param array|null $updates array of (string)component => array of \core\update\info objects or null
711      * @param int|null $fetch timestamp of the most recent updates fetch or null (unknown)
712      * @return string
713      */
714     protected function available_updates($updates, $fetch) {
716         $updateinfo = '';
717         $someupdateavailable = false;
718         if (is_array($updates)) {
719             if (is_array($updates['core'])) {
720                 $someupdateavailable = true;
721                 $updateinfo .= $this->heading(get_string('updateavailable', 'core_admin'), 3);
722                 foreach ($updates['core'] as $update) {
723                     $updateinfo .= $this->moodle_available_update_info($update);
724                 }
725                 $updateinfo .= html_writer::tag('p', get_string('updateavailablerecommendation', 'core_admin'),
726                     array('class' => 'updateavailablerecommendation'));
727             }
728             unset($updates['core']);
729             // If something has left in the $updates array now, it is updates for plugins.
730             if (!empty($updates)) {
731                 $someupdateavailable = true;
732                 $updateinfo .= $this->heading(get_string('updateavailableforplugin', 'core_admin'), 3);
733                 $pluginsoverviewurl = new moodle_url('/admin/plugins.php', array('updatesonly' => 1));
734                 $updateinfo .= $this->container(get_string('pluginsoverviewsee', 'core_admin',
735                     array('url' => $pluginsoverviewurl->out())));
736             }
737         }
739         if (!$someupdateavailable) {
740             $now = time();
741             if ($fetch and ($fetch <= $now) and ($now - $fetch < HOURSECS)) {
742                 $updateinfo .= $this->heading(get_string('updateavailablenot', 'core_admin'), 3);
743             }
744         }
746         $updateinfo .= $this->container_start('checkforupdates');
747         $fetchurl = new moodle_url('/admin/index.php', array('fetchupdates' => 1, 'sesskey' => sesskey(), 'cache' => 0));
748         $updateinfo .= $this->single_button($fetchurl, get_string('checkforupdates', 'core_plugin'));
749         if ($fetch) {
750             $updateinfo .= $this->container(get_string('checkforupdateslast', 'core_plugin',
751                 userdate($fetch, get_string('strftimedatetime', 'core_langconfig'))));
752         }
753         $updateinfo .= $this->container_end();
755         return $this->warning($updateinfo);
756     }
758     /**
759      * Display a warning about not being registered on Moodle.org if necesary.
760      *
761      * @param boolean $registered true if the site is registered on Moodle.org
762      * @return string HTML to output.
763      */
764     protected function registration_warning($registered) {
766         if (!$registered) {
768             $registerbutton = $this->single_button(new moodle_url('/admin/registration/register.php',
769                     array('huburl' =>  HUB_MOODLEORGHUBURL, 'hubname' => 'Moodle.org')),
770                     get_string('register', 'admin'));
772             return $this->warning( get_string('registrationwarning', 'admin')
773                     . '&nbsp;' . $this->help_icon('registration', 'admin') . $registerbutton );
774         }
776         return '';
777     }
779     /**
780      * Helper method to render the information about the available Moodle update
781      *
782      * @param \core\update\info $updateinfo information about the available Moodle core update
783      */
784     protected function moodle_available_update_info(\core\update\info $updateinfo) {
786         $boxclasses = 'moodleupdateinfo';
787         $info = array();
789         if (isset($updateinfo->release)) {
790             $info[] = html_writer::tag('span', get_string('updateavailable_release', 'core_admin', $updateinfo->release),
791                 array('class' => 'info release'));
792         }
794         if (isset($updateinfo->version)) {
795             $info[] = html_writer::tag('span', get_string('updateavailable_version', 'core_admin', $updateinfo->version),
796                 array('class' => 'info version'));
797         }
799         if (isset($updateinfo->maturity)) {
800             $info[] = html_writer::tag('span', get_string('maturity'.$updateinfo->maturity, 'core_admin'),
801                 array('class' => 'info maturity'));
802             $boxclasses .= ' maturity'.$updateinfo->maturity;
803         }
805         if (isset($updateinfo->download)) {
806             $info[] = html_writer::link($updateinfo->download, get_string('download'), array('class' => 'info download'));
807         }
809         if (isset($updateinfo->url)) {
810             $info[] = html_writer::link($updateinfo->url, get_string('updateavailable_moreinfo', 'core_plugin'),
811                 array('class' => 'info more'));
812         }
814         $box  = $this->output->box_start($boxclasses);
815         $box .= $this->output->box(implode(html_writer::tag('span', ' ', array('class' => 'separator')), $info), '');
816         $box .= $this->output->box_end();
818         return $box;
819     }
821     /**
822      * Display a link to the release notes.
823      * @return string HTML to output.
824      */
825     protected function release_notes_link() {
826         $releasenoteslink = get_string('releasenoteslink', 'admin', 'http://docs.moodle.org/dev/Releases');
827         $releasenoteslink = str_replace('target="_blank"', 'onclick="this.target=\'_blank\'"', $releasenoteslink); // extremely ugly validation hack
828         return $this->box($releasenoteslink, 'generalbox releasenoteslink');
829     }
831     /**
832      * Display the reload link that appears on several upgrade/install pages.
833      * @return string HTML to output.
834      */
835     function upgrade_reload($url) {
836         return html_writer::empty_tag('br') .
837                 html_writer::tag('div',
838                     html_writer::link($url, $this->pix_icon('i/reload', '', '', array('class' => 'icon icon-pre')) .
839                             get_string('reload'), array('title' => get_string('reload'))),
840                 array('class' => 'continuebutton')) . html_writer::empty_tag('br');
841     }
843     /**
844      * Displays all known plugins and information about their installation or upgrade
845      *
846      * This default implementation renders all plugins into one big table. The rendering
847      * options support:
848      *     (bool)full = false: whether to display up-to-date plugins, too
849      *     (bool)xdep = false: display the plugins with unsatisified dependecies only
850      *
851      * @param core_plugin_manager $pluginman provides information about the plugins.
852      * @param int $version the version of the Moodle code from version.php.
853      * @param array $options rendering options
854      * @return string HTML code
855      */
856     public function plugins_check_table(core_plugin_manager $pluginman, $version, array $options = array()) {
858         $plugininfo = $pluginman->get_plugins();
860         if (empty($plugininfo)) {
861             return '';
862         }
864         $options['full'] = isset($options['full']) ? (bool)$options['full'] : false;
865         $options['xdep'] = isset($options['xdep']) ? (bool)$options['xdep'] : false;
867         $table = new html_table();
868         $table->id = 'plugins-check';
869         $table->head = array(
870             get_string('displayname', 'core_plugin').' / '.get_string('rootdir', 'core_plugin'),
871             get_string('versiondb', 'core_plugin'),
872             get_string('versiondisk', 'core_plugin'),
873             get_string('requires', 'core_plugin'),
874             get_string('source', 'core_plugin').' / '.get_string('status', 'core_plugin'),
875         );
876         $table->colclasses = array(
877             'displayname', 'versiondb', 'versiondisk', 'requires', 'status',
878         );
879         $table->data = array();
881         // Number of displayed plugins per type.
882         $numdisplayed = array();
883         // Number of plugins known to the plugin manager.
884         $sumtotal = 0;
885         // Number of plugins requiring attention.
886         $sumattention = 0;
887         // List of all components we can cancel installation of.
888         $installabortable = array();
890         foreach ($plugininfo as $type => $plugins) {
892             $header = new html_table_cell($pluginman->plugintype_name_plural($type));
893             $header->header = true;
894             $header->colspan = count($table->head);
895             $header = new html_table_row(array($header));
896             $header->attributes['class'] = 'plugintypeheader type-' . $type;
898             $numdisplayed[$type] = 0;
900             if (empty($plugins) and $options['full']) {
901                 $msg = new html_table_cell(get_string('noneinstalled', 'core_plugin'));
902                 $msg->colspan = count($table->head);
903                 $row = new html_table_row(array($msg));
904                 $row->attributes['class'] .= 'msg msg-noneinstalled';
905                 $table->data[] = $header;
906                 $table->data[] = $row;
907                 continue;
908             }
910             $plugintyperows = array();
912             foreach ($plugins as $name => $plugin) {
913                 $sumtotal++;
914                 $row = new html_table_row();
915                 $row->attributes['class'] = 'type-' . $plugin->type . ' name-' . $plugin->type . '_' . $plugin->name;
917                 if ($this->page->theme->resolve_image_location('icon', $plugin->type . '_' . $plugin->name, null)) {
918                     $icon = $this->output->pix_icon('icon', '', $plugin->type . '_' . $plugin->name, array('class' => 'smallicon pluginicon'));
919                 } else {
920                     $icon = '';
921                 }
923                 $displayname = new html_table_cell(
924                     $icon.
925                     html_writer::span($plugin->displayname, 'pluginname').
926                     html_writer::div($plugin->get_dir(), 'plugindir')
927                 );
929                 $versiondb = new html_table_cell($plugin->versiondb);
930                 $versiondisk = new html_table_cell($plugin->versiondisk);
932                 if ($isstandard = $plugin->is_standard()) {
933                     $row->attributes['class'] .= ' standard';
934                     $sourcelabel = html_writer::span(get_string('sourcestd', 'core_plugin'), 'sourcetext label');
935                 } else {
936                     $row->attributes['class'] .= ' extension';
937                     $sourcelabel = html_writer::span(get_string('sourceext', 'core_plugin'), 'sourcetext label label-info');
938                 }
940                 $coredependency = $plugin->is_core_dependency_satisfied($version);
941                 $otherpluginsdependencies = $pluginman->are_dependencies_satisfied($plugin->get_other_required_plugins());
942                 $dependenciesok = $coredependency && $otherpluginsdependencies;
944                 $statuscode = $plugin->get_status();
945                 $row->attributes['class'] .= ' status-' . $statuscode;
946                 $statusclass = 'statustext label ';
947                 switch ($statuscode) {
948                     case core_plugin_manager::PLUGIN_STATUS_NEW:
949                         $statusclass .= $dependenciesok ? 'label-success' : 'label-warning';
950                         break;
951                     case core_plugin_manager::PLUGIN_STATUS_UPGRADE:
952                         $statusclass .= $dependenciesok ? 'label-info' : 'label-warning';
953                         break;
954                     case core_plugin_manager::PLUGIN_STATUS_MISSING:
955                     case core_plugin_manager::PLUGIN_STATUS_DOWNGRADE:
956                     case core_plugin_manager::PLUGIN_STATUS_DELETE:
957                         $statusclass .= 'label-important';
958                         break;
959                     case core_plugin_manager::PLUGIN_STATUS_NODB:
960                     case core_plugin_manager::PLUGIN_STATUS_UPTODATE:
961                         $statusclass .= $dependenciesok ? '' : 'label-warning';
962                         break;
963                 }
964                 $status = html_writer::span(get_string('status_' . $statuscode, 'core_plugin'), $statusclass);
966                 if ($statuscode == core_plugin_manager::PLUGIN_STATUS_NEW and !$plugin->is_standard()) {
967                     if ($pluginman->is_plugin_folder_removable($plugin->component)) {
968                         $installabortable[] = $plugin->component;
969                         $status .= $this->output->single_button(
970                             new moodle_url($this->page->url, array('abortinstall' => $plugin->component)),
971                             get_string('cancelinstallone', 'core_plugin'),
972                             'post',
973                             array('class' => 'actionbutton')
974                         );
975                     }
976                 }
978                 $availableupdates = $plugin->available_updates();
979                 if (!empty($availableupdates)) {
980                     foreach ($availableupdates as $availableupdate) {
981                         $status .= $this->plugin_available_update_info($pluginman, $availableupdate);
982                     }
983                 }
985                 $status = new html_table_cell($sourcelabel.' '.$status);
987                 $requires = new html_table_cell($this->required_column($plugin, $pluginman, $version));
989                 $statusisboring = in_array($statuscode, array(
990                         core_plugin_manager::PLUGIN_STATUS_NODB, core_plugin_manager::PLUGIN_STATUS_UPTODATE));
992                 if ($options['xdep']) {
993                     // we want to see only plugins with failed dependencies
994                     if ($dependenciesok) {
995                         continue;
996                     }
998                 } else if ($statusisboring and $dependenciesok and empty($availableupdates)) {
999                     // no change is going to happen to the plugin - display it only
1000                     // if the user wants to see the full list
1001                     if (empty($options['full'])) {
1002                         continue;
1003                     }
1005                 } else {
1006                     $sumattention++;
1007                 }
1009                 // The plugin should be displayed.
1010                 $numdisplayed[$type]++;
1011                 $row->cells = array($displayname, $versiondb, $versiondisk, $requires, $status);
1012                 $plugintyperows[] = $row;
1013             }
1015             if (empty($numdisplayed[$type]) and empty($options['full'])) {
1016                 continue;
1017             }
1019             $table->data[] = $header;
1020             $table->data = array_merge($table->data, $plugintyperows);
1021         }
1023         // Total number of displayed plugins.
1024         $sumdisplayed = array_sum($numdisplayed);
1026         if ($options['xdep']) {
1027             // At the plugins dependencies check page, display the table only.
1028             return html_writer::table($table);
1029         }
1031         $out = $this->output->container_start('', 'plugins-check-info');
1033         if ($sumdisplayed == 0) {
1034             $out .= $this->output->heading(get_string('pluginchecknone', 'core_plugin'));
1036         } else {
1037             if (empty($options['full'])) {
1038                 $out .= $this->output->heading(get_string('plugincheckattention', 'core_plugin'));
1039             } else {
1040                 $out .= $this->output->heading(get_string('plugincheckall', 'core_plugin'));
1041             }
1042         }
1044         $out .= $this->output->container_start('actions');
1045         if ($installabortable) {
1046             $out .= $this->output->single_button(
1047                 new moodle_url($this->page->url, array('abortinstallx' => 1)),
1048                 get_string('cancelinstallall', 'core_plugin', count($installabortable)),
1049                 'post',
1050                 array('class' => 'singlebutton cancelinstallall')
1051             );
1052         }
1054         $installableupdates = $pluginman->filter_installable($pluginman->available_updates());
1055         if ($installableupdates) {
1056             $out .= $this->output->single_button(
1057                 new moodle_url($this->page->url, array('installupdatex' => 1)),
1058                 get_string('updateavailableinstallall', 'core_admin', count($installableupdates)),
1059                 'post',
1060                 array('class' => 'singlebutton updateavailableinstallall')
1061             );
1062         }
1064         $out .= html_writer::div(html_writer::link(new moodle_url($this->page->url, array('showallplugins' => 0)),
1065             get_string('plugincheckattention', 'core_plugin')).' '.html_writer::span($sumattention, 'badge'));
1067         $out .= html_writer::div(html_writer::link(new moodle_url($this->page->url, array('showallplugins' => 1)),
1068             get_string('plugincheckall', 'core_plugin')).' '.html_writer::span($sumtotal, 'badge'));
1070         $out .= $this->output->container_end(); // .actions
1071         $out .= $this->output->container_end(); // #plugins-check-info
1073         if ($sumdisplayed > 0 or $options['full']) {
1074             $out .= html_writer::table($table);
1075         }
1077         return $out;
1078     }
1080     /**
1081      * Display the continue / cancel widgets for the plugins management pages.
1082      *
1083      * @param null|moodle_url $continue URL for the continue button, should it be displayed
1084      * @param moodle_url $cancel URL for the cancel link, defaults to the current page
1085      * @return string HTML
1086      */
1087     public function plugins_management_confirm_buttons(moodle_url $continue=null, moodle_url $cancel=null) {
1089         $out = html_writer::start_div('plugins-management-confirm-buttons');
1091         if (!empty($continue)) {
1092             $out .= $this->output->single_button($continue, get_string('continue'), 'post', array('class' => 'continue'));
1093         }
1095         if (empty($cancel)) {
1096             $cancel = $this->page->url;
1097         }
1098         $out .= html_writer::div(html_writer::link($cancel, get_string('cancel')), 'cancel');
1100         return $out;
1101     }
1103     /**
1104      * Displays the information about missing dependencies
1105      *
1106      * @param core_plugin_manager $pluginman
1107      * @return string
1108      */
1109     protected function missing_dependencies(core_plugin_manager $pluginman) {
1111         $dependencies = $pluginman->missing_dependencies();
1113         if (empty($dependencies)) {
1114             return '';
1115         }
1117         $available = array();
1118         $unavailable = array();
1119         $unknown = array();
1121         foreach ($dependencies as $component => $remoteinfo) {
1122             if ($remoteinfo === false) {
1123                 // The required version is not available. Let us check if there
1124                 // is at least some version in the plugins directory.
1125                 $remoteinfoanyversion = $pluginman->get_remote_plugin_info($component, ANY_VERSION, false);
1126                 if ($remoteinfoanyversion === false) {
1127                     $unknown[$component] = $component;
1128                 } else {
1129                     $unavailable[$component] = $remoteinfoanyversion;
1130                 }
1131             } else {
1132                 $available[$component] = $remoteinfo;
1133             }
1134         }
1136         $out  = $this->output->container_start('plugins-check-dependencies');
1138         if ($unavailable or $unknown) {
1139             $out .= $this->output->heading(get_string('misdepsunavail', 'core_plugin'));
1140             if ($unknown) {
1141                 $out .= $this->output->notification(get_string('misdepsunknownlist', 'core_plugin', implode($unknown, ', ')));
1142             }
1143             if ($unavailable) {
1144                 $unavailablelist = array();
1145                 foreach ($unavailable as $component => $remoteinfoanyversion) {
1146                     $unavailablelistitem = html_writer::link('https://moodle.org/plugins/view.php?plugin='.$component,
1147                         '<strong>'.$remoteinfoanyversion->name.'</strong>');
1148                     if ($remoteinfoanyversion->version) {
1149                         $unavailablelistitem .= ' ('.$component.' &gt; '.$remoteinfoanyversion->version->version.')';
1150                     } else {
1151                         $unavailablelistitem .= ' ('.$component.')';
1152                     }
1153                     $unavailablelist[] = $unavailablelistitem;
1154                 }
1155                 $out .= $this->output->notification(get_string('misdepsunavaillist', 'core_plugin',
1156                     implode($unavailablelist, ', ')));
1157             }
1158             $out .= $this->output->container_start('plugins-check-dependencies-actions');
1159             $out .= ' '.html_writer::link(new moodle_url('/admin/tool/installaddon/'),
1160                 get_string('dependencyuploadmissing', 'core_plugin'));
1161             $out .= $this->output->container_end(); // .plugins-check-dependencies-actions
1162         }
1164         if ($available) {
1165             $out .= $this->output->heading(get_string('misdepsavail', 'core_plugin'));
1166             $out .= $this->output->container_start('plugins-check-dependencies-actions');
1168             $installable = $pluginman->filter_installable($available);
1169             if ($installable) {
1170                 $out .= $this->output->single_button(
1171                     new moodle_url($this->page->url, array('installdepx' => 1)),
1172                     get_string('dependencyinstallmissing', 'core_plugin', count($installable)),
1173                     'post',
1174                     array('class' => 'singlebutton dependencyinstallmissing')
1175                 );
1176             }
1178             $out.= html_writer::div(html_writer::link(new moodle_url('/admin/tool/installaddon/'),
1179                 get_string('dependencyuploadmissing', 'core_plugin')), 'dependencyuploadmissing');
1181             $out .= $this->output->container_end(); // .plugins-check-dependencies-actions
1183             $out .= $this->available_missing_dependencies_list($pluginman, $available);
1184         }
1186         $out .= $this->output->container_end(); // .plugins-check-dependencies
1188         return $out;
1189     }
1191     /**
1192      * Displays the list if available missing dependencies.
1193      *
1194      * @param core_plugin_manager $pluginman
1195      * @param array $dependencies
1196      * @return string
1197      */
1198     protected function available_missing_dependencies_list(core_plugin_manager $pluginman, array $dependencies) {
1199         global $CFG;
1201         $table = new html_table();
1202         $table->id = 'plugins-check-available-dependencies';
1203         $table->head = array(
1204             get_string('displayname', 'core_plugin'),
1205             get_string('release', 'core_plugin'),
1206             get_string('version', 'core_plugin'),
1207             get_string('supportedmoodleversions', 'core_plugin'),
1208             get_string('info', 'core'),
1209         );
1210         $table->colclasses = array('displayname', 'release', 'version', 'supportedmoodleversions', 'info');
1211         $table->data = array();
1213         foreach ($dependencies as $plugin) {
1215             $supportedmoodles = array();
1216             foreach ($plugin->version->supportedmoodles as $moodle) {
1217                 if ($CFG->branch == str_replace('.', '', $moodle->release)) {
1218                     $supportedmoodles[] = html_writer::span($moodle->release, 'label label-success');
1219                 } else {
1220                     $supportedmoodles[] = html_writer::span($moodle->release, 'label');
1221                 }
1222             }
1224             $requriedby = $pluginman->other_plugins_that_require($plugin->component);
1225             if ($requriedby) {
1226                 foreach ($requriedby as $ix => $val) {
1227                     $inf = $pluginman->get_plugin_info($val);
1228                     if ($inf) {
1229                         $requriedby[$ix] = $inf->displayname.' ('.$inf->component.')';
1230                     }
1231                 }
1232                 $info = html_writer::div(
1233                     get_string('requiredby', 'core_plugin', implode(', ', $requriedby)),
1234                     'requiredby'
1235                 );
1236             } else {
1237                 $info = '';
1238             }
1240             $info .= $this->output->container_start('actions');
1242             $info .= html_writer::div(
1243                 html_writer::link('https://moodle.org/plugins/view.php?plugin='.$plugin->component,
1244                     get_string('misdepinfoplugin', 'core_plugin')),
1245                 'misdepinfoplugin'
1246             );
1248             $info .= html_writer::div(
1249                 html_writer::link('https://moodle.org/plugins/pluginversion.php?id='.$plugin->version->id,
1250                     get_string('misdepinfoversion', 'core_plugin')),
1251                 'misdepinfoversion'
1252             );
1254             $info .= html_writer::div(html_writer::link($plugin->version->downloadurl, get_string('download')), 'misdepdownload');
1256             if ($pluginman->is_remote_plugin_installable($plugin->component, $plugin->version->version, $reason)) {
1257                 $info .= $this->output->single_button(
1258                     new moodle_url($this->page->url, array('installdep' => $plugin->component)),
1259                     get_string('dependencyinstall', 'core_plugin'),
1260                     'post',
1261                     array('class' => 'singlebutton dependencyinstall')
1262                 );
1263             } else {
1264                 $reasonhelp = $this->info_remote_plugin_not_installable($reason);
1265                 if ($reasonhelp) {
1266                     $info .= html_writer::div($reasonhelp, 'reasonhelp dependencyinstall');
1267                 }
1268             }
1270             $info .= $this->output->container_end(); // .actions
1272             $table->data[] = array(
1273                 html_writer::div($plugin->name, 'name').' '.html_writer::div($plugin->component, 'component'),
1274                 $plugin->version->release,
1275                 $plugin->version->version,
1276                 implode($supportedmoodles, ' '),
1277                 $info
1278             );
1279         }
1281         return html_writer::table($table);
1282     }
1284     /**
1285      * Explain why {@link core_plugin_manager::is_remote_plugin_installable()} returned false.
1286      *
1287      * @param string $reason the reason code as returned by the plugin manager
1288      * @return string
1289      */
1290     protected function info_remote_plugin_not_installable($reason) {
1292         if ($reason === 'notwritableplugintype' or $reason === 'notwritableplugin') {
1293             return $this->output->help_icon('notwritable', 'core_plugin', get_string('notwritable', 'core_plugin'));
1294         }
1296         if ($reason === 'remoteunavailable') {
1297             return $this->output->help_icon('notdownloadable', 'core_plugin', get_string('notdownloadable', 'core_plugin'));
1298         }
1300         return false;
1301     }
1303     /**
1304      * Formats the information that needs to go in the 'Requires' column.
1305      * @param \core\plugininfo\base $plugin the plugin we are rendering the row for.
1306      * @param core_plugin_manager $pluginman provides data on all the plugins.
1307      * @param string $version
1308      * @return string HTML code
1309      */
1310     protected function required_column(\core\plugininfo\base $plugin, core_plugin_manager $pluginman, $version) {
1312         $requires = array();
1313         $displayuploadlink = false;
1314         $displayupdateslink = false;
1316         foreach ($pluginman->resolve_requirements($plugin, $version) as $reqname => $reqinfo) {
1317             if ($reqname === 'core') {
1318                 if ($reqinfo->status == $pluginman::REQUIREMENT_STATUS_OK) {
1319                     $class = 'requires-ok';
1320                     $label = '';
1321                 } else {
1322                     $class = 'requires-failed';
1323                     $label = html_writer::span(get_string('dependencyfails', 'core_plugin'), 'label label-important');
1324                 }
1325                 $requires[] = html_writer::tag('li',
1326                     html_writer::span(get_string('moodleversion', 'core_plugin', $plugin->versionrequires), 'dep dep-core').
1327                     ' '.$label, array('class' => $class));
1329             } else {
1330                 $actions = array();
1332                 if ($reqinfo->status == $pluginman::REQUIREMENT_STATUS_OK) {
1333                     $label = '';
1334                     $class = 'requires-ok';
1336                 } else if ($reqinfo->status == $pluginman::REQUIREMENT_STATUS_MISSING) {
1337                     if ($reqinfo->availability == $pluginman::REQUIREMENT_AVAILABLE) {
1338                         $label = html_writer::span(get_string('dependencymissing', 'core_plugin'), 'label label-warning');
1339                         $label .= ' '.html_writer::span(get_string('dependencyavailable', 'core_plugin'), 'label label-warning');
1340                         $class = 'requires-failed requires-missing requires-available';
1341                         $actions[] = html_writer::link(
1342                             new moodle_url('https://moodle.org/plugins/view.php', array('plugin' => $reqname)),
1343                             get_string('misdepinfoplugin', 'core_plugin')
1344                         );
1346                     } else {
1347                         $label = html_writer::span(get_string('dependencymissing', 'core_plugin'), 'label label-important');
1348                         $label .= ' '.html_writer::span(get_string('dependencyunavailable', 'core_plugin'),
1349                             'label label-important');
1350                         $class = 'requires-failed requires-missing requires-unavailable';
1351                     }
1352                     $displayuploadlink = true;
1354                 } else if ($reqinfo->status == $pluginman::REQUIREMENT_STATUS_OUTDATED) {
1355                     if ($reqinfo->availability == $pluginman::REQUIREMENT_AVAILABLE) {
1356                         $label = html_writer::span(get_string('dependencyfails', 'core_plugin'), 'label label-warning');
1357                         $label .= ' '.html_writer::span(get_string('dependencyavailable', 'core_plugin'), 'label label-warning');
1358                         $class = 'requires-failed requires-outdated requires-available';
1359                         $displayupdateslink = true;
1361                     } else {
1362                         $label = html_writer::span(get_string('dependencyfails', 'core_plugin'), 'label label-important');
1363                         $label .= ' '.html_writer::span(get_string('dependencyunavailable', 'core_plugin'),
1364                             'label label-important');
1365                         $class = 'requires-failed requires-outdated requires-unavailable';
1366                     }
1367                     $displayuploadlink = true;
1368                 }
1370                 if ($reqinfo->reqver != ANY_VERSION) {
1371                     $str = 'otherpluginversion';
1372                 } else {
1373                     $str = 'otherplugin';
1374                 }
1376                 $requires[] = html_writer::tag('li', html_writer::span(
1377                     get_string($str, 'core_plugin', array('component' => $reqname, 'version' => $reqinfo->reqver)),
1378                     'dep dep-plugin').' '.$label.' '.html_writer::span(implode(' | ', $actions), 'actions'),
1379                     array('class' => $class)
1380                 );
1381             }
1382         }
1384         if (!$requires) {
1385             return '';
1386         }
1388         $out = html_writer::tag('ul', implode("\n", $requires));
1390         if ($displayuploadlink) {
1391             $out .= html_writer::div(
1392                 html_writer::link(
1393                     new moodle_url('/admin/tool/installaddon/'),
1394                     get_string('dependencyuploadmissing', 'core_plugin')
1395                 ),
1396                 'dependencyuploadmissing'
1397             );
1398         }
1400         if ($displayupdateslink) {
1401             $out .= html_writer::div(
1402                 html_writer::link(
1403                     new moodle_url($this->page->url, array('sesskey' => sesskey(), 'fetchupdates' => 1)),
1404                     get_string('checkforupdates', 'core_plugin')
1405                 ),
1406                 'checkforupdates'
1407             );
1408         }
1410         return $out;
1412     }
1414     /**
1415      * Prints an overview about the plugins - number of installed, number of extensions etc.
1416      *
1417      * @param core_plugin_manager $pluginman provides information about the plugins
1418      * @param array $options filtering options
1419      * @return string as usually
1420      */
1421     public function plugins_overview_panel(core_plugin_manager $pluginman, array $options = array()) {
1423         $plugininfo = $pluginman->get_plugins();
1425         $numtotal = $numextension = $numupdatable = 0;
1427         foreach ($plugininfo as $type => $plugins) {
1428             foreach ($plugins as $name => $plugin) {
1429                 if ($plugin->available_updates()) {
1430                     $numupdatable++;
1431                 }
1432                 if ($plugin->get_status() === core_plugin_manager::PLUGIN_STATUS_MISSING) {
1433                     continue;
1434                 }
1435                 $numtotal++;
1436                 if (!$plugin->is_standard()) {
1437                     $numextension++;
1438                 }
1439             }
1440         }
1442         $infoall = html_writer::link(
1443             new moodle_url($this->page->url, array('contribonly' => 0, 'updatesonly' => 0)),
1444             get_string('overviewall', 'core_plugin'),
1445             array('title' => get_string('filterall', 'core_plugin'))
1446         ).' '.html_writer::span($numtotal, 'badge number number-all');
1448         $infoext = html_writer::link(
1449             new moodle_url($this->page->url, array('contribonly' => 1, 'updatesonly' => 0)),
1450             get_string('overviewext', 'core_plugin'),
1451             array('title' => get_string('filtercontribonly', 'core_plugin'))
1452         ).' '.html_writer::span($numextension, 'badge number number-additional');
1454         if ($numupdatable) {
1455             $infoupdatable = html_writer::link(
1456                 new moodle_url($this->page->url, array('contribonly' => 0, 'updatesonly' => 1)),
1457                 get_string('overviewupdatable', 'core_plugin'),
1458                 array('title' => get_string('filterupdatesonly', 'core_plugin'))
1459             ).' '.html_writer::span($numupdatable, 'badge badge-info number number-updatable');
1460         } else {
1461             // No updates, or the notifications disabled.
1462             $infoupdatable = '';
1463         }
1465         $out = html_writer::start_div('', array('id' => 'plugins-overview-panel'));
1467         if (!empty($options['updatesonly'])) {
1468             $out .= $this->output->heading(get_string('overviewupdatable', 'core_plugin'), 3);
1469         } else if (!empty($options['contribonly'])) {
1470             $out .= $this->output->heading(get_string('overviewext', 'core_plugin'), 3);
1471         }
1473         if ($numupdatable) {
1474             $installableupdates = $pluginman->filter_installable($pluginman->available_updates());
1475             if ($installableupdates) {
1476                 $out .= $this->output->single_button(
1477                     new moodle_url($this->page->url, array('installupdatex' => 1)),
1478                     get_string('updateavailableinstallall', 'core_admin', count($installableupdates)),
1479                     'post',
1480                     array('class' => 'singlebutton updateavailableinstallall')
1481                 );
1482             }
1483         }
1485         $out .= html_writer::div($infoall, 'info info-all').
1486             html_writer::div($infoext, 'info info-ext').
1487             html_writer::div($infoupdatable, 'info info-updatable');
1489         $out .= html_writer::end_div(); // #plugins-overview-panel
1491         return $out;
1492     }
1494     /**
1495      * Displays all known plugins and links to manage them
1496      *
1497      * This default implementation renders all plugins into one big table.
1498      *
1499      * @param core_plugin_manager $pluginman provides information about the plugins.
1500      * @param array $options filtering options
1501      * @return string HTML code
1502      */
1503     public function plugins_control_panel(core_plugin_manager $pluginman, array $options = array()) {
1505         $plugininfo = $pluginman->get_plugins();
1507         // Filter the list of plugins according the options.
1508         if (!empty($options['updatesonly'])) {
1509             $updateable = array();
1510             foreach ($plugininfo as $plugintype => $pluginnames) {
1511                 foreach ($pluginnames as $pluginname => $pluginfo) {
1512                     $pluginavailableupdates = $pluginfo->available_updates();
1513                     if (!empty($pluginavailableupdates)) {
1514                         foreach ($pluginavailableupdates as $pluginavailableupdate) {
1515                             $updateable[$plugintype][$pluginname] = $pluginfo;
1516                         }
1517                     }
1518                 }
1519             }
1520             $plugininfo = $updateable;
1521         }
1523         if (!empty($options['contribonly'])) {
1524             $contribs = array();
1525             foreach ($plugininfo as $plugintype => $pluginnames) {
1526                 foreach ($pluginnames as $pluginname => $pluginfo) {
1527                     if (!$pluginfo->is_standard()) {
1528                         $contribs[$plugintype][$pluginname] = $pluginfo;
1529                     }
1530                 }
1531             }
1532             $plugininfo = $contribs;
1533         }
1535         if (empty($plugininfo)) {
1536             return '';
1537         }
1539         $table = new html_table();
1540         $table->id = 'plugins-control-panel';
1541         $table->head = array(
1542             get_string('displayname', 'core_plugin'),
1543             get_string('version', 'core_plugin'),
1544             get_string('availability', 'core_plugin'),
1545             get_string('actions', 'core_plugin'),
1546             get_string('notes','core_plugin'),
1547         );
1548         $table->headspan = array(1, 1, 1, 2, 1);
1549         $table->colclasses = array(
1550             'pluginname', 'version', 'availability', 'settings', 'uninstall', 'notes'
1551         );
1553         foreach ($plugininfo as $type => $plugins) {
1554             $heading = $pluginman->plugintype_name_plural($type);
1555             $pluginclass = core_plugin_manager::resolve_plugininfo_class($type);
1556             if ($manageurl = $pluginclass::get_manage_url()) {
1557                 $heading .= $this->output->action_icon($manageurl, new pix_icon('i/settings',
1558                     get_string('settings', 'core_plugin')));
1559             }
1560             $header = new html_table_cell(html_writer::tag('span', $heading, array('id'=>'plugin_type_cell_'.$type)));
1561             $header->header = true;
1562             $header->colspan = array_sum($table->headspan);
1563             $header = new html_table_row(array($header));
1564             $header->attributes['class'] = 'plugintypeheader type-' . $type;
1565             $table->data[] = $header;
1567             if (empty($plugins)) {
1568                 $msg = new html_table_cell(get_string('noneinstalled', 'core_plugin'));
1569                 $msg->colspan = array_sum($table->headspan);
1570                 $row = new html_table_row(array($msg));
1571                 $row->attributes['class'] .= 'msg msg-noneinstalled';
1572                 $table->data[] = $row;
1573                 continue;
1574             }
1576             foreach ($plugins as $name => $plugin) {
1577                 $row = new html_table_row();
1578                 $row->attributes['class'] = 'type-' . $plugin->type . ' name-' . $plugin->type . '_' . $plugin->name;
1580                 if ($this->page->theme->resolve_image_location('icon', $plugin->type . '_' . $plugin->name)) {
1581                     $icon = $this->output->pix_icon('icon', '', $plugin->type . '_' . $plugin->name, array('class' => 'icon pluginicon'));
1582                 } else {
1583                     $icon = $this->output->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
1584                 }
1585                 $status = $plugin->get_status();
1586                 $row->attributes['class'] .= ' status-'.$status;
1587                 $pluginname  = html_writer::tag('div', $icon.$plugin->displayname, array('class' => 'displayname')).
1588                                html_writer::tag('div', $plugin->component, array('class' => 'componentname'));
1589                 $pluginname  = new html_table_cell($pluginname);
1591                 $version = html_writer::div($plugin->versiondb, 'versionnumber');
1592                 if ((string)$plugin->release !== '') {
1593                     $version = html_writer::div($plugin->release, 'release').$version;
1594                 }
1595                 $version = new html_table_cell($version);
1597                 $isenabled = $plugin->is_enabled();
1598                 if (is_null($isenabled)) {
1599                     $availability = new html_table_cell('');
1600                 } else if ($isenabled) {
1601                     $row->attributes['class'] .= ' enabled';
1602                     $availability = new html_table_cell(get_string('pluginenabled', 'core_plugin'));
1603                 } else {
1604                     $row->attributes['class'] .= ' disabled';
1605                     $availability = new html_table_cell(get_string('plugindisabled', 'core_plugin'));
1606                 }
1608                 $settingsurl = $plugin->get_settings_url();
1609                 if (!is_null($settingsurl)) {
1610                     $settings = html_writer::link($settingsurl, get_string('settings', 'core_plugin'), array('class' => 'settings'));
1611                 } else {
1612                     $settings = '';
1613                 }
1614                 $settings = new html_table_cell($settings);
1616                 if ($uninstallurl = $pluginman->get_uninstall_url($plugin->component, 'overview')) {
1617                     $uninstall = html_writer::link($uninstallurl, get_string('uninstall', 'core_plugin'));
1618                 } else {
1619                     $uninstall = '';
1620                 }
1621                 $uninstall = new html_table_cell($uninstall);
1623                 if ($plugin->is_standard()) {
1624                     $row->attributes['class'] .= ' standard';
1625                     //$source = html_writer::div(get_string('sourcestd', 'core_plugin'), 'source label');
1626                     $source = '';
1627                 } else {
1628                     $row->attributes['class'] .= ' extension';
1629                     $source = html_writer::div(get_string('sourceext', 'core_plugin'), 'source label label-info');
1630                 }
1632                 if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
1633                     $msg = html_writer::div(get_string('status_missing', 'core_plugin'), 'statusmsg label label-important');
1634                 } else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
1635                     $msg = html_writer::div(get_string('status_new', 'core_plugin'), 'statusmsg label label-success');
1636                 } else {
1637                     $msg = '';
1638                 }
1640                 $requriedby = $pluginman->other_plugins_that_require($plugin->component);
1641                 if ($requriedby) {
1642                     $requiredby = html_writer::tag('div', get_string('requiredby', 'core_plugin', implode(', ', $requriedby)),
1643                         array('class' => 'requiredby'));
1644                 } else {
1645                     $requiredby = '';
1646                 }
1648                 $updateinfo = '';
1649                 if (is_array($plugin->available_updates())) {
1650                     foreach ($plugin->available_updates() as $availableupdate) {
1651                         $updateinfo .= $this->plugin_available_update_info($pluginman, $availableupdate);
1652                     }
1653                 }
1655                 $notes = new html_table_cell($source.$msg.$requiredby.$updateinfo);
1657                 $row->cells = array(
1658                     $pluginname, $version, $availability, $settings, $uninstall, $notes
1659                 );
1660                 $table->data[] = $row;
1661             }
1662         }
1664         return html_writer::table($table);
1665     }
1667     /**
1668      * Helper method to render the information about the available plugin update
1669      *
1670      * @param core_plugin_manager $pluginman plugin manager instance
1671      * @param \core\update\info $updateinfo information about the available update for the plugin
1672      */
1673     protected function plugin_available_update_info(core_plugin_manager $pluginman, \core\update\info $updateinfo) {
1675         $boxclasses = 'pluginupdateinfo';
1676         $info = array();
1678         if (isset($updateinfo->release)) {
1679             $info[] = html_writer::div(
1680                 get_string('updateavailable_release', 'core_plugin', $updateinfo->release),
1681                 'info release'
1682             );
1683         }
1685         if (isset($updateinfo->maturity)) {
1686             $info[] = html_writer::div(
1687                 get_string('maturity'.$updateinfo->maturity, 'core_admin'),
1688                 'info maturity'
1689             );
1690             $boxclasses .= ' maturity'.$updateinfo->maturity;
1691         }
1693         if (isset($updateinfo->download)) {
1694             $info[] = html_writer::div(
1695                 html_writer::link($updateinfo->download, get_string('download')),
1696                 'info download'
1697             );
1698         }
1700         if (isset($updateinfo->url)) {
1701             $info[] = html_writer::div(
1702                 html_writer::link($updateinfo->url, get_string('updateavailable_moreinfo', 'core_plugin')),
1703                 'info more'
1704             );
1705         }
1707         $box = html_writer::start_div($boxclasses);
1708         $box .= html_writer::div(
1709             get_string('updateavailable', 'core_plugin', $updateinfo->version),
1710             'version'
1711         );
1712         $box .= html_writer::div(
1713             implode(html_writer::span(' ', 'separator'), $info),
1714             'infos'
1715         );
1717         if ($pluginman->is_remote_plugin_installable($updateinfo->component, $updateinfo->version, $reason)) {
1718             $box .= $this->output->single_button(
1719                 new moodle_url($this->page->url, array('installupdate' => $updateinfo->component,
1720                     'installupdateversion' => $updateinfo->version)),
1721                 get_string('updateavailableinstall', 'core_admin'),
1722                 'post',
1723                 array('class' => 'singlebutton updateavailableinstall')
1724             );
1725         } else {
1726             $reasonhelp = $this->info_remote_plugin_not_installable($reason);
1727             if ($reasonhelp) {
1728                 $box .= html_writer::div($reasonhelp, 'reasonhelp updateavailableinstall');
1729             }
1730         }
1731         $box .= html_writer::end_div();
1733         return $box;
1734     }
1736     /**
1737      * This function will render one beautiful table with all the environmental
1738      * configuration and how it suits Moodle needs.
1739      *
1740      * @param boolean $result final result of the check (true/false)
1741      * @param environment_results[] $environment_results array of results gathered
1742      * @return string HTML to output.
1743      */
1744     public function environment_check_table($result, $environment_results) {
1745         global $CFG;
1747         // Table headers
1748         $servertable = new html_table();//table for server checks
1749         $servertable->head  = array(
1750             get_string('name'),
1751             get_string('info'),
1752             get_string('report'),
1753             get_string('plugin'),
1754             get_string('status'),
1755         );
1756         $servertable->colclasses = array('centeralign name', 'centeralign info', 'leftalign report', 'leftalign plugin', 'centeralign status');
1757         $servertable->attributes['class'] = 'admintable environmenttable generaltable';
1758         $servertable->id = 'serverstatus';
1760         $serverdata = array('ok'=>array(), 'warn'=>array(), 'error'=>array());
1762         $othertable = new html_table();//table for custom checks
1763         $othertable->head  = array(
1764             get_string('info'),
1765             get_string('report'),
1766             get_string('plugin'),
1767             get_string('status'),
1768         );
1769         $othertable->colclasses = array('aligncenter info', 'alignleft report', 'alignleft plugin', 'aligncenter status');
1770         $othertable->attributes['class'] = 'admintable environmenttable generaltable';
1771         $othertable->id = 'otherserverstatus';
1773         $otherdata = array('ok'=>array(), 'warn'=>array(), 'error'=>array());
1775         // Iterate over each environment_result
1776         $continue = true;
1777         foreach ($environment_results as $environment_result) {
1778             $errorline   = false;
1779             $warningline = false;
1780             $stringtouse = '';
1781             if ($continue) {
1782                 $type = $environment_result->getPart();
1783                 $info = $environment_result->getInfo();
1784                 $status = $environment_result->getStatus();
1785                 $plugin = $environment_result->getPluginName();
1786                 $error_code = $environment_result->getErrorCode();
1787                 // Process Report field
1788                 $rec = new stdClass();
1789                 // Something has gone wrong at parsing time
1790                 if ($error_code) {
1791                     $stringtouse = 'environmentxmlerror';
1792                     $rec->error_code = $error_code;
1793                     $status = get_string('error');
1794                     $errorline = true;
1795                     $continue = false;
1796                 }
1798                 if ($continue) {
1799                     if ($rec->needed = $environment_result->getNeededVersion()) {
1800                         // We are comparing versions
1801                         $rec->current = $environment_result->getCurrentVersion();
1802                         if ($environment_result->getLevel() == 'required') {
1803                             $stringtouse = 'environmentrequireversion';
1804                         } else {
1805                             $stringtouse = 'environmentrecommendversion';
1806                         }
1808                     } else if ($environment_result->getPart() == 'custom_check') {
1809                         // We are checking installed & enabled things
1810                         if ($environment_result->getLevel() == 'required') {
1811                             $stringtouse = 'environmentrequirecustomcheck';
1812                         } else {
1813                             $stringtouse = 'environmentrecommendcustomcheck';
1814                         }
1816                     } else if ($environment_result->getPart() == 'php_setting') {
1817                         if ($status) {
1818                             $stringtouse = 'environmentsettingok';
1819                         } else if ($environment_result->getLevel() == 'required') {
1820                             $stringtouse = 'environmentmustfixsetting';
1821                         } else {
1822                             $stringtouse = 'environmentshouldfixsetting';
1823                         }
1825                     } else {
1826                         if ($environment_result->getLevel() == 'required') {
1827                             $stringtouse = 'environmentrequireinstall';
1828                         } else {
1829                             $stringtouse = 'environmentrecommendinstall';
1830                         }
1831                     }
1833                     // Calculate the status value
1834                     if ($environment_result->getBypassStr() != '') {            //Handle bypassed result (warning)
1835                         $status = get_string('bypassed');
1836                         $warningline = true;
1837                     } else if ($environment_result->getRestrictStr() != '') {   //Handle restricted result (error)
1838                         $status = get_string('restricted');
1839                         $errorline = true;
1840                     } else {
1841                         if ($status) {                                          //Handle ok result (ok)
1842                             $status = get_string('ok');
1843                         } else {
1844                             if ($environment_result->getLevel() == 'optional') {//Handle check result (warning)
1845                                 $status = get_string('check');
1846                                 $warningline = true;
1847                             } else {                                            //Handle error result (error)
1848                                 $status = get_string('check');
1849                                 $errorline = true;
1850                             }
1851                         }
1852                     }
1853                 }
1855                 // Build the text
1856                 $linkparts = array();
1857                 $linkparts[] = 'admin/environment';
1858                 $linkparts[] = $type;
1859                 if (!empty($info)){
1860                    $linkparts[] = $info;
1861                 }
1862                 // Plugin environments do not have docs pages yet.
1863                 if (empty($CFG->docroot) or $environment_result->plugin) {
1864                     $report = get_string($stringtouse, 'admin', $rec);
1865                 } else {
1866                     $report = $this->doc_link(join($linkparts, '/'), get_string($stringtouse, 'admin', $rec));
1867                 }
1869                 // Format error or warning line
1870                 if ($errorline || $warningline) {
1871                     $messagetype = $errorline? 'error':'warn';
1872                 } else {
1873                     $messagetype = 'ok';
1874                 }
1875                 $status = '<span class="'.$messagetype.'">'.$status.'</span>';
1876                 // Here we'll store all the feedback found
1877                 $feedbacktext = '';
1878                 // Append the feedback if there is some
1879                 $feedbacktext .= $environment_result->strToReport($environment_result->getFeedbackStr(), $messagetype);
1880                 //Append the bypass if there is some
1881                 $feedbacktext .= $environment_result->strToReport($environment_result->getBypassStr(), 'warn');
1882                 //Append the restrict if there is some
1883                 $feedbacktext .= $environment_result->strToReport($environment_result->getRestrictStr(), 'error');
1885                 $report .= $feedbacktext;
1887                 // Add the row to the table
1888                 if ($environment_result->getPart() == 'custom_check'){
1889                     $otherdata[$messagetype][] = array ($info, $report, $plugin, $status);
1890                 } else {
1891                     $serverdata[$messagetype][] = array ($type, $info, $report, $plugin, $status);
1892                 }
1893             }
1894         }
1896         //put errors first in
1897         $servertable->data = array_merge($serverdata['error'], $serverdata['warn'], $serverdata['ok']);
1898         $othertable->data = array_merge($otherdata['error'], $otherdata['warn'], $otherdata['ok']);
1900         // Print table
1901         $output = '';
1902         $output .= $this->heading(get_string('serverchecks', 'admin'));
1903         $output .= html_writer::table($servertable);
1904         if (count($othertable->data)){
1905             $output .= $this->heading(get_string('customcheck', 'admin'));
1906             $output .= html_writer::table($othertable);
1907         }
1909         // Finally, if any error has happened, print the summary box
1910         if (!$result) {
1911             $output .= $this->box(get_string('environmenterrortodo', 'admin'), 'environmentbox errorbox');
1912         }
1914         return $output;
1915     }
1917     /**
1918      * Render a simple page for providing the upgrade key.
1919      *
1920      * @param moodle_url|string $url
1921      * @return string
1922      */
1923     public function upgradekey_form_page($url) {
1925         $output = '';
1926         $output .= $this->header();
1927         $output .= $this->container_start('upgradekeyreq');
1928         $output .= $this->heading(get_string('upgradekeyreq', 'core_admin'));
1929         $output .= html_writer::start_tag('form', array('method' => 'POST', 'action' => $url));
1930         $output .= html_writer::empty_tag('input', array('name' => 'upgradekey', 'type' => 'password'));
1931         $output .= html_writer::empty_tag('input', array('value' => get_string('submit'), 'type' => 'submit'));
1932         $output .= html_writer::end_tag('form');
1933         $output .= $this->container_end();
1934         $output .= $this->footer();
1936         return $output;
1937     }