MDL-21938 support for json renderer and error handler
authorPetr Skoda <skodak@moodle.org>
Sun, 28 Mar 2010 09:05:47 +0000 (09:05 +0000)
committerPetr Skoda <skodak@moodle.org>
Sun, 28 Mar 2010 09:05:47 +0000 (09:05 +0000)
lang/en_utf8/error.php
lib/outputfactories.php
lib/outputrenderers.php
lib/setup.php
lib/setuplib.php
lib/weblib.php

index 0f16d2f..a9c7f01 100644 (file)
@@ -384,6 +384,7 @@ $string['prefixcannotbeempty'] = '<p>Error: database table prefix cannot be empt
 $string['prefixtoolong'] = '<p>Error: database table prefix is too long ($a->dbfamily)</p>
 <p>The site administrator must fix this problem. Maximum length for table prefixes in $a->dbfamily is $a->maxlength characters.</p>';
 $string['processingstops'] = 'Processing stops here. Remaining records ignored.';
+$string['redirecterrordetected'] = 'Unsupported redirect detected, script execution terminated';
 $string['refoundtoorigi'] = 'Refunded to original amount: $a';
 $string['refoundto'] = 'Can be refunded to $a';
 $string['remotedownloaderror'] = 'Download of component to your server failed, please verify proxy settings, PHP cURL extension is highly recommended.<br /><br />You must download the <a href=\"$a->url\">$a->url</a> file manually, copy it to \"$a->dest\" in your server and unzip it there.';
index 6b4e78c..4329172 100644 (file)
@@ -33,6 +33,9 @@ define('RENDERER_TARGET_GENERAL', 'general');
 /** Plain text rendering for CLI scripts and cron */
 define('RENDERER_TARGET_CLI', 'cli');
 
+/** Plain text rendering for Ajax scripts*/
+define('RENDERER_TARGET_AJAX', 'ajax');
+
 /** Plain text rendering intended for sending via email */
 define('RENDERER_TARGET_TEXTEMAIL', 'textemail');
 
@@ -120,13 +123,18 @@ abstract class renderer_factory_base implements renderer_factory {
      * @return array two element array, first element is target, second the target suffix string
      */
     protected function get_target_suffix($target) {
-        if (empty($target) and CLI_SCRIPT) {
-            // automatically guessed default for all CLI scripts
-            $target = RENDERER_TARGET_CLI;
+        if (empty($target)) {
+            // automatically guessed defaults
+            if (CLI_SCRIPT) {
+                $target = RENDERER_TARGET_CLI;
+            } else if (AJAX_SCRIPT) {
+                $target = RENDERER_TARGET_AJAX;
+            }
         }
 
         switch ($target) {
             case RENDERER_TARGET_CLI: $suffix = '_cli'; break;
+            case RENDERER_TARGET_AJAX: $suffix = '_ajax'; break;
             case RENDERER_TARGET_TEXTEMAIL: $suffix = '_textemail'; break;
             case RENDERER_TARGET_HTMLEMAIL: $suffix = '_htmlemail'; break;
             default: $target = RENDERER_TARGET_GENERAL; $suffix = '';
index 938634f..1b0afa1 100644 (file)
@@ -1201,7 +1201,7 @@ class core_renderer extends renderer_base {
         if (!empty($CFG->doctonewwindow)) {
             $attributes['id'] = $this->add_action_handler(new popup_action('click', $url));
         }
-        
+
         return html_writer::tag('a', $icon.$text, $attributes);
     }
 
@@ -1283,7 +1283,7 @@ class core_renderer extends renderer_base {
                 $scalemax = $rating->settings->scale->scaleitems;
                 $ratingstr = round($rating->aggregate,1);
             }
-            
+
             $aggstr = "{$ratingstr} / $scalemax ({$rating->count}) ";
 
             if ($rating->settings->permissions->canviewall) {
@@ -2081,3 +2081,52 @@ class core_renderer_cli extends core_renderer {
     }
 }
 
+
+/**
+ * A renderer that generates output for ajax scripts.
+ *
+ * This renderer prevents accidental sends back only json
+ * encoded error messages, all other output is ignored.
+ *
+ * @copyright 2010 Petr Skoda
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class core_renderer_ajax extends core_renderer {
+    /**
+     * Returns a template fragment representing a fatal error.
+     * @param string $message The message to output
+     * @param string $moreinfourl URL where more info can be found about the error
+     * @param string $link Link for the Continue button
+     * @param array $backtrace The execution backtrace
+     * @param string $debuginfo Debugging information
+     * @return string A template fragment for a fatal error
+     */
+    public function fatal_error($message, $moreinfourl, $link, $backtrace, $debuginfo = null) {
+        $e = new stdClass();
+        $e->error      = $message;
+        $e->stacktrace = NULL;
+        $e->debuginfo  = NULL;
+        if (!empty($CFG->debug) and $CFG->debug >= DEBUG_DEVELOPER) {
+            if (!empty($debuginfo)) {
+                $e->debuginfo = $debuginfo;
+            }
+            if (!empty($backtrace)) {
+                $e->stacktrace = format_backtrace($backtrace, true);
+            }
+        }
+        return json_encode($e);
+    }
+
+    public function notification($message, $classes = 'notifyproblem') {
+    }
+    public function redirect_message($encodedurl, $message, $delay, $debugdisableredirect) {
+    }
+    public function header() {
+    }
+    public function footer() {
+    }
+    public function heading($text, $level, $classes = 'main', $id = null) {
+    }
+}
+
index 0adbf78..8c16a07 100644 (file)
@@ -207,6 +207,11 @@ if (!defined('CLI_SCRIPT')) { // CLI_SCRIPT might be defined in 'fake' CLI scrip
     }
 }
 
+// Detect ajax scripts - they are similar to CLI because we can not redirect, output html, etc.
+if (!defined('AJAX_SCRIPT')) {
+    define('AJAX_SCRIPT', false);
+}
+
 // sometimes default PHP settings are borked on shared hosting servers, I wonder why they have to do that??
 @ini_set('precision', 14); // needed for upgrades and gradebook
 
index dc4979d..b9c369d 100644 (file)
@@ -224,9 +224,14 @@ function default_exception_handler($ex) {
             // default exception handler MUST not throw any exceptions!!
             // the problem here is we do not know if page already started or not, we only know that somebody messed up in outputlib or theme
             // so we just print at least something instead of "Exception thrown without a stack frame in Unknown on line 0":-(
-            echo bootstrap_renderer::early_error_content($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo);
-            $outinfo = get_exception_info($out_ex);
-            echo bootstrap_renderer::early_error_content($outinfo->message, $outinfo->moreinfourl, $outinfo->link, $outinfo->backtrace, $outinfo->debuginfo);
+            if (CLI_SCRIPT or AJAX_SCRIPT) {
+                // just ignore the error and send something back using the safest method
+                echo bootstrap_renderer::early_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo);
+            } else {
+                echo bootstrap_renderer::early_error_content($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo);
+                $outinfo = get_exception_info($out_ex);
+                echo bootstrap_renderer::early_error_content($outinfo->message, $outinfo->moreinfourl, $outinfo->link, $outinfo->backtrace, $outinfo->debuginfo);
+            }
         }
     }
 
@@ -950,6 +955,37 @@ width: 80%; -moz-border-radius: 20px; padding: 15px">
      * @return string
      */
     public static function early_error($message, $moreinfourl, $link, $backtrace, $debuginfo = null) {
+        global $CFG;
+
+        if (CLI_SCRIPT) {
+            echo "!!! $message !!!\n";
+            if (!empty($CFG->debug) and $CFG->debug >= DEBUG_DEVELOPER) {
+                if (!empty($debuginfo)) {
+                    echo "\nDebug info: $debuginfo";
+                }
+                if (!empty($backtrace)) {
+                    echo "\nStack trace: " . format_backtrace($backtrace, true);
+                }
+            }
+            return;
+
+        } else if (AJAX_SCRIPT) {
+            $e = new stdClass();
+            $e->error      = $message;
+            $e->stacktrace = NULL;
+            $e->debuginfo  = NULL;
+            if (!empty($CFG->debug) and $CFG->debug >= DEBUG_DEVELOPER) {
+                if (!empty($debuginfo)) {
+                    $e->debuginfo = $debuginfo;
+                }
+                if (!empty($backtrace)) {
+                    $e->stacktrace = format_backtrace($backtrace, true);
+                }
+            }
+            echo json_encode($e);
+            return;
+        }
+
         // In the name of protocol correctness, monitoring and performance
         // profiling, set the appropriate error headers for machine consumption
         if (isset($_SERVER['SERVER_PROTOCOL'])) {
index 9207b80..cdbbbd6 100644 (file)
@@ -2523,6 +2523,12 @@ function notice ($message, $link='', $course=NULL) {
 function redirect($url, $message='', $delay=-1) {
     global $OUTPUT, $PAGE, $SESSION, $CFG;
 
+    if (CLI_SCRIPT or AJAX_SCRIPT) {
+        // this is wrong - developers should not use redirect in these scripts,
+        // but it should not be very likely
+        throw new moodle_exception('redirecterrordetected', 'error');
+    }
+
     if ($url instanceof moodle_url) {
         $url = $url->out(false);
     }