Note:

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

Output API: Difference between revisions

From MoodleDocs
m (Text replacement - "<code (.*)>" to "<syntaxhighlight lang="$1">")
m (Update migration status and path)
Line 1: Line 1:
{{Template:Migrated|newDocId=/docs/apis/subsystems/output}}
{{Template:Work in progress}}
{{Template:Work in progress}}



Revision as of 03:16, 14 June 2022

Important:

This content of this page has been updated and migrated to the new Moodle Developer Resources. The information contained on the page should no longer be seen up-to-date.

Why not view this page on the new site and help us to migrate more content to the new site!

Note: This page is a work-in-progress. Feedback and suggested improvements are welcome. Please join the discussion on moodle.org or use the page comments.


Moodle 2.9


This page is a fresh attempt to explain how renderers, renderables, themes and templates all work together for Moodle 2.9.

It will work with Moodle 3.x, if you additionally follow instructions given here (creating version.php file).

Lets start with building a page that is part of an admin tool.

E.g. /admin/tool/demo/index.php

<?php
// Standard GPL and phpdocs
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->libdir.'/adminlib.php');

admin_externalpage_setup('tooldemo');

// Set up the page.
$title = get_string('pluginname', 'tool_demo');
$pagetitle = $title;
$url = new moodle_url("/admin/tool/demo/index.php");
$PAGE->set_url($url);
$PAGE->set_title($title);
$PAGE->set_heading($title);

$output = $PAGE->get_renderer('tool_demo');

echo $output->header();
echo $output->heading($pagetitle);

$renderable = new \tool_demo\output\index_page('Some text');
echo $output->render($renderable);

echo $output->footer();

Explaining this demo step by step we have firstly the standard config and includes, plus the admin_externalpage_setup('tooldemo') which calls require_login and performs permissions checks for admin pages.

// Standard GPL and phpdocs
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->libdir.'/adminlib.php');

admin_externalpage_setup('tooldemo');

Then we load the title of the page from a lang string (see String_API ). We use this string to set some $PAGE properties (url title and heading).

   
// Set up the page.
$title = get_string('pluginname', 'tool_demo');
$pagetitle = $title;
$url = new moodle_url("/admin/tool/demo/index.php");
$PAGE->set_url($url);
$PAGE->set_title($title);
$PAGE->set_heading($title);

What is $PAGE and where did it come from ?

$PAGE is a global variable used to track the state of the page that is being returned. It is an instance of the moodle_page class defined in lib/pagelib.php. See Page_API for more information on the $PAGE variable.

The most important properties stored in $PAGE are the page context, the url, the layout, title and headings. $PAGE also gives access to some other important classes such as $PAGE->requires, which is an instance of the page_requirements_manager (lib/outputrequirementslib.php). The page_requirements_manager class lets us set dependencies on e.g. javascript and css to be inserted in the correct place in the page (The order things are inserted in the page is hugely important for performance).

$PAGE also lets us load specific renderers for a plugin, or plugin and subtype. We will cover renderers in more detail next.

$output = $PAGE->get_renderer('tool_demo');

This gets an instance of the plugin_renderer_base class that we use to create all output for our page. Themers can subclass this renderer to override specific render methods in order to customise Moodle's output. See Output_renderers for more information, and Overriding_a_renderer for information about how themers can customise a renderer.

Note - some pages use the global variable $OUTPUT to generate their output. This is a generic renderer used for core pages etc, but plugins should always use a more specific plugin renderer.


echo $output->header();
echo $output->heading($pagetitle);

This code prints the header of the page and adds one heading to the page at the top of the content region. Page headings are very important in Moodle and should be applied consistently. See HTML_Guidelines for more information on how and where to use headings.

$renderable = new \tool_demo\output\index_page('Some text');
echo $output->render($renderable);

This is the most interesting part of our page. We are creating a renderable and telling our renderer to render it. The renderable is usually more complex than this, it should hold all the data required for the renderer to display the page. This means we should perform all our logic such as database queries, page parameters and access checks in advance and the results should be passed as data to the renderable. The renderable then just takes that data and returns a HTML representation of it.

echo $output->footer();

This prints the HTML for the bottom of the page. It is very important because it also prints out things that were added to the page_requirements_manager and need to be printed in the footer. Things like javascript includes, navigation tree setup, closing open containers tags etc. The reason all javascripts are added to the footer of the page is for performance. If you add javascript includes to the top of the page, or inline with the content the browser must stop and execute the javascript before it can render the page. See https://developers.google.com/speed/docs/insights/BlockingJS for more information.

In the code above, we created a renderable. This is a class that you have to add to your plugin. It holds all the data required to display something on the page. Here is the renderable for this example:

/admin/tool/demo/classes/output/index_page.php

<?php
// Standard GPL and phpdocs
namespace tool_demo\output;                                                                                                         
                                                                                                                                    
use renderable;                                                                                                                     
use renderer_base;                                                                                                                  
use templatable;                                                                                                                    
use stdClass;                                                                                                                       
             
class index_page implements renderable, templatable {                                                                               
    /** @var string $sometext Some text to show how to pass data to a template. */                                                  
    var $sometext = null;                                                                                                           
            
    public function __construct($sometext) {                                                                                        
        $this->sometext = $sometext;                                                                                                
    }

    /**                                                                                                                             
     * Export this data so it can be used as the context for a mustache template.                                                   
     *                                                                                                                              
     * @return stdClass                                                                                                             
     */                                                                                                                             
    public function export_for_template(renderer_base $output) {                                                                    
        $data = new stdClass();                                                                                                     
        $data->sometext = $this->sometext;                                                                                          
        return $data;                                                                                                               
    }
}

This class implements the renderable interface, which has no methods, and the templatable interface, which means that this class could be rendered with a template, so it must implement the "export_for_template" method. So in this example, the class accepts data via it's constructor, and stores that data in class variables. It does nothing else fancy with the data in this example (but it could). Note that the export_for_template function should only return simple types (arrays, stdClass, bool, int, float, string).

Now lets look at the renderer for this plugin.

admin/tool/demo/classes/output/renderer.php

<?php
// Standard GPL and phpdocs
namespace tool_demo\output;                                                                                                         
                                                                                                                                    
defined('MOODLE_INTERNAL') || die;                                                                                                  
                                                                                                                                    
use plugin_renderer_base;  

class renderer extends plugin_renderer_base {
    /**                                                                                                                             
     * Defer to template.                                                                                                           
     *                                                                                                                              
     * @param index_page $page                                                                                                      
     *                                                                                                                              
     * @return string html for the page                                                                                             
     */                                                                                                                             
    public function render_index_page($page) {                                                                                      
        $data = $page->export_for_template($this);                                                                                  
        return parent::render_from_template('tool_demo/index_page', $data);                                                         
    }           
}

The renderer exists to provide render_x methods for all renderables used in the plugin. A theme designer can provide a custom version of this renderer that changes the behaviour of any of the render methods and so to customize their theme. In this example, the render method for the index page (render_index_page) does 2 things. It asks the renderable to export it's data so that it is suitable for passing as the context to a template, and then renders a specific template with this context. A themer could either manipulate the data in the render method (e.g. removing menu entries), or change the template (change the generated html) to customize the output.

The template used in this plugin is located in the plugins templates folder. The template can also be overridden by a themer.

admin/tool/demo/templates/index_page.mustache

<div class="hero-unit">                                                                                                             
  <h1>Heading</h1>                                                                                                                  
  <p>{{sometext}}</p>                                                                                                               
</div>

This is the mustache template for this demo. It uses some bootstrap classes directly to position and style the content on the page. Template:sometext is replaced with the variable from the context when this template is rendered. For more information on templates see Templates.

See also