Difference between revisions of "Output renderers"

Jump to: navigation, search
(core_renderer)
m (Added UML and small rewording. Adding the information about plugin renderers)
Line 9: Line 9:
 
{{Moodle 2.0}}
 
{{Moodle 2.0}}
  
=Goals=
+
== Goals ==
 
* stable API
 
* stable API
 
* easy to use
 
* easy to use
 
* easy to customise via themes
 
* easy to customise via themes
  
=Renderers=
+
== Renderers ==
  
Output renderer is a class with collection of methods that handle rendering of visual aspects of Moodle pages, emails, html export, etc. In 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 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.
  
Output renderer instances are obtained through moodle_page::get_renderer($component, $subtype = null, $target = null) method. Current core_renderer is available through the global $OUTPUT variable, please note this global should not be used in low level APIs.
+
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, custom_corners_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. For example, beside mod_glossary_renderer there can be mod_glossary_faq_renderer and mod_glossary_list_renderer.
 +
 
 +
; 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.
 +
 
 +
: 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.
  
The general renderer class naming convention is:
+
[[Image:uml_output_renderers.png|800px|left]]
{theme name}_{plugin type}_{plugin_name}_renderer_{target} - custom_corners_core_renderer, mod_workshop_renderer, mod_forum_renderer_cli, etc.
+
<br clear="both" />
  
==Renderer targets==
+
=== renderer_base ===
  
I most cases we are rendering page output for web browsers, sometimes we need to generate different output for command line scripts, email messages, etc.
+
Abstract class every other renderer must extend. Renderer base implements basic methods and support for renderer dispatching.
  
==renderer_base==
+
=== core_renderer ===
  
Abstract class every other renderer must extend. Renderer base implements basic methods and support for renderer dispatching. The most important method is render(), it is using class name of the widget passed in first parameter to find fine correct protected rendering method (widget rendering methods have ''render_'' prefix.
+
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();
  
==core_renderer==
+
Beside these page header and footer related things, core_renderer knows how to display
Core renderer implements:
+
# boxes and containers - basically &lt;div&gt; elements
# page header and footer related rendering
+
# general widgets - headings, single buttons, selection forms, arrows in the breadcrumb navigation, language selection popup, user login info etc.
# boxes and containers
 
# general widget methods - heading, single button, select form, heading, arrows, language selection, user login info
 
 
# error messages, notifications
 
# error messages, notifications
 
# blocks
 
# blocks
  
For example user avatar rendering involves following methods and classes:
+
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:
 
<code php>
 
<code php>
 
class core_renderer extends renderer_base {
 
class core_renderer extends renderer_base {
  // ...
 
  
  public function render(widget $widget) {
+
    public function render(widget $widget) {
    // calls $this->render_user_image($widget) when parameter with class user_image submitted
+
        // if $widget is instance of user_image, then call $this->render_user_image($widget)
  }
+
    }
  protected function render_user_picture(user_picture $userpicture) {
+
 
    // returns html markup for user avatar
+
    // ...
  }
+
 
  public function user_picture($userrecord, array $options=null) {
+
    protected function render_user_picture(user_picture $userpicture) {
    // helper method that constructs $user_image widget and calls $this->render
+
        // returns HTML code to be echoed for displaying the user avatar
    $picture = new user_picture($userrecord, $options);
+
    }
    return $this->render($picture);
+
    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);
 +
    }
  
 
   // ...
 
   // ...
Line 61: Line 83:
 
class user_picture extends widget {
 
class user_picture extends widget {
 
   public $userrecord;
 
   public $userrecord;
   public $options;
+
   public $courseid = 0;
 +
  public $link = true;
  
   public function __construct($userrecord, array $options=null) {
+
   public function __construct(stdclass $userrecord, array $options=null) {
 
       // ...
 
       // ...
 
   }
 
   }
Line 69: Line 92:
 
</code>
 
</code>
  
Developer has two options, either call
+
There are two ways how a developer can render the user picture. Either using a one-line style:
<code php>echo $OUTPUT->user_picture($user);</code>
+
<code php>
or create new instance:  
+
$user = $DB->get_record('user', array(...));
 +
echo $OUTPUT->user_picture($user, array('courseid' => $courseid, 'link' => true));
 +
</code>
 +
 
 +
or an explicit stye:
 
<code php>
 
<code php>
$picture = new user_picture($user);
+
$user = $DB->get_record('user', array(...));
echo $OUTPUT->render($picture);
+
$avatar = new user_picture($user);
 +
$avatar->courseid = $courseid;
 +
$avatar->link = true;
 +
echo $OUTPUT->render($avatar);
 
</code>
 
</code>
The point is that the first is usually one line only, it is also possible to have multiple helpers with different parameters. The second options enables more options without cluttering the simple API with tens of optional method parameters.
 
  
==core_renderer_cli==
+
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.
 
''_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==
+
== 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.
 +
<code php>
 +
// in renderer.php:
 +
class mod_workshop_renderer extends plugin_renderer_base {
 +
 
 +
    public method submission(stdclass $submission) {
 +
        $out  = $this->output->heading(format_string($submission->title));
 +
        $out .= $this->output->container(format_text($submission->content, FORMAT_HTML));
 +
        return $out;
 +
    }
 +
}
 +
 
 +
// in view.php
 +
$submission = get_record('workshop_submissions', array(...));
 +
$wsoutput = $PAGE->get_renderer('mod_workshop');
 +
echo $OUTPUT->header();
 +
echo $wsoutput->submission($submission);
 +
echo $OUTPUT->footer();
 +
</code>
  
==Plugin renderers==
+
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.
  
Plugin renderers are stored in renderer.php file in the same folder as the lib.php file of each plugin.
+
''Note:'' if we follow the core_renderer rules strictly, there should be workshop_submission class defined to contain all information about the submission widget. Applying format_text() and other preparation would then be handled in this widget class. That is a clean way but may be a bit annoying for module developers.
  
==Bootstrap renderer==
+
== Bootstrap renderer ==
  
 
Bootstrap renderer is used as a temporary $OUTPUT before the full initialisation of standard core renderer.
 
Bootstrap renderer is used as a temporary $OUTPUT before the full initialisation of standard core renderer.
  
=Theme customisations=
+
== Theme customisations ==
  
==Theme renderers==
+
=== 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.
 
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=
+
== Low level HTML output ==
  
=See also=
+
== See also ==
 
* [[Theme_changes]]
 
* [[Theme_changes]]
  
 
[[Category:Themes]]
 
[[Category:Themes]]

Revision as of 00:42, 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, custom_corners_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. For example, beside mod_glossary_renderer there can be mod_glossary_faq_renderer and mod_glossary_list_renderer.
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.
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 mod_workshop_renderer extends plugin_renderer_base {
 
    public method submission(stdclass $submission) {
        $out  = $this->output->heading(format_string($submission->title));
        $out .= $this->output->container(format_text($submission->content, FORMAT_HTML));
        return $out;
    }
}
 
// in view.php
$submission = get_record('workshop_submissions', array(...));
$wsoutput = $PAGE->get_renderer('mod_workshop');
echo $OUTPUT->header();
echo $wsoutput->submission($submission);
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.

Note: if we follow the core_renderer rules strictly, there should be workshop_submission class defined to contain all information about the submission widget. Applying format_text() and other preparation would then be handled in this widget class. That is a clean way but may be a bit annoying for module developers.

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