MDL-56041 error: Enable custom 404 and 500 error pages
authorBrendan Heywood <brendan@catalyst-au.net>
Tue, 5 May 2020 07:00:22 +0000 (17:00 +1000)
committerBrendan Heywood <brendan@catalyst-au.net>
Tue, 7 Jul 2020 23:36:21 +0000 (09:36 +1000)
error/index.php
error/plainpage.php [new file with mode: 0644]
lang/en/admin.php
lang/en/error.php
lib/adminlib.php
lib/classes/form/error_feedback.php [new file with mode: 0644]
lib/db/access.php
lib/setuplib.php
version.php

index dbb69cd..96b372b 100644 (file)
 <?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-    require('../config.php');
-
-    // Form submitted, do not check referer (original page unknown).
-    if ($form = data_submitted()) {
-        // Only deal with real users.
-        if (!isloggedin()) {
-            redirect($CFG->wwwroot);
-        }
-
-        // Send the message and redirect.
-        $eventdata = new \core\message\message();
-        $eventdata->courseid         = SITEID;
-        $eventdata->component        = 'moodle';
-        $eventdata->name             = 'errors';
-        $eventdata->userfrom          = $USER;
-        $eventdata->userto            = core_user::get_support_user();
-        $eventdata->subject           = 'Error: '. $form->referer .' -> '. $form->requested;
-        $eventdata->fullmessage       = $form->text;
-        $eventdata->fullmessageformat = FORMAT_PLAIN;
-        $eventdata->fullmessagehtml   = '';
-        $eventdata->smallmessage      = '';
-        message_send($eventdata);
-
-        redirect($CFG->wwwroot .'/course/', 'Message sent, thanks', 3);
-        exit;
-    }
+/**
+ * Moodle 404 Error page
+ *
+ * This is for 404 error pages served by the webserver and then passed
+ * to Moodle to be rendered using the site theme.
+ *
+ * ErrorDocument 404 /error/index.php
+ *
+ * @package    core
+ * @copyright  2020 Brendan Heywood <brendan@catalyst-au.net>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
 
-    $site = get_site();
-    $redirecturl = empty($_SERVER['REDIRECT_URL']) ? '' : $_SERVER['REDIRECT_URL'];
-    $httpreferer = get_local_referer(false);
-    $requesturi  = empty($_SERVER['REQUEST_URI'])  ? '' : $_SERVER['REQUEST_URI'];
-
-    header("HTTP/1.0 404 Not Found");
-    header("Status: 404 Not Found");
-
-    $PAGE->set_url('/error/');
-    $PAGE->set_context(context_system::instance());
-    $PAGE->set_title($site->fullname .':Error');
-    $PAGE->set_heading($site->fullname .': Error 404');
-    $PAGE->navbar->add('Error 404 - File not Found');
-    echo $OUTPUT->header();
-    echo $OUTPUT->box(get_string('pagenotexist', 'error'). '<br />'.s($requesturi), 'generalbox boxaligncenter');
-
-    if (isloggedin()) {
-?>
-        <p><?php echo get_string('pleasereport', 'error'); ?>
-        <p><form action="<?php echo $CFG->wwwroot ?>/error/index.php" method="post">
-           <textarea rows="3" cols="50" name="text" id="text" spellcheck="true"></textarea><br />
-           <input type="hidden" name="referer" value="<?php p($httpreferer) ?>">
-           <input type="hidden" name="requested" value="<?php p($requesturi) ?>">
-           <input type="submit" value="<?php echo get_string('sendmessage', 'error'); ?>">
-           </form>
-<?php
-    } else {
-        echo $OUTPUT->continue_button($CFG->wwwroot);
+// @codingStandardsIgnoreStart
+require('../config.php');
+// @codingStandardsIgnoreEnd
+
+$context = context_system::instance();
+$title = get_string('pagenotexisttitle', 'error');
+$PAGE->set_url('/error/index.php');
+$PAGE->set_context($context);
+$PAGE->set_title($title);
+$PAGE->set_heading($title);
+$PAGE->navbar->add($title);
+
+$canmessage = has_capability('moodle/site:senderrormessage', $context);
+
+$supportuser = core_user::get_support_user();
+
+// We can only message support if both the user has the capability
+// and the support user is a real user.
+if ($canmessage) {
+    $canmessage = core_user::is_real_user($supportuser->id);
+}
+
+$mform = new \core\form\error_feedback($CFG->wwwroot . '/error/index.php');
+
+if ($data = $mform->get_data()) {
+
+    if (!$canmessage) {
+        redirect($CFG->wwwroot);
     }
-    echo $OUTPUT->footer();
-?>
+
+    // Send the message and redirect.
+    $message = new \core\message\message();
+    $message->courseid         = SITEID;
+    $message->component        = 'moodle';
+    $message->name             = 'errors';
+    $message->userfrom          = $USER;
+    $message->userto            = core_user::get_support_user();
+    $message->subject           = 'Error: '. $data->referer .' -> '. $data->requested;
+    $message->fullmessage       = $data->text;
+    $message->fullmessageformat = FORMAT_PLAIN;
+    $message->fullmessagehtml   = '';
+    $message->smallmessage      = '';
+    $message->contexturl = $data->requested;
+    message_send($message);
+
+    redirect($CFG->wwwroot, get_string('sendmessagesent', 'error', $data->requested), 5);
+    exit;
+}
+
+echo $OUTPUT->header();
+echo $OUTPUT->notification(get_string('pagenotexist', 'error', s($ME)), 'error');
+
+if (!empty($CFG->supportpage)) {
+    echo \html_writer::tag('h4', get_string('supportpage', 'admin'));
+    $link = \html_writer::link($CFG->supportpage, $CFG->supportpage);
+    echo \html_writer::tag('p', $link);
+}
+if (!empty($CFG->supportemail)) {
+    echo \html_writer::tag('h4', get_string('supportemail', 'admin'));
+    $link = \html_writer::link('mailto:' . $CFG->supportemail, $CFG->supportemail);
+    echo \html_writer::tag('p', $link);
+}
+
+if ($canmessage) {
+    echo \html_writer::tag('h4', get_string('sendmessage', 'error'));
+    $mform->display();
+} else {
+    echo $OUTPUT->continue_button($CFG->wwwroot);
+}
+
+echo $OUTPUT->footer();
+
diff --git a/error/plainpage.php b/error/plainpage.php
new file mode 100644 (file)
index 0000000..3d2cbf5
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Moodle Generic plain page
+ *
+ * This is used for various pages, usually errors, early in the Moodle
+ * bootstrap. It can be safetly customized by editing this file directly
+ * but it MUST NOT contain any Moodle resources such as theme files generated
+ * by php, it can only contain references to static css and images, and as a
+ * precaution its recommended that everything is inlined rather than
+ * references. This is why this file is located here as it cannot be inside
+ * a Moodle theme.
+ *
+ * @package    core
+ * @copyright  2020 Brendan Heywood <brendan@catalyst-au.net>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+// @codingStandardsIgnoreStart
+?>
+<!DOCTYPE html>
+<html <?php echo $htmllang ?>>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+        <?php echo $meta ?>
+        <title><?php echo $title ?></title>
+        <style>
+<?php
+// This is a very small modified subset of the bootstrap / boost css classes.
+?>
+body {
+    margin: 0;
+    font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
+    font-size: .9375rem;
+    font-weight: 400;
+    line-height: 1.5;
+    color: #343a40;
+    text-align: left;
+    background-color: #f2f2f2;
+}
+#page {
+    margin-top: 15px;
+    background: white;
+    max-width: 600px;
+    margin: 0 auto;
+    padding: 15px;
+}
+#region-main {
+    margin: 0 auto;
+    border: 1px solid rgba(0,0,0,.125);
+    padding: 1rem 1.25rem 1.25rem;
+    background-color: #fff;
+}
+h1 {
+    font-size: 2.34rem;
+    margin: 0 0 .5rem;
+    font-weight: 300;
+    line-height: 1.2;
+}
+.alert-danger {
+    color: #6e211e;
+    background-color: #f6d9d8;
+    border-color: #f3c9c8;
+    padding: .75rem 1.25rem;
+}
+    </style>
+    </head>
+    <body>
+        <div id="page">
+            <div id="region-main">
+                <h1><?php echo $title ?></h1>
+                <?php echo $content ?>
+                <?php echo $footer ?>
+            </div>
+        </div>
+    </body>
+</html>
+
index 4080712..31c4acb 100644 (file)
@@ -1159,6 +1159,7 @@ $string['simplexmlrequired'] = 'The SimpleXML PHP extension is now required by M
 $string['sitemenubar'] = 'Site navigation';
 $string['sitemailcharset'] = 'Character set';
 $string['sitemaintenance'] = 'The site is undergoing maintenance and is currently not available';
+$string['sitemaintenancetitle'] = '{$a} under maintenance';
 $string['sitemaintenancemode'] = 'Maintenance mode';
 $string['sitemaintenanceoff'] = 'Maintenance mode has been disabled and the site is running normally again';
 $string['sitemaintenanceon'] = 'Your site is currently in maintenance mode (only admins can log in or use the site).';
index 5a6c463..cd1cc95 100644 (file)
@@ -466,7 +466,8 @@ $string['onlyadmins'] = 'Only administrators can do that';
 $string['onlyeditingteachers'] = 'Only editing teachers can do that';
 $string['onlyeditown'] = 'You can only edit your own information';
 $string['orderidnotfound'] = 'Order ID {$a} not found';
-$string['pagenotexist'] = 'An unusual error occurred (tried to reach a page that does not exist)';
+$string['pagenotexisttitle'] = '404 Error: File not found';
+$string['pagenotexist'] = '<p>An unusual error occurred trying to view a page that does not exist:</p>{$a}';
 $string['pathdoesnotstartslash'] = 'No valid arguments supplied, path does not start with slash!';
 $string['pleasereport'] = 'If you have time, please let us know what you were trying to do when the error occurred:';
 $string['pluginrequirementsnotmet'] = 'Plugin "{$a->pluginname}" ({$a->pluginversion}) could not be installed.  It requires a newer version of Moodle (currently you are using {$a->currentmoodle}, you need {$a->requiremoodle}).';
@@ -498,6 +499,7 @@ $string['rpcerror'] = 'Ooops! Your MNET communication has failed! Here\'s that e
 $string['secretalreadyused'] = 'Change password confirmation link was already used, password was not changed';
 $string['sectionnotexist'] = 'This section does not exist';
 $string['sendmessage'] = 'Send message';
+$string['sendmessagesent'] = 'Thanks for your feedback about:<br>{$a}';
 $string['serverconnection'] = 'Error connecting to the server';
 $string['servicedonotexist'] = 'The service does not exist';
 $string['sessionwaiterr'] = 'Timed out while waiting for session lock.<br />Wait for your current requests to finish and try again later.';
index fd87ec4..8a75d3c 100644 (file)
@@ -628,7 +628,7 @@ function is_dataroot_insecure($fetchtest=false) {
  * Enables CLI maintenance mode by creating new dataroot/climaintenance.html file.
  */
 function enable_cli_maintenance_mode() {
-    global $CFG;
+    global $CFG, $SITE;
 
     if (file_exists("$CFG->dataroot/climaintenance.html")) {
         unlink("$CFG->dataroot/climaintenance.html");
@@ -645,7 +645,7 @@ function enable_cli_maintenance_mode() {
     } else {
         $data = get_string('sitemaintenance', 'admin');
         $data = bootstrap_renderer::early_error_content($data, null, null, null);
-        $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
+        $data = bootstrap_renderer::plain_page(get_string('sitemaintenancetitle', 'admin', $SITE->fullname), $data);
     }
 
     file_put_contents("$CFG->dataroot/climaintenance.html", $data);
diff --git a/lib/classes/form/error_feedback.php b/lib/classes/form/error_feedback.php
new file mode 100644 (file)
index 0000000..b4e3fd6
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Moodle 404 Error page feedback form
+ *
+ * @package    core
+ * @copyright  2020 Brendan Heywood <brendan@catalyst-au.net>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\form;
+defined('MOODLE_INTERNAL') || die();
+
+use moodleform;
+
+require_once($CFG->libdir.'/formslib.php');
+
+/**
+ * Moodle 404 Error page feedback form
+ *
+ * @package    core
+ * @copyright  2020 Brendan Heywood <brendan@catalyst-au.net>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class error_feedback extends moodleform {
+
+    /**
+     * Error form definition
+     */
+    public function definition() {
+        global $CFG;
+
+        $mform = $this->_form;
+        $mform->addElement('hidden', 'referer', get_local_referer(false));
+        $mform->setType('referer', PARAM_URL);
+
+        $mform->addElement('hidden', 'requested', (empty($_SERVER['REDIRECT_URL']) ? '' : $_SERVER['REDIRECT_URL']));
+        $mform->setType('requested', PARAM_URL);
+
+        $mform->addElement('textarea', 'text', get_string('pleasereport', 'error'), 'wrap="virtual" rows="10" cols="50"');
+        $mform->addElement('submit', 'submitbutton', get_string('sendmessage', 'error'));
+    }
+}
+
index 0ae1c2a..3de202d 100644 (file)
@@ -119,6 +119,15 @@ $capabilities = array(
         )
     ),
 
+    'moodle/site:senderrormessage' => [
+        'riskbitmask' => RISK_SPAM,
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_SYSTEM,
+        'archetypes' => array(
+            'user' => CAP_ALLOW
+        )
+    ],
+
     'moodle/site:deleteownmessage' => array(
 
         'captype' => 'write',
index 4aa3b9a..941b344 100644 (file)
@@ -1968,11 +1968,7 @@ class bootstrap_renderer {
     public static function early_error_content($message, $moreinfourl, $link, $backtrace, $debuginfo = null) {
         global $CFG;
 
-        $content = '<div style="margin-top: 6em; margin-left:auto; margin-right:auto; color:#990000; text-align:center; font-size:large; border-width:1px;
-border-color:black; background-color:#ffffee; border-style:solid; border-radius: 20px; border-collapse: collapse;
-width: 80%; -moz-border-radius: 20px; padding: 15px">
-' . $message . '
-</div>';
+        $content = "<div class='alert-danger'>$message</div>";
         // Check whether debug is set.
         $debug = (!empty($CFG->debug) && $CFG->debug >= DEBUG_DEVELOPER);
         // Also check we have it set in the config file. This occurs if the method to read the config table from the
@@ -2117,6 +2113,8 @@ width: 80%; -moz-border-radius: 20px; padding: 15px">
      * @return string html page
      */
     public static function plain_page($title, $content, $meta = '') {
+        global $CFG;
+
         if (function_exists('get_string') && function_exists('get_html_lang')) {
             $htmllang = get_html_lang();
         } else {
@@ -2131,12 +2129,11 @@ width: 80%; -moz-border-radius: 20px; padding: 15px">
             }
         }
 
-        return '<!DOCTYPE html>
-<html ' . $htmllang . '>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-'.$meta.'
-<title>' . $title . '</title>
-</head><body>' . $content . $footer . '</body></html>';
+        ob_start();
+        include($CFG->dirroot . '/error/plainpage.php');
+        $html = ob_get_contents();
+        ob_end_clean();
+
+        return $html;
     }
 }
index 002a769..4216ca0 100644 (file)
@@ -29,7 +29,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2020070400.00;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2020070400.01;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.
 $release  = '4.0dev (Build: 20200704)'; // Human-friendly version name