Difference between revisions of "Output renderers"

Jump to: navigation, search
m (Plugin renderers: added workshop_submission widget and fixed a way how to render it)
m (plugin_renderer_base: fixed the name of rendering method)
Line 140: Line 140:
 
class mod_workshop_renderer extends plugin_renderer_base {
 
class mod_workshop_renderer extends plugin_renderer_base {
  
     public method submission(workshop_submission $submission) {
+
     protected function render_workshop_submission(workshop_submission $submission) {
 
         $out  = $this->output->heading(format_string($submission->title), 2);
 
         $out  = $this->output->heading(format_string($submission->title), 2);
 
         $out .= $this->output->container(format_string($submission->authorname), 'author');
 
         $out .= $this->output->container(format_string($submission->authorname), 'author');

Revision as of 10:08, 8 January 2010

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.

Output renderers
Project state Specification
Tracker issue MDL-21235
Discussion n/a
Assignee Petr Škoda (škoďák) + David Mudrak + feedback and ideas from other developers

Moodle 2.0


Goals

  • stable API
  • easy to use
  • easy to customise via themes

Renderers

Output renderer is a class with collection of methods that handle rendering of visual aspects of Moodle pages, emails, html export, etc. In Moodle 1.9, general output related functions were located in weblib.php and modules stored rendering code in lib.php, locallib.php, view.php, etc. Output renderer instances are obtained through moodle_page::get_renderer($component, $subtype = null, $target = null) method.

The general renderer class naming convention is:

{theme name}_{plugin type}_{plugin_name}_{subtype}_renderer_{target}

where theme name prefix, subtype infix and target suffix are optional. Examples of renderer class name are core_renderer, mytheme_core_renderer, mod_workshop_renderer, mod_forum_renderer_cli, etc.

Renderer subtype 
When get_renderer() is called, $subtype parameter can be provided to obtain a different renderer. Renderer subtypes are used mainly for core subsystems, but it si also possible to use them in modules.
Renderer targets 
In most cases, we are rendering page output for web browsers. Sometimes we need to generate different output for command line scripts, email messages, etc. When get_renderer() is called, $target parameter can be provided to obtain a different renderer. The list of targets will be part of the specification, individual plugins should not invent new rendering targets.
note that both $subtype and $target works similarly - they just influence the name of the class to be returned by get_renderer(). But there is semantic difference and we will probably have the list of allowed targets limited (defaults to html).

Overall picture

The following UML diagram describes the main classes involved in HTML output rendering and their relationships.

uml output renderers.png


renderer_base

Abstract class every other renderer must extend. Renderer base implements basic methods and support for renderer dispatching.

core_renderer

Current core_renderer is available through the global $OUTPUT variable. This global should not be used in lower level APIs - only in the main scripts like view.php. When you want to start to print something, you will probably call

echo $OUTPUT->header();

in your view.php or similar script. To print a page footer and terminate your PHP script, you will call

echo $OUTPUT->footer();
die();

Beside these page header and footer related things, core_renderer knows how to display

  1. boxes and containers - basically <div> elements
  2. general widgets - headings, single buttons, selection forms, arrows in the breadcrumb navigation, language selection popup, user login info etc.
  3. error messages, notifications
  4. blocks

Various widgets (headings, buttons etc.) are represented as classes. In most cases, they will look like a simple object with just some properties and a constructor to set the most common ones. Core renderer implements protected methods to render these widgets. These widget rendering methods are called render_something() where something is the name of the widget. Their first parameter must be an instance of something widget class. These methods can be overridden by themes.

Public method to render a widget is called render(). It accepts the only parameter which must be an instance of a widget. It uses the class name of the widget passed in as parameter to find the correspondind protected rendering method.

For some widgets, a helper method may be implemented to instantiate the widget and render it in the background. Such helper methods have the same name as the widget class and accepts parameters needed to create the widget instance.

Example: User avatar rendering involves following methods and classes:

class core_renderer extends renderer_base {
 
    public function render(widget $widget) {
        // if $widget is instance of user_image, then call $this->render_user_image($widget)
    }
 
    // ...
 
    protected function render_user_picture(user_picture $userpicture) {
        // returns HTML code to be echoed for displaying the user avatar
    }
    public function user_picture(stdclass $userrecord, array $options=null) {
        // helper method that constructs $user_image widget and calls $this->render()
        $picture = new user_picture($userrecord, $options);
        return $this->render($picture);
    }
 
  // ...
}
 
class user_picture extends widget {
  public $userrecord;
  public $courseid = 0;
  public $link = true;
 
  public function __construct(stdclass $userrecord, array $options=null) {
      // ...
  }
}

There are two ways how a developer can render the user picture. Either using a one-line style:

$user = $DB->get_record('user', array(...));
echo $OUTPUT->user_picture($user, array('courseid' => $courseid, 'link' => true));

or an explicit stye:

$user = $DB->get_record('user', array(...));
$avatar = new user_picture($user);
$avatar->courseid = $courseid;
$avatar->link = true;
echo $OUTPUT->render($avatar);

The point is that the first style is usually one line only, it is also possible to have multiple helpers with different parameters. The second style allows more options without cluttering the simple API with tons of optional method parameters.

core_renderer_cli

_cli suffix indicates this is a core renderer for CLI rendering target (CLI scripts). For example headings are using => instead if H2 html tag.

Core subsystem renderers

Plugin renderers

Plugin renderers are stored in renderer.php file in the same folder as the lib.php file of each plugin. They all extend plugin_renderer_base method.

plugin_renderer_base

An important thing about this class is that it keeps a reference to an instance of (typically) core_renderer which is used as an underlying renderer. This reference is kept in protected $output member variable. So, there is no more $OUTPUT in plugin renderers. They all use $this->output->something() to render the output.

Example: Workshop module defines mod_workshop_renderer class in mod/workshop/renderer.php file. The renderer defines a method to display a student submission.

// in renderer.php:
class workshop_submission implements widget {
 
    public function __construct(stdclass $submission, $anonymous = false, array $attachments = null) {
        // here the widget is prepared and all necessary logic is performed
        if ($anonymous) {
            $this->authorname = get_string('anonymousauthor', 'workshop');
        } else {
            $this->authorname = fullname(...);
        }
    }
}
 
class mod_workshop_renderer extends plugin_renderer_base {
 
    protected function render_workshop_submission(workshop_submission $submission) {
        $out  = $this->output->heading(format_string($submission->title), 2);
        $out .= $this->output->container(format_string($submission->authorname), 'author');
        $out .= $this->output->container(format_text($submission->content, FORMAT_HTML), 'content');
        return $this->output->container($out, 'submission');
    }
}
 
// in view.php
$wsoutput = $PAGE->get_renderer('mod_workshop');
$submissionrecord = get_record('workshop_submissions', array(...));
$submissionwidget = new workshop_submission($submissionrecord, false);
echo $OUTPUT->header();
echo $wsoutput->render($submissionwidget);
echo $OUTPUT->footer();

Plugin renderers are able to forward calls to the underlying renderer automatically. So, in view.php, there could be echo $wsoutput->header(); which would call the same method.

Bootstrap renderer

Bootstrap renderer is used as a temporary $OUTPUT before the full initialisation of standard core renderer.

Theme customisations

Theme renderers

Theme renderers are stored in renderers.php file in theme folder. The actual overriding of renderers is the responsibility of renderer_factory classes which may be selected in theme config.php.

Low level HTML output

See also