]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-19077 - change how $OUTPUT is initialised.
authortjhunt <tjhunt>
Mon, 29 Jun 2009 05:00:45 +0000 (05:00 +0000)
committertjhunt <tjhunt>
Mon, 29 Jun 2009 05:00:45 +0000 (05:00 +0000)
Please read the comment at the top of bootstrap_renderer in setuplib.php

lib/accesslib.php
lib/moodlelib.php
lib/outputlib.php
lib/pagelib.php
lib/setup.php
lib/setuplib.php
lib/simpletest/testoutputlib.php
lib/weblib.php
theme/custom_corners/config.php

index 761e5adbef8bd812cc92a4e9adf2318089b181c3..afaf568e28d774d51830c2b243c6b6dc602fb073 100755 (executable)
@@ -480,6 +480,10 @@ function has_capability($capability, $context, $userid=NULL, $doanything=true) {
     }
 
     if (empty($userid)) { // we must accept null, 0, '0', '' etc. in $userid
+        if (empty($USER->id)) {
+            // Session not set up yet.
+            return false;
+        }
         $userid = $USER->id;
     }
 
index 8e38f1fb6e97fcb4d33ad7c8848760ba155baba3..6bc0484d9df8d67b9cbd82150c11c992b4dc2a60 100644 (file)
@@ -249,18 +249,6 @@ define('PARAM_PERMISSION',   0x80000);
  */
 define('PAGE_COURSE_VIEW', 'course-view');
 
-/// Debug levels ///
-/** no warnings at all */
-define ('DEBUG_NONE', 0);
-/** E_ERROR | E_PARSE */
-define ('DEBUG_MINIMAL', 5);
-/** E_ERROR | E_PARSE | E_WARNING | E_NOTICE */
-define ('DEBUG_NORMAL', 15);
-/** E_ALL without E_STRICT for now, do show recoverable fatal errors */
-define ('DEBUG_ALL', 6143);
-/** DEBUG_ALL with extra Moodle debug messages - (DEBUG_ALL | 32768) */
-define ('DEBUG_DEVELOPER', 38911);
-
 /** Get remote addr constant */
 define('GETREMOTEADDR_SKIP_HTTP_CLIENT_IP', '1');
 /** Get remote addr constant */
@@ -5588,14 +5576,13 @@ function print_string($identifier, $module='', $a=NULL) {
  *
  * Notes for develpers
  * ===================
- *
  * Performance of this class is important. If you decide to change this class,
  * please use the lib/simpletest/getstringperformancetester.php script to make
  * sure your changes do not cause a performance problem.
  *
- * In some cases (for example _print_early_error) get_string gets called very
- * early on during Moodle's self-initialisation. Think very carefully before
- * relying on the normal Moodle libraries here.
+ * In some cases (for example bootstrap_renderer::early_error) get_string gets
+ * called very early on during Moodle's self-initialisation. Think very carefully
+ * before relying on the normal Moodle libraries here.
  *
  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  * @package moodlecore
index 7d4d8b92412ea2b36d38d829c3ad68c3eef595ba..b7bcb4d33cd6b162c2d122738a8c0f4720102cf4 100644 (file)
  */
 
 
-/**
- * Set up a preliminary $OUTPUT. This will be changed later once the correct theme
- * for this page is known. Must be called after $PAGE is setup.
- */
-function setup_bootstrat_output() {
-    global $OUTPUT, $PAGE;
+function initialise_theme_and_output() {
+    global $CFG, $OUTPUT, $PAGE, $THEME;
+    if (!($OUTPUT instanceof bootstrap_renderer)) {
+        return; // Already done.
+    }
+    if (!isset($CFG->theme) || empty($PAGE)) {
+        // Too soon to do anything.
+        return;
+    }
+    theme_setup();
     if (CLI_SCRIPT) {
-        $OUTPUT = new cli_core_renderer(new xhtml_container_stack(), $PAGE);
+        $rendererfactory = new cli_renderer_factory($THEME, $PAGE);
     } else {
-        $OUTPUT = new moodle_core_renderer(new xhtml_container_stack(), $PAGE);
+        $classname = $THEME->rendererfactory;
+        $rendererfactory = new $classname($THEME, $PAGE);
     }
+    $OUTPUT = $rendererfactory->get_renderer('core');
 }
 
 
@@ -181,7 +187,7 @@ class standard_renderer_factory extends renderer_factory_base {
     /* Implement the subclass method. */
     public function create_renderer($module) {
         if ($module == 'core') {
-            return new moodle_core_renderer($this->opencontainers, $this->page);
+            return new moodle_core_renderer($this->opencontainers, $this->page, $this);
         } else {
             $class = $this->standard_renderer_class_for_module($module);
             return new $class($this->opencontainers, $this->get_renderer('core'), $this->page);
@@ -208,7 +214,27 @@ class custom_corners_renderer_factory extends standard_renderer_factory {
      */
     public function __construct($theme, $page) {
         parent::__construct($theme, $page);
-        $this->renderers = array('core' => new custom_corners_core_renderer($this->opencontainers, $this->page));
+        $this->renderers = array('core' => new custom_corners_core_renderer($this->opencontainers, $this->page, $this));
+    }
+}
+
+
+/**
+ * This is a slight variation on the standard_renderer_factory used by CLI scripts.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class cli_renderer_factory extends standard_renderer_factory {
+    /**
+     * Constructor.
+     * @param object $theme the theme we are rendering for.
+     * @param moodle_page $page the page we are doing output for.
+     */
+    public function __construct($theme, $page) {
+        parent::__construct($theme, $page);
+        $this->renderers = array('core' => new cli_core_renderer($this->opencontainers, $this->page, $this));
     }
 }
 
@@ -262,7 +288,7 @@ class theme_overridden_renderer_factory extends standard_renderer_factory {
             $classname = $prefix . $module . '_renderer';
             if (class_exists($classname)) {
                 if ($module == 'core') {
-                    return new $classname($this->opencontainers, $this->page);
+                    return new $classname($this->opencontainers, $this->page, $this);
                 } else {
                     return new $classname($this->opencontainers, $this->get_renderer('core'), $this->page);
                 }
@@ -342,7 +368,7 @@ class template_renderer_factory extends renderer_factory_base {
 
         // Create a template_renderer that copies the API of the standard renderer.
         $copiedclass = $this->standard_renderer_class_for_module($module);
-        return new template_renderer($copiedclass, $searchpaths, $this->opencontainers, $this->page);
+        return new template_renderer($copiedclass, $searchpaths, $this->opencontainers, $this->page, $this);
     }
 }
 
@@ -432,6 +458,7 @@ class template_renderer extends moodle_renderer_base {
     protected $copiedclass;
     /** @var array of places to search for templates. */
     protected $searchpaths;
+    protected $rendererfactory;
 
     /**
      * Magic word used when breaking apart container templates to implement
@@ -445,11 +472,22 @@ class template_renderer extends moodle_renderer_base {
      * @param $searchpaths a list of folders to search for templates in.
      * @param $opencontainers the xhtml_container_stack to use.
      * @param moodle_page $page the page we are doing output for.
+     * @param renderer_factory $rendererfactory the renderer factory that created us.
      */
-    public function __construct($copiedclass, $searchpaths, $opencontainers, $page) {
+    public function __construct($copiedclass, $searchpaths, $opencontainers, $page, $rendererfactory) {
         parent::__construct($opencontainers, $page);
         $this->copiedclass = new ReflectionClass($copiedclass);
         $this->searchpaths = $searchpaths;
+        $this->rendererfactory = $rendererfactory;
+    }
+
+    /**
+     * Get a renderer for another part of Moodle.
+     * @param $module the name of part of moodle. E.g. 'core', 'quiz', 'qtype_multichoice'.
+     * @return object an object implementing the requested renderer interface.
+     */
+    public function get_other_renderer($module) {
+        $this->rendererfactory->get_renderer($module);
     }
 
     /* PHP magic method implementation. */
@@ -704,6 +742,27 @@ class moodle_core_renderer extends moodle_renderer_base {
     const END_HTML_TOKEN = '%%ENDHTML%%';
     const MAIN_CONTENT_TOKEN = '[MAIN CONTENT GOES HERE]';
     protected $contenttype;
+    protected $rendererfactory;
+
+    /**
+     * Constructor
+     * @param $opencontainers the xhtml_container_stack to use.
+     * @param moodle_page $page the page we are doing output for.
+     * @param renderer_factory $rendererfactory the renderer factory that created us.
+     */
+    public function __construct($opencontainers, $page, $rendererfactory) {
+        parent::__construct($opencontainers, $page);
+        $this->rendererfactory = $rendererfactory;
+    }
+
+    /**
+     * Get a renderer for another part of Moodle.
+     * @param $module the name of part of moodle. E.g. 'core', 'quiz', 'qtype_multichoice'.
+     * @return object an object implementing the requested renderer interface.
+     */
+    public function get_other_renderer($module) {
+        $this->rendererfactory->get_renderer($module);
+    }
 
     public function doctype() {
         global $CFG;
@@ -1114,7 +1173,8 @@ class moodle_core_renderer extends moodle_renderer_base {
         if ($this->opencontainers->count() == 0) {
             // Header not yet printed
             @header('HTTP/1.0 404 Not Found');
-            print_header(get_string('error'));
+            $this->page->set_title(get_string('error'));
+            $output .= $this->header();
         } else {
             $output .= $this->opencontainers->pop_all_but_last();
         }
@@ -1142,7 +1202,7 @@ class moodle_core_renderer extends moodle_renderer_base {
             $output .= $this->continue_button($link);
         }
 
-        print_footer();
+        $output .= $this->footer();
 
         // Padding to encourage IE to display our error page, rather than its own.
         $output .= str_repeat(' ', 512);
@@ -1471,6 +1531,8 @@ class cli_core_renderer extends moodle_core_renderer {
  * @since     Moodle 2.0
  */
 class custom_corners_core_renderer extends moodle_core_renderer {
+    protected $wraplevel = 1;
+
     protected function custom_corners_divs($classes = '', $idbase = '') {
         if (strpos($classes, 'clearfix') !== false) {
             $clearfix = ' clearfix';
@@ -1495,11 +1557,8 @@ class custom_corners_core_renderer extends moodle_core_renderer {
             $idbb = $idbase . '-bb';
         }
 
-        // Calculate current level
-        $level = $this->opencontainers->count();
-
         // Create start tags.
-        $start = $this->output_start_tag('div', array('id' => $idbb, 'class' => "wrap wraplevel$level $classes")) . "\n";
+        $start = $this->output_start_tag('div', array('id' => $id, 'class' => "wrap wraplevel{$this->wraplevel} $classes")) . "\n";
         $start .= $this->output_tag('div', array('id' => $idbt, 'class' => 'bt'), '<div>&nbsp;</div>') . "\n";
         $start .= $this->output_start_tag('div', array('id' => $idi1, 'class' => 'i1'));
         $start .= $this->output_start_tag('div', array('id' => $idi2, 'class' => 'i2'));
@@ -1518,12 +1577,24 @@ class custom_corners_core_renderer extends moodle_core_renderer {
     public function box_start($classes = 'generalbox', $id = '') {
         list($start, $end) = $this->custom_corners_divs('ccbox box ' . moodle_renderer_base::prepare_classes($classes), $id);
         $this->opencontainers->push('box', $end);
+        $this->wraplevel += 1;
         return $start;
     }
 
+    public function box_end() {
+        $this->wraplevel -= 1;
+        return parent::box_end();
+    }
+
     public function container_start($classes = '', $id = '') {
         list($start, $end) = $this->custom_corners_divs(moodle_renderer_base::prepare_classes($classes), $id);
         $this->opencontainers->push('container', $end);
+        $this->wraplevel += 1;
         return $start;
     }
+
+    public function container_end() {
+        $this->wraplevel -= 1;
+        return parent::container_end();
+    }
 }
index 548a990b83a80744607cc8e8c2760a302ecd08c6..14686bb693a26c2fe19753f85ca21c2daba69d23 100644 (file)
@@ -335,14 +335,17 @@ class moodle_page {
      * @return blocks_manager the blocks manager object for this page.
      */
     public function get_blocks() {
-        global $CFG;
+        global $CFG, $THEME;
         if (is_null($this->_blocks)) {
+            initialise_theme_and_output();
             if (!empty($CFG->blockmanagerclass)) {
                 $classname = $CFG->blockmanagerclass;
             } else {
                 $classname = 'block_manager';
             }
             $this->_blocks = new $classname($this);
+            $this->_blocks->add_regions($THEME->blockregions);
+            $this->_blocks->set_default_region($THEME->defaultblockregion);
         }
         return $this->_blocks;
     }
@@ -481,7 +484,6 @@ class moodle_page {
         }
 
         moodle_setlocale();
-        theme_setup();
     }
 
     /**
@@ -743,7 +745,6 @@ class moodle_page {
             $this->_course = new stdClass;
             $this->_course->id = 1;
             moodle_setlocale();
-            theme_setup();
             return;
         }
 
index 57bd20c431ddda3ab1430f364a9e675352acb4b5..4131b03f7e947db950ca48526a2be6c4b0ac0710 100644 (file)
@@ -97,8 +97,10 @@ global $COURSE;
  * $OUTPUT is an instance of moodle_core_renderer or one of its subclasses. Use
  * it to generate HTML for output.
  *
- * $OUTPUT is initialised when the theme is setup. That normally happens during
- * the call to require_login, or $PAGE->set_course.
+ * $OUTPUT is initialised the first time it is used. See {@link bootstrap_renderer}
+ * for the magic that does that. After $OUTPUT has been initialised, any attempt
+ * to change something that affects the current theme ($PAGE->course, logged in use,
+ * httpsrequried ... will result in an exception.)
  *
  * @global object $OUTPUT
  * @name $OUTPUT
@@ -205,6 +207,11 @@ global $SCRIPT;
 /// Time to start counting
     init_performance_info();
 
+/// Put $OUTPUT in place, so errors can be displayed.
+    $OUTPUT = new bootstrap_renderer();
+
+/// set handler for uncought exceptions - equivalent to print_error() call
+    set_exception_handler('default_exception_handler');
 
 /// If there are any errors in the standard libraries we want to know!
     error_reporting(E_ALL);
@@ -249,9 +256,6 @@ global $SCRIPT;
     //point zend include path to moodles lib/zend so that includes and requires will search there for files before anywhere else
     ini_set('include_path', $CFG->libdir.'/zend' . PATH_SEPARATOR . ini_get('include_path'));
 
-/// set handler for uncought exceptions - equivalent to print_error() call
-    set_exception_handler('default_exception_handler');
-
 /// make sure PHP is not severly misconfigured
     setup_validate_php_configuration();
 
@@ -346,7 +350,6 @@ global $SCRIPT;
     unset($originaldatabasedebug);
     error_reporting($CFG->debug);
 
-
 /// find out if PHP cofigured to display warnings
     if (ini_get_bool('display_errors')) {
         define('WARN_DISPLAY_ERRORS_ENABLED', true);
@@ -369,19 +372,6 @@ global $SCRIPT;
         @ini_set('log_errors', '1');
     }
 
-/// Create the $PAGE global.
-    if (!empty($CFG->moodlepageclass)) {
-        $classname = $CFG->moodlepageclass;
-    } else {
-        $classname = 'moodle_page';
-    }
-    $PAGE = new $classname();
-
-/// Create an initial $OUTPUT. This will be changes later once we know the theme.
-    setup_bootstrat_output();
-
-    unset($classname);
-
 /// detect unsupported upgrade jump as soon as possible - do not change anything, do not use system functions
     if (!empty($CFG->version) and $CFG->version < 2007101509) {
         print_error('upgraderequires19', 'error');
@@ -475,6 +465,15 @@ global $SCRIPT;
     $CFG->javascript  = $CFG->libdir .'/javascript.php';
     $CFG->moddata     = 'moddata';
 
+/// Create the $PAGE global.
+    if (!empty($CFG->moodlepageclass)) {
+        $classname = $CFG->moodlepageclass;
+    } else {
+        $classname = 'moodle_page';
+    }
+    $PAGE = new $classname();
+    unset($classname);
+
 /// A hack to get around magic_quotes_gpc being turned on
 /// It is strongly recommended to disable "magic_quotes_gpc"!
     if (ini_get_bool('magic_quotes_gpc')) {
index 0b9bc8befd500cec0c5f9710682ad70d9d58ca2d..bfaa2231340b5643e08223686b9bb4aa85753a81 100644 (file)
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+/// Debug levels ///
+/** no warnings at all */
+define ('DEBUG_NONE', 0);
+/** E_ERROR | E_PARSE */
+define ('DEBUG_MINIMAL', 5);
+/** E_ERROR | E_PARSE | E_WARNING | E_NOTICE */
+define ('DEBUG_NORMAL', 15);
+/** E_ALL without E_STRICT for now, do show recoverable fatal errors */
+define ('DEBUG_ALL', 6143);
+/** DEBUG_ALL with extra Moodle debug messages - (DEBUG_ALL | 32768) */
+define ('DEBUG_DEVELOPER', 38911);
+
 /**
  * Simple class
  *
@@ -38,6 +50,9 @@ class object {};
 /**
  * Base Moodle Exception class
  *
+ * Although this class is defined here, you cannot throw a moodle_exception until
+ * after moodlelib.php has been included (which will happen very soon).
+ *
  * @package   moodlecore
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@@ -124,11 +139,11 @@ class invalid_state_exception extends moodle_exception {
  * Does not return. Terminates execution.
  */
 function default_exception_handler($ex, $isupgrade = false, $plugin = null) {
-    global $CFG, $DB, $SCRIPT;
+    global $CFG, $DB, $OUTPUT, $SCRIPT;
 
     // detect active db transactions, rollback and log as error
-    if ($DB->is_transaction_started()) {
-        error_log('Database transaction aborted by exception in '.$CFG->dirroot.$SCRIPT);
+    if ($DB && $DB->is_transaction_started()) {
+        error_log('Database transaction aborted by exception in ' . $CFG->dirroot . $SCRIPT);
         try {
             // note: transaction blocks should never change current $_SESSION
             $DB->rollback_sql();
@@ -140,9 +155,8 @@ function default_exception_handler($ex, $isupgrade = false, $plugin = null) {
     $place = array('file'=>$ex->getFile(), 'line'=>$ex->getLine(), 'exception'=>get_class($ex));
     array_unshift($backtrace, $place);
 
-    $earlyerror = empty($OUTPUT);
     foreach ($backtrace as $stackframe) {
-        if (isset($stackframe['function']) && $stackframe['function'] == 'print_header') {
+        if (isset($stackframe['function']) && $stackframe['function'] == 'default_exception_handler') {
             $earlyerror = true;
             break;
         }
@@ -172,13 +186,7 @@ function default_exception_handler($ex, $isupgrade = false, $plugin = null) {
         $CFG->debug = DEBUG_DEVELOPER;
     }
 
-    if ($earlyerror) {
-        // Error found before setup.php finished
-        _print_early_error($message, $backtrace, $debuginfo);
-    } else {
-        echo $OUTPUT->fatal_error($message, $moreinfourl, $link, debug_backtrace());
-    }
-
+    echo $OUTPUT->fatal_error($message, $moreinfourl, $link, debug_backtrace());
     exit(1); // General error code
 }
 
@@ -202,13 +210,7 @@ function print_error($errorcode, $module = 'error', $link = '', $a = null) {
     }
 
     list($message, $moreinfourl, $link) = prepare_error_message($errorcode, $module, $link, $a);
-
-    if (empty($OUTPUT)) {
-        // Error found before setup.php finished
-        _print_early_error($message);
-    } else {
-        echo $OUTPUT->fatal_error($message, $moreinfourl, $link, debug_backtrace());
-    }
+    echo $OUTPUT->fatal_error($message, $moreinfourl, $link, debug_backtrace());
 
     exit(1); // General error code
 }
@@ -229,15 +231,26 @@ function prepare_error_message($errorcode, $module, $link, $a) {
         $DB->set_debug(0);
     }
 
+    // Be careful, no guarantee moodlelib.php is loaded.
     if (empty($module) || $module == 'moodle' || $module == 'core') {
         $module = 'error';
     }
-    $message = get_string($errorcode, $module, $a);
-    if ($module === 'error' and strpos($message, '[[') === 0) {
-        // Search in moodle file if error specified - needed for backwards compatibility
-        $message = get_string($errorcode, 'moodle', $a);
+    if (function_exists('get_string')) {
+        $message = get_string($errorcode, $module, $a);
+        if ($module === 'error' and strpos($message, '[[') === 0) {
+            // Search in moodle file if error specified - needed for backwards compatibility
+            $message = get_string($errorcode, 'moodle', $a);
+        }
+    } else {
+        $message = $module . '/' . $errorcode;
+    }
+
+    // Be careful, no guarantee weblib.php is loaded.
+    if (function_exists('clean_text')) {
+        $message = clean_text($message);
+    } else {
+        $message = htmlspecialchars($message);
     }
-    $message = clean_text($message);
 
     if (!empty($CFG->errordocroot)) {
         $errordocroot = $CFG->errordocroot;
@@ -265,56 +278,6 @@ function prepare_error_message($errorcode, $module, $link, $a) {
     return array($message, $moreinfourl, $link);
 }
 
-/**
- * Internal function used by print_error. Do not use this directly!!
- *
- * Displays a fatal error before the theme is fully initialised.
- * For example errors that occur during lib/setup.php.
- *
- * @param string $message
- * @param string $link
- * @param array $backtrace
- * @param string $debuginfo
- */
-function _print_early_error($message, $backtrace = null, $debuginfo = null) {
-    // In the name of protocol correctness, monitoring and performance
-    // profiling, set the appropriate error headers for machine comsumption
-    if (isset($_SERVER['SERVER_PROTOCOL'])) {
-        // Avoid it with cron.php. Note that we assume it's HTTP/1.x
-        @header($_SERVER['SERVER_PROTOCOL'] . ' 503 Service Unavailable');
-    }
-
-    // better disable any caching
-    @header('Content-Type: text/html; charset=utf-8');
-    @header('Cache-Control: no-store, no-cache, must-revalidate');
-    @header('Cache-Control: post-check=0, pre-check=0', false);
-    @header('Pragma: no-cache');
-    @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
-    @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
-
-    echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" ' . get_html_lang() . '>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<title>' . get_string('error') . '</title>
-</head><body>
-<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>';
-    if (debugging('', DEBUG_DEVELOPER)) {
-        if (!empty($debuginfo)) {
-            echo '<div class="notifytiny">' . $debuginfo . '</div>';
-        }
-        if (!empty($backtrace)) {
-            echo '<div class="notifytiny">Stack trace: ' . format_backtrace($backtrace, false) . '</div>';
-        }
-    }
-
-    echo '</body></html>';
-}
-
 /**
  * Formats a backtrace ready for output.
  *
@@ -526,7 +489,7 @@ function init_performance_info() {
     $PERF->logwrites = 0;
     if (function_exists('microtime')) {
         $PERF->starttime = microtime();
-        }
+    }
     if (function_exists('memory_get_usage')) {
         $PERF->startmemory = memory_get_usage();
     }
@@ -739,5 +702,130 @@ function init_eaccelerator() {
 }
 
 
+/**
+ * This class solves the problem of how to initialise $OUTPUT.
+ *
+ * The problem is caused be two factors
+ * <ol>
+ * <li>On the one hand, we cannot be sure when output will start. In particular,
+ * an error, which needs to be displayed, could br thrown at any time.</li>
+ * <li>On the other hand, we cannot be sure when we will have all the information
+ * necessary to correctly initialise $OUTPUT. $OUTPUT depends on the theme, which
+ * (potentially) depends on the current course, course categories, and logged in user.
+ * It also depends on whether the current page requires HTTPS.</li>
+ * </ol>
+ *
+ * So, it is hard to find a single natural place during Moodle script execution,
+ * which we can guarantee is the right time to initialise $OUTPUT. Instead we
+ * adopt the following strategy
+ * <ol>
+ * <li>We will initialise $OUTPUT the first time it is used.</li>
+ * <li>If, after $OUTPUT has been initialised, the script tries to change something
+ * that $OUPTUT depends on, we throw an exception making it clear that the script
+ * did something wrong.
+ * </ol>
+ *
+ * The only problem with that is, how do we initialise $OUTPUT on first use if,
+ * it is going to be used like $OUTPUT->somthing(...)? Well that is where this
+ * class comes in. Initially, we set up $OUTPUT = new bootstrap_renderer(). Then,
+ * when any method is called on that object, we initialise $OUTPUT, and pass the call on.
+ *
+ * Note that this class is used before lib/outputlib.php has been loaded, so we
+ * must be careful referring to classes/funtions from there, they may not be
+ * defined yet, and we must avoid fatal errors.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class bootstrap_renderer {
+    /**
+     * Handles re-entrancy. Without this, errors or debugging output that occur
+     * during the initialisation of $OUTPUT, cause infinite recursion.
+     * @var boolean
+     */
+    protected $initialising = false;
+
+    public function __call($method, $arguments) {
+        global $OUTPUT;
+
+        // If lib/outputlib.php has been loaded, call it.
+        if (!$this->initialising && function_exists('initialise_theme_and_output')) {
+            $this->initialising = true;
+            initialise_theme_and_output(debug_backtrace());
+            if (!($OUTPUT instanceof bootstrap_renderer)) {
+                return call_user_func_array(array($OUTPUT, $method), $arguments);
+            }
+        }
 
-?>
+        $this->initialising = true;
+        // Too soon to initialise $OUTPUT, provide a couple of key methods.
+        $earlymethods = array(
+            'fatal_error' => 'early_error',
+            'notification' => 'early_notification',
+        );
+        if (array_key_exists($method, $earlymethods)) {
+            return call_user_func_array(array('bootstrap_renderer', $earlymethods[$method]), $arguments);
+        }
+
+        throw new coding_exception('Attempt to start output before enough information is known to initialise the theme.');
+    }
+
+    /**
+     * This function should only be called by this class, or by 
+     * @return unknown_type
+     */
+    public static function early_error($message, $moreinfourl, $link, $backtrace,
+                $debuginfo = null, $showerrordebugwarning = false) {
+        // In the name of protocol correctness, monitoring and performance
+        // profiling, set the appropriate error headers for machine comsumption
+        if (isset($_SERVER['SERVER_PROTOCOL'])) {
+            // Avoid it with cron.php. Note that we assume it's HTTP/1.x
+            @header($_SERVER['SERVER_PROTOCOL'] . ' 503 Service Unavailable');
+        }
+
+        // better disable any caching
+        @header('Content-Type: text/html; charset=utf-8');
+        @header('Cache-Control: no-store, no-cache, must-revalidate');
+        @header('Cache-Control: post-check=0, pre-check=0', false);
+        @header('Pragma: no-cache');
+        @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
+        @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
+
+        if (function_exists('get_string') && function_exists('get_html_lang')) {
+            $htmllang = get_html_lang();
+            $strerror = get_string('error');
+        } else {
+            $htmllang = '';
+            $strerror = 'Error';
+        }
+
+
+        $output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" ' . $htmllang . '>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>' . $strerror . '</title>
+</head><body>
+<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>';
+        if (!empty($CFG->debug) && $CFG->debug >= DEBUG_DEVELOPER) {
+            if (!empty($debuginfo)) {
+                $output .= '<div class="notifytiny">' . $debuginfo . '</div>';
+            }
+            if (!empty($backtrace)) {
+                $output .= '<div class="notifytiny">Stack trace: ' . format_backtrace($backtrace, false) . '</div>';
+            }
+        }
+    
+        $output .= '</body></html>';
+        return $output;
+    }
+
+    public static function early_notification($message, $classes = 'notifyproblem') {
+        return '<div class="' . $classes . '">' . $message . '</div>';
+    }
+}
index 002f6059f74d621c506ed0162fcc725a532daeaf..d638f9a0fa5cf490e8aa225e601dc857099573e2 100644 (file)
@@ -50,7 +50,7 @@ class testable_renderer_factory extends renderer_factory_base {
 
     public function create_renderer($module) {
         $this->createcalls[] = $module;
-        return new moodle_core_renderer(new xhtml_container_stack(), null);
+        return new moodle_core_renderer(new xhtml_container_stack(), null, null);
     }
 
     public function standard_renderer_class_for_module($module) {
@@ -64,7 +64,7 @@ class testable_renderer_factory extends renderer_factory_base {
  */
 class moodle_test_renderer extends moodle_core_renderer {
     public function __construct($containerstack, $page) {
-        parent::__construct($containerstack, $page);
+        parent::__construct($containerstack, $page, null);
     }
 
     public function greeting($name = 'world') {
@@ -722,7 +722,7 @@ class template_renderer_test extends UnitTestCase {
         $page = new stdClass;
         $page->course = new stdClass;
         $this->renderer = new template_renderer('moodle_test_renderer',
-                array($this->templatefolder), new xhtml_container_stack(), $page);
+                array($this->templatefolder), new xhtml_container_stack(), $page, null);
         $this->savedtemplates = array();
     }
 
@@ -787,7 +787,7 @@ class moodle_core_renderer_test extends UnitTestCase {
     public function setUp() {
         parent::setUp();
         $this->containerstack = new xhtml_container_stack();
-        $this->renderer = new moodle_core_renderer($this->containerstack, null);
+        $this->renderer = new moodle_core_renderer($this->containerstack, null, null);
     }
 
     public function test_select_menu_simple() {
index 081dd60ee15a3372a05df28986195c566cff8816..4aa9b3c5f2f1c909743590f8829b6ec869f491c1 100644 (file)
@@ -3198,18 +3198,16 @@ function theme_setup($theme = '', $params=NULL) {
         $CFG->stylesheets[] = $CFG->themewww.'/'.$theme.'/rtl.css'.$paramstring;
     }
 
-    /// Set up the block regions.
-    if (!empty($THEME->blockregions)) {
-        $PAGE->blocks->add_regions($THEME->blockregions);
-    } else {
-        // Support legacy themes by supplying a sensible default.
-        $PAGE->blocks->add_regions(array('side-pre', 'side-post'));
+    // Support legacy themes, by setting sensible defaults for some of the new
+    // properties that were introduced in Moodle 2.0.
+    if (empty($THEME->rendererfactory)) {
+        $THEME->rendererfactory = 'standard_renderer_factory';
     }
-    if (!empty($THEME->defaultblockregion)) {
-        $PAGE->blocks->set_default_region($THEME->defaultblockregion);
-    } else {
-        // Support legacy themes by supplying a sensible default.
-        $PAGE->blocks->set_default_region('side-post');
+    if (empty($THEME->blockregions)) {
+        $THEME->blockregions = array('side-pre', 'side-post');
+    }
+    if (empty($THEME->defaultblockregion)) {
+        $THEME->defaultblockregion = 'side-post';
     }
 }
 
index 37be46b0f7c900ad7663a24b08a41ebc4864d6ac..28b893d4d96ea62399541b6712a2bb545e35e189 100644 (file)
@@ -184,4 +184,13 @@ $THEME->custompix = false;
 /// "pix/mod" directory containing all the icons 
 /// for all the activity modules.
 ////////////////////////////////////////////////////////////////////////////////
-?>
+
+
+$THEME->rendererfactory = 'custom_corners_renderer_factory';
+/// This is an advanced features that lets you control the HTML that Moodle
+/// generates. You need to specify a class that implements the renderer_factory
+/// interface. As well as the default 'standard_renderer_factory', there is
+/// also the experimental 'template_renderer_factory', or you could implement
+/// your own. For more information, please see
+/// http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML
+////////////////////////////////////////////////////////////////////////////////