From 1adaa40466b94fa8066693f46b855e5d4d05b96b Mon Sep 17 00:00:00 2001 From: Petr Skoda Date: Sun, 28 Mar 2010 09:05:47 +0000 Subject: [PATCH] MDL-21938 support for json renderer and error handler --- lang/en_utf8/error.php | 1 + lib/outputfactories.php | 14 ++++++++--- lib/outputrenderers.php | 53 +++++++++++++++++++++++++++++++++++++++-- lib/setup.php | 5 ++++ lib/setuplib.php | 42 +++++++++++++++++++++++++++++--- lib/weblib.php | 6 +++++ 6 files changed, 113 insertions(+), 8 deletions(-) diff --git a/lang/en_utf8/error.php b/lang/en_utf8/error.php index 0f16d2f797e..a9c7f01b95c 100644 --- a/lang/en_utf8/error.php +++ b/lang/en_utf8/error.php @@ -384,6 +384,7 @@ $string['prefixcannotbeempty'] = '

Error: database table prefix cannot be empt $string['prefixtoolong'] = '

Error: database table prefix is too long ($a->dbfamily)

The site administrator must fix this problem. Maximum length for table prefixes in $a->dbfamily is $a->maxlength characters.

'; $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.

You must download the url\">$a->url file manually, copy it to \"$a->dest\" in your server and unzip it there.'; diff --git a/lib/outputfactories.php b/lib/outputfactories.php index 6b4e78c2df6..4329172b4dc 100644 --- a/lib/outputfactories.php +++ b/lib/outputfactories.php @@ -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 = ''; diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index 938634f3217..1b0afa143f7 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -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) { + } +} + diff --git a/lib/setup.php b/lib/setup.php index 0adbf78678e..8c16a075ce5 100644 --- a/lib/setup.php +++ b/lib/setup.php @@ -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 diff --git a/lib/setuplib.php b/lib/setuplib.php index dc4979d24e1..b9c369dc0c8 100644 --- a/lib/setuplib.php +++ b/lib/setuplib.php @@ -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'])) { diff --git a/lib/weblib.php b/lib/weblib.php index 9207b800f85..cdbbbd6baf5 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -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); }