If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Migrating your code to the 2.0 rendering API

From MoodleDocs
Warning: This page is no longer in use. The information contained on the page should NOT be seen as relevant or reliable.

It seems that you can apply spirit of this page to Moodle 2.2. Whenever this page references theme's layout.php, Moodle 2.2 uses a file from theme's layout/ subfolder (one of: general.php, frontpage.php, embedded.php, report.php, potentially more). The file is mapped to a layour via $THEME->layouts, set in theme's config.php. Just see theme/base/layout and others in Moodle 2.2. In Moodle 2.2 $OUTPUT seems to be an instance of renderer_base ((or its subclasses plugin_renderer_base and core_renderer, or their subclasses, if any). See its API documentation (HEAD) (this documentation may differ to 2.2/2.3 STABLE).
OK, but what are we supposed to use instead of this page? Where is $OUTPUT documented? Chris Fryer
As far as I know, it is not documented at all except in the code. If anybody wants current documentation in a hurry, they probably will have to write it themselves. I'm still pointing people to this document... Sam marshall
I just made a lot of updates to Output_API and friends, so hopefully everything is more up-to date and useful.

This page explains how to update your code to take advantage of the new navigation/block/themes/rendering APIs in Moodle 2.0. These changes are explained on Navigation 2.0, Navigation 2.0 implementation plan and linked pages. You should read that background to understand the changes. This page just explains how you should update your code, not why.

Note that most of the code here has extensive php documentor documentation. Please use that API documentation if you want more details of how to use this new code. If you really want to know how it works see How_Moodle_outputs_HTML, and I hope the code is readable and well commented.

General note

First of all, go to Administration -> Server -> Debugging, and turn debugging up to DEVELOPER level.

Most of the things that have been deprecated or that need to be changed cause debugging output that explains how to update your code.

I went to a lot of effort to implement all that helpful output, please use it!

Outputting HTML

See Outputting HTML in 2.0 for a detailed list of functions to migrate.

HTML used to be output through a mixture of direct echo/print statements, and calls to functions like print_header, print_box, ... in lib/weblib.php.

The functions in weblib have been replaced by a new global $OUTPUT. This generates HTML that you need to echo, so



global $OUTPUT; // If necessary.

(Why do we do this, well by default $OUTPUT is a moodle_core_renderer. However, the theme can choose a different class, and so use different output for, say, a box. As an example look in theme/custom_corners/renderers.php.)

For low level output functions to output raw HTML (such as you might use within a renderer), see html_writer.

Outputting complex objects

Some of the old weblib.php functions had a lot of arguments, for example:

choose_from_menu($options, 'outcome_'.$n.'['.$userid.']',
        $outcome->grades[$submission->userid]->grade, get_string('nooutcome', 'grades'),
        '', 0, false, false, 0, 'menuoutcome_'.$n);

That was quite difficult to work with. (What do those two false parameters mean?). The equivalent new code looks like

$menu = new moodle_select_menu;
$menu->options = $options;
$menu->name = 'outcome_'.$n.'['.$userid.']';
$menu->selectedoption = $outcome->grades[$submission->userid]->grade;
$menu->nothinglabel = get_string('nooutcome', 'grades');
$menu->id = 'menuoutcome_'.$n;
echo $OUTPUT->select_menu($menu);

The new code is more verbose, but much easier to understand (discussion).

If fact if you remember the old print_table method, this approach will be quite familiar. However, note that we now use a real class, rather than stdClass. The main reason for that is that it lets us document the structure ([ see here for an example). It also makes it easy to set defaults and lets IDEs offer autocompletion.


The old print_header function becomes $OUTPUT->header(). Instead of passing millions of arguments to that function, instead you set propeties on $PAGE. For exampe:

The Moodle 1.9 code

$navlinks = array(array('name'=>$strparticipants, 'link'=>$CFG->wwwroot.'/user/index.php?id='.$courseid, 'type'=>'misc'),
                  array('name'=>$strgroups, 'link'=>'', 'type'=>'misc'));
$navigation = build_navigation($navlinks);
print_header(get_string('publishcourse'), $course->fullname, $navigation,'',$meta, true, ' ', user_login_string($course, $USER));

Becomes the following Moodle 2.0 code

require_login( $course );
$PAGE->set_headingmenu(user_login_string($course, $USER));
$PAGE->navbar->add($strparticipants, null, null, navigation_node::TYPE_CUSTOM, new moodle_url($CFG->wwwroot.'/user/index.php?id='.$courseid));
echo $OUTPUT->header();

NOTE: The require_login() does some magic in the background to set up $PAGE. If you don't have it you may have to do some more work.

The following arguments are no longer supported at all. They were not being used anywhere in core code in a way that could not be handled by a better mechanism.

  • meta
  • usexml
  • bodytags

Outputting HTML specific to your module

$OUTPUT lets themes customise the HTML used for standard things like boxes, but what about places where code echoes HTML directly? Well, in addition to moodle_core_renderer, every plugin should also define its own renderer, like moodle_mod_workshop_renderer, and all module-specific HTML output should done in methods of that class. That class should be defined in a files called renderers.php inside your plugin.

When you actually want to do some output, you need to get an instance of your class (or whatever subclass the theme wants to substitute). You do that like this:

global $PAGE; // If necessary.
$wsoutput = $PAGE->theme->get_renderer('mod_workshop', $PAGE);
echo $wsoutput->manual_allocation_interface($workshop, $allocationdata);

The moodle_mod_workshop_renderer object will have access to a moodle_core_renderer available as $this->output. You should use this instead of global $OUTPUT, and you should use it. That is, boxes in the workshop should look like boxes elsewhere, so boxes in the workshop should be output with $this->output->box().

Don't panic!

Completely updating your code to use the new output methods everywhere, and writing a renderer class is a big job. The good news is that you don't have to. All the old weblib methods have been moved to deprecatedlib, and now call $OUTPUT, so all your old code will go on working. You can update it at your leisure. (Of course, eventually, the old methods will be removed, so you should update some time.)


Settings forms


config_global.html files must be replaced by a settings.php file, just like other plugin types. (There is rudimentary support for config_global.html files still in the code, but you should not rely on it.)


config_instance.html files must be replaced by a proper settings form. The form must be a subclass of block_settings_form called block_myblock_edit_form defined in the file blocks/myblock/edit_form.php.

In addition, the old instance_config_print is no longer used, so any code there needs to be moved into the form class you create.

blocks/html/edit_form.php is a nice simple example to look at. Other blocks mentioned in MDL-19889 provide more complex examples. The changes associated with that bug show you exactly how to covert a block.

There is currently no backwards-compatible support for old config_instance.html files. It would be hard to do that. It might be possible if people really need it, but no-one has had time to try yet.

Other API changes

  • The method block_base::preferred_width is no longer used the width of blocks is entirely controlled by the theme. If your block is too wide, it will probably get cropped with overflow: hidden. See for discussion.
  • Code in the block that relied on $this->instance->... will probably break. Information about the page the block is appearing on is available from $this->page->.... Specifically:
    • $this->instance->pagetype should be changed to $this->page->pagetype
    • $this->instance->pageid - there is no longer a concept of page id. Instead, you are probably looking for $this->page->course, $this->page->cm or $this->page->activityrecord. See below for more on $PAGE.

Useful new stuff

  • Every block now has access to the page it appears on, via $this->page. (Therefore, you do not have to use the global $PAGE object in blocks!)
  • Every block now has its own context, and you can always access it via $this->context. However when checking permissions you need to decide which of two contexts is more appropriate:
    1. The block context ($this->context)
    2. The page context the block is appearing on ($this->page->context). For sticky blocks, this may be a more appropriate choice.

For example, when deciding whether the user can edit the block settings, or see the block, you should use the block context. When deciding whether the user can do something relating to the context where the block appears (can the user see participants here) you should probably use the page context.

Other things to be aware of

Note that the way the blocks are output to HTML has changed. However, there has been no change to the blocks API, so you should not have to make any changes, unless you had overridden formerly 'private' methods.

Activity modules

See also the section on #Outputting blocks below.

Blocks upgrade

If you have a custom module that has a pagelib.php file, then you need to ensure it is included in the blocks upgrade. Just look for the bit in lib/db/upgrade.php that says

$moduleswithblocks = array('chat', 'data', 'lesson', 'quiz', 'dimdim', 'game', 'wiki', 'oublog');

and add the name of your module. You must do this before trying to upgrade your site.

If you have done something more complicated than what is in the NEWMODULE template, then you will have to write more custom code.

in mod/.../view.php

You need to change how $PAGE is initialised.

  • Don't call page_create_instance any more. It is deprecated, and if you do, you should see a debug warning. (You do you development with debugging set to DEBUG_DEVELOPER, right?)
  • In fact, you probably only need to replace the line
$PAGE = page_create_instance($chat->id);


$PAGE->set_url('mod/MYMOD/view.php', array('id' => $cm->id));
  • Take the code that is left in page_generic_activity::print_header in inline it in your view.php file at the point where you used to call $PAGE::print_header. You will need to fix it up to make it work, but normally that involves deleting about half of it and editing the rest a bit. Some helpful notes:
    • $this->modulerecord -> $cm
    • $this->$course -> $course
    • $this->activityname -> 'MYMOD'
    • Change the complicated mess that computes $title into a simple line of code.
    • Other instances of $this -> $PAGE
    • See if $meta, $bodytags and so on are acutally used, and if not remove them.
  • Delete your 'mod/MYMOD/pagelib.php' file, and any places where it is included.

To help with this conversion, the regex page_|\$PAGE|pagelib is a good thing to search for.

in ..._delete_instance in lib.php

You used to have to worry about deleting blocks when an instance of your module was deleted. You can forget about that now, since all blocks data are deleted automatically when a context is deleted.

That is, code like

$pagetypes = page_import_types('mod/chat/');
foreach($pagetypes as $pagetype) {
    if(!blocks_delete_all_on_page($pagetype, $chat->id)) {
        $result = false;

in lib.php can just be deleted.


It is no longer necessary to write subclasses of page_base, and in fact, such subclasses are no longer used. Instead there is a single moodle_page class, and an instance of that is automatically created as $PAGE.

Do not set $CFG->pagepath. Instead, call $PAGE->set_pagetype(); For example

$CFG->pagepath = 'question/type/' . $question->qtype;


$PAGE->set_pagetype('question-type-' . $question->qtype);

blocks_... methods

If your page subclass used to try to control blocks you need to change it to get equivalent functionality

You no longer control this. These settings now belong to the theme. However, if you want special extra block areas on certain pages, then you can call $PAGE->blocks->add_region('myregion');
No longer supported. Instead, in your .../db/install.php you should probably do blocks_create_block('blockname', get_context_instance(CONTEXT_SYSTEM), BLOCKS_SHOWINSUBCONTEXTS, 'my-page-type');
No longer required at all. The way blocks editing now works means that it is unnecessary.

url_... methods

Instead of implementing the url_get_parameters and url_get_path() methods, you should instead be setting the $PAGE->set_url(...) method from within your script.

user_... methods

is now implemented by the base class and should not be changed.
user_allowed_editing is also implemented by the base class, you must call $PAGE->set_block_editing_capability and/or $PAGE->set_other_editing_capability to set the right logic. By default, editing can be turned on on a page if if the user has the 'moodle/site
manageblocks' capability in the page context.


the page object no longer has any responsibility for printing the header. Your script should do it directly. This means that if you page class has a print_header method, you should move that code elsewhere.

Other changes

  • Code that used to rely on the ->courserecord field of pages should be changed to refer to ->course.
  • Calls to $PAGE->get_type() should be changed to $page->pagetype.
  • page_id_and_class, which was used to initialise and return the pagetype (as $getid) is no longer necessary. Just use $PAGE->pagetype.
  • $PAGE->get_format_name is deprecated. use $PAGE->pagetype instead.

Libraries to include

lib/blocklib.php and lib/pagelib.php are now always included by config.php. Therefore you should clean up any places in your code where you explicitly require_once them.

$CFG->pixpath and $CFG->modpixpath

$CFG->pixpath and $CFG->modpixpath are now deprecated. Old code like "$CFG->pixpath/i/course.gif" should be changed to $OUTPUT->pix_url('i/course') and code like "$CFG->modpixpath/$mod/icon.gif" should be changed to $OUTPUT->pix_url('icon', $mod->modname).

When I started converting Moodle core code, there were 467 matches for the regular expression /\$CFG->(mod)?pixpath/ - that will find all the places in the code you have to update.

I used the following regular expressions (Eclipse regular expression search and replace) to fix a lot of them semi-automatically.

\$CFG->pixpath ?\. ?'/((\w/)?[a-zA-Z0-9_-]+)\.(gif|png)
\$OUTPUT->pix_url('$1') . '
(225 matches in HEAD)

" . \$OUTPUT->pix_url('$1') . "
(68 matches in HEAD)

\$CFG->pixpath ?\. ?'/([a-zA-Z0-9_-]+)\.(gif|png)
\$OUTPUT->pix_url('$1') . '
(29 matches in HEAD)

\$CFG->modpixpath ?\. ?'/' ?\. ?([^\.]+) ?\. ?'/icon\.gif
\$OUTPUT->mod_icon_url('icon', $1) . '
(12 matches in HEAD)

" . \$OUTPUT->mod_icon_url('icon', $1) . "
(13 matches in HEAD)

After you have done the automatic replaces, you need to review the changes (e.g. using cvs diff) because sometimes you end up with code like . "" . that should be cleaned up, and the search and replace cannot add global $OUTPUT; where necessary, nor remove no longer necessary global $CFG; statements.

Outputting blocks

Blocks are now output by the theme, in its layout.php file. Any code you have in your scripts that calls blocks_setup, blocks_preferred_width, blocks_have_content or blocks_print_group should just be deleted.;a=commitdiff;h=d4a03c00ea3885ac2b264b7d7a8c6c635d5714d2#patch31 is typical of how to update a script.


Please don't forget to take the advice above and turn debugging up to DEVELOPER level. That will give you lots of helpful debug output that tells you what you need to update.

header.html and footer.html replaced by template(s) under layout/

Instead of separate header.html and footer.html files for the top and bottom of the page, there is now one or more template(s) under theme's layout subfolder. One template file covers what was previously in both header.html and footer.html.

You can have various types of those templates under layout/, at least general.php, frontpage.php, embedded.php, report.php.

To create a template under layout/, you basically need to merge your header.html and footer.html files with various PHP calls and with <?php echo $OUTPUT->main_content() ?> in the middle. Look at e.g. standardold/layout or base/layout in Moodle 2.2. This is explained in more detail in the next few sections (may be obsolete).

layout.php should get data from $PAGE

In the past, header.html and footer.html had to rely on certain variables that were set up in the print_header and print_footer functions. Instead, you should now get the information you want from properties of $PAGE, or call $OUTPUT methods to output various standard things.

The replacements for the various old variables are:

// Old code in header/footer.html ->   // New code in layout.php
[hard-coded doctype]              ->   <?php echo $OUTPUT->doctype() ?>
<?php echo $direction ?>          ->   <?php echo $OUTPUT->htmlattributes(); ?>
$title                            ->   $PAGE->title;
$heading                          ->   $PAGE->heading;
$focus                            ->   $PAGE->focuscontrol;
$button                           ->   $PAGE->button;
$pageid                           ->   $PAGE->pagetype;
$pageclass                        ->   $PAGE->bodyclasses;
<?php echo " $bodytags"; ?>       ->   id="<?php echo $PAGE->pagetype ?>" class="<?php echo $PAGE->bodyclasses ?>"
if ($home) {...                   ->   if ($PAGE->generaltype == 'home') {...
$homelink                         ->   $OUTPUT->home_link();
$loggedinas                       ->   $OUTPUT->login_info();
$course                           ->   $PAGE->course;

Some standard bits that used to be present need to be deleted:

// Delete these bits that used to be in header/footer.html
<?php echo $meta ?>
<meta name="keywords" content="moodle, <?php echo $title ?> " />
<?php include("$CFG->javascript"); ?>
  if (!empty($performanceinfo)) {
    echo $performanceinfo;

And certain new bits need to be added:

// Add these bits in the appropriate places in layout.php
<?php echo $OUTPUT->standard_head_html() ?>
<?php echo $OUTPUT->standard_top_of_body_html() ?>
<?php echo $OUTPUT->standard_footer_html() ?>
<?php echo $OUTPUT->standard_end_of_body_html() ?>

Themes must print the blocks

You must also add code to your layout.php to output any blocks.

Typically the code will look something like:

// Add code like this in the appropriate places in layout.php
<?php if ($PAGE->blocks->region_has_content('side-pre')) { ?>
<div id="region-side-pre" class="block-region">
    <?php echo $OUTPUT->blocks_for_region('side-pre') ?>
<?php } ?>

As I said above, the quickest way to get the hang of all the things you need to do in layout.php is by example. Look at theme/standard/layout.php, or other examples in the standard Moodle themes.

You can have different layout.php files for different page.

Pages in Moodle are now classified into one of a few general types, like 'normal', 'home', 'form', 'popup', ...

If you wish, you can specify a different template for each one. Also, each type of template can have any number of regions for blocks. This is controlled by the $THEME->layouts setting in your theme's config.php. Here is an example.

// Code in theme config.php
$THEME->layouts = array(
    // Most pages. Put this first, so if we encounter an unknown page type, this is used.
    'normal' => array(
        'layout' => 'standard:layout.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-post'
    // The site home page.
    'home' => array(
        'layout' => 'layout-home.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-post'
    // Settings form pages, like course of module settings.
    'form' => array(
        'layout' => 'standard:layout.php',
        'regions' => array(),
    // Pages that appear in pop-up windows.
    'popup' => array(
        'layout' => 'standard:layout-popup.php',
        'regions' => array(),
    // Used during upgrade and install, and for the 'This site is undergoing maintenance' message.
    'maintenance' => array(
        'layout' => 'standard:layout-popup.php',
        'regions' => array(),

What you see there is that for each type of page, which layout file to use, and where blocks may appear.

There are three ways you can specify a layout file. It may be in this theme (for example layout-home.php) or it may come from the parent theme (for example you could say parent:layout.php) or from the standard theme (for example standard:layout-popup.php).

Some templates have 'regions' for blocks and others don't. You can even use a template that has block regions (like standard:layout.php) but then say you don't want any blocks there, as for 'form' pages in the example above. Note that the default names for regions are now side-pre and side-post. That makes more sends than left and right in right-to-left languages.

If there are block regions, then you must specify one of them as the default. That is where the 'Add new blocks' pretend block appears, and where new blocks are added.

Also, suppose someone adds some blocks while using another theme that uses one set of names for its block regions, and then switches to your theme that uses different names. We don't want some of the blocks to just disappear, so any blocks from unrecognised regions are shown at the end of the default region.


The standard form for the styles.php file has changed (become simpler). Please copy the latest theme/standard/styles.php into your theme.

No longer supported options in config.php

Note that there is now documentation about all the things you can set in config.php at

All the separate $THEME->modsheets = true; $THEME->blocksheets = true; ... settings have been replaced by a single

$THEME->pluginsheets = array('mod', 'block', 'format', 'gradereport');

setting, and all plugin types are now supported.

$THEME->customcorners = true; should be changed to $this->rendererfactory = 'custom_corners_renderer_factory';

$THEME->cssconstants = true; is no longer supported in config.php files. Use $THEME->customcssoutputfunction = 'output_css_replacing_constants';

$THEME->CSSEdit = true; is no longer supported in config.php files. Use $THEME->customcssoutputfunction = 'output_css_for_css_edit';


The way in which JavaScript is included for a page has now changed and methods have been provided for $PAGE to require JavaScript within a page. As part of this change several JavaScript libraries that were been included on every page are not any more, and if you have code that was using them you need to modify it to manually include these JavaScript files.

Replacement for require_js()

In the past we have had a function called require_js(), which made it very easy to make sure that you JavaScript files you wanted got linked to somewhere, and that each file was linked to no more than once.

That was very nice, but only went so far. For example, there was no equivalent require_css(), and some of the YUI libraries need their own CSS files to work properly. Also, you could not control when the JS was linked to and as discussed here, it is best to link to most of the JavaScript from the end of the HTML, not from <head> because that gives better page-load performance.

Of course some things cannot wait that long and do need to be included in HEAD or in some cases within the body of the page, in other cases if your script is at the end of the page and you need to call an init function then that initialise function had also better be at the end of the page.

As such the requirements have become more complicated and in order to handle them a new page_requirements_manager was created to replace require_js().

The following are examples of how to use the new page requirements manager.

$PAGE->requires->yui_lib('autocomplete'); // YUI requirements are known about  
// and automatically included, without you needing to call yui_lib for each one
echo $PAGE->requires->js('mod/mymod/small_but_urgent.js')->asap();
$PAGE->requires->js_function_call('init_mymod', array($data))->on_dom_ready();
$PAGE->requires->data_for_js('mydata', Array('name1'=>$value1, 'name2'=>$value2));

Unobtrusive Flash Objects - ufo.js

The UFO JS object used to display inline media is no longer included on every page. If you have any code that is relying on the UFO library you will need to update it in order for it to work.

In order to use the UFO you will need to include the following line of code.

global $PAGE; // You will only need this if you are within a function
$PAGE->requires->js('lib/ufo.js'); // Only use ->asap() or ->in_head() if you really need to

A good idea at the same time might be to convert your code to use the new $PAGE methods to the full extent which can be done in the following fashion.


echo '<script type="text/javascript">';
echo '//<![CDATA[';
echo '  var FO = { movie:"'.$CFG->wwwroot.'/filter/mediaplugin/mp3player.swf?src='.$url.'",';
echo '    width:"90", height:"15", majorversion:"6", build:"40", flashvars:"'.$c.'", quality: "high" };';
echo '  UFO.create(FO, "'.$id.'");';
echo '//]]>';
echo '</script>';


$ufoargs = Array();
$ufoargs['movie'] = $CFG->wwwroot.'/filter/mediaplugin/mp3player.swf?src='.$url;
$ufoargs['width'] = 90;
$ufoargs['height'] = 15;
$ufoargs['majorversion'] = 6;
$ufoargs['build'] = 40;
$ufoargs['quality'] = 'high';
$PAGE->requires->data_for_js('FO', $ufoargs);
$PAGE->requires->js_function_call('create_UFO_object', Array('myelementid'));

DOM events

In the past we have used DOM events inline within HTML elements. Now that we are avoiding inline JavaScript, these methods are no longer suitable. The new rendering API in lib/outputlib.php abstracts all event handlers into a simple approach.

  1. JS functions you want to call when an event is triggered must be written in a JS-only text file (no PHP)
  2. These functions MUST have 2 params only: event and args. Args is a JS object with named variables which you can use in your function. The event object is used to determine the target of the event (the element to which the event handler is attached), and the type of event that was triggered.
  3. To output a HTML element that has an event handler, you must use a subclass of moodle_html_component. These objects have an add_action() method which takes a component_action object or 2-3 params as described in the phpdocs.
  4. In the rendering methods of the $OUTPUT object, the render::prepare_event_handlers($component) is called and the appropriate YUI Javascript is added to the page, creating an event listener on the given object.

An example follows:

$image = new html_image();
$image->src = $src;
$image->add_action('click', 'show_large_version', array('title' => 'Large version', 'description' => 'a lovely image'));
echo $OUTPUT->image($image);

This examples will output an image which, when clicked, will trigger the call to the show_large_version() JS function, passing the event object and a JS object with two named variables: title and description. That function may look like this:

function show_large_version(e, args) {
    var title = args.title;
    var description = args.description;
    popup(this.src, title, description); // An imaginary function

Note, in the JS function above, the use of "this", which is the element that triggered the event. It can be obtained through the event object too.

Information for admins

This probably needs to be moved into the release notes when it is done.

Some hidden blocks my become un-hidden during the upgrade

In Moodle 1.9, it was, in theory, possible to add a sticky block to a type of page, and then make that stickyblock hidden. The way blocks are hidden in Moodle 2.0 is slightly different, so if in your Moodle 1.9 site you had hidden sticky blocks, they will become visible during the upgrade to Moodle 2.0.

Also, in a few situations, other non-sticky blocks that were hidden may becomes visible. (This will only happen if the block appeared on more than one page. For example, if you have a hidden block on a course page, and also have enabled showing course blocks on resource pages, then the block will be visible on the resource page until you hide it again.) If there are specific identifiable instances of this problem, it may be possible to improve the upgrade code while Moodle is in beta testing.

See also