2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Interface and classes for creating appropriate renderers for various parts of Moodle.
20 * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML
23 * @copyright 2009 Tim Hunt
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
29 defined('MOODLE_INTERNAL') ||
die();
31 /** General rendering target, usually normal browser page */
32 define('RENDERER_TARGET_GENERAL', 'general');
34 /** Plain text rendering for CLI scripts and cron */
35 define('RENDERER_TARGET_CLI', 'cli');
37 /** Plain text rendering for Ajax scripts*/
38 define('RENDERER_TARGET_AJAX', 'ajax');
40 /** Plain text rendering intended for sending via email */
41 define('RENDERER_TARGET_TEXTEMAIL', 'textemail');
43 /** Rich text html rendering intended for sending via email */
44 define('RENDERER_TARGET_HTMLEMAIL', 'htmlemail');
46 // note: maybe we could define portfolio export target too
50 * A renderer factory is just responsible for creating an appropriate renderer
51 * for any given part of Moodle.
53 * Which renderer factory to use is chose by the current theme, and an instance
54 * if created automatically when the theme is set up.
56 * A renderer factory must also have a constructor that takes a theme_config object.
57 * (See {@link renderer_factory_base::__construct} for an example.)
59 * @copyright 2009 Tim Hunt
60 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
65 interface renderer_factory
{
68 * Return the renderer for a particular part of Moodle.
70 * The renderer interfaces are defined by classes called {plugin}_renderer
71 * where {plugin} is the name of the component. The renderers for core Moodle are
72 * defined in lib/renderer.php. For plugins, they will be defined in a file
73 * called renderer.php inside the plugin.
75 * Renderers will normally want to subclass the renderer_base class.
76 * (However, if you really know what you are doing, you don't have to do that.)
78 * There is no separate interface definition for renderers. The default
79 * {plugin}_renderer implementation also serves to define the API for
80 * other implementations of the interface, whether or not they subclass it.
82 * A particular plugin can define multiple renderers if it wishes, using the
83 * $subtype parameter. For example workshop_renderer,
84 * workshop_allocation_manual_renderer etc.
86 * @param moodle_page $page the page the renderer is outputting content for.
87 * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
88 * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
89 * @param string $target one of rendering target constants
90 * @return renderer_base an object implementing the requested renderer interface.
92 public function get_renderer(moodle_page
$page, $component, $subtype=null, $target=null);
97 * This is a base class to help you implement the renderer_factory interface.
99 * It keeps a cache of renderers that have been constructed, so you only need
100 * to construct each one once in you subclass.
102 * It also has a method to get the name of, and include the renderer.php with
103 * the definition of, the standard renderer class for a given module.
105 * @copyright 2009 Tim Hunt
106 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
111 abstract class renderer_factory_base
implements renderer_factory
{
113 * @var theme_config The theme we belong to.
120 * @param theme_config $theme the theme we belong to.
122 public function __construct(theme_config
$theme) {
123 $this->theme
= $theme;
127 * Returns suffix of renderer class expected for given target.
129 * @param string $target one of the renderer target constants, target is guessed if null used
130 * @return array two element array, first element is target, second the target suffix string
132 protected function get_target_suffix($target) {
133 if (empty($target)) {
134 // automatically guessed defaults
136 $target = RENDERER_TARGET_CLI
;
137 } else if (AJAX_SCRIPT
) {
138 $target = RENDERER_TARGET_AJAX
;
143 case RENDERER_TARGET_CLI
: $suffix = '_cli'; break;
144 case RENDERER_TARGET_AJAX
: $suffix = '_ajax'; break;
145 case RENDERER_TARGET_TEXTEMAIL
: $suffix = '_textemail'; break;
146 case RENDERER_TARGET_HTMLEMAIL
: $suffix = '_htmlemail'; break;
147 default: $target = RENDERER_TARGET_GENERAL
; $suffix = '';
150 return array($target, $suffix);
154 * For a given module name, return the name of the standard renderer class
155 * that defines the renderer interface for that module.
157 * Also, if it exists, include the renderer.php file for that module, so
158 * the class definition of the default renderer has been loaded.
160 * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
161 * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
162 * @return string the name of the standard renderer class for that module.
164 protected function standard_renderer_classname($component, $subtype = null) {
165 global $CFG; // needed in included files
167 // standardize component name ala frankenstyle
168 list($plugin, $type) = normalize_component($component);
169 if ($type === null) {
170 $component = $plugin;
172 $component = $plugin.'_'.$type;
175 if ($component !== 'core') {
176 // renderers are stored in renderer.php files
177 if (!$compdirectory = get_component_directory($component)) {
178 throw new coding_exception('Invalid component specified in renderer request');
180 $rendererfile = $compdirectory . '/renderer.php';
181 if (file_exists($rendererfile)) {
182 include_once($rendererfile);
185 } else if (!empty($subtype)) {
186 $coresubsystems = get_core_subsystems();
187 if (!isset($coresubsystems[$subtype])) {
188 throw new coding_exception('Invalid core subtype "' . $subtype . '" in renderer request');
190 $rendererfile = $CFG->dirroot
. '/' . $coresubsystems[$subtype] . '/renderer.php';
191 if (file_exists($rendererfile)) {
192 include_once($rendererfile);
196 if (empty($subtype)) {
197 $class = $component . '_renderer';
199 $class = $component . '_' . $subtype . '_renderer';
206 * This is the default renderer factory for Moodle.
208 * It simply returns an instance of the appropriate standard renderer class.
210 * @copyright 2009 Tim Hunt
211 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
216 class standard_renderer_factory
extends renderer_factory_base
{
219 * Implement the subclass method
221 * @param moodle_page $page the page the renderer is outputting content for.
222 * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
223 * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
224 * @param string $target one of rendering target constants
225 * @return renderer_base an object implementing the requested renderer interface.
227 public function get_renderer(moodle_page
$page, $component, $subtype = null, $target = null) {
228 $classname = $this->standard_renderer_classname($component, $subtype);
229 if (!class_exists($classname)) {
230 throw new coding_exception('Request for an unknown renderer class ' . $classname);
233 list($target, $suffix) = $this->get_target_suffix($target);
234 if (class_exists($classname . $suffix)) {
235 // use the specialised renderer for given target, default renderer might also decide
236 // to implement support for more targets
237 $classname = $classname . $suffix;
240 return new $classname($page, $target);
246 * This is renderer factory allows themes to override the standard renderers using php code.
248 * It will load any code from theme/mytheme/renderers.php and
249 * theme/parenttheme/renderers.php, if then exist. Then whenever you ask for
250 * a renderer for 'component', it will create a mytheme_component_renderer or a
251 * parenttheme_component_renderer, instead of a component_renderer,
252 * if either of those classes exist.
254 * @copyright 2009 Tim Hunt
255 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
260 class theme_overridden_renderer_factory
extends renderer_factory_base
{
263 * @var array An array of renderer prefixes
265 protected $prefixes = array();
269 * @param theme_config $theme the theme we are rendering for.
271 public function __construct(theme_config
$theme) {
272 parent
::__construct($theme);
273 // Initialise $this->prefixes.
274 $this->prefixes
= $theme->renderer_prefixes();
278 * Implement the subclass method
280 * @param moodle_page $page the page the renderer is outputting content for.
281 * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
282 * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
283 * @param string $target one of rendering target constants
284 * @return renderer_base an object implementing the requested renderer interface.
286 public function get_renderer(moodle_page
$page, $component, $subtype = null, $target = null) {
287 $classname = $this->standard_renderer_classname($component, $subtype);
288 if (!class_exists($classname)) {
289 // standard renderer must always exist
290 throw new coding_exception('Request for an unknown renderer class ' . $classname);
293 list($target, $suffix) = $this->get_target_suffix($target);
295 // theme lib.php and renderers.php files are loaded automatically
296 // when loading the theme configs
298 // first try the renderers with correct suffix
299 foreach ($this->prefixes
as $prefix) {
300 if (class_exists($prefix . '_' . $classname . $suffix)) {
301 $classname = $prefix . '_' . $classname . $suffix;
302 return new $classname($page, $target);
305 if (class_exists($classname . $suffix)) {
306 // use the specialised renderer for given target, default renderer might also decide
307 // to implement support for more targets
308 $classname = $classname . $suffix;
309 return new $classname($page, $target);
312 // then try general renderer
313 foreach ($this->prefixes
as $prefix) {
314 if (class_exists($prefix . '_' . $classname)) {
315 $classname = $prefix . '_' . $classname;
316 return new $classname($page, $target);
320 return new $classname($page, $target);