d9c8f425 |
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 | * Classes for rendering HTML output for Moodle. |
20 | * |
21 | * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML |
22 | * for an overview. |
23 | * |
24 | * @package moodlecore |
25 | * @copyright 2009 Tim Hunt |
26 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
27 | */ |
28 | |
29 | /** |
30 | * Simple base class for Moodle renderers. |
31 | * |
32 | * Tracks the xhtml_container_stack to use, which is passed in in the constructor. |
33 | * |
34 | * Also has methods to facilitate generating HTML output. |
35 | * |
36 | * @copyright 2009 Tim Hunt |
37 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
38 | * @since Moodle 2.0 |
39 | */ |
40 | class moodle_renderer_base { |
41 | /** @var xhtml_container_stack the xhtml_container_stack to use. */ |
42 | protected $opencontainers; |
43 | /** @var moodle_page the page we are rendering for. */ |
44 | protected $page; |
45 | |
46 | /** |
47 | * Constructor |
48 | * @param moodle_page $page the page we are doing output for. |
49 | */ |
50 | public function __construct($page) { |
51 | $this->opencontainers = $page->opencontainers; |
52 | $this->page = $page; |
53 | } |
54 | |
55 | /** |
56 | * Have we started output yet? |
57 | * @return boolean true if the header has been printed. |
58 | */ |
59 | public function has_started() { |
60 | return $this->page->state >= moodle_page::STATE_IN_BODY; |
61 | } |
62 | |
63 | /** |
64 | * Outputs a tag with attributes and contents |
65 | * @param string $tagname The name of tag ('a', 'img', 'span' etc.) |
66 | * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.) |
67 | * @param string $contents What goes between the opening and closing tags |
68 | * @return string HTML fragment |
69 | */ |
70 | protected function output_tag($tagname, $attributes, $contents) { |
71 | return $this->output_start_tag($tagname, $attributes) . $contents . |
72 | $this->output_end_tag($tagname); |
73 | } |
74 | |
75 | /** |
76 | * Outputs an opening tag with attributes |
77 | * @param string $tagname The name of tag ('a', 'img', 'span' etc.) |
78 | * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.) |
79 | * @return string HTML fragment |
80 | */ |
81 | protected function output_start_tag($tagname, $attributes) { |
82 | return '<' . $tagname . $this->output_attributes($attributes) . '>'; |
83 | } |
84 | |
85 | /** |
86 | * Outputs a closing tag |
87 | * @param string $tagname The name of tag ('a', 'img', 'span' etc.) |
88 | * @return string HTML fragment |
89 | */ |
90 | protected function output_end_tag($tagname) { |
91 | return '</' . $tagname . '>'; |
92 | } |
93 | |
94 | /** |
95 | * Outputs an empty tag with attributes |
96 | * @param string $tagname The name of tag ('input', 'img', 'br' etc.) |
97 | * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.) |
98 | * @return string HTML fragment |
99 | */ |
100 | protected function output_empty_tag($tagname, $attributes) { |
101 | return '<' . $tagname . $this->output_attributes($attributes) . ' />'; |
102 | } |
103 | |
104 | /** |
105 | * Outputs a HTML attribute and value |
106 | * @param string $name The name of the attribute ('src', 'href', 'class' etc.) |
107 | * @param string $value The value of the attribute. The value will be escaped with {@link s()} |
108 | * @return string HTML fragment |
109 | */ |
110 | protected function output_attribute($name, $value) { |
111 | if (is_array($value)) { |
112 | debugging("Passed an array for the HTML attribute $name", DEBUG_DEVELOPER); |
113 | } |
114 | |
115 | $value = trim($value); |
116 | if ($value == HTML_ATTR_EMPTY) { |
117 | return ' ' . $name . '=""'; |
118 | } else if ($value || is_numeric($value)) { // We want 0 to be output. |
119 | return ' ' . $name . '="' . s($value) . '"'; |
120 | } |
121 | } |
122 | |
123 | /** |
124 | * Outputs a list of HTML attributes and values |
125 | * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.) |
126 | * The values will be escaped with {@link s()} |
127 | * @return string HTML fragment |
128 | */ |
129 | protected function output_attributes($attributes) { |
130 | if (empty($attributes)) { |
131 | $attributes = array(); |
132 | } |
133 | $output = ''; |
134 | foreach ($attributes as $name => $value) { |
135 | $output .= $this->output_attribute($name, $value); |
136 | } |
137 | return $output; |
138 | } |
139 | |
140 | /** |
141 | * Given an array or space-separated list of classes, prepares and returns the HTML class attribute value |
142 | * @param mixed $classes Space-separated string or array of classes |
143 | * @return string HTML class attribute value |
144 | */ |
145 | public static function prepare_classes($classes) { |
146 | if (is_array($classes)) { |
147 | return implode(' ', array_unique($classes)); |
148 | } |
149 | return $classes; |
150 | } |
151 | |
152 | /** |
153 | * Return the URL for an icon identified as in pre-Moodle 2.0 code. |
154 | * |
155 | * Suppose you have old code like $url = "$CFG->pixpath/i/course.gif"; |
156 | * then old_icon_url('i/course'); will return the equivalent URL that is correct now. |
157 | * |
158 | * @param string $iconname the name of the icon. |
159 | * @return string the URL for that icon. |
160 | */ |
161 | public function old_icon_url($iconname) { |
162 | return $this->page->theme->old_icon_url($iconname); |
163 | } |
164 | |
165 | /** |
166 | * Return the URL for an icon identified as in pre-Moodle 2.0 code. |
167 | * |
168 | * Suppose you have old code like $url = "$CFG->modpixpath/$mod/icon.gif"; |
169 | * then mod_icon_url('icon', $mod); will return the equivalent URL that is correct now. |
170 | * |
171 | * @param string $iconname the name of the icon. |
172 | * @param string $module the module the icon belongs to. |
173 | * @return string the URL for that icon. |
174 | */ |
175 | public function mod_icon_url($iconname, $module) { |
176 | return $this->page->theme->mod_icon_url($iconname, $module); |
177 | } |
178 | |
179 | /** |
180 | * A helper function that takes a moodle_html_component subclass as param. |
181 | * If that component has an id attribute and an array of valid component_action objects, |
182 | * it sets up the appropriate event handlers. |
183 | * |
184 | * @param moodle_html_component $component |
185 | * @return void; |
186 | */ |
187 | protected function prepare_event_handlers(&$component) { |
188 | $actions = $component->get_actions(); |
189 | if (!empty($actions) && is_array($actions) && $actions[0] instanceof component_action) { |
190 | foreach ($actions as $action) { |
191 | if (!empty($action->jsfunction)) { |
192 | $this->page->requires->event_handler($component->id, $action->event, $action->jsfunction, $action->jsfunctionargs); |
193 | } |
194 | } |
195 | } |
196 | } |
197 | |
198 | /** |
199 | * Given a moodle_html_component with height and/or width set, translates them |
200 | * to appropriate CSS rules. |
201 | * |
202 | * @param moodle_html_component $component |
203 | * @return string CSS rules |
204 | */ |
205 | protected function prepare_legacy_width_and_height($component) { |
206 | $output = ''; |
207 | if (!empty($component->height)) { |
208 | // We need a more intelligent way to handle these warnings. If $component->height have come from |
209 | // somewhere in deprecatedlib.php, then there is no point outputting a warning here. |
210 | // debugging('Explicit height given to moodle_html_component leads to inline css. Use a proper CSS class instead.', DEBUG_DEVELOPER); |
211 | $output .= "height: {$component->height}px;"; |
212 | } |
213 | if (!empty($component->width)) { |
214 | // debugging('Explicit width given to moodle_html_component leads to inline css. Use a proper CSS class instead.', DEBUG_DEVELOPER); |
215 | $output .= "width: {$component->width}px;"; |
216 | } |
217 | return $output; |
218 | } |
219 | } |
220 | |
221 | |
222 | /** |
223 | * This is the templated renderer which copies the API of another class, replacing |
224 | * all methods calls with instantiation of a template. |
225 | * |
226 | * When the method method_name is called, this class will search for a template |
227 | * called method_name.php in the folders in $searchpaths, taking the first one |
228 | * that it finds. Then it will set up variables for each of the arguments of that |
229 | * method, and render the template. This is implemented in the {@link __call()} |
230 | * PHP magic method. |
231 | * |
232 | * Methods like print_box_start and print_box_end are handles specially, and |
233 | * implemented in terms of the print_box.php method. |
234 | * |
235 | * @copyright 2009 Tim Hunt |
236 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
237 | * @since Moodle 2.0 |
238 | */ |
239 | class template_renderer extends moodle_renderer_base { |
240 | /** @var ReflectionClass information about the class whose API we are copying. */ |
241 | protected $copiedclass; |
242 | /** @var array of places to search for templates. */ |
243 | protected $searchpaths; |
244 | protected $rendererfactory; |
245 | |
246 | /** |
247 | * Magic word used when breaking apart container templates to implement |
248 | * _start and _end methods. |
249 | */ |
250 | const CONTENTSTOKEN = '-@#-Contents-go-here-#@-'; |
251 | |
252 | /** |
253 | * Constructor |
254 | * @param string $copiedclass the name of a class whose API we should be copying. |
255 | * @param array $searchpaths a list of folders to search for templates in. |
256 | * @param moodle_page $page the page we are doing output for. |
257 | */ |
258 | public function __construct($copiedclass, $searchpaths, $page) { |
259 | parent::__construct($page); |
260 | $this->copiedclass = new ReflectionClass($copiedclass); |
261 | $this->searchpaths = $searchpaths; |
262 | } |
263 | |
264 | /** |
265 | * PHP magic method implementation. Do not use this method directly. |
266 | * @param string $method The method to call |
267 | * @param array $arguments The arguments to pass to the method |
268 | * @return mixed The return value of the called method |
269 | */ |
270 | public function __call($method, $arguments) { |
271 | if (substr($method, -6) == '_start') { |
272 | return $this->process_start(substr($method, 0, -6), $arguments); |
273 | } else if (substr($method, -4) == '_end') { |
274 | return $this->process_end(substr($method, 0, -4), $arguments); |
275 | } else { |
276 | return $this->process_template($method, $arguments); |
277 | } |
278 | } |
279 | |
280 | /** |
281 | * Render the template for a given method of the renderer class we are copying, |
282 | * using the arguments passed. |
283 | * @param string $method the method that was called. |
284 | * @param array $arguments the arguments that were passed to it. |
285 | * @return string the HTML to be output. |
286 | */ |
287 | protected function process_template($method, $arguments) { |
288 | if (!$this->copiedclass->hasMethod($method) || |
289 | !$this->copiedclass->getMethod($method)->isPublic()) { |
290 | throw new coding_exception('Unknown method ' . $method); |
291 | } |
292 | |
293 | // Find the template file for this method. |
294 | $template = $this->find_template($method); |
295 | |
296 | // Use the reflection API to find out what variable names the arguments |
297 | // should be stored in, and fill in any missing ones with the defaults. |
298 | $namedarguments = array(); |
299 | $expectedparams = $this->copiedclass->getMethod($method)->getParameters(); |
300 | foreach ($expectedparams as $param) { |
301 | $paramname = $param->getName(); |
302 | if (!empty($arguments)) { |
303 | $namedarguments[$paramname] = array_shift($arguments); |
304 | } else if ($param->isDefaultValueAvailable()) { |
305 | $namedarguments[$paramname] = $param->getDefaultValue(); |
306 | } else { |
307 | throw new coding_exception('Missing required argument ' . $paramname); |
308 | } |
309 | } |
310 | |
311 | // Actually render the template. |
312 | return $this->render_template($template, $namedarguments); |
313 | } |
314 | |
315 | /** |
316 | * Actually do the work of rendering the template. |
317 | * @param string $_template the full path to the template file. |
318 | * @param array $_namedarguments an array variable name => value, the variables |
319 | * that should be available to the template. |
320 | * @return string the HTML to be output. |
321 | */ |
322 | protected function render_template($_template, $_namedarguments) { |
323 | // Note, we intentionally break the coding guidelines with regards to |
324 | // local variable names used in this function, so that they do not clash |
325 | // with the names of any variables being passed to the template. |
326 | |
327 | global $CFG, $SITE, $THEME, $USER; |
328 | // The next lines are a bit tricky. The point is, here we are in a method |
329 | // of a renderer class, and this object may, or may not, be the same as |
330 | // the global $OUTPUT object. When rendering the template, we want to use |
331 | // this object. However, people writing Moodle code expect the current |
332 | // renderer to be called $OUTPUT, not $this, so define a variable called |
333 | // $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE. |
334 | $OUTPUT = $this; |
335 | $PAGE = $this->page; |
336 | $COURSE = $this->page->course; |
337 | |
338 | // And the parameters from the function call. |
339 | extract($_namedarguments); |
340 | |
341 | // Include the template, capturing the output. |
342 | ob_start(); |
343 | include($_template); |
344 | $_result = ob_get_contents(); |
345 | ob_end_clean(); |
346 | |
347 | return $_result; |
348 | } |
349 | |
350 | /** |
351 | * Searches the folders in {@link $searchpaths} to try to find a template for |
352 | * this method name. Throws an exception if one cannot be found. |
353 | * @param string $method the method name. |
354 | * @return string the full path of the template to use. |
355 | */ |
356 | protected function find_template($method) { |
357 | foreach ($this->searchpaths as $path) { |
358 | $filename = $path . '/' . $method . '.php'; |
359 | if (file_exists($filename)) { |
360 | return $filename; |
361 | } |
362 | } |
363 | throw new coding_exception('Cannot find template for ' . $this->copiedclass->getName() . '::' . $method); |
364 | } |
365 | |
366 | /** |
367 | * Handle methods like print_box_start by using the print_box template, |
368 | * splitting the result, pushing the end onto the stack, then returning the start. |
369 | * @param string $method the method that was called, with _start stripped off. |
370 | * @param array $arguments the arguments that were passed to it. |
371 | * @return string the HTML to be output. |
372 | */ |
373 | protected function process_start($method, $arguments) { |
374 | array_unshift($arguments, self::CONTENTSTOKEN); |
375 | $html = $this->process_template($method, $arguments); |
376 | list($start, $end) = explode(self::CONTENTSTOKEN, $html, 2); |
377 | $this->opencontainers->push($method, $end); |
378 | return $start; |
379 | } |
380 | |
381 | /** |
382 | * Handle methods like print_box_end, we just need to pop the end HTML from |
383 | * the stack. |
384 | * @param string $method the method that was called, with _end stripped off. |
385 | * @param array $arguments not used. Assumed to be irrelevant. |
386 | * @return string the HTML to be output. |
387 | */ |
388 | protected function process_end($method, $arguments) { |
389 | return $this->opencontainers->pop($method); |
390 | } |
391 | |
392 | /** |
393 | * @return array the list of paths where this class searches for templates. |
394 | */ |
395 | public function get_search_paths() { |
396 | return $this->searchpaths; |
397 | } |
398 | |
399 | /** |
400 | * @return string the name of the class whose API we are copying. |
401 | */ |
402 | public function get_copied_class() { |
403 | return $this->copiedclass->getName(); |
404 | } |
405 | } |
406 | |
407 | /** |
408 | * The standard implementation of the moodle_core_renderer interface. |
409 | * |
410 | * @copyright 2009 Tim Hunt |
411 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
412 | * @since Moodle 2.0 |
413 | */ |
414 | class moodle_core_renderer extends moodle_renderer_base { |
415 | /** @var string used in {@link header()}. */ |
416 | const PERFORMANCE_INFO_TOKEN = '%%PERFORMANCEINFO%%'; |
417 | /** @var string used in {@link header()}. */ |
418 | const END_HTML_TOKEN = '%%ENDHTML%%'; |
419 | /** @var string used in {@link header()}. */ |
420 | const MAIN_CONTENT_TOKEN = '[MAIN CONTENT GOES HERE]'; |
421 | /** @var string used to pass information from {@link doctype()} to {@link standard_head_html()}. */ |
422 | protected $contenttype; |
423 | /** @var string used by {@link redirect_message()} method to communicate with {@link header()}. */ |
424 | protected $metarefreshtag = ''; |
425 | |
426 | /** |
427 | * Get the DOCTYPE declaration that should be used with this page. Designed to |
428 | * be called in theme layout.php files. |
429 | * @return string the DOCTYPE declaration (and any XML prologue) that should be used. |
430 | */ |
431 | public function doctype() { |
432 | global $CFG; |
433 | |
434 | $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n"; |
435 | $this->contenttype = 'text/html; charset=utf-8'; |
436 | |
437 | if (empty($CFG->xmlstrictheaders)) { |
438 | return $doctype; |
439 | } |
440 | |
441 | // We want to serve the page with an XML content type, to force well-formedness errors to be reported. |
442 | $prolog = '<?xml version="1.0" encoding="utf-8"?>' . "\n"; |
443 | if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'application/xhtml+xml') !== false) { |
444 | // Firefox and other browsers that can cope natively with XHTML. |
445 | $this->contenttype = 'application/xhtml+xml; charset=utf-8'; |
446 | |
447 | } else if (preg_match('/MSIE.*Windows NT/', $_SERVER['HTTP_USER_AGENT'])) { |
448 | // IE can't cope with application/xhtml+xml, but it will cope if we send application/xml with an XSL stylesheet. |
449 | $this->contenttype = 'application/xml; charset=utf-8'; |
450 | $prolog .= '<?xml-stylesheet type="text/xsl" href="' . $CFG->httpswwwroot . '/lib/xhtml.xsl"?>' . "\n"; |
451 | |
452 | } else { |
453 | $prolog = ''; |
454 | } |
455 | |
456 | return $prolog . $doctype; |
457 | } |
458 | |
459 | /** |
460 | * The attributes that should be added to the <html> tag. Designed to |
461 | * be called in theme layout.php files. |
462 | * @return string HTML fragment. |
463 | */ |
464 | public function htmlattributes() { |
465 | return get_html_lang(true) . ' xmlns="http://www.w3.org/1999/xhtml"'; |
466 | } |
467 | |
468 | /** |
469 | * The standard tags (meta tags, links to stylesheets and JavaScript, etc.) |
470 | * that should be included in the <head> tag. Designed to be called in theme |
471 | * layout.php files. |
472 | * @return string HTML fragment. |
473 | */ |
474 | public function standard_head_html() { |
475 | global $CFG; |
476 | $output = ''; |
477 | $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . "\n"; |
478 | $output .= '<meta name="keywords" content="moodle, ' . $this->page->title . '" />' . "\n"; |
479 | if (!$this->page->cacheable) { |
480 | $output .= '<meta http-equiv="pragma" content="no-cache" />' . "\n"; |
481 | $output .= '<meta http-equiv="expires" content="0" />' . "\n"; |
482 | } |
483 | // This is only set by the {@link redirect()} method |
484 | $output .= $this->metarefreshtag; |
485 | |
486 | // Check if a periodic refresh delay has been set and make sure we arn't |
487 | // already meta refreshing |
488 | if ($this->metarefreshtag=='' && $this->page->periodicrefreshdelay!==null) { |
489 | $output .= '<meta http-equiv="refresh" content="'.$this->page->periodicrefreshdelay.';url='.$this->page->url->out().'" />'; |
490 | } |
491 | |
492 | $this->page->requires->js('lib/javascript-static.js')->in_head(); |
493 | $this->page->requires->js('lib/javascript-deprecated.js')->in_head(); |
494 | $this->page->requires->js('lib/javascript-mod.php')->in_head(); |
495 | $this->page->requires->js('lib/overlib/overlib.js')->in_head(); |
496 | $this->page->requires->js('lib/overlib/overlib_cssstyle.js')->in_head(); |
497 | $this->page->requires->js('lib/cookies.js')->in_head(); |
498 | $this->page->requires->js_function_call('setTimeout', Array('fix_column_widths()', 20)); |
499 | |
500 | $focus = $this->page->focuscontrol; |
501 | if (!empty($focus)) { |
502 | if (preg_match("#forms\['([a-zA-Z0-9]+)'\].elements\['([a-zA-Z0-9]+)'\]#", $focus, $matches)) { |
503 | // This is a horrifically bad way to handle focus but it is passed in |
504 | // through messy formslib::moodleform |
505 | $this->page->requires->js_function_call('old_onload_focus', Array($matches[1], $matches[2])); |
506 | } else if (strpos($focus, '.')!==false) { |
507 | // Old style of focus, bad way to do it |
508 | debugging('This code is using the old style focus event, Please update this code to focus on an element id or the moodleform focus method.', DEBUG_DEVELOPER); |
509 | $this->page->requires->js_function_call('old_onload_focus', explode('.', $focus, 2)); |
510 | } else { |
511 | // Focus element with given id |
512 | $this->page->requires->js_function_call('focuscontrol', Array($focus)); |
513 | } |
514 | } |
515 | |
516 | // Add the meta tags from the themes if any were requested. |
517 | $output .= $this->page->theme->get_meta_tags($this->page); |
518 | |
519 | // Get any HTML from the page_requirements_manager. |
520 | $output .= $this->page->requires->get_head_code(); |
521 | |
522 | // List alternate versions. |
523 | foreach ($this->page->alternateversions as $type => $alt) { |
524 | $output .= $this->output_empty_tag('link', array('rel' => 'alternate', |
525 | 'type' => $type, 'title' => $alt->title, 'href' => $alt->url)); |
526 | } |
527 | |
528 | return $output; |
529 | } |
530 | |
531 | /** |
532 | * The standard tags (typically skip links) that should be output just inside |
533 | * the start of the <body> tag. Designed to be called in theme layout.php files. |
534 | * @return string HTML fragment. |
535 | */ |
536 | public function standard_top_of_body_html() { |
537 | return $this->page->requires->get_top_of_body_code(); |
538 | } |
539 | |
540 | /** |
541 | * The standard tags (typically performance information and validation links, |
542 | * if we are in developer debug mode) that should be output in the footer area |
543 | * of the page. Designed to be called in theme layout.php files. |
544 | * @return string HTML fragment. |
545 | */ |
546 | public function standard_footer_html() { |
547 | global $CFG; |
548 | |
549 | // This function is normally called from a layout.php file in {@link header()} |
550 | // but some of the content won't be known until later, so we return a placeholder |
551 | // for now. This will be replaced with the real content in {@link footer()}. |
552 | $output = self::PERFORMANCE_INFO_TOKEN; |
553 | if (!empty($CFG->debugpageinfo)) { |
554 | $output .= '<div class="performanceinfo">This page is: ' . $this->page->debug_summary() . '</div>'; |
555 | } |
556 | if (!empty($CFG->debugvalidators)) { |
557 | $output .= '<div class="validators"><ul> |
558 | <li><a href="http://validator.w3.org/check?verbose=1&ss=1&uri=' . urlencode(qualified_me()) . '">Validate HTML</a></li> |
559 | <li><a href="http://www.contentquality.com/mynewtester/cynthia.exe?rptmode=-1&url1=' . urlencode(qualified_me()) . '">Section 508 Check</a></li> |
560 | <li><a href="http://www.contentquality.com/mynewtester/cynthia.exe?rptmode=0&warnp2n3e=1&url1=' . urlencode(qualified_me()) . '">WCAG 1 (2,3) Check</a></li> |
561 | </ul></div>'; |
562 | } |
563 | return $output; |
564 | } |
565 | |
566 | /** |
567 | * The standard tags (typically script tags that are not needed earlier) that |
568 | * should be output after everything else, . Designed to be called in theme layout.php files. |
569 | * @return string HTML fragment. |
570 | */ |
571 | public function standard_end_of_body_html() { |
572 | // This function is normally called from a layout.php file in {@link header()} |
573 | // but some of the content won't be known until later, so we return a placeholder |
574 | // for now. This will be replaced with the real content in {@link footer()}. |
575 | echo self::END_HTML_TOKEN; |
576 | } |
577 | |
578 | /** |
579 | * Return the standard string that says whether you are logged in (and switched |
580 | * roles/logged in as another user). |
581 | * @return string HTML fragment. |
582 | */ |
583 | public function login_info() { |
584 | global $USER; |
585 | return user_login_string($this->page->course, $USER); |
586 | } |
587 | |
588 | /** |
589 | * Return the 'back' link that normally appears in the footer. |
590 | * @return string HTML fragment. |
591 | */ |
592 | public function home_link() { |
593 | global $CFG, $SITE; |
594 | |
595 | if ($this->page->pagetype == 'site-index') { |
596 | // Special case for site home page - please do not remove |
597 | return '<div class="sitelink">' . |
598 | '<a title="Moodle ' . $CFG->release . '" href="http://moodle.org/">' . |
599 | '<img style="width:100px;height:30px" src="' . $CFG->httpswwwroot . '/pix/moodlelogo.gif" alt="moodlelogo" /></a></div>'; |
600 | |
601 | } else if (!empty($CFG->target_release) && $CFG->target_release != $CFG->release) { |
602 | // Special case for during install/upgrade. |
603 | return '<div class="sitelink">'. |
604 | '<a title="Moodle ' . $CFG->target_release . '" href="http://docs.moodle.org/en/Administrator_documentation" onclick="this.target=\'_blank\'">' . |
605 | '<img style="width:100px;height:30px" src="' . $CFG->httpswwwroot . '/pix/moodlelogo.gif" alt="moodlelogo" /></a></div>'; |
606 | |
607 | } else if ($this->page->course->id == $SITE->id || strpos($this->page->pagetype, 'course-view') === 0) { |
608 | return '<div class="homelink"><a href="' . $CFG->wwwroot . '/">' . |
609 | get_string('home') . '</a></div>'; |
610 | |
611 | } else { |
612 | return '<div class="homelink"><a href="' . $CFG->wwwroot . '/course/view.php?id=' . $this->page->course->id . '">' . |
613 | format_string($this->page->course->shortname) . '</a></div>'; |
614 | } |
615 | } |
616 | |
617 | /** |
618 | * Redirects the user by any means possible given the current state |
619 | * |
620 | * This function should not be called directly, it should always be called using |
621 | * the redirect function in lib/weblib.php |
622 | * |
623 | * The redirect function should really only be called before page output has started |
624 | * however it will allow itself to be called during the state STATE_IN_BODY |
625 | * |
626 | * @param string $encodedurl The URL to send to encoded if required |
627 | * @param string $message The message to display to the user if any |
628 | * @param int $delay The delay before redirecting a user, if $message has been |
629 | * set this is a requirement and defaults to 3, set to 0 no delay |
630 | * @param boolean $debugdisableredirect this redirect has been disabled for |
631 | * debugging purposes. Display a message that explains, and don't |
632 | * trigger the redirect. |
633 | * @return string The HTML to display to the user before dying, may contain |
634 | * meta refresh, javascript refresh, and may have set header redirects |
635 | */ |
636 | public function redirect_message($encodedurl, $message, $delay, $debugdisableredirect) { |
637 | global $CFG; |
638 | $url = str_replace('&', '&', $encodedurl); |
639 | |
640 | switch ($this->page->state) { |
641 | case moodle_page::STATE_BEFORE_HEADER : |
642 | // No output yet it is safe to delivery the full arsenal of redirect methods |
643 | if (!$debugdisableredirect) { |
644 | // Don't use exactly the same time here, it can cause problems when both redirects fire at the same time. |
645 | $this->metarefreshtag = '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />'."\n"; |
646 | $this->page->requires->js_function_call('document.location.replace', array($url))->after_delay($delay + 3); |
647 | } |
648 | $output = $this->header(); |
649 | break; |
650 | case moodle_page::STATE_PRINTING_HEADER : |
651 | // We should hopefully never get here |
652 | throw new coding_exception('You cannot redirect while printing the page header'); |
653 | break; |
654 | case moodle_page::STATE_IN_BODY : |
655 | // We really shouldn't be here but we can deal with this |
656 | debugging("You should really redirect before you start page output"); |
657 | if (!$debugdisableredirect) { |
658 | $this->page->requires->js_function_call('document.location.replace', array($url))->after_delay($delay); |
659 | } |
660 | $output = $this->opencontainers->pop_all_but_last(); |
661 | break; |
662 | case moodle_page::STATE_DONE : |
663 | // Too late to be calling redirect now |
664 | throw new coding_exception('You cannot redirect after the entire page has been generated'); |
665 | break; |
666 | } |
667 | $output .= $this->notification($message, 'redirectmessage'); |
668 | $output .= '<a href="'. $encodedurl .'">'. get_string('continue') .'</a>'; |
669 | if ($debugdisableredirect) { |
670 | $output .= '<p><strong>Error output, so disabling automatic redirect.</strong></p>'; |
671 | } |
672 | $output .= $this->footer(); |
673 | return $output; |
674 | } |
675 | |
676 | /** |
677 | * Start output by sending the HTTP headers, and printing the HTML <head> |
678 | * and the start of the <body>. |
679 | * |
680 | * To control what is printed, you should set properties on $PAGE. If you |
681 | * are familiar with the old {@link print_header()} function from Moodle 1.9 |
682 | * you will find that there are properties on $PAGE that correspond to most |
683 | * of the old parameters to could be passed to print_header. |
684 | * |
685 | * Not that, in due course, the remaining $navigation, $menu parameters here |
686 | * will be replaced by more properties of $PAGE, but that is still to do. |
687 | * |
688 | * @param string $navigation legacy, like the old parameter to print_header. Will be |
689 | * removed when there is a $PAGE->... replacement. |
690 | * @param string $menu legacy, like the old parameter to print_header. Will be |
691 | * removed when there is a $PAGE->... replacement. |
692 | * @return string HTML that you must output this, preferably immediately. |
693 | */ |
694 | public function header($navigation = '', $menu='') { |
695 | // TODO remove $navigation and $menu arguments - replace with $PAGE->navigation |
696 | global $USER, $CFG; |
697 | |
698 | $this->page->set_state(moodle_page::STATE_PRINTING_HEADER); |
699 | |
700 | // Find the appropriate page template, based on $this->page->generaltype. |
701 | $templatefile = $this->page->theme->template_for_page($this->page->generaltype); |
702 | if ($templatefile) { |
703 | // Render the template. |
704 | $template = $this->render_page_template($templatefile, $menu, $navigation); |
705 | } else { |
706 | // New style template not found, fall back to using header.html and footer.html. |
707 | $template = $this->handle_legacy_theme($navigation, $menu); |
708 | } |
709 | |
710 | // Slice the template output into header and footer. |
711 | $cutpos = strpos($template, self::MAIN_CONTENT_TOKEN); |
712 | if ($cutpos === false) { |
713 | throw new coding_exception('Layout template ' . $templatefile . |
714 | ' does not contain the string "' . self::MAIN_CONTENT_TOKEN . '".'); |
715 | } |
716 | $header = substr($template, 0, $cutpos); |
717 | $footer = substr($template, $cutpos + strlen(self::MAIN_CONTENT_TOKEN)); |
718 | |
719 | if (empty($this->contenttype)) { |
720 | debugging('The layout template did not call $OUTPUT->doctype()'); |
721 | $this->doctype(); |
722 | } |
723 | |
724 | send_headers($this->contenttype, $this->page->cacheable); |
725 | $this->opencontainers->push('header/footer', $footer); |
726 | $this->page->set_state(moodle_page::STATE_IN_BODY); |
727 | return $header . $this->skip_link_target(); |
728 | } |
729 | |
730 | /** |
731 | * Renders and outputs the page template. |
732 | * @param string $templatefile The name of the template's file |
733 | * @param array $menu The menu that will be used in the included file |
734 | * @param array $navigation The navigation that will be used in the included file |
735 | * @return string HTML code |
736 | */ |
737 | protected function render_page_template($templatefile, $menu, $navigation) { |
738 | global $CFG, $SITE, $THEME, $USER; |
739 | // The next lines are a bit tricky. The point is, here we are in a method |
740 | // of a renderer class, and this object may, or may not, be the same as |
741 | // the global $OUTPUT object. When rendering the template, we want to use |
742 | // this object. However, people writing Moodle code expect the current |
743 | // renderer to be called $OUTPUT, not $this, so define a variable called |
744 | // $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE. |
745 | $OUTPUT = $this; |
746 | $PAGE = $this->page; |
747 | $COURSE = $this->page->course; |
748 | |
749 | ob_start(); |
750 | include($templatefile); |
751 | $template = ob_get_contents(); |
752 | ob_end_clean(); |
753 | return $template; |
754 | } |
755 | |
756 | /** |
757 | * Renders and outputs a legacy template. |
758 | * @param array $navigation The navigation that will be used in the included file |
759 | * @param array $menu The menu that will be used in the included file |
760 | * @return string HTML code |
761 | */ |
762 | protected function handle_legacy_theme($navigation, $menu) { |
763 | global $CFG, $SITE, $USER; |
764 | // Set a pretend global from the properties of this class. |
765 | // See the comment in render_page_template for a fuller explanation. |
766 | $COURSE = $this->page->course; |
767 | $THEME = $this->page->theme; |
768 | |
769 | // Set up local variables that header.html expects. |
770 | $direction = $this->htmlattributes(); |
771 | $title = $this->page->title; |
772 | $heading = $this->page->heading; |
773 | $focus = $this->page->focuscontrol; |
774 | $button = $this->page->button; |
775 | $pageid = $this->page->pagetype; |
776 | $pageclass = $this->page->bodyclasses; |
777 | $bodytags = ' class="' . $pageclass . '" id="' . $pageid . '"'; |
778 | $home = $this->page->generaltype == 'home'; |
779 | |
780 | $meta = $this->standard_head_html(); |
781 | // The next line is a nasty hack. having set $meta to standard_head_html, we have already |
782 | // got the contents of include($CFG->javascript). However, legacy themes are going to |
783 | // include($CFG->javascript) again. We want to make sure that when they do, nothing is output. |
784 | $CFG->javascript = $CFG->libdir . '/emptyfile.php'; |
785 | |
786 | // Set up local variables that footer.html expects. |
787 | $homelink = $this->home_link(); |
788 | $loggedinas = $this->login_info(); |
789 | $course = $this->page->course; |
790 | $performanceinfo = self::PERFORMANCE_INFO_TOKEN; |
791 | |
792 | if (!$menu && $navigation) { |
793 | $menu = $loggedinas; |
794 | } |
795 | |
796 | if (!empty($this->page->theme->layouttable)) { |
797 | $lt = $this->page->theme->layouttable; |
798 | } else { |
799 | $lt = array('left', 'middle', 'right'); |
800 | } |
801 | |
802 | if (!empty($this->page->theme->block_l_max_width)) { |
803 | $preferredwidthleft = $this->page->theme->block_l_max_width; |
804 | } else { |
805 | $preferredwidthleft = 210; |
806 | } |
807 | if (!empty($this->page->theme->block_r_max_width)) { |
808 | $preferredwidthright = $this->page->theme->block_r_max_width; |
809 | } else { |
810 | $preferredwidthright = 210; |
811 | } |
812 | |
813 | ob_start(); |
814 | include($this->page->theme->dir . '/header.html'); |
815 | |
816 | echo '<table id="layout-table"><tr>'; |
817 | foreach ($lt as $column) { |
818 | if ($column == 'left' && $this->page->blocks->region_has_content(BLOCK_POS_LEFT, $this)) { |
819 | echo '<td id="left-column" class="block-region" style="width: ' . $preferredwidthright . 'px; vertical-align: top;">'; |
820 | echo $this->container_start(); |
821 | echo $this->blocks_for_region(BLOCK_POS_LEFT); |
822 | echo $this->container_end(); |
823 | echo '</td>'; |
824 | |
825 | } else if ($column == 'middle') { |
826 | echo '<td id="middle-column" style="vertical-align: top;">'; |
827 | echo $this->container_start(); |
828 | echo $this->skip_link_target(); |
829 | echo self::MAIN_CONTENT_TOKEN; |
830 | echo $this->container_end(); |
831 | echo '</td>'; |
832 | |
833 | } else if ($column == 'right' && $this->page->blocks->region_has_content(BLOCK_POS_RIGHT, $this)) { |
834 | echo '<td id="right-column" class="block-region" style="width: ' . $preferredwidthright . 'px; vertical-align: top;">'; |
835 | echo $this->container_start(); |
836 | echo $this->blocks_for_region(BLOCK_POS_RIGHT); |
837 | echo $this->container_end(); |
838 | echo '</td>'; |
839 | } |
840 | } |
841 | echo '</tr></table>'; |
842 | |
843 | $menu = str_replace('navmenu', 'navmenufooter', $menu); |
844 | include($THEME->dir . '/footer.html'); |
845 | |
846 | $output = ob_get_contents(); |
847 | ob_end_clean(); |
848 | |
849 | // Put in the start of body code. Bit of a hack, put it in before the first |
850 | // <div or <table. |
851 | $divpos = strpos($output, '<div'); |
852 | $tablepos = strpos($output, '<table'); |
853 | if ($divpos === false || ($tablepos !== false && $tablepos < $divpos)) { |
854 | $pos = $tablepos; |
855 | } else { |
856 | $pos = $divpos; |
857 | } |
858 | $output = substr($output, 0, $divpos) . $this->standard_top_of_body_html() . |
859 | substr($output, $divpos); |
860 | |
861 | // Put in the end token before the end of body. |
862 | $output = str_replace('</body>', self::END_HTML_TOKEN . '</body>', $output); |
863 | |
864 | // Make sure we use the correct doctype. |
865 | $output = preg_replace('/(<!DOCTYPE.+?>)/s', $this->doctype(), $output); |
866 | |
867 | return $output; |
868 | } |
869 | |
870 | /** |
871 | * Outputs the page's footer |
872 | * @return string HTML fragment |
873 | */ |
874 | public function footer() { |
875 | $output = $this->opencontainers->pop_all_but_last(true); |
876 | |
877 | $footer = $this->opencontainers->pop('header/footer'); |
878 | |
879 | // Provide some performance info if required |
880 | $performanceinfo = ''; |
881 | if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) { |
882 | $perf = get_performance_info(); |
883 | if (defined('MDL_PERFTOLOG') && !function_exists('register_shutdown_function')) { |
884 | error_log("PERF: " . $perf['txt']); |
885 | } |
886 | if (defined('MDL_PERFTOFOOT') || debugging() || $CFG->perfdebug > 7) { |
887 | $performanceinfo = $perf['html']; |
888 | } |
889 | } |
890 | $footer = str_replace(self::PERFORMANCE_INFO_TOKEN, $performanceinfo, $footer); |
891 | |
892 | $footer = str_replace(self::END_HTML_TOKEN, $this->page->requires->get_end_code(), $footer); |
893 | |
894 | $this->page->set_state(moodle_page::STATE_DONE); |
895 | |
896 | |
897 | return $output . $footer; |
898 | } |
899 | |
900 | /** |
901 | * Output the row of editing icons for a block, as defined by the controls array. |
902 | * @param array $controls an array like {@link block_contents::$controls}. |
903 | * @return HTML fragment. |
904 | */ |
905 | public function block_controls($controls) { |
906 | if (empty($controls)) { |
907 | return ''; |
908 | } |
909 | $controlshtml = array(); |
910 | foreach ($controls as $control) { |
911 | $controlshtml[] = $this->output_tag('a', array('class' => 'icon', |
912 | 'title' => $control['caption'], 'href' => $control['url']), |
913 | $this->output_empty_tag('img', array('src' => $this->old_icon_url($control['icon']), |
914 | 'alt' => $control['caption']))); |
915 | } |
916 | return $this->output_tag('div', array('class' => 'commands'), implode('', $controlshtml)); |
917 | } |
918 | |
919 | /** |
920 | * Prints a nice side block with an optional header. |
921 | * |
922 | * The content is described |
923 | * by a {@link block_contents} object. |
924 | * |
925 | * @param block_contents $bc HTML for the content |
926 | * @param string $region the region the block is appearing in. |
927 | * @return string the HTML to be output. |
928 | */ |
929 | function block($bc, $region) { |
930 | $bc = clone($bc); // Avoid messing up the object passed in. |
931 | $bc->prepare(); |
932 | |
933 | $skiptitle = strip_tags($bc->title); |
934 | if (empty($skiptitle)) { |
935 | $output = ''; |
936 | $skipdest = ''; |
937 | } else { |
938 | $output = $this->output_tag('a', array('href' => '#sb-' . $bc->skipid, 'class' => 'skip-block'), |
939 | get_string('skipa', 'access', $skiptitle)); |
940 | $skipdest = $this->output_tag('span', array('id' => 'sb-' . $bc->skipid, 'class' => 'skip-block-to'), ''); |
941 | } |
942 | |
943 | $bc->attributes['id'] = $bc->id; |
944 | $bc->attributes['class'] = $bc->get_classes_string(); |
945 | $output .= $this->output_start_tag('div', $bc->attributes); |
946 | |
947 | $controlshtml = $this->block_controls($bc->controls); |
948 | |
949 | $title = ''; |
950 | if ($bc->title) { |
951 | $title = $this->output_tag('h2', null, $bc->title); |
952 | } |
953 | |
954 | if ($title || $controlshtml) { |
955 | $output .= $this->output_tag('div', array('class' => 'header'), |
956 | $this->output_tag('div', array('class' => 'title'), |
957 | $title . $controlshtml)); |
958 | } |
959 | |
960 | $output .= $this->output_start_tag('div', array('class' => 'content')); |
961 | $output .= $bc->content; |
962 | |
963 | if ($bc->footer) { |
964 | $output .= $this->output_tag('div', array('class' => 'footer'), $bc->footer); |
965 | } |
966 | |
967 | $output .= $this->output_end_tag('div'); |
968 | $output .= $this->output_end_tag('div'); |
969 | |
970 | if ($bc->annotation) { |
971 | $output .= $this->output_tag('div', array('class' => 'blockannotation'), $bc->annotation); |
972 | } |
973 | $output .= $skipdest; |
974 | |
975 | $this->init_block_hider_js($bc); |
976 | return $output; |
977 | } |
978 | |
979 | /** |
980 | * Calls the JS require function to hide a block. |
981 | * @param block_contents $bc A block_contents object |
982 | * @return void |
983 | */ |
984 | protected function init_block_hider_js($bc) { |
985 | if ($bc->collapsible != block_contents::NOT_HIDEABLE) { |
986 | $userpref = 'block' . $bc->blockinstanceid . 'hidden'; |
987 | user_preference_allow_ajax_update($userpref, PARAM_BOOL); |
988 | $this->page->requires->yui_lib('dom'); |
989 | $this->page->requires->yui_lib('event'); |
990 | $plaintitle = strip_tags($bc->title); |
991 | $this->page->requires->js_function_call('new block_hider', array($bc->id, $userpref, |
992 | get_string('hideblocka', 'access', $plaintitle), get_string('showblocka', 'access', $plaintitle), |
993 | $this->old_icon_url('t/switch_minus'), $this->old_icon_url('t/switch_plus'))); |
994 | } |
995 | } |
996 | |
997 | /** |
998 | * Render the contents of a block_list. |
999 | * @param array $icons the icon for each item. |
1000 | * @param array $items the content of each item. |
1001 | * @return string HTML |
1002 | */ |
1003 | public function list_block_contents($icons, $items) { |
1004 | $row = 0; |
1005 | $lis = array(); |
1006 | foreach ($items as $key => $string) { |
1007 | $item = $this->output_start_tag('li', array('class' => 'r' . $row)); |
2c5ec833 |
1008 | if (!empty($icons[$key])) { //test if the content has an assigned icon |
1009 | $item .= $this->output_tag('div', array('class' => 'icon column c0'), $icons[$key]); |
d9c8f425 |
1010 | } |
1011 | $item .= $this->output_tag('div', array('class' => 'column c1'), $string); |
1012 | $item .= $this->output_end_tag('li'); |
1013 | $lis[] = $item; |
1014 | $row = 1 - $row; // Flip even/odd. |
1015 | } |
1016 | return $this->output_tag('ul', array('class' => 'list'), implode("\n", $lis)); |
1017 | } |
1018 | |
1019 | /** |
1020 | * Output all the blocks in a particular region. |
1021 | * @param string $region the name of a region on this page. |
1022 | * @return string the HTML to be output. |
1023 | */ |
1024 | public function blocks_for_region($region) { |
1025 | $blockcontents = $this->page->blocks->get_content_for_region($region, $this); |
1026 | |
1027 | $output = ''; |
1028 | foreach ($blockcontents as $bc) { |
1029 | if ($bc instanceof block_contents) { |
1030 | $output .= $this->block($bc, $region); |
1031 | } else if ($bc instanceof block_move_target) { |
1032 | $output .= $this->block_move_target($bc); |
1033 | } else { |
1034 | throw new coding_exception('Unexpected type of thing (' . get_class($bc) . ') found in list of block contents.'); |
1035 | } |
1036 | } |
1037 | return $output; |
1038 | } |
1039 | |
1040 | /** |
1041 | * Output a place where the block that is currently being moved can be dropped. |
1042 | * @param block_move_target $target with the necessary details. |
1043 | * @return string the HTML to be output. |
1044 | */ |
1045 | public function block_move_target($target) { |
1046 | return $this->output_tag('a', array('href' => $target->url, 'class' => 'blockmovetarget'), |
1047 | $this->output_tag('span', array('class' => 'accesshide'), $target->text)); |
1048 | } |
1049 | |
1050 | /** |
1051 | * Given a html_link object, outputs an <a> tag that uses the object's attributes. |
1052 | * |
1053 | * @param mixed $link A html_link object or a string URL (text param required in second case) |
1054 | * @param string $text A descriptive text for the link. If $link is a html_link, this is not required. |
1055 | * @return string HTML fragment |
1056 | */ |
1057 | public function link($link, $text=null) { |
1058 | $attributes = array(); |
1059 | |
1060 | if (is_a($link, 'html_link')) { |
1061 | $link = clone($link); |
db49be13 |
1062 | |
1063 | if ($link->has_action('popup_action')) { |
1064 | return $this->link_to_popup($link); |
1065 | } |
1066 | |
d9c8f425 |
1067 | $link->prepare(); |
1068 | $this->prepare_event_handlers($link); |
1069 | $attributes['href'] = prepare_url($link->url); |
1070 | $attributes['class'] = $link->get_classes_string(); |
1071 | $attributes['title'] = $link->title; |
1072 | $attributes['id'] = $link->id; |
1073 | |
1074 | $text = $link->text; |
1075 | |
1076 | } else if (empty($text)) { |
1077 | throw new coding_exception('$OUTPUT->link() must have a string as second parameter if the first param ($link) is a string'); |
1078 | |
1079 | } else { |
1080 | $attributes['href'] = prepare_url($link); |
1081 | } |
1082 | |
1083 | return $this->output_tag('a', $attributes, $text); |
1084 | } |
1085 | |
1086 | /** |
1087 | * Print a message along with button choices for Continue/Cancel. Labels default to Yes(Continue)/No(Cancel). |
1088 | * If a string or moodle_url is given instead of a html_button, method defaults to post and text to Yes/No |
1089 | * @param string $message The question to ask the user |
1090 | * @param mixed $continue The html_form component representing the Continue answer. Can also be a moodle_url or string URL |
1091 | * @param mixed $cancel The html_form component representing the Cancel answer. Can also be a moodle_url or string URL |
1092 | * @return string HTML fragment |
1093 | */ |
1094 | public function confirm($message, $continue, $cancel) { |
1095 | if ($continue instanceof html_form) { |
1096 | $continue = clone($continue); |
1097 | } else if (is_string($continue)) { |
1098 | $continueform = new html_form(); |
1099 | $continueform->url = new moodle_url($continue); |
1100 | $continue = $continueform; |
1101 | } else if ($continue instanceof moodle_url) { |
1102 | $continueform = new html_form(); |
1103 | $continueform->url = $continue; |
1104 | $continue = $continueform; |
1105 | } else { |
1106 | throw new coding_exception('The continue param to $OUTPUT->confirm must be either a URL (string/moodle_url) or a html_form object.'); |
1107 | } |
1108 | |
1109 | if ($cancel instanceof html_form) { |
1110 | $cancel = clone($cancel); |
1111 | } else if (is_string($cancel)) { |
1112 | $cancelform = new html_form(); |
1113 | $cancelform->url = new moodle_url($cancel); |
1114 | $cancel = $cancelform; |
1115 | } else if ($cancel instanceof moodle_url) { |
1116 | $cancelform = new html_form(); |
1f1aa445 |
1117 | $cancelform->button->text = get_string('cancel'); |
d9c8f425 |
1118 | $cancelform->url = $cancel; |
1119 | $cancel = $cancelform; |
1120 | } else { |
1121 | throw new coding_exception('The cancel param to $OUTPUT->confirm must be either a URL (string/moodle_url) or a html_form object.'); |
1122 | } |
1123 | |
1124 | if (empty($continue->button->text)) { |
1125 | $continue->button->text = get_string('yes'); |
1126 | } |
1127 | if (empty($cancel->button->text)) { |
1128 | $cancel->button->text = get_string('no'); |
1129 | } |
1130 | |
1131 | $output = $this->box_start('generalbox', 'notice'); |
1132 | $output .= $this->output_tag('p', array(), $message); |
1133 | $output .= $this->output_tag('div', array('class' => 'buttons'), $this->button($continue) . $this->button($cancel)); |
1134 | $output .= $this->box_end(); |
1135 | return $output; |
1136 | } |
1137 | |
1138 | /** |
1139 | * Given a html_form object, outputs an <input> tag within a form that uses the object's attributes. |
1140 | * |
1141 | * @param html_form $form A html_form object |
1142 | * @return string HTML fragment |
1143 | */ |
1144 | public function button($form) { |
1145 | if (empty($form->button) or !($form->button instanceof html_button)) { |
1146 | throw new coding_exception('$OUTPUT->button($form) requires $form to have a button (html_button) value'); |
1147 | } |
1148 | $form = clone($form); |
1149 | $form->button->prepare(); |
1150 | |
1151 | $this->prepare_event_handlers($form->button); |
1152 | |
1153 | $buttonattributes = array('class' => $form->button->get_classes_string(), |
1154 | 'type' => 'submit', |
1155 | 'value' => $form->button->text, |
1156 | 'disabled' => $form->button->disabled, |
1157 | 'id' => $form->button->id); |
1158 | |
1159 | $buttonoutput = $this->output_empty_tag('input', $buttonattributes); |
1160 | |
1161 | // Removing the button so it doesn't get output again |
1162 | unset($form->button); |
1163 | |
7a5c78e0 |
1164 | return $this->output_tag('div', array('class' => 'singlebutton'), $this->form($form, $buttonoutput)); |
d9c8f425 |
1165 | } |
1166 | |
1167 | /** |
1168 | * Given a html_form component and an optional rendered submit button, |
1169 | * outputs a HTML form with correct divs and inputs and a single submit button. |
1170 | * This doesn't render any other visible inputs. Use moodleforms for these. |
1171 | * @param html_form $form A html_form instance |
1172 | * @param string $contents HTML fragment to put inside the form. If given, must contain at least the submit button. |
1173 | * @return string HTML fragment |
1174 | */ |
1175 | public function form($form, $contents=null) { |
1176 | $form = clone($form); |
1177 | $form->prepare(); |
1178 | $this->prepare_event_handlers($form); |
1179 | $buttonoutput = null; |
1180 | |
1181 | if (empty($contents) && !empty($form->button)) { |
1182 | debugging("You probably want to use \$OUTPUT->button(\$form), please read that function's documentation", DEBUG_DEVELOPER); |
1183 | } else if (empty($contents)) { |
1184 | $contents = $this->output_empty_tag('input', array('type' => 'submit', 'value' => get_string('ok'))); |
1185 | } else if (!empty($form->button)) { |
1186 | $form->button->prepare(); |
d9c8f425 |
1187 | $this->prepare_event_handlers($form->button); |
1188 | |
1189 | $buttonattributes = array('class' => $form->button->get_classes_string(), |
1190 | 'type' => 'submit', |
1191 | 'value' => $form->button->text, |
1192 | 'disabled' => $form->button->disabled, |
1193 | 'id' => $form->button->id); |
1194 | |
b65bfc3e |
1195 | $buttonoutput = $this->output_empty_tag('input', $buttonattributes); |
1196 | |
1197 | // Hide the submit button if the button has a JS submit action |
1198 | if ($form->jssubmitaction) { |
1199 | $buttonoutput = $this->output_start_tag('div', array('id' => "noscript$form->id")) . $buttonoutput . $this->output_end_tag('div'); |
1200 | $this->page->requires->js_function_call('hide_item', array("noscript$form->id")); |
1201 | } |
d9c8f425 |
1202 | |
1203 | } |
1204 | |
1205 | $hiddenoutput = ''; |
1206 | |
1207 | foreach ($form->url->params() as $var => $val) { |
1208 | $hiddenoutput .= $this->output_empty_tag('input', array('type' => 'hidden', 'name' => $var, 'value' => $val)); |
1209 | } |
1210 | |
1211 | $formattributes = array( |
1212 | 'method' => $form->method, |
1213 | 'action' => prepare_url($form->url, true), |
1214 | 'id' => $form->id, |
1215 | 'class' => $form->get_classes_string()); |
1216 | |
1217 | $divoutput = $this->output_tag('div', array(), $hiddenoutput . $contents . $buttonoutput); |
7a5c78e0 |
1218 | $output = $this->output_tag('form', $formattributes, $divoutput); |
d9c8f425 |
1219 | |
1220 | return $output; |
1221 | } |
1222 | |
1223 | /** |
1224 | * Returns a string containing a link to the user documentation. |
1225 | * Also contains an icon by default. Shown to teachers and admin only. |
1226 | * @param string $path The page link after doc root and language, no leading slash. |
1227 | * @param string $text The text to be displayed for the link |
1228 | * @param string $iconpath The path to the icon to be displayed |
1229 | */ |
1230 | public function doc_link($path, $text=false, $iconpath=false) { |
1231 | global $CFG, $OUTPUT; |
beb56299 |
1232 | $icon = new moodle_action_icon(); |
d9c8f425 |
1233 | $icon->linktext = $text; |
1234 | $icon->image->alt = $text; |
1235 | $icon->image->add_class('iconhelp'); |
1236 | $icon->link->url = new moodle_url(get_docs_url($path)); |
1237 | |
1238 | if (!empty($iconpath)) { |
1239 | $icon->image->src = $iconpath; |
1240 | } else { |
1241 | $icon->image->src = $this->old_icon_url('docs'); |
1242 | } |
1243 | |
1244 | if (!empty($CFG->doctonewwindow)) { |
1245 | $icon->actions[] = new popup_action('click', $icon->link->url); |
1246 | } |
1247 | |
1248 | return $this->action_icon($icon); |
1249 | |
1250 | } |
1251 | |
1252 | /** |
beb56299 |
1253 | * Given a moodle_action_icon object, outputs an image linking to an action (URL or AJAX). |
d9c8f425 |
1254 | * |
beb56299 |
1255 | * @param moodle_action_icon $icon A moodle_action_icon object |
d9c8f425 |
1256 | * @return string HTML fragment |
1257 | */ |
1258 | public function action_icon($icon) { |
1259 | $icon = clone($icon); |
1260 | $icon->prepare(); |
1261 | $imageoutput = $this->image($icon->image); |
1262 | |
1263 | if ($icon->linktext) { |
1264 | $imageoutput .= $icon->linktext; |
1265 | } |
1266 | $icon->link->text = $imageoutput; |
1267 | |
1268 | return $this->link($icon->link); |
1269 | } |
1270 | |
1271 | /* |
1272 | * Centered heading with attached help button (same title text) |
1273 | * and optional icon attached |
94056d9d |
1274 | * @param moodle_help_icon $helpicon A moodle_help_icon object |
d9c8f425 |
1275 | * @param mixed $image An image URL or a html_image object |
1276 | * @return string HTML fragment |
1277 | */ |
1278 | public function heading_with_help($helpicon, $image=false) { |
1279 | if (!($image instanceof html_image) && !empty($image)) { |
1280 | $htmlimage = new html_image(); |
1281 | $htmlimage->src = $image; |
1282 | $image = $htmlimage; |
1283 | } |
1284 | return $this->container($this->image($image) . $this->heading($helpicon->text, 2, 'main help') . $this->help_icon($helpicon), 'heading-with-help'); |
1285 | } |
1286 | |
1287 | /** |
1288 | * Print a help icon. |
1289 | * |
94056d9d |
1290 | * @param moodle_help_icon $helpicon A moodle_help_icon object, subclass of html_link |
d9c8f425 |
1291 | * |
1292 | * @return string HTML fragment |
1293 | */ |
1294 | public function help_icon($icon) { |
1295 | global $COURSE; |
1296 | $icon = clone($icon); |
1297 | $icon->prepare(); |
1298 | |
1299 | $popup = new popup_action('click', $icon->link->url); |
1300 | $icon->link->add_action($popup); |
1301 | |
1302 | $image = null; |
1303 | |
1304 | if (!empty($icon->image)) { |
1305 | $image = $icon->image; |
1306 | $image->add_class('iconhelp'); |
1307 | } |
1308 | |
1309 | return $this->output_tag('span', array('class' => 'helplink'), $this->link_to_popup($icon->link, $image)); |
1310 | } |
1311 | |
1312 | /** |
1313 | * Creates and returns a button to a popup window |
1314 | * |
1315 | * @param html_link $link Subclass of moodle_html_component |
1316 | * @param moodle_popup $popup A moodle_popup object |
1317 | * @param html_image $image An optional image replacing the link text |
1318 | * |
1319 | * @return string HTML fragment |
1320 | */ |
1321 | public function link_to_popup($link, $image=null) { |
1322 | $link = clone($link); |
1323 | $link->prepare(); |
1324 | |
1325 | $this->prepare_event_handlers($link); |
1326 | |
1327 | if (empty($link->url)) { |
1328 | throw new coding_exception('Called $OUTPUT->link_to_popup($link) method without $link->url set.'); |
1329 | } |
1330 | |
1331 | $linkurl = prepare_url($link->url); |
1332 | |
1333 | $tagoptions = array( |
1334 | 'title' => $link->title, |
1335 | 'id' => $link->id, |
1336 | 'href' => ($linkurl) ? $linkurl : prepare_url($popup->url), |
1337 | 'class' => $link->get_classes_string()); |
1338 | |
1339 | // Use image if one is given |
1340 | if (!empty($image) && $image instanceof html_image) { |
1341 | |
b65bfc3e |
1342 | if (empty($image->alt) || $image->alt == HTML_ATTR_EMPTY) { |
d9c8f425 |
1343 | $image->alt = $link->text; |
b65bfc3e |
1344 | $image->title = $link->text; |
d9c8f425 |
1345 | } |
1346 | |
1347 | $link->text = $this->image($image); |
1348 | |
1349 | if (!empty($link->linktext)) { |
1350 | $link->text = "$link->title $link->text"; |
1351 | } |
1352 | } |
1353 | |
1354 | return $this->output_tag('a', $tagoptions, $link->text); |
1355 | } |
1356 | |
1357 | /** |
1358 | * Creates and returns a spacer image with optional line break. |
1359 | * |
1360 | * @param html_image $image Subclass of moodle_html_component |
1361 | * |
1362 | * @return string HTML fragment |
1363 | */ |
1364 | public function spacer($image) { |
1365 | $image = clone($image); |
d9c8f425 |
1366 | |
1367 | if (empty($image->src)) { |
1368 | $image->src = $this->old_icon_url('spacer'); |
1369 | } |
1370 | |
b65bfc3e |
1371 | $image->prepare(); |
1372 | $image->add_class('spacer'); |
1373 | |
d9c8f425 |
1374 | $output = $this->image($image); |
1375 | |
1376 | return $output; |
1377 | } |
1378 | |
1379 | /** |
1380 | * Creates and returns an image. |
1381 | * |
1382 | * @param html_image $image Subclass of moodle_html_component |
1383 | * |
1384 | * @return string HTML fragment |
1385 | */ |
1386 | public function image($image) { |
1387 | if ($image === false) { |
1388 | return false; |
1389 | } |
1390 | |
1391 | $image = clone($image); |
1392 | $image->prepare(); |
1393 | |
1394 | $this->prepare_event_handlers($image); |
1395 | |
1396 | $attributes = array('class' => $image->get_classes_string(), |
1397 | 'src' => prepare_url($image->src), |
1398 | 'alt' => $image->alt, |
1399 | 'style' => $image->style, |
1400 | 'title' => $image->title, |
1401 | 'id' => $image->id); |
1402 | |
1403 | if (!empty($image->height) || !empty($image->width)) { |
1404 | $attributes['style'] .= $this->prepare_legacy_width_and_height($image); |
1405 | } |
1406 | return $this->output_empty_tag('img', $attributes); |
1407 | } |
1408 | |
1409 | /** |
1410 | * Print the specified user's avatar. |
1411 | * |
1412 | * This method can be used in two ways: |
1413 | * <pre> |
1414 | * // Option 1: |
beb56299 |
1415 | * $userpic = new moodle_user_picture(); |
d9c8f425 |
1416 | * // Set properties of $userpic |
1417 | * $OUTPUT->user_picture($userpic); |
1418 | * |
1419 | * // Option 2: (shortcut for simple cases) |
1420 | * // $user has come from the DB and has fields id, picture, imagealt, firstname and lastname |
1421 | * $OUTPUT->user_picture($user, $COURSE->id); |
1422 | * </pre> |
1423 | * |
1424 | * @param object $userpic Object with at least fields id, picture, imagealt, firstname, lastname |
1425 | * If any of these are missing, or if a userid is passed, the database is queried. Avoid this |
1426 | * if at all possible, particularly for reports. It is very bad for performance. |
beb56299 |
1427 | * A moodle_user_picture object is a better parameter. |
d9c8f425 |
1428 | * @param int $courseid courseid Used when constructing the link to the user's profile. Required if $userpic |
beb56299 |
1429 | * is not a moodle_user_picture object |
d9c8f425 |
1430 | * @return string HTML fragment |
1431 | */ |
1432 | public function user_picture($userpic, $courseid=null) { |
beb56299 |
1433 | // Instantiate a moodle_user_picture object if $user is not already one |
1434 | if (!($userpic instanceof moodle_user_picture)) { |
d9c8f425 |
1435 | if (empty($courseid)) { |
1436 | throw new coding_exception('Called $OUTPUT->user_picture with a $user object but no $courseid.'); |
1437 | } |
1438 | |
1439 | $user = $userpic; |
beb56299 |
1440 | $userpic = new moodle_user_picture(); |
d9c8f425 |
1441 | $userpic->user = $user; |
1442 | $userpic->courseid = $courseid; |
1443 | } else { |
1444 | $userpic = clone($userpic); |
1445 | } |
1446 | |
1447 | $userpic->prepare(); |
1448 | |
1449 | $output = $this->image($userpic->image); |
1450 | |
1451 | if (!empty($userpic->url)) { |
1452 | $actions = $userpic->get_actions(); |
1453 | if ($userpic->popup && !empty($actions)) { |
1454 | $link = new html_link(); |
1455 | $link->url = $userpic->url; |
1456 | $link->text = fullname($userpic->user); |
1457 | $link->title = fullname($userpic->user); |
1458 | |
1459 | foreach ($actions as $action) { |
1460 | $link->add_action($action); |
1461 | } |
1462 | $output = $this->link_to_popup($link, $userpic->image); |
1463 | } else { |
1464 | $output = $this->link(prepare_url($userpic->url), $output); |
1465 | } |
1466 | } |
1467 | |
1468 | return $output; |
1469 | } |
1470 | |
1471 | /** |
1472 | * Prints the 'Update this Modulename' button that appears on module pages. |
1473 | * |
1474 | * @param string $cmid the course_module id. |
1475 | * @param string $modulename the module name, eg. "forum", "quiz" or "workshop" |
1476 | * @return string the HTML for the button, if this user has permission to edit it, else an empty string. |
1477 | */ |
1478 | public function update_module_button($cmid, $modulename) { |
1479 | global $CFG; |
1480 | if (has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_MODULE, $cmid))) { |
1481 | $modulename = get_string('modulename', $modulename); |
1482 | $string = get_string('updatethis', '', $modulename); |
1483 | |
1484 | $form = new html_form(); |
1485 | $form->url = new moodle_url("$CFG->wwwroot/course/mod.php", array('update' => $cmid, 'return' => true, 'sesskey' => sesskey())); |
1486 | $form->button->text = $string; |
1487 | return $this->button($form); |
1488 | } else { |
1489 | return ''; |
1490 | } |
1491 | } |
1492 | |
1493 | /** |
1494 | * Prints a "Turn editing on/off" button in a form. |
1495 | * @param moodle_url $url The URL + params to send through when clicking the button |
1496 | * @return string HTML the button |
1497 | */ |
1498 | public function edit_button(moodle_url $url) { |
1499 | global $USER; |
1500 | if (!empty($USER->editing)) { |
1501 | $string = get_string('turneditingoff'); |
1502 | $edit = '0'; |
1503 | } else { |
1504 | $string = get_string('turneditingon'); |
1505 | $edit = '1'; |
1506 | } |
1507 | |
1508 | $form = new html_form(); |
1509 | $form->url = $url; |
1510 | $form->url->param('edit', $edit); |
1511 | $form->button->text = $string; |
1512 | |
1513 | return $this->button($form); |
1514 | } |
1515 | |
1516 | /** |
1517 | * Outputs a HTML nested list |
1518 | * |
1519 | * @param html_list $list A html_list object |
1520 | * @return string HTML structure |
1521 | */ |
1522 | public function htmllist($list) { |
1523 | $list = clone($list); |
1524 | $list->prepare(); |
1525 | |
1526 | $this->prepare_event_handlers($list); |
1527 | |
1528 | if ($list->type == 'ordered') { |
1529 | $tag = 'ol'; |
1530 | } else if ($list->type == 'unordered') { |
1531 | $tag = 'ul'; |
1532 | } |
1533 | |
1534 | $output = $this->output_start_tag($tag, array('class' => $list->get_classes_string())); |
1535 | |
1536 | foreach ($list->items as $listitem) { |
1537 | if ($listitem instanceof html_list) { |
b65bfc3e |
1538 | $output .= $this->output_start_tag('li', array()) . "\n"; |
1539 | $output .= $this->htmllist($listitem) . "\n"; |
1540 | $output .= $this->output_end_tag('li') . "\n"; |
d9c8f425 |
1541 | } else if ($listitem instanceof html_list_item) { |
1542 | $listitem->prepare(); |
1543 | $this->prepare_event_handlers($listitem); |
b65bfc3e |
1544 | $output .= $this->output_tag('li', array('class' => $listitem->get_classes_string()), $listitem->value) . "\n"; |
d9c8f425 |
1545 | } |
1546 | } |
1547 | |
b65bfc3e |
1548 | if ($list->text) { |
1549 | $output = $list->text . $output; |
1550 | } |
1551 | |
d9c8f425 |
1552 | return $output . $this->output_end_tag($tag); |
1553 | } |
54a007e8 |
1554 | |
1555 | /** |
1556 | * Prints an inline span element with optional text contents. |
1557 | * |
319770d7 |
1558 | * @param mixed $span A html_span object or some string content to wrap in a span |
1559 | * @param mixed $classes A space-separated list or an array of classes. Only used if $span is a string |
54a007e8 |
1560 | * @return string A HTML fragment |
1561 | */ |
319770d7 |
1562 | public function span($span, $classes='') { |
1563 | if (!($span instanceof html_span)) { |
1564 | $text = $span; |
1565 | $span = new html_span(); |
1566 | $span->contents = $text; |
1567 | $span->add_classes($classes); |
1568 | } |
1569 | |
54a007e8 |
1570 | $span = clone($span); |
1571 | $span->prepare(); |
1572 | $this->prepare_event_handlers($span); |
1573 | $attributes = array('class' => $span->get_classes_string(), |
1574 | 'alt' => $span->alt, |
1575 | 'style' => $span->style, |
1576 | 'title' => $span->title, |
1577 | 'id' => $span->id); |
1578 | return $this->output_tag('span', $attributes, $span->contents); |
1579 | } |
d9c8f425 |
1580 | |
1581 | /** |
1582 | * Prints a simple button to close a window |
1583 | * |
1584 | * @global objec)t |
1585 | * @param string $text The lang string for the button's label (already output from get_string()) |
1586 | * @return string|void if $return is true, void otherwise |
1587 | */ |
7a5c78e0 |
1588 | public function close_window_button($text='') { |
d9c8f425 |
1589 | if (empty($text)) { |
1590 | $text = get_string('closewindow'); |
1591 | } |
1592 | $closeform = new html_form(); |
1593 | $closeform->url = '#'; |
7a5c78e0 |
1594 | $closeform->method = 'get'; |
d9c8f425 |
1595 | $closeform->button->text = $text; |
1596 | $closeform->button->add_action('click', 'close_window'); |
1597 | $closeform->button->prepare(); |
1598 | return $this->container($this->button($closeform), 'closewindow'); |
1599 | } |
1600 | |
1601 | /** |
1602 | * Outputs a <select> menu or a list of radio/checkbox inputs. |
1603 | * |
1604 | * This method is extremely versatile, and can be used to output yes/no menus, |
1605 | * form-enclosed menus with automatic redirects when an option is selected, |
1606 | * descriptive labels and help icons. By default it just outputs a select |
1607 | * menu. |
1608 | * |
7b1f2c82 |
1609 | * To add a descriptive label, use html_select::set_label($text, $for) or |
1610 | * html_select::set_label($label) passing a html_label object |
d9c8f425 |
1611 | * |
7b1f2c82 |
1612 | * To add a help icon, use html_select::set_help($page, $text, $linktext) or |
1613 | * html_select::set_help($helpicon) passing a moodle_help_icon object |
d9c8f425 |
1614 | * |
7b1f2c82 |
1615 | * If you html_select::$rendertype to "radio", it will render radio buttons |
d9c8f425 |
1616 | * instead of a <select> menu, unless $multiple is true, in which case it |
1617 | * will render checkboxes. |
1618 | * |
7b1f2c82 |
1619 | * To surround the menu with a form, simply set html_select->form as a |
d9c8f425 |
1620 | * valid html_form object. Note that this function will NOT automatically |
1621 | * add a form for non-JS browsers. If you do not set one up, it assumes |
1622 | * that you are providing your own form in some other way. |
1623 | * |
7b1f2c82 |
1624 | * You can either call this function with a single html_select argument |
d9c8f425 |
1625 | * or, with a list of parameters, in which case those parameters are sent to |
7b1f2c82 |
1626 | * the html_select constructor. |
d9c8f425 |
1627 | * |
7b1f2c82 |
1628 | * @param html_select $select a html_select that describes |
d9c8f425 |
1629 | * the select menu you want output. |
1630 | * @return string the HTML for the <select> |
1631 | */ |
1632 | public function select($select) { |
1633 | $select = clone($select); |
1634 | $select->prepare(); |
1635 | |
1636 | $this->prepare_event_handlers($select); |
1637 | |
1638 | if (empty($select->id)) { |
1639 | $select->id = 'menu' . str_replace(array('[', ']'), '', $select->name); |
1640 | } |
1641 | |
1642 | $attributes = array( |
1643 | 'name' => $select->name, |
1644 | 'id' => $select->id, |
1645 | 'class' => $select->get_classes_string() |
1646 | ); |
1647 | if ($select->disabled) { |
1648 | $attributes['disabled'] = 'disabled'; |
1649 | } |
1650 | if ($select->tabindex) { |
1651 | $attributes['tabindex'] = $tabindex; |
1652 | } |
1653 | |
1654 | if ($select->rendertype == 'menu' && $select->listbox) { |
1655 | if (is_integer($select->listbox)) { |
1656 | $size = $select->listbox; |
1657 | } else { |
1658 | $size = min($select->maxautosize, count($select->options)); |
1659 | } |
1660 | $attributes['size'] = $size; |
1661 | if ($select->multiple) { |
1662 | $attributes['multiple'] = 'multiple'; |
1663 | } |
1664 | } |
1665 | |
1666 | $html = ''; |
1667 | |
1668 | if (!empty($select->label)) { |
1669 | $html .= $this->label($select->label); |
1670 | } |
1671 | |
94056d9d |
1672 | if (!empty($select->helpicon) && $select->helpicon instanceof moodle_help_icon) { |
d9c8f425 |
1673 | $html .= $this->help_icon($select->helpicon); |
1674 | } |
1675 | |
1676 | if ($select->rendertype == 'menu') { |
1677 | $html .= $this->output_start_tag('select', $attributes) . "\n"; |
1678 | |
1679 | foreach ($select->options as $option) { |
1680 | // $OUTPUT->select_option detects if $option is an option or an optgroup |
1681 | $html .= $this->select_option($option); |
1682 | } |
1683 | |
1684 | $html .= $this->output_end_tag('select') . "\n"; |
1685 | } else if ($select->rendertype == 'radio') { |
1686 | $currentradio = 0; |
1687 | foreach ($select->options as $option) { |
1688 | $html .= $this->radio($option, $select->name); |
1689 | $currentradio++; |
1690 | } |
1691 | } else if ($select->rendertype == 'checkbox') { |
1692 | $currentcheckbox = 0; |
1ae3767a |
1693 | // If only two choices are available, suggest using the checkbox method instead |
1694 | if (count($select->options) < 3 && !$select->multiple) { |
1695 | debugging('You are using $OUTPUT->select() to render two mutually exclusive choices using checkboxes. Please use $OUTPUT->checkbox(html_select_option) instead.', DEBUG_DEVELOPER); |
1696 | } else { |
1697 | foreach ($select->options as $option) { |
1698 | $html .= $this->checkbox($option, $select->name); |
1699 | $currentcheckbox++; |
1700 | } |
d9c8f425 |
1701 | } |
1702 | } |
1703 | |
1704 | if (!empty($select->form) && $select->form instanceof html_form) { |
1705 | $html = $this->form($select->form, $html); |
1706 | } |
1707 | |
1708 | return $html; |
1709 | } |
1710 | |
1711 | /** |
1712 | * Outputs a <input type="radio" /> element. Optgroups are ignored, so do not |
1713 | * pass a html_select_optgroup as a param to this function. |
1714 | * |
1715 | * @param html_select_option $option a html_select_option |
1716 | * @return string the HTML for the <input type="radio"> |
1717 | */ |
1718 | public function radio($option, $name='unnamed') { |
1ae3767a |
1719 | static $currentradio = array(); |
e57c283d |
1720 | |
1ae3767a |
1721 | if (empty($currentradio[$name])) { |
1722 | $currentradio[$name] = 0; |
1723 | } |
1724 | |
d9c8f425 |
1725 | if ($option instanceof html_select_optgroup) { |
1726 | throw new coding_exception('$OUTPUT->radio($option) does not support a html_select_optgroup object as param.'); |
1727 | } else if (!($option instanceof html_select_option)) { |
1728 | throw new coding_exception('$OUTPUT->radio($option) only accepts a html_select_option object as param.'); |
1729 | } |
1730 | $option = clone($option); |
1731 | $option->prepare(); |
1732 | $option->label->for = $option->id; |
1733 | $this->prepare_event_handlers($option); |
1734 | |
1ae3767a |
1735 | $output = $this->output_start_tag('span', array('class' => "radiogroup $name rb{$currentradio[$name]}")) . "\n"; |
d9c8f425 |
1736 | $output .= $this->label($option->label); |
1737 | |
1738 | if ($option->selected == 'selected') { |
1739 | $option->selected = 'checked'; |
1740 | } |
1741 | |
1742 | $output .= $this->output_empty_tag('input', array( |
1743 | 'type' => 'radio', |
1744 | 'value' => $option->value, |
1745 | 'name' => $name, |
1746 | 'alt' => $option->alt, |
1747 | 'id' => $option->id, |
1748 | 'class' => $option->get_classes_string(), |
1749 | 'checked' => $option->selected)); |
1750 | |
1751 | $output .= $this->output_end_tag('span'); |
1ae3767a |
1752 | $currentradio[$name]++; |
d9c8f425 |
1753 | return $output; |
1754 | } |
1755 | |
1756 | /** |
1757 | * Outputs a <input type="checkbox" /> element. Optgroups are ignored, so do not |
1758 | * pass a html_select_optgroup as a param to this function. |
1759 | * |
1760 | * @param html_select_option $option a html_select_option |
1761 | * @return string the HTML for the <input type="checkbox"> |
1762 | */ |
1763 | public function checkbox($option, $name='unnamed') { |
1764 | if ($option instanceof html_select_optgroup) { |
1765 | throw new coding_exception('$OUTPUT->checkbox($option) does not support a html_select_optgroup object as param.'); |
1766 | } else if (!($option instanceof html_select_option)) { |
1767 | throw new coding_exception('$OUTPUT->checkbox($option) only accepts a html_select_option object as param.'); |
1768 | } |
1769 | $option = clone($option); |
1770 | $option->prepare(); |
1771 | |
1772 | $option->label->for = $option->id; |
1773 | $this->prepare_event_handlers($option); |
1774 | |
1775 | $output = $this->output_start_tag('span', array('class' => "checkbox $name")) . "\n"; |
1776 | |
1777 | if ($option->selected == 'selected') { |
1778 | $option->selected = 'checked'; |
1779 | } |
1780 | |
1781 | $output .= $this->output_empty_tag('input', array( |
1782 | 'type' => 'checkbox', |
1783 | 'value' => $option->value, |
1784 | 'name' => $name, |
1785 | 'id' => $option->id, |
1786 | 'alt' => $option->alt, |
1787 | 'class' => $option->get_classes_string(), |
1788 | 'checked' => $option->selected)); |
1789 | $output .= $this->label($option->label); |
1790 | |
1791 | $output .= $this->output_end_tag('span'); |
1792 | |
1793 | return $output; |
1794 | } |
1795 | |
1796 | /** |
1797 | * Output an <option> or <optgroup> element. If an optgroup element is detected, |
1798 | * this will recursively output its options as well. |
1799 | * |
7b1f2c82 |
1800 | * @param mixed $option a html_select_option or html_select_optgroup |
d9c8f425 |
1801 | * @return string the HTML for the <option> or <optgroup> |
1802 | */ |
1803 | public function select_option($option) { |
1804 | $option = clone($option); |
1805 | $option->prepare(); |
1806 | $this->prepare_event_handlers($option); |
1807 | |
1808 | if ($option instanceof html_select_option) { |
1809 | return $this->output_tag('option', array( |
1810 | 'value' => $option->value, |
1811 | 'class' => $option->get_classes_string(), |
1812 | 'selected' => $option->selected), $option->text); |
1813 | } else if ($option instanceof html_select_optgroup) { |
1814 | $output = $this->output_start_tag('optgroup', array('label' => $option->text, 'class' => $option->get_classes_string())); |
1815 | foreach ($option->options as $selectoption) { |
1816 | $output .= $this->select_option($selectoption); |
1817 | } |
1818 | $output .= $this->output_end_tag('optgroup'); |
1819 | return $output; |
1820 | } |
1821 | } |
1822 | |
1823 | /** |
1824 | * Output an <input type="text"> element |
1825 | * |
1826 | * @param html_field $field a html_field object |
1827 | * @return string the HTML for the <input> |
1828 | */ |
1829 | public function textfield($field) { |
1c1f64a2 |
1830 | return $this->output_tag('span', array('class' => "textfield $field->name"), $this->field($field)); |
1831 | } |
1832 | |
1833 | /** |
1834 | * Output an <input/> element |
1835 | * |
1836 | * @param html_field $field a html_field object |
1837 | * @return string the HTML for the <input> |
1838 | */ |
1839 | public function field($field) { |
d9c8f425 |
1840 | $field = clone($field); |
1841 | $field->prepare(); |
1842 | $this->prepare_event_handlers($field); |
1c1f64a2 |
1843 | if (!empty($field->label->text)) { |
3cc457db |
1844 | $output .= $this->label($field->label); |
1845 | } |
1c1f64a2 |
1846 | return $this->output_empty_tag('input', array( |
1847 | 'type' => $field->type, |
d9c8f425 |
1848 | 'name' => $field->name, |
1849 | 'id' => $field->id, |
1850 | 'value' => $field->value, |
1851 | 'style' => $field->style, |
1852 | 'alt' => $field->alt, |
1c1f64a2 |
1853 | 'title' => $field->title, |
d9c8f425 |
1854 | 'maxlength' => $field->maxlength)); |
d9c8f425 |
1855 | } |
1856 | |
1857 | /** |
1858 | * Outputs a <label> element. |
1859 | * @param html_label $label A html_label object |
1860 | * @return HTML fragment |
1861 | */ |
1862 | public function label($label) { |
1863 | $label = clone($label); |
1864 | $label->prepare(); |
1865 | $this->prepare_event_handlers($label); |
1866 | return $this->output_tag('label', array('for' => $label->for, 'class' => $label->get_classes_string()), $label->text); |
1867 | } |
1868 | |
1869 | /** |
1870 | * Output an error message. By default wraps the error message in <span class="error">. |
1871 | * If the error message is blank, nothing is output. |
1872 | * @param string $message the error message. |
1873 | * @return string the HTML to output. |
1874 | */ |
1875 | public function error_text($message) { |
1876 | if (empty($message)) { |
1877 | return ''; |
1878 | } |
1879 | return $this->output_tag('span', array('class' => 'error'), $message); |
1880 | } |
1881 | |
1882 | /** |
1883 | * Do not call this function directly. |
1884 | * |
1885 | * To terminate the current script with a fatal error, call the {@link print_error} |
1886 | * function, or throw an exception. Doing either of those things will then call this |
1887 | * function to display the error, before terminating the execution. |
1888 | * |
1889 | * @param string $message The message to output |
1890 | * @param string $moreinfourl URL where more info can be found about the error |
1891 | * @param string $link Link for the Continue button |
1892 | * @param array $backtrace The execution backtrace |
1893 | * @param string $debuginfo Debugging information |
1894 | * @param bool $showerrordebugwarning Whether or not to show a debugging warning |
1895 | * @return string the HTML to output. |
1896 | */ |
1897 | public function fatal_error($message, $moreinfourl, $link, $backtrace, |
1898 | $debuginfo = null, $showerrordebugwarning = false) { |
1899 | |
1900 | $output = ''; |
1901 | |
e57c283d |
1902 | if (!debugging('', DEBUG_DEVELOPER)) { |
1903 | return false; |
1904 | } |
1905 | |
d9c8f425 |
1906 | if ($this->has_started()) { |
1907 | $output .= $this->opencontainers->pop_all_but_last(); |
1908 | } else { |
1909 | // Header not yet printed |
1910 | @header('HTTP/1.0 404 Not Found'); |
1911 | $this->page->set_title(get_string('error')); |
1912 | $output .= $this->header(); |
1913 | } |
1914 | |
1915 | $message = '<p class="errormessage">' . $message . '</p>'. |
1916 | '<p class="errorcode"><a href="' . $moreinfourl . '">' . |
1917 | get_string('moreinformation') . '</a></p>'; |
1918 | $output .= $this->box($message, 'errorbox'); |
1919 | |
e57c283d |
1920 | if ($showerrordebugwarning) { |
1921 | $output .= $this->notification('error() is a deprecated function. ' . |
1922 | 'Please call print_error() instead of error()', 'notifytiny'); |
1923 | } |
1924 | if (!empty($debuginfo)) { |
1925 | $output .= $this->notification($debuginfo, 'notifytiny'); |
1926 | } |
1927 | if (!empty($backtrace)) { |
1928 | $output .= $this->notification('Stack trace: ' . |
1929 | format_backtrace($backtrace), 'notifytiny'); |
d9c8f425 |
1930 | } |
1931 | |
1932 | if (!empty($link)) { |
1933 | $output .= $this->continue_button($link); |
1934 | } |
1935 | |
1936 | $output .= $this->footer(); |
1937 | |
1938 | // Padding to encourage IE to display our error page, rather than its own. |
1939 | $output .= str_repeat(' ', 512); |
1940 | |
1941 | return $output; |
1942 | } |
1943 | |
1944 | /** |
1945 | * Output a notification (that is, a status message about something that has |
1946 | * just happened). |
1947 | * |
1948 | * @param string $message the message to print out |
1949 | * @param string $classes normally 'notifyproblem' or 'notifysuccess'. |
1950 | * @return string the HTML to output. |
1951 | */ |
1952 | public function notification($message, $classes = 'notifyproblem') { |
1953 | return $this->output_tag('div', array('class' => |
1954 | moodle_renderer_base::prepare_classes($classes)), clean_text($message)); |
1955 | } |
1956 | |
1957 | /** |
1958 | * Print a continue button that goes to a particular URL. |
1959 | * |
1960 | * @param string|moodle_url $link The url the button goes to. |
1961 | * @return string the HTML to output. |
1962 | */ |
1963 | public function continue_button($link) { |
1964 | if (!is_a($link, 'moodle_url')) { |
1965 | $link = new moodle_url($link); |
1966 | } |
1967 | $form = new html_form(); |
1968 | $form->url = $link; |
1969 | $form->values = $link->params(); |
1970 | $form->button->text = get_string('continue'); |
1971 | $form->method = 'get'; |
1972 | |
1973 | return $this->output_tag('div', array('class' => 'continuebutton') , $this->button($form)); |
1974 | } |
1975 | |
1976 | /** |
1977 | * Prints a single paging bar to provide access to other pages (usually in a search) |
1978 | * |
1979 | * @param string|moodle_url $link The url the button goes to. |
1980 | * @return string the HTML to output. |
1981 | */ |
1982 | public function paging_bar($pagingbar) { |
1983 | $output = ''; |
1984 | $pagingbar = clone($pagingbar); |
1985 | $pagingbar->prepare(); |
1986 | |
1987 | if ($pagingbar->totalcount > $pagingbar->perpage) { |
1988 | $output .= get_string('page') . ':'; |
1989 | |
1990 | if (!empty($pagingbar->previouslink)) { |
1991 | $output .= ' (' . $this->link($pagingbar->previouslink) . ') '; |
1992 | } |
1993 | |
1994 | if (!empty($pagingbar->firstlink)) { |
1995 | $output .= ' ' . $this->link($pagingbar->firstlink) . ' ...'; |
1996 | } |
1997 | |
1998 | foreach ($pagingbar->pagelinks as $link) { |
1999 | if ($link instanceof html_link) { |
2000 | $output .= ' ' . $this->link($link); |
2001 | } else { |
2002 | $output .= " $link"; |
2003 | } |
2004 | } |
2005 | |
2006 | if (!empty($pagingbar->lastlink)) { |
2007 | $output .= ' ...' . $this->link($pagingbar->lastlink) . ' '; |
2008 | } |
2009 | |
2010 | if (!empty($pagingbar->nextlink)) { |
2011 | $output .= ' (' . $this->link($pagingbar->nextlink) . ')'; |
2012 | } |
2013 | } |
2014 | |
2015 | return $this->output_tag('div', array('class' => 'paging'), $output); |
2016 | } |
2017 | |
2018 | /** |
2019 | * Render a HTML table |
2020 | * |
2021 | * @param object $table {@link html_table} instance containing all the information needed |
2022 | * @return string the HTML to output. |
2023 | */ |
2024 | public function table(html_table $table) { |
2025 | $table = clone($table); |
2026 | $table->prepare(); |
2027 | $attributes = array( |
2028 | 'id' => $table->id, |
2029 | 'width' => $table->width, |
2030 | 'summary' => $table->summary, |
2031 | 'cellpadding' => $table->cellpadding, |
2032 | 'cellspacing' => $table->cellspacing, |
2033 | 'class' => $table->get_classes_string()); |
2034 | $output = $this->output_start_tag('table', $attributes) . "\n"; |
2035 | |
2036 | $countcols = 0; |
2037 | |
2038 | if (!empty($table->head)) { |
2039 | $countcols = count($table->head); |
319770d7 |
2040 | $output .= $this->output_start_tag('thead', $table->headclasses) . "\n"; |
d9c8f425 |
2041 | $output .= $this->output_start_tag('tr', array()) . "\n"; |
2042 | $keys = array_keys($table->head); |
2043 | $lastkey = end($keys); |
54a007e8 |
2044 | |
d9c8f425 |
2045 | foreach ($table->head as $key => $heading) { |
54a007e8 |
2046 | // Convert plain string headings into html_table_cell objects |
2047 | if (!($heading instanceof html_table_cell)) { |
2048 | $headingtext = $heading; |
2049 | $heading = new html_table_cell(); |
2050 | $heading->text = $headingtext; |
2051 | $heading->header = true; |
2052 | } |
2053 | |
2054 | $this->prepare_event_handlers($heading); |
2055 | |
2056 | $heading->add_classes(array('header', 'c' . $key)); |
d9c8f425 |
2057 | if (isset($table->headspan[$key]) && $table->headspan[$key] > 1) { |
54a007e8 |
2058 | $heading->colspan = $table->headspan[$key]; |
d9c8f425 |
2059 | $countcols += $table->headspan[$key] - 1; |
54a007e8 |
2060 | } |
2061 | |
d9c8f425 |
2062 | if ($key == $lastkey) { |
54a007e8 |
2063 | $heading->add_class('lastcol'); |
d9c8f425 |
2064 | } |
2065 | if (isset($table->colclasses[$key])) { |
54a007e8 |
2066 | $heading->add_class($table->colclasses[$key]); |
d9c8f425 |
2067 | } |
2068 | if ($table->rotateheaders) { |
2069 | // we need to wrap the heading content |
54a007e8 |
2070 | $heading->text = $this->output_tag('span', '', $heading->text); |
d9c8f425 |
2071 | } |
54a007e8 |
2072 | |
d9c8f425 |
2073 | $attributes = array( |
54a007e8 |
2074 | 'style' => $table->align[$key] . $table->size[$key] . $heading->style, |
2075 | 'class' => $heading->get_classes_string(), |
2076 | 'scope' => $heading->scope, |
2077 | 'colspan' => $heading->colspan); |
2078 | |
2079 | $output .= $this->output_tag('th', $attributes, $heading->text) . "\n"; |
d9c8f425 |
2080 | } |
2081 | $output .= $this->output_end_tag('tr') . "\n"; |
2082 | $output .= $this->output_end_tag('thead') . "\n"; |
2083 | } |
2084 | |
2085 | if (!empty($table->data)) { |
2086 | $oddeven = 1; |
2087 | $keys = array_keys($table->data); |
2088 | $lastrowkey = end($keys); |
319770d7 |
2089 | $output .= $this->output_start_tag('tbody', $table->bodyclasses) . "\n"; |
d9c8f425 |
2090 | |
2091 | foreach ($table->data as $key => $row) { |
2092 | if (($row === 'hr') && ($countcols)) { |
2093 | $output .= $this->output_tag('td', array('colspan' => $countcols), |
2094 | $this->output_tag('div', array('class' => 'tabledivider'), '')) . "\n"; |
2095 | } else { |
2096 | // Convert array rows to html_table_rows and cell strings to html_table_cell objects |
2097 | if (!($row instanceof html_table_row)) { |
2098 | $newrow = new html_table_row(); |
2099 | |
2100 | foreach ($row as $unused => $item) { |
2101 | $cell = new html_table_cell(); |
2102 | $cell->text = $item; |
54a007e8 |
2103 | $this->prepare_event_handlers($cell); |
d9c8f425 |
2104 | $newrow->cells[] = $cell; |
2105 | } |
2106 | $row = $newrow; |
2107 | } |
54a007e8 |
2108 | |
2109 | $this->prepare_event_handlers($row); |
d9c8f425 |
2110 | |
2111 | $oddeven = $oddeven ? 0 : 1; |
2112 | if (isset($table->rowclasses[$key])) { |
2113 | $row->add_classes(array_unique(moodle_html_component::clean_classes($table->rowclasses[$key]))); |
2114 | } |
2115 | |
2116 | $row->add_class('r' . $oddeven); |
2117 | if ($key == $lastrowkey) { |
2118 | $row->add_class('lastrow'); |
2119 | } |
2120 | |
2121 | $output .= $this->output_start_tag('tr', array('class' => $row->get_classes_string(), 'style' => $row->style, 'id' => $row->id)) . "\n"; |
2122 | $keys2 = array_keys($row->cells); |
2123 | $lastkey = end($keys2); |
2124 | |
2125 | foreach ($row->cells as $key => $cell) { |
54a007e8 |
2126 | if (!($cell instanceof html_table_cell)) { |
2127 | $mycell = new html_table_cell(); |
2128 | $mycell->text = $cell; |
2129 | $this->prepare_event_handlers($mycell); |
2130 | $cell = $mycell; |
2131 | } |
2132 | |
d9c8f425 |
2133 | if (isset($table->colclasses[$key])) { |
2134 | $cell->add_classes(array_unique(moodle_html_component::clean_classes($table->colclasses[$key]))); |
2135 | } |
2136 | |
2137 | $cell->add_classes('cell'); |
2138 | $cell->add_classes('c' . $key); |
2139 | if ($key == $lastkey) { |
2140 | $cell->add_classes('lastcol'); |
2141 | } |
2142 | $tdstyle = ''; |
2143 | $tdstyle .= isset($table->align[$key]) ? $table->align[$key] : ''; |
2144 | $tdstyle .= isset($table->size[$key]) ? $table->size[$key] : ''; |
2145 | $tdstyle .= isset($table->wrap[$key]) ? $table->wrap[$key] : ''; |
2146 | $tdattributes = array( |
2147 | 'style' => $tdstyle . $cell->style, |
2148 | 'colspan' => $cell->colspan, |
2149 | 'rowspan' => $cell->rowspan, |
2150 | 'id' => $cell->id, |
2151 | 'class' => $cell->get_classes_string(), |
2152 | 'abbr' => $cell->abbr, |
2153 | 'scope' => $cell->scope); |
1ae3767a |
2154 | $tagtype = 'td'; |
2155 | if ($cell->header) { |
2156 | $tagtype = 'th'; |
2157 | } |
2158 | $output .= $this->output_tag($tagtype, $tdattributes, $cell->text) . "\n"; |
d9c8f425 |
2159 | } |
2160 | } |
2161 | $output .= $this->output_end_tag('tr') . "\n"; |
2162 | } |
2163 | $output .= $this->output_end_tag('tbody') . "\n"; |
2164 | } |
2165 | $output .= $this->output_end_tag('table') . "\n"; |
2166 | |
2167 | if ($table->rotateheaders && can_use_rotated_text()) { |
2168 | $this->page->requires->yui_lib('event'); |
2169 | $this->page->requires->js('course/report/progress/textrotate.js'); |
2170 | } |
2171 | |
2172 | return $output; |
2173 | } |
2174 | |
2175 | /** |
2176 | * Output the place a skip link goes to. |
2177 | * @param string $id The target name from the corresponding $PAGE->requires->skip_link_to($target) call. |
2178 | * @return string the HTML to output. |
2179 | */ |
2180 | public function skip_link_target($id = '') { |
2181 | return $this->output_tag('span', array('id' => $id), ''); |
2182 | } |
2183 | |
2184 | /** |
2185 | * Outputs a heading |
2186 | * @param string $text The text of the heading |
2187 | * @param int $level The level of importance of the heading. Defaulting to 2 |
2188 | * @param string $classes A space-separated list of CSS classes |
2189 | * @param string $id An optional ID |
2190 | * @return string the HTML to output. |
2191 | */ |
2192 | public function heading($text, $level = 2, $classes = 'main', $id = '') { |
2193 | $level = (integer) $level; |
2194 | if ($level < 1 or $level > 6) { |
2195 | throw new coding_exception('Heading level must be an integer between 1 and 6.'); |
2196 | } |
2197 | return $this->output_tag('h' . $level, |
2198 | array('id' => $id, 'class' => moodle_renderer_base::prepare_classes($classes)), $text); |
2199 | } |
2200 | |
2201 | /** |
2202 | * Outputs a box. |
2203 | * @param string $contents The contents of the box |
2204 | * @param string $classes A space-separated list of CSS classes |
2205 | * @param string $id An optional ID |
2206 | * @return string the HTML to output. |
2207 | */ |
2208 | public function box($contents, $classes = 'generalbox', $id = '') { |
2209 | return $this->box_start($classes, $id) . $contents . $this->box_end(); |
2210 | } |
2211 | |
2212 | /** |
2213 | * Outputs the opening section of a box. |
2214 | * @param string $classes A space-separated list of CSS classes |
2215 | * @param string $id An optional ID |
2216 | * @return string the HTML to output. |
2217 | */ |
2218 | public function box_start($classes = 'generalbox', $id = '') { |
2219 | $this->opencontainers->push('box', $this->output_end_tag('div')); |
2220 | return $this->output_start_tag('div', array('id' => $id, |
2221 | 'class' => 'box ' . moodle_renderer_base::prepare_classes($classes))); |
2222 | } |
2223 | |
2224 | /** |
2225 | * Outputs the closing section of a box. |
2226 | * @return string the HTML to output. |
2227 | */ |
2228 | public function box_end() { |
2229 | return $this->opencontainers->pop('box'); |
2230 | } |
2231 | |
2232 | /** |
2233 | * Outputs a container. |
2234 | * @param string $contents The contents of the box |
2235 | * @param string $classes A space-separated list of CSS classes |
2236 | * @param string $id An optional ID |
2237 | * @return string the HTML to output. |
2238 | */ |
2239 | public function container($contents, $classes = '', $id = '') { |
2240 | return $this->container_start($classes, $id) . $contents . $this->container_end(); |
2241 | } |
2242 | |
2243 | /** |
2244 | * Outputs the opening section of a container. |
2245 | * @param string $classes A space-separated list of CSS classes |
2246 | * @param string $id An optional ID |
2247 | * @return string the HTML to output. |
2248 | */ |
2249 | public function container_start($classes = '', $id = '') { |
2250 | $this->opencontainers->push('container', $this->output_end_tag('div')); |
2251 | return $this->output_start_tag('div', array('id' => $id, |
2252 | 'class' => moodle_renderer_base::prepare_classes($classes))); |
2253 | } |
2254 | |
2255 | /** |
2256 | * Outputs the closing section of a container. |
2257 | * @return string the HTML to output. |
2258 | */ |
2259 | public function container_end() { |
2260 | return $this->opencontainers->pop('container'); |
2261 | } |
2262 | } |
2263 | |
2264 | |
2265 | /// RENDERERS |
2266 | |
2267 | /** |
2268 | * A renderer that generates output for command-line scripts. |
2269 | * |
2270 | * The implementation of this renderer is probably incomplete. |
2271 | * |
2272 | * @copyright 2009 Tim Hunt |
2273 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
2274 | * @since Moodle 2.0 |
2275 | */ |
2276 | class cli_core_renderer extends moodle_core_renderer { |
2277 | /** |
2278 | * Returns the page header. |
2279 | * @return string HTML fragment |
2280 | */ |
2281 | public function header() { |
2282 | output_starting_hook(); |
2283 | return $this->page->heading . "\n"; |
2284 | } |
2285 | |
2286 | /** |
2287 | * Returns a template fragment representing a Heading. |
2288 | * @param string $text The text of the heading |
2289 | * @param int $level The level of importance of the heading |
2290 | * @param string $classes A space-separated list of CSS classes |
2291 | * @param string $id An optional ID |
2292 | * @return string A template fragment for a heading |
2293 | */ |
2294 | public function heading($text, $level, $classes = 'main', $id = '') { |
2295 | $text .= "\n"; |
2296 | switch ($level) { |
2297 | case 1: |
2298 | return '=>' . $text; |
2299 | case 2: |
2300 | return '-->' . $text; |
2301 | default: |
2302 | return $text; |
2303 | } |
2304 | } |
2305 | |
2306 | /** |
2307 | * Returns a template fragment representing a fatal error. |
2308 | * @param string $message The message to output |
2309 | * @param string $moreinfourl URL where more info can be found about the error |
2310 | * @param string $link Link for the Continue button |
2311 | * @param array $backtrace The execution backtrace |
2312 | * @param string $debuginfo Debugging information |
2313 | * @param bool $showerrordebugwarning Whether or not to show a debugging warning |
2314 | * @return string A template fragment for a fatal error |
2315 | */ |
2316 | public function fatal_error($message, $moreinfourl, $link, $backtrace, |
2317 | $debuginfo = null, $showerrordebugwarning = false) { |
2318 | $output = "!!! $message !!!\n"; |
2319 | |
2320 | if (debugging('', DEBUG_DEVELOPER)) { |
2321 | if (!empty($debuginfo)) { |
2322 | $this->notification($debuginfo, 'notifytiny'); |
2323 | } |
2324 | if (!empty($backtrace)) { |
2325 | $this->notification('Stack trace: ' . format_backtrace($backtrace, true), 'notifytiny'); |
2326 | } |
2327 | } |
2328 | } |
2329 | |
2330 | /** |
2331 | * Returns a template fragment representing a notification. |
2332 | * @param string $message The message to include |
2333 | * @param string $classes A space-separated list of CSS classes |
2334 | * @return string A template fragment for a notification |
2335 | */ |
2336 | public function notification($message, $classes = 'notifyproblem') { |
2337 | $message = clean_text($message); |
2338 | if ($classes === 'notifysuccess') { |
2339 | return "++ $message ++\n"; |
2340 | } |
2341 | return "!! $message !!\n"; |
2342 | } |
2343 | } |
2344 | |