MDL-16438 standardized component names
[moodle.git] / lib / outputlib.php
CommitLineData
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 */
45interface 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 */
80abstract 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 */
154class 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 */
186class 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 */
215class 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 */
282class 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 *
8954245a 338 * Also has methods to facilitate generating HTML output.
339 *
571fa828 340 * @copyright 2009 Tim Hunt
341 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
342 * @since Moodle 2.0
343 */
344class moodle_renderer_base {
345 /** @var xhtml_container_stack the xhtml_container_stack to use. */
346 protected $containerstack;
347
348 /**
349 * Constructor
350 * @param $containerstack the xhtml_container_stack to use.
351 */
352 public function __construct($containerstack) {
353 $this->containerstack = $containerstack;
354 }
8954245a 355
356 protected function output_tag($tagname, $attributes, $contents) {
357 return $this->output_start_tag($tagname, $attributes) . $contents .
358 $this->output_end_tag($tagname);
359 }
360 protected function output_start_tag($tagname, $attributes) {
361 return '<' . $tagname . $this->output_attributes($attributes) . '>';
362 }
363 protected function output_end_tag($tagname) {
364 return '</' . $tagname . '>';
365 }
366 protected function output_empty_tag($tagname, $attributes) {
367 return '<' . $tagname . $this->output_attributes($attributes) . ' />';
368 }
369
370 protected function output_attribute($name, $value) {
371 if ($value || is_numeric($value)) { // We want 0 to be output.
372 return ' ' . $name . '="' . $value . '"';
373 }
374 }
375 protected function output_attributes($attributes) {
376 $output = '';
377 foreach ($attributes as $name => $value) {
378 $output .= $this->output_attribute($name, $value);
379 }
380 return $output;
381 }
382 protected function output_class_attribute($classes) {
383 return $this->output_attribute('class', implode(' ', $classes));
384 }
571fa828 385}
386
387
388/**
389 * This is the templated renderer which copies the API of another class, replacing
390 * all methods calls with instantiation of a template.
391 *
392 * When the method method_name is called, this class will search for a template
393 * called method_name.php in the folders in $searchpaths, taking the first one
394 * that it finds. Then it will set up variables for each of the arguments of that
395 * method, and render the template. This is implemented in the {@link __call()}
396 * PHP magic method.
397 *
398 * Methods like print_box_start and print_box_end are handles specially, and
399 * implemented in terms of the print_box.php method.
400 *
401 * @copyright 2009 Tim Hunt
402 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
403 * @since Moodle 2.0
404 */
405class template_renderer extends moodle_renderer_base {
406 /** @var ReflectionClass information about the class whose API we are copying. */
407 protected $copiedclass;
408 /** @var array of places to search for templates. */
409 protected $searchpaths;
410
411 /**
412 * Magic word used when breaking apart container templates to implement
413 * _start and _end methods.
414 */
415 const contentstoken = '-@#-Contents-go-here-#@-';
416
417 /**
418 * Constructor
419 * @param string $copiedclass the name of a class whose API we should be copying.
420 * @param $searchpaths a list of folders to search for templates in.
421 * @param $containerstack the xhtml_container_stack to use.
422 */
423 public function __construct($copiedclass, $searchpaths, $containerstack) {
424 parent::__construct($containerstack);
425 $this->copiedclass = new ReflectionClass($copiedclass);
426 $this->searchpaths = $searchpaths;
427 }
428
429 /* PHP magic method implementation. */
430 public function __call($method, $arguments) {
431 if (substr($method, -6) == '_start') {
432 return $this->process_start(substr($method, 0, -6), $arguments);
433 } else if (substr($method, -4) == '_end') {
434 return $this->process_end(substr($method, 0, -4), $arguments);
435 } else {
436 return $this->process_template($method, $arguments);
437 }
438 }
439
440 /**
441 * Render the template for a given method of the renderer class we are copying,
442 * using the arguments passed.
443 * @param string $method the method that was called.
444 * @param array $arguments the arguments that were passed to it.
445 * @return string the HTML to be output.
446 */
447 protected function process_template($method, $arguments) {
448 if (!$this->copiedclass->hasMethod($method) ||
449 !$this->copiedclass->getMethod($method)->isPublic()) {
450 throw new coding_exception('Unknown method ' . $method);
451 }
452
453 // Find the template file for this method.
454 $template = $this->find_template($method);
455
456 // Use the reflection API to find out what variable names the arguments
457 // should be stored in, and fill in any missing ones with the defaults.
458 $namedarguments = array();
459 $expectedparams = $this->copiedclass->getMethod($method)->getParameters();
460 foreach ($expectedparams as $param) {
461 $paramname = $param->getName();
462 if (!empty($arguments)) {
463 $namedarguments[$paramname] = array_shift($arguments);
464 } else if ($param->isDefaultValueAvailable()) {
465 $namedarguments[$paramname] = $param->getDefaultValue();
466 } else {
467 throw new coding_exception('Missing required argument ' . $paramname);
468 }
469 }
470
471 // Actually render the template.
472 return $this->render_template($template, $namedarguments);
473 }
474
475 /**
476 * Actually do the work of rendering the template.
477 * @param $_template the full path to the template file.
478 * @param $_namedarguments an array variable name => value, the variables
479 * that should be available to the template.
480 * @return string the HTML to be output.
481 */
482 protected function render_template($_template, $_namedarguments) {
483 // Note, we intentionally break the coding guidelines with regards to
484 // local variable names used in this function, so that they do not clash
485 // with the names of any variables being passed to the template.
486
487 // Set up the global variables that the template may wish to access.
488 global $CFG, $PAGE, $THEME;
489
490 // And the parameters from the function call.
491 extract($_namedarguments);
492
493 // Include the template, capturing the output.
494 ob_start();
495 include($_template);
496 $_result = ob_get_contents();
497 ob_end_clean();
498
499 return $_result;
500 }
501
502 /**
503 * Searches the folders in {@link $searchpaths} to try to find a template for
504 * this method name. Throws an exception if one cannot be found.
505 * @param string $method the method name.
506 * @return string the full path of the template to use.
507 */
508 protected function find_template($method) {
509 foreach ($this->searchpaths as $path) {
510 $filename = $path . '/' . $method . '.php';
511 if (file_exists($filename)) {
512 return $filename;
513 }
514 }
515 throw new coding_exception('Cannot find template for ' . $this->copiedclass->getName() . '::' . $method);
516 }
517
518 /**
519 * Handle methods like print_box_start by using the print_box template,
520 * splitting the result, pusing the end onto the stack, then returning the start.
521 * @param string $method the method that was called, with _start stripped off.
522 * @param array $arguments the arguments that were passed to it.
523 * @return string the HTML to be output.
524 */
525 protected function process_start($template, $arguments) {
526 array_unshift($arguments, self::contentstoken);
527 $html = $this->process_template($template, $arguments);
528 list($start, $end) = explode(self::contentstoken, $html, 2);
529 $this->containerstack->push($template, $end);
530 return $start;
531 }
532
533 /**
534 * Handle methods like print_box_end, we just need to pop the end HTML from
535 * the stack.
536 * @param string $method the method that was called, with _end stripped off.
537 * @param array $arguments not used. Assumed to be irrelevant.
538 * @return string the HTML to be output.
539 */
540 protected function process_end($template, $arguments) {
541 return $this->containerstack->pop($template);
542 }
543
544 /**
545 * @return array the list of paths where this class searches for templates.
546 */
547 public function get_search_paths() {
548 return $this->searchpaths;
549 }
550
551 /**
552 * @return string the name of the class whose API we are copying.
553 */
554 public function get_copied_class() {
555 return $this->copiedclass->getName();
556 }
557}
558
559
560/**
561 * This class keeps track of which HTML tags are currently open.
562 *
563 * This makes it much easier to always generate well formed XHTML output, even
564 * if execution terminates abruptly. Any time you output some opening HTML
565 * without the matching closing HTML, you should push the neccessary close tags
566 * onto the stack.
567 *
568 * @copyright 2009 Tim Hunt
569 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
570 * @since Moodle 2.0
571 */
572class xhtml_container_stack {
573 /** @var array stores the list of open containers. */
574 protected $opencontainsers = array();
575
576 /**
577 * Push the close HTML for a recently opened container onto the stack.
578 * @param string $type The type of container. This is checked when {@link pop()}
579 * is called and must match, otherwise a developer debug warning is output.
580 * @param string $closehtml The HTML required to close the container.
581 */
582 public function push($type, $closehtml) {
583 $container = new stdClass;
584 $container->type = $type;
585 $container->closehtml = $closehtml;
586 array_push($this->opencontainsers, $container);
587 }
588
589 /**
590 * Pop the HTML for the next closing container from the stack. The $type
591 * must match the type passed when the container was opened, otherwise a
592 * warning will be output.
593 * @param string $type The type of container.
594 * @return string the HTML requried to close the container.
595 */
596 public function pop($type) {
597 if (empty($this->opencontainsers)) {
598 debugging('There are no more open containers. This suggests there is a nesting problem.', DEBUG_DEVELOPER);
599 return;
600 }
601
602 $container = array_pop($this->opencontainsers);
603 if ($container->type != $type) {
604 debugging('The type of container to be closed (' . $container->type .
605 ') does not match the type of the next open container (' . $type .
606 '). This suggests there is a nesting problem.', DEBUG_DEVELOPER);
607 }
608 return $container->closehtml;
609 }
610
611 /**
612 * Close all but the last open container. This is useful in places like error
613 * handling, where you want to close all the open containers (apart from <body>)
614 * before outputting the error message.
615 * @return string the HTML requried to close any open containers inside <body>.
616 */
617 public function pop_all_but_last() {
618 $output = '';
619 while (count($this->opencontainsers) > 1) {
620 $container = array_pop($this->opencontainsers);
621 $output .= $container->closehtml;
622 }
623 return $output;
624 }
625
626 /**
627 * You can call this function if you want to throw away an instance of this
628 * class without properly emptying the stack (for example, in a unit test).
629 * Calling this method stops the destruct method from outputting a developer
630 * debug warning. After calling this method, the instance can no longer be used.
631 */
632 public function discard() {
633 $this->opencontainsers = null;
634 }
635
636 /**
637 * Emergency fallback. If we get to the end of processing and not all
638 * containers have been closed, output the rest with a developer debug warning.
639 */
640 public function __destruct() {
641 if (empty($this->opencontainsers)) {
642 return;
643 }
644
645 debugging('Some containers were left open. This suggests there is a nesting problem.', DEBUG_DEVELOPER);
646 echo $this->pop_all_but_last();
647 $container = array_pop($this->opencontainsers);
648 echo $container->closehtml;
649 }
650}
651
652
653/**
654 * The standard implementation of the moodle_core_renderer interface.
655 *
656 * @copyright 2009 Tim Hunt
657 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
658 * @since Moodle 2.0
659 */
660class moodle_core_renderer extends moodle_renderer_base {
8954245a 661 public function link_to_popup_window() {
662
663 }
571fa828 664
8954245a 665 public function button_to_popup_window() {
666
667 }
668
669 public function close_window_button($buttontext = null, $reloadopener = false) {
670 if (empty($buttontext)) {
671 $buttontext = get_string('closewindow');
672 }
673 // TODO
674 }
675
676 public function close_window($delay = 0, $reloadopener = false) {
677 // TODO
678 }
679
680 /**
681 * Output a <select> menu.
682 *
683 * You can either call this function with a single moodle_select_menu argument
684 * or, with a list of parameters, in which case those parameters are sent to
685 * the moodle_select_menu constructor.
686 *
687 * @param moodle_select_menu $selectmenu a moodle_select_menu that describes
688 * the select menu you want output.
689 * @return string the HTML for the <select>
690 */
691 public function select_menu($selectmenu) {
692 $selectmenu = clone($selectmenu);
693 $selectmenu->prepare();
694
695 if ($selectmenu->nothinglabel) {
696 $selectmenu->options = array($selectmenu->nothingvalue => $selectmenu->nothinglabel) +
697 $selectmenu->options;
698 }
699
700 if (empty($selectmenu->id)) {
701 $selectmenu->id = 'menu' . str_replace(array('[', ']'), '', $selectmenu->name);
702 }
703
704 $attributes = array(
705 'name' => $selectmenu->name,
706 'id' => $selectmenu->id,
707 'class' => $selectmenu->get_classes_string(),
708 'onchange' => $selectmenu->script,
709 );
710 if ($selectmenu->disabled) {
711 $attributes['disabled'] = 'disabled';
712 }
713 if ($selectmenu->tabindex) {
714 $attributes['tabindex'] = $tabindex;
715 }
716
717 if ($selectmenu->listbox) {
718 if (is_integer($selectmenu->listbox)) {
719 $size = $selectmenu->listbox;
720 } else {
721 $size = min($selectmenu->maxautosize, count($selectmenu->options));
722 }
723 $attributes['size'] = $size;
724 if ($selectmenu->multiple) {
725 $attributes['multiple'] = 'multiple';
726 }
727 }
728
729 $html = $this->output_start_tag('select', $attributes) . "\n";
730 foreach ($selectmenu->options as $value => $label) {
731 $attributes = array('value' => $value);
732 if ((string)$value == (string)$selectmenu->selectedvalue ||
733 (is_array($selectmenu->selectedvalue) && in_array($value, $selectmenu->selectedvalue))) {
734 $attributes['selected'] = 'selected';
735 }
736 $html .= ' ' . $this->output_tag('option', $attributes, s($label)) . "\n";
737 }
738 $html .= $this->output_end_tag('select') . "\n";
739
740 return $html;
741 }
742
743 // TODO choose_from_menu_nested
744
745 // TODO choose_from_radio
746
747 /**
748 * Output an error message. By default wraps the error message in <span class="error">.
749 * If the error message is blank, nothing is output.
750 * @param $message the error message.
751 * @return string the HTML to output.
752 */
753 public function error_text($message) {
754 if (empty($message)) {
755 return '';
756 }
757 return $this->output_tag('span', array('class' => 'error'), $message);
758 }
759}
760
761
762/**
763 * Base class for classes representing HTML elements, like moodle_select_menu.
764 *
765 * Handles the id and class attribues.
766 *
767 * @copyright 2009 Tim Hunt
768 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
769 * @since Moodle 2.0
770 */
771class moodle_html_component {
772 /**
773 * @var string value to use for the id attribute of this HTML tag.
774 */
775 public $id = '';
776 /**
777 * @var array class names to add to this HTML element.
778 */
779 public $classes = array();
780
781 /**
782 * Ensure some class names are an array.
783 * @param mixed $classes either an array of class names or a space-separated
784 * string containing class names.
785 * @return array the class names as an array.
786 */
787 public static function clean_clases($classes) {
788 if (is_array($classes)) {
789 return $classes;
790 } else {
791 return explode(' '. trim($classes));
792 }
793 }
794
795 /**
796 * Set the class name array.
797 * @param mixed $classes either an array of class names or a space-separated
798 * string containing class names.
799 */
800 public function set_classes($classes) {
801 $this->classes = self::clean_clases($classes);
802 }
803
804 /**
805 * Add a class name to the class names array.
806 * @param string $class the new class name to add.
807 */
808 public function add_class($class) {
809 $this->classes[] = $class;
810 }
811
812 /**
813 * Add a whole lot of class names to the class names array.
814 * @param mixed $classes either an array of class names or a space-separated
815 * string containing class names.
816 */
817 public function add_classes($classes) {
818 $this->classes += self::clean_clases($classes);
819 }
820
821 /**
822 * Get the class names as a string.
823 * @return string the class names as a space-separated string. Ready to be put in the class="" attribute.
824 */
825 public function get_classes_string() {
826 return implode(' ', $this->classes);
827 }
828
829 /**
830 * Perform any cleanup or final processing that should be done before an
831 * instance of this class is output.
832 */
833 public function prepare() {
834 $this->classes = array_unique(self::clean_clases($this->classes));
835 }
836}
837
838
839/**
840 * This class hold all the information required to describe a <select> menu that
841 * will be printed by {@link moodle_core_renderer::select_menu()}. (Or by an overrides
842 * version of that method in a subclass.)
843 *
844 * All the fields that are not set by the constructor have sensible defaults, so
845 * you only need to set the properties where you want non-default behaviour.
846 *
847 * @copyright 2009 Tim Hunt
848 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
849 * @since Moodle 2.0
850 */
851class moodle_select_menu extends moodle_html_component {
852 /**
853 * @var array the choices to show in the menu. An array $value => $label.
854 */
855 public $options;
856 /**
857 * @var string the name of this form control. That is, the name of the GET/POST
858 * variable that will be set if this select is submmitted as part of a form.
859 */
860 public $name;
861 /**
862 * @var string the option to select initially. Should match one
863 * of the $options array keys. Default none.
864 */
865 public $selectedvalue;
866 /**
867 * @var string The label for the 'nothing is selected' option.
868 * Defaults to get_string('choosedots').
869 * Set this to '' if you do not want a 'nothing is selected' option.
870 */
871 public $nothinglabel = null;
872 /**
873 * @var string The value returned by the 'nothing is selected' option. Defaults to 0.
874 */
875 public $nothingvalue = 0;
876 /**
877 * @var boolean set this to true if you want the control to appear disabled.
878 */
879 public $disabled = false;
880 /**
881 * @var integer if non-zero, sets the tabindex attribute on the <select> element. Default 0.
882 */
883 public $tabindex = 0;
884 /**
885 * @var mixed Defaults to false, which means display the select as a dropdown menu.
886 * If true, display this select as a list box whose size is chosen automatically.
887 * If an integer, display as list box of that size.
888 */
889 public $listbox = false;
890 /**
891 * @var integer if you are using $listbox === true to get an automatically
892 * sized list box, the size of the list box will be the number of options,
893 * or this number, whichever is smaller.
894 */
895 public $maxautosize = 10;
896 /**
897 * @var boolean if true, allow multiple selection. Only used if $listbox is true.
898 */
899 public $multiple = false;
900 /**
901 * @deprecated
902 * @var string JavaScript to add as an onchange attribute. Do not use this.
903 * Use the YUI even library instead.
904 */
905 public $script = '';
906
907 /* @see lib/moodle_html_component#prepare() */
908 public function prepare() {
909 if (empty($this->id)) {
910 $this->id = 'menu' . str_replace(array('[', ']'), '', $this->name);
911 }
912 if (empty($this->classes)) {
913 $this->set_classes(array('menu' . str_replace(array('[', ']'), '', $this->name)));
914 }
915 $this->add_class('select');
916 parent::prepare();
917 }
918
919 /**
920 * This is a shortcut for making a simple select menu. It lets you specify
921 * the options, name and selected option in one line of code.
922 * @param array $options used to initialise {@link $options}.
923 * @param string $name used to initialise {@link $name}.
924 * @param string $selected used to initialise {@link $selected}.
925 * @return moodle_select_menu A moodle_select_menu object with the three common fields initialised.
926 */
927 public static function make($options, $name, $selected = '') {
928 $menu = new moodle_select_menu();
929 $menu->options = $options;
930 $menu->name = $name;
931 $menu->selectedvalue = $selected;
932 return $menu;
933 }
934
935 /**
936 * This is a shortcut for making a yes/no select menu.
937 * @param string $name used to initialise {@link $name}.
938 * @param string $selected used to initialise {@link $selected}.
939 * @return moodle_select_menu A menu initialised with yes/no options.
940 */
941 public static function make_yes_no($name, $selected) {
942 return self::make(array(0 => get_string('no'), 1 => get_string('yes')), $name, $selected);
943 }
571fa828 944}
945
946
947/**
948 * A renderer for the custom corner theme, and other themes based on it.
949 *
950 * Generates the slightly different HTML that the custom corners theme wants.
951 *
952 * @copyright 2009 Tim Hunt
953 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
954 * @since Moodle 2.0
955 */
956class custom_corners_core_renderer extends moodle_core_renderer {
957
958 // TODO
959}
960