571fa828 |
1 | <?php |
2 | |
3 | // This file is part of Moodle - http://moodle.org/ |
4 | // |
5 | // Moodle is free software: you can redistribute it and/or modify |
6 | // it under the terms of the GNU General Public License as published by |
7 | // the Free Software Foundation, either version 3 of the License, or |
8 | // (at your option) any later version. |
9 | // |
10 | // Moodle is distributed in the hope that it will be useful, |
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | // GNU General Public License for more details. |
14 | // |
15 | // You should have received a copy of the GNU General Public License |
16 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. |
17 | |
18 | |
19 | /** |
20 | * Functions for generating the HTML that Moodle should output. |
21 | * |
22 | * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML |
23 | * for an overview. |
24 | * |
25 | * @package moodlecore |
26 | * @copyright 2009 Tim Hunt |
27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later (5) |
28 | */ |
29 | |
30 | |
31 | /** |
32 | * A renderer factory is just responsible for creating an appropriate renderer |
33 | * for any given part of Moodle. |
34 | * |
35 | * Which renderer factory to use is chose by the current theme, and an instance |
36 | * if created automatically when the theme is set up. |
37 | * |
38 | * A renderer factory must also have a constructor that takes a theme object and |
39 | * a moodle_page object. (See {@link renderer_factory_base::__construct} for an example.) |
40 | * |
41 | * @copyright 2009 Tim Hunt |
42 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
43 | * @since Moodle 2.0 |
44 | */ |
45 | interface renderer_factory { |
46 | /** |
47 | * Return the renderer for a particular part of Moodle. |
48 | * |
49 | * The renderer interfaces are defined by classes called moodle_..._renderer |
50 | * where ... is the name of the module, which, will be defined in this file |
51 | * for core parts of Moodle, and in a file called renderer.php for plugins. |
52 | * |
53 | * There is no separate interface definintion for renderers. Instead we |
54 | * take advantage of PHP being a dynamic languages. The renderer returned |
55 | * does not need to be a subclass of the moodle_..._renderer base class, it |
56 | * just needs to impmenent the same interface. This is sometimes called |
57 | * 'Duck typing'. For a tricky example, see {@link template_renderer} below. |
58 | * renderer ob |
59 | * |
60 | * @param $module the name of part of moodle. E.g. 'core', 'quiz', 'qtype_multichoice'. |
61 | * @return object an object implementing the requested renderer interface. |
62 | */ |
63 | public function get_renderer($module); |
64 | } |
65 | |
66 | |
67 | /** |
68 | * This is a base class to help you implement the renderer_factory interface. |
69 | * |
70 | * It keeps a cache of renderers that have been constructed, so you only need |
71 | * to construct each one once in you subclass. |
72 | * |
73 | * It also has a method to get the name of, and include the renderer.php with |
74 | * the definition of, the standard renderer class for a given module. |
75 | * |
76 | * @copyright 2009 Tim Hunt |
77 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
78 | * @since Moodle 2.0 |
79 | */ |
80 | abstract class renderer_factory_base implements renderer_factory { |
81 | /** The theme we are rendering for. */ |
82 | protected $theme; |
83 | |
84 | /** The page we are doing output for. */ |
85 | protected $page; |
86 | |
87 | /** Used to cache renderers as they are created. */ |
88 | protected $renderers = array(); |
89 | |
90 | /** |
91 | * Constructor. |
92 | * @param object $theme the theme we are rendering for. |
93 | * @param moodle_page $page the page we are doing output for. |
94 | */ |
95 | public function __construct($theme, $page) { |
96 | $this->theme = $theme; |
97 | $this->page = $page; |
98 | } |
99 | |
100 | /* Implement the interface method. */ |
101 | public function get_renderer($module) { |
102 | // Cache the renderers by module name, and delegate the actual |
103 | // construction to the create_renderer method. |
104 | if (!array_key_exists($module, $this->renderers)) { |
105 | $this->renderers[$module] = $this->create_renderer($module); |
106 | } |
107 | |
108 | return $this->renderers[$module]; |
109 | } |
110 | |
111 | /** |
112 | * Subclasses should override this method to actually create an instance of |
113 | * the appropriate renderer class, based on the module name. That is, |
114 | * this method should implement the same contract as |
115 | * {@link renderer_factory::get_renderer}. |
116 | * |
117 | * @param $module the name of part of moodle. E.g. 'core', 'quiz', 'qtype_multichoice'. |
118 | * @return object an object implementing the requested renderer interface. |
119 | */ |
120 | abstract public function create_renderer($module); |
121 | |
122 | /** |
123 | * For a given module name, return the name of the standard renderer class |
124 | * that defines the renderer interface for that module. |
125 | * |
126 | * Also, if it exists, include the renderer.php file for that module, so |
127 | * the class definition of the default renderer has been loaded. |
128 | * |
129 | * @param string $module the name of part of moodle. E.g. 'core', 'quiz', 'qtype_multichoice'. |
130 | * @return string the name of the standard renderer class for that module. |
131 | */ |
132 | protected function standard_renderer_class_for_module($module) { |
133 | $pluginrenderer = get_plugin_dir($module) . '/renderer.php'; |
134 | if (file_exists($pluginrenderer)) { |
135 | include_once($pluginrenderer); |
136 | } |
137 | $class = 'moodle_' . $module . '_renderer'; |
138 | if (!class_exists($class)) { |
139 | throw new coding_exception('Request for an unknown renderer class ' . $class); |
140 | } |
141 | return $class; |
142 | } |
143 | } |
144 | |
145 | |
146 | /** |
147 | * This is the default renderer factory for Moodle. It simply returns an instance |
148 | * of the appropriate standard renderer class. |
149 | * |
150 | * @copyright 2009 Tim Hunt |
151 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
152 | * @since Moodle 2.0 |
153 | */ |
154 | class standard_renderer_factory extends renderer_factory_base { |
155 | /** |
156 | * Constructor. |
157 | * @param object $theme the theme we are rendering for. |
158 | * @param moodle_page $page the page we are doing output for. |
159 | */ |
160 | public function __construct($theme, $page) { |
161 | parent::__construct($theme, $page); |
162 | } |
163 | |
164 | /* Implement the subclass method. */ |
165 | public function create_renderer($module) { |
166 | if ($module == 'core') { |
167 | return new moodle_core_renderer($this->page->opencontainers); |
168 | } else { |
169 | $class = $this->standard_renderer_class_for_module($module); |
170 | return new $class($this->page->opencontainers, $this->get_renderer('core')); |
171 | } |
172 | } |
173 | } |
174 | |
175 | |
176 | /** |
177 | * This is a slight variatoin on the standard_renderer_factory that uses |
178 | * custom_corners_core_renderer instead of moodle_core_renderer. |
179 | * |
180 | * This generates the slightly different HTML that the custom_corners theme expects. |
181 | * |
182 | * @copyright 2009 Tim Hunt |
183 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
184 | * @since Moodle 2.0 |
185 | */ |
186 | class custom_corners_renderer_factory extends standard_renderer_factory { |
187 | /** |
188 | * Constructor. |
189 | * @param object $theme the theme we are rendering for. |
190 | * @param moodle_page $page the page we are doing output for. |
191 | */ |
192 | public function __construct($theme, $page) { |
193 | parent::__construct($theme, $page); |
194 | $this->renderers = array('core' => new custom_corners_core_renderer($this->page->opencontainers)); |
195 | } |
196 | } |
197 | |
198 | |
199 | /** |
200 | * This is renderer factory allows themes to override the standard renderers using |
201 | * php code. |
202 | * |
203 | * It will load any code from theme/mytheme/renderers.php and |
204 | * theme/parenttheme/renderers.php, if then exist. Then whenever you ask for |
205 | * a renderer for 'component', it will create a mytheme_component_renderer or a |
206 | * parenttheme_component_renderer, instead of a moodle_component_renderer, |
207 | * if either of those classes exist. |
208 | * |
209 | * This generates the slightly different HTML that the custom_corners theme expects. |
210 | * |
211 | * @copyright 2009 Tim Hunt |
212 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
213 | * @since Moodle 2.0 |
214 | */ |
215 | class theme_overridden_renderer_factory extends standard_renderer_factory { |
216 | protected $prefixes = array(); |
217 | |
218 | /** |
219 | * Constructor. |
220 | * @param object $theme the theme we are rendering for. |
221 | * @param moodle_page $page the page we are doing output for. |
222 | */ |
223 | public function __construct($theme, $page) { |
224 | global $CFG; |
225 | parent::__construct($theme, $page); |
226 | |
227 | // Initialise $this->prefixes. |
228 | $renderersfile = $theme->dir . '/renderers.php'; |
229 | if (is_readable($renderersfile)) { |
230 | include_once($renderersfile); |
231 | $this->prefixes[] = $theme->name . '_'; |
232 | } |
233 | if (!empty($theme->parent)) { |
234 | $renderersfile = $CFG->themedir .'/'. $theme->parent . '/renderers.php'; |
235 | if (is_readable($renderersfile)) { |
236 | include_once($renderersfile); |
237 | $this->prefixes[] = $theme->parent . '_'; |
238 | } |
239 | } |
240 | } |
241 | |
242 | /* Implement the subclass method. */ |
243 | public function create_renderer($module) { |
244 | foreach ($this->prefixes as $prefix) { |
245 | $classname = $prefix . $module . '_renderer'; |
246 | if (class_exists($classname)) { |
247 | if ($module == 'core') { |
248 | return new $classname($this->page->opencontainers); |
249 | } else { |
250 | return new $classname($this->page->opencontainers, $this->get_renderer('core')); |
251 | } |
252 | } |
253 | } |
254 | return parent::create_renderer($module); |
255 | } |
256 | } |
257 | |
258 | |
259 | /** |
260 | * This is renderer factory that allows you to create templated themes. |
261 | * |
262 | * This should be considered an experimental proof of concept. In particular, |
263 | * the performance is probably not very good. Do not try to use in on a busy site |
264 | * without doing careful load testing first! |
265 | * |
266 | * This renderer factory returns instances of {@link template_renderer} class |
267 | * which which implement the corresponding renderer interface in terms of |
268 | * templates. To use this your theme must have a templates folder inside it. |
269 | * Then suppose the method moodle_core_renderer::greeting($name = 'world'); |
270 | * exists. Then, a call to $OUTPUT->greeting() will cause the template |
271 | * /theme/yourtheme/templates/core/greeting.php to be rendered, with the variable |
272 | * $name available. The greeting.php template might contain |
273 | * |
274 | * <pre> |
275 | * <h1>Hello <?php echo $name ?>!</h1> |
276 | * </pre> |
277 | * |
278 | * @copyright 2009 Tim Hunt |
279 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
280 | * @since Moodle 2.0 |
281 | */ |
282 | class template_renderer_factory extends renderer_factory_base { |
283 | /** |
284 | * An array of paths of where to search for templates. Normally this theme, |
285 | * the parent theme then the standardtemplate theme. (If some of these do |
286 | * not exist, or are the same as each other, then the list will be shorter. |
287 | */ |
288 | protected $searchpaths = array(); |
289 | |
290 | /** |
291 | * Constructor. |
292 | * @param object $theme the theme we are rendering for. |
293 | * @param moodle_page $page the page we are doing output for. |
294 | */ |
295 | public function __construct($theme, $page) { |
296 | global $CFG; |
297 | parent::__construct($theme, $page); |
298 | |
299 | // Initialise $this->searchpaths. |
300 | if ($theme->name != 'standardtemplate') { |
301 | $templatesdir = $theme->dir . '/templates'; |
302 | if (is_dir($templatesdir)) { |
303 | $this->searchpaths[] = $templatesdir; |
304 | } |
305 | } |
306 | if (!empty($theme->parent)) { |
307 | $templatesdir = $CFG->themedir .'/'. $theme->parent . '/templates'; |
308 | if (is_dir($templatesdir)) { |
309 | $this->searchpaths[] = $templatesdir; |
310 | } |
311 | } |
312 | $this->searchpaths[] = $CFG->themedir .'/standardtemplate/templates'; |
313 | } |
314 | |
315 | /* Implement the subclass method. */ |
316 | public function create_renderer($module) { |
317 | // Refine the list of search paths for this module. |
318 | $searchpaths = array(); |
319 | foreach ($this->searchpaths as $rootpath) { |
320 | $path = $rootpath . '/' . $module; |
321 | if (is_dir($path)) { |
322 | $searchpaths[] = $path; |
323 | } |
324 | } |
325 | |
326 | // Create a template_renderer that copies the API of the standard renderer. |
327 | $copiedclass = $this->standard_renderer_class_for_module($module); |
328 | return new template_renderer($copiedclass, $searchpaths, $this->page->opencontainers); |
329 | } |
330 | } |
331 | |
332 | |
333 | /** |
334 | * Simple base class for Moodle renderers. |
335 | * |
336 | * Tracks the xhtml_container_stack to use, which is passed in in the constructor. |
337 | * |
338 | * @copyright 2009 Tim Hunt |
339 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
340 | * @since Moodle 2.0 |
341 | */ |
342 | class moodle_renderer_base { |
343 | /** @var xhtml_container_stack the xhtml_container_stack to use. */ |
344 | protected $containerstack; |
345 | |
346 | /** |
347 | * Constructor |
348 | * @param $containerstack the xhtml_container_stack to use. |
349 | */ |
350 | public function __construct($containerstack) { |
351 | $this->containerstack = $containerstack; |
352 | } |
353 | } |
354 | |
355 | |
356 | /** |
357 | * This is the templated renderer which copies the API of another class, replacing |
358 | * all methods calls with instantiation of a template. |
359 | * |
360 | * When the method method_name is called, this class will search for a template |
361 | * called method_name.php in the folders in $searchpaths, taking the first one |
362 | * that it finds. Then it will set up variables for each of the arguments of that |
363 | * method, and render the template. This is implemented in the {@link __call()} |
364 | * PHP magic method. |
365 | * |
366 | * Methods like print_box_start and print_box_end are handles specially, and |
367 | * implemented in terms of the print_box.php method. |
368 | * |
369 | * @copyright 2009 Tim Hunt |
370 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
371 | * @since Moodle 2.0 |
372 | */ |
373 | class template_renderer extends moodle_renderer_base { |
374 | /** @var ReflectionClass information about the class whose API we are copying. */ |
375 | protected $copiedclass; |
376 | /** @var array of places to search for templates. */ |
377 | protected $searchpaths; |
378 | |
379 | /** |
380 | * Magic word used when breaking apart container templates to implement |
381 | * _start and _end methods. |
382 | */ |
383 | const contentstoken = '-@#-Contents-go-here-#@-'; |
384 | |
385 | /** |
386 | * Constructor |
387 | * @param string $copiedclass the name of a class whose API we should be copying. |
388 | * @param $searchpaths a list of folders to search for templates in. |
389 | * @param $containerstack the xhtml_container_stack to use. |
390 | */ |
391 | public function __construct($copiedclass, $searchpaths, $containerstack) { |
392 | parent::__construct($containerstack); |
393 | $this->copiedclass = new ReflectionClass($copiedclass); |
394 | $this->searchpaths = $searchpaths; |
395 | } |
396 | |
397 | /* PHP magic method implementation. */ |
398 | public function __call($method, $arguments) { |
399 | if (substr($method, -6) == '_start') { |
400 | return $this->process_start(substr($method, 0, -6), $arguments); |
401 | } else if (substr($method, -4) == '_end') { |
402 | return $this->process_end(substr($method, 0, -4), $arguments); |
403 | } else { |
404 | return $this->process_template($method, $arguments); |
405 | } |
406 | } |
407 | |
408 | /** |
409 | * Render the template for a given method of the renderer class we are copying, |
410 | * using the arguments passed. |
411 | * @param string $method the method that was called. |
412 | * @param array $arguments the arguments that were passed to it. |
413 | * @return string the HTML to be output. |
414 | */ |
415 | protected function process_template($method, $arguments) { |
416 | if (!$this->copiedclass->hasMethod($method) || |
417 | !$this->copiedclass->getMethod($method)->isPublic()) { |
418 | throw new coding_exception('Unknown method ' . $method); |
419 | } |
420 | |
421 | // Find the template file for this method. |
422 | $template = $this->find_template($method); |
423 | |
424 | // Use the reflection API to find out what variable names the arguments |
425 | // should be stored in, and fill in any missing ones with the defaults. |
426 | $namedarguments = array(); |
427 | $expectedparams = $this->copiedclass->getMethod($method)->getParameters(); |
428 | foreach ($expectedparams as $param) { |
429 | $paramname = $param->getName(); |
430 | if (!empty($arguments)) { |
431 | $namedarguments[$paramname] = array_shift($arguments); |
432 | } else if ($param->isDefaultValueAvailable()) { |
433 | $namedarguments[$paramname] = $param->getDefaultValue(); |
434 | } else { |
435 | throw new coding_exception('Missing required argument ' . $paramname); |
436 | } |
437 | } |
438 | |
439 | // Actually render the template. |
440 | return $this->render_template($template, $namedarguments); |
441 | } |
442 | |
443 | /** |
444 | * Actually do the work of rendering the template. |
445 | * @param $_template the full path to the template file. |
446 | * @param $_namedarguments an array variable name => value, the variables |
447 | * that should be available to the template. |
448 | * @return string the HTML to be output. |
449 | */ |
450 | protected function render_template($_template, $_namedarguments) { |
451 | // Note, we intentionally break the coding guidelines with regards to |
452 | // local variable names used in this function, so that they do not clash |
453 | // with the names of any variables being passed to the template. |
454 | |
455 | // Set up the global variables that the template may wish to access. |
456 | global $CFG, $PAGE, $THEME; |
457 | |
458 | // And the parameters from the function call. |
459 | extract($_namedarguments); |
460 | |
461 | // Include the template, capturing the output. |
462 | ob_start(); |
463 | include($_template); |
464 | $_result = ob_get_contents(); |
465 | ob_end_clean(); |
466 | |
467 | return $_result; |
468 | } |
469 | |
470 | /** |
471 | * Searches the folders in {@link $searchpaths} to try to find a template for |
472 | * this method name. Throws an exception if one cannot be found. |
473 | * @param string $method the method name. |
474 | * @return string the full path of the template to use. |
475 | */ |
476 | protected function find_template($method) { |
477 | foreach ($this->searchpaths as $path) { |
478 | $filename = $path . '/' . $method . '.php'; |
479 | if (file_exists($filename)) { |
480 | return $filename; |
481 | } |
482 | } |
483 | throw new coding_exception('Cannot find template for ' . $this->copiedclass->getName() . '::' . $method); |
484 | } |
485 | |
486 | /** |
487 | * Handle methods like print_box_start by using the print_box template, |
488 | * splitting the result, pusing the end onto the stack, then returning the start. |
489 | * @param string $method the method that was called, with _start stripped off. |
490 | * @param array $arguments the arguments that were passed to it. |
491 | * @return string the HTML to be output. |
492 | */ |
493 | protected function process_start($template, $arguments) { |
494 | array_unshift($arguments, self::contentstoken); |
495 | $html = $this->process_template($template, $arguments); |
496 | list($start, $end) = explode(self::contentstoken, $html, 2); |
497 | $this->containerstack->push($template, $end); |
498 | return $start; |
499 | } |
500 | |
501 | /** |
502 | * Handle methods like print_box_end, we just need to pop the end HTML from |
503 | * the stack. |
504 | * @param string $method the method that was called, with _end stripped off. |
505 | * @param array $arguments not used. Assumed to be irrelevant. |
506 | * @return string the HTML to be output. |
507 | */ |
508 | protected function process_end($template, $arguments) { |
509 | return $this->containerstack->pop($template); |
510 | } |
511 | |
512 | /** |
513 | * @return array the list of paths where this class searches for templates. |
514 | */ |
515 | public function get_search_paths() { |
516 | return $this->searchpaths; |
517 | } |
518 | |
519 | /** |
520 | * @return string the name of the class whose API we are copying. |
521 | */ |
522 | public function get_copied_class() { |
523 | return $this->copiedclass->getName(); |
524 | } |
525 | } |
526 | |
527 | |
528 | /** |
529 | * This class keeps track of which HTML tags are currently open. |
530 | * |
531 | * This makes it much easier to always generate well formed XHTML output, even |
532 | * if execution terminates abruptly. Any time you output some opening HTML |
533 | * without the matching closing HTML, you should push the neccessary close tags |
534 | * onto the stack. |
535 | * |
536 | * @copyright 2009 Tim Hunt |
537 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
538 | * @since Moodle 2.0 |
539 | */ |
540 | class xhtml_container_stack { |
541 | /** @var array stores the list of open containers. */ |
542 | protected $opencontainsers = array(); |
543 | |
544 | /** |
545 | * Push the close HTML for a recently opened container onto the stack. |
546 | * @param string $type The type of container. This is checked when {@link pop()} |
547 | * is called and must match, otherwise a developer debug warning is output. |
548 | * @param string $closehtml The HTML required to close the container. |
549 | */ |
550 | public function push($type, $closehtml) { |
551 | $container = new stdClass; |
552 | $container->type = $type; |
553 | $container->closehtml = $closehtml; |
554 | array_push($this->opencontainsers, $container); |
555 | } |
556 | |
557 | /** |
558 | * Pop the HTML for the next closing container from the stack. The $type |
559 | * must match the type passed when the container was opened, otherwise a |
560 | * warning will be output. |
561 | * @param string $type The type of container. |
562 | * @return string the HTML requried to close the container. |
563 | */ |
564 | public function pop($type) { |
565 | if (empty($this->opencontainsers)) { |
566 | debugging('There are no more open containers. This suggests there is a nesting problem.', DEBUG_DEVELOPER); |
567 | return; |
568 | } |
569 | |
570 | $container = array_pop($this->opencontainsers); |
571 | if ($container->type != $type) { |
572 | debugging('The type of container to be closed (' . $container->type . |
573 | ') does not match the type of the next open container (' . $type . |
574 | '). This suggests there is a nesting problem.', DEBUG_DEVELOPER); |
575 | } |
576 | return $container->closehtml; |
577 | } |
578 | |
579 | /** |
580 | * Close all but the last open container. This is useful in places like error |
581 | * handling, where you want to close all the open containers (apart from <body>) |
582 | * before outputting the error message. |
583 | * @return string the HTML requried to close any open containers inside <body>. |
584 | */ |
585 | public function pop_all_but_last() { |
586 | $output = ''; |
587 | while (count($this->opencontainsers) > 1) { |
588 | $container = array_pop($this->opencontainsers); |
589 | $output .= $container->closehtml; |
590 | } |
591 | return $output; |
592 | } |
593 | |
594 | /** |
595 | * You can call this function if you want to throw away an instance of this |
596 | * class without properly emptying the stack (for example, in a unit test). |
597 | * Calling this method stops the destruct method from outputting a developer |
598 | * debug warning. After calling this method, the instance can no longer be used. |
599 | */ |
600 | public function discard() { |
601 | $this->opencontainsers = null; |
602 | } |
603 | |
604 | /** |
605 | * Emergency fallback. If we get to the end of processing and not all |
606 | * containers have been closed, output the rest with a developer debug warning. |
607 | */ |
608 | public function __destruct() { |
609 | if (empty($this->opencontainsers)) { |
610 | return; |
611 | } |
612 | |
613 | debugging('Some containers were left open. This suggests there is a nesting problem.', DEBUG_DEVELOPER); |
614 | echo $this->pop_all_but_last(); |
615 | $container = array_pop($this->opencontainsers); |
616 | echo $container->closehtml; |
617 | } |
618 | } |
619 | |
620 | |
621 | /** |
622 | * The standard implementation of the moodle_core_renderer interface. |
623 | * |
624 | * @copyright 2009 Tim Hunt |
625 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
626 | * @since Moodle 2.0 |
627 | */ |
628 | class moodle_core_renderer extends moodle_renderer_base { |
629 | |
630 | // TODO |
631 | } |
632 | |
633 | |
634 | /** |
635 | * A renderer for the custom corner theme, and other themes based on it. |
636 | * |
637 | * Generates the slightly different HTML that the custom corners theme wants. |
638 | * |
639 | * @copyright 2009 Tim Hunt |
640 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
641 | * @since Moodle 2.0 |
642 | */ |
643 | class custom_corners_core_renderer extends moodle_core_renderer { |
644 | |
645 | // TODO |
646 | } |
647 | |